From e48f1e2e01162714d606aad8a0ea9b031997a757 Mon Sep 17 00:00:00 2001 From: Timo Welde Date: Thu, 3 Dec 2020 17:36:52 +0100 Subject: [PATCH] feat: export Blockchain utils (#315) --- docs/example.ts | 12 +- src/__integrationtests__/Attestation.spec.ts | 31 ++-- .../AttestationPE.spec.ts | 5 +- src/__integrationtests__/Balance.spec.ts | 23 +-- src/__integrationtests__/Ctypes.spec.ts | 14 +- src/__integrationtests__/Delegation.spec.ts | 25 ++-- src/__integrationtests__/ErrorHandler.spec.ts | 6 +- src/actor/Attester.ts | 4 +- src/balance/Balance.spec.ts | 6 +- src/blockchain/Blockchain.spec.ts | 5 +- src/blockchain/Blockchain.ts | 126 +--------------- src/blockchain/Blockchain.utils.ts | 134 ++++++++++++++++++ src/blockchain/index.ts | 1 + src/ctype/CType.spec.ts | 4 +- src/delegation/DelegationNode.spec.ts | 4 +- src/delegation/DelegationRootNode.spec.ts | 6 +- src/did/Did.spec.ts | 4 +- src/identity/AttesterIdentity.ts | 4 +- src/index.ts | 3 +- 19 files changed, 218 insertions(+), 199 deletions(-) create mode 100644 src/blockchain/Blockchain.utils.ts diff --git a/docs/example.ts b/docs/example.ts index d87a1af48..567ef9f44 100644 --- a/docs/example.ts +++ b/docs/example.ts @@ -10,7 +10,7 @@ import Kilt, { IRevocationHandle, PublicAttesterIdentity, } from '../src' -import Blockchain, { IS_IN_BLOCK } from '../src/blockchain/Blockchain' +import { BlockchainUtils } from '../src/blockchain' import constants from '../src/test/constants' const NODE_URL = 'ws://127.0.0.1:9944' @@ -82,11 +82,11 @@ async function setup(): Promise<{ // ! This costs tokens ! // Also note, that the completely same ctype can only be stored once on the blockchain. try { - await ctype - .store(attester) - .then((tx) => - Blockchain.submitTxWithReSign(tx, attester, { resolveOn: IS_IN_BLOCK }) - ) + await ctype.store(attester).then((tx) => + BlockchainUtils.submitTxWithReSign(tx, attester, { + resolveOn: BlockchainUtils.IS_IN_BLOCK, + }) + ) } catch (e) { console.log( 'Error while storing CType. Probably either insufficient funds or ctype does already exist.', diff --git a/src/__integrationtests__/Attestation.spec.ts b/src/__integrationtests__/Attestation.spec.ts index 45ff1c32f..30094c241 100644 --- a/src/__integrationtests__/Attestation.spec.ts +++ b/src/__integrationtests__/Attestation.spec.ts @@ -8,11 +8,12 @@ import { IAttestedClaim, IClaim } from '..' import Attestation from '../attestation/Attestation' import { revoke } from '../attestation/Attestation.chain' import AttestedClaim from '../attestedclaim/AttestedClaim' -import Blockchain, { +import { IBlockchainApi } from '../blockchain/Blockchain' +import { IS_IN_BLOCK, IS_READY, - IBlockchainApi, -} from '../blockchain/Blockchain' + submitTxWithReSign, +} from '../blockchain/Blockchain.utils' import getCached, { DEFAULT_WS_ADDRESS } from '../blockchainApiConnection' import Claim from '../claim/Claim' import Credential from '../credential/Credential' @@ -47,7 +48,7 @@ describe('handling attestations that do not exist', () => { it('Attestation.revoke', async () => { return expect( Attestation.revoke('0x012012012', alice).then((tx) => - Blockchain.submitTxWithReSign(tx, alice, { resolveOn: IS_IN_BLOCK }) + submitTxWithReSign(tx, alice, { resolveOn: IS_IN_BLOCK }) ) ).rejects.toThrow() }, 30_000) @@ -68,7 +69,7 @@ describe('When there is an attester, claimer and ctype drivers license', () => { // console.log(`verify stored: ${await DriversLicense.verifyStored()}`) if (!ctypeExists) { await DriversLicense.store(attester).then((tx) => - Blockchain.submitTxWithReSign(tx, attester, { resolveOn: IS_READY }) + submitTxWithReSign(tx, attester, { resolveOn: IS_READY }) ) } }, 60_000) @@ -107,7 +108,7 @@ describe('When there is an attester, claimer and ctype drivers license', () => { await attestation .store(attester) .then((tx) => - Blockchain.submitTxWithReSign(tx, attester, { resolveOn: IS_IN_BLOCK }) + submitTxWithReSign(tx, attester, { resolveOn: IS_IN_BLOCK }) ) const cred = await Credential.fromRequestAndAttestation( claimer, @@ -143,7 +144,7 @@ describe('When there is an attester, claimer and ctype drivers license', () => { await expect( attestation.store(bobbyBroke).then((tx) => - Blockchain.submitTxWithReSign(tx, bobbyBroke, { + submitTxWithReSign(tx, bobbyBroke, { resolveOn: IS_IN_BLOCK, }) ) @@ -189,7 +190,7 @@ describe('When there is an attester, claimer and ctype drivers license', () => { ) await expect( attestation.store(attester).then((tx) => - Blockchain.submitTxWithReSign(tx, attester, { + submitTxWithReSign(tx, attester, { resolveOn: IS_IN_BLOCK, }) ) @@ -214,7 +215,7 @@ describe('When there is an attester, claimer and ctype drivers license', () => { attester.getPublicIdentity() ) await attestation.store(attester).then((tx) => - Blockchain.submitTxWithReSign(tx, attester, { + submitTxWithReSign(tx, attester, { resolveOn: IS_IN_BLOCK, }) ) @@ -230,7 +231,7 @@ describe('When there is an attester, claimer and ctype drivers license', () => { it('should not be possible to attest the same claim twice', async () => { await expect( attClaim.attestation.store(attester).then((tx) => - Blockchain.submitTxWithReSign(tx, attester, { + submitTxWithReSign(tx, attester, { resolveOn: IS_IN_BLOCK, }) ) @@ -258,7 +259,7 @@ describe('When there is an attester, claimer and ctype drivers license', () => { it('should not be possible for the claimer to revoke an attestation', async () => { await expect( revoke(attClaim.getHash(), claimer).then((tx) => - Blockchain.submitTxWithReSign(tx, claimer, { resolveOn: IS_IN_BLOCK }) + submitTxWithReSign(tx, claimer, { resolveOn: IS_IN_BLOCK }) ) ).rejects.toThrowError('not permitted') await expect(attClaim.verify()).resolves.toBeTruthy() @@ -267,7 +268,7 @@ describe('When there is an attester, claimer and ctype drivers license', () => { it('should be possible for the attester to revoke an attestation', async () => { await expect(attClaim.verify()).resolves.toBeTruthy() await revoke(attClaim.getHash(), attester).then((tx) => - Blockchain.submitTxWithReSign(tx, attester, { resolveOn: IS_IN_BLOCK }) + submitTxWithReSign(tx, attester, { resolveOn: IS_IN_BLOCK }) ) await expect(attClaim.verify()).resolves.toBeFalsy() }, 40_000) @@ -277,7 +278,7 @@ describe('When there is an attester, claimer and ctype drivers license', () => { beforeAll(async () => { if (!(await CtypeOnChain(IsOfficialLicenseAuthority))) { await IsOfficialLicenseAuthority.store(faucet).then((tx) => - Blockchain.submitTxWithReSign(tx, faucet, { resolveOn: IS_IN_BLOCK }) + submitTxWithReSign(tx, faucet, { resolveOn: IS_IN_BLOCK }) ) } await expect( @@ -308,7 +309,7 @@ describe('When there is an attester, claimer and ctype drivers license', () => { await licenseAuthorizationGranted .store(faucet) .then((tx) => - Blockchain.submitTxWithReSign(tx, faucet, { resolveOn: IS_IN_BLOCK }) + submitTxWithReSign(tx, faucet, { resolveOn: IS_IN_BLOCK }) ) // make request including legitimation const iBelieveICanDrive = Claim.fromCTypeAndClaimContents( @@ -336,7 +337,7 @@ describe('When there is an attester, claimer and ctype drivers license', () => { attester.getPublicIdentity() ) await LicenseGranted.store(attester).then((tx) => - Blockchain.submitTxWithReSign(tx, attester, { resolveOn: IS_IN_BLOCK }) + submitTxWithReSign(tx, attester, { resolveOn: IS_IN_BLOCK }) ) const license = await Credential.fromRequestAndAttestation( claimer, diff --git a/src/__integrationtests__/AttestationPE.spec.ts b/src/__integrationtests__/AttestationPE.spec.ts index d88a87bbf..9c15187dd 100644 --- a/src/__integrationtests__/AttestationPE.spec.ts +++ b/src/__integrationtests__/AttestationPE.spec.ts @@ -11,8 +11,7 @@ import { import { Claim, IBlockchainApi, IClaim, Message } from '..' import { Attester, Claimer, Verifier } from '../actor' import { ClaimerAttestationSession } from '../actor/Claimer' -import Blockchain from '../blockchain' -import { IS_IN_BLOCK } from '../blockchain/Blockchain' +import { IS_IN_BLOCK, submitTxWithReSign } from '../blockchain/Blockchain.utils' import getCached, { DEFAULT_WS_ADDRESS } from '../blockchainApiConnection' import Credential from '../credential' import Identity, { AttesterIdentity } from '../identity' @@ -54,7 +53,7 @@ describe('Privacy enhanced claim, attestation, verification process', () => { // set up claim (ctype missing on fresh chain) if (!(await CtypeOnChain(DriversLicense))) { await DriversLicense.store(attester).then((tx) => - Blockchain.submitTxWithReSign(tx, attester, { resolveOn: IS_IN_BLOCK }) + submitTxWithReSign(tx, attester, { resolveOn: IS_IN_BLOCK }) ) } claim = Claim.fromCTypeAndClaimContents( diff --git a/src/__integrationtests__/Balance.spec.ts b/src/__integrationtests__/Balance.spec.ts index 37529c966..2be7e830c 100644 --- a/src/__integrationtests__/Balance.spec.ts +++ b/src/__integrationtests__/Balance.spec.ts @@ -10,11 +10,12 @@ import { listenToBalanceChanges, makeTransfer, } from '../balance/Balance.chain' -import Blockchain, { +import { IBlockchainApi } from '../blockchain/Blockchain' +import { IS_IN_BLOCK, IS_READY, - IBlockchainApi, -} from '../blockchain/Blockchain' + submitTxWithReSign, +} from '../blockchain/Blockchain.utils' import getCached, { DEFAULT_WS_ADDRESS } from '../blockchainApiConnection' import Identity from '../identity/Identity' import { @@ -70,7 +71,7 @@ describe('when there is a dev chain with a faucet', () => { listenToBalanceChanges(ident.address, funny) const balanceBefore = await getBalance(faucet.address) await makeTransfer(faucet, ident.address, MIN_TRANSACTION).then((tx) => - Blockchain.submitTxWithReSign(tx, faucet, { resolveOn: IS_IN_BLOCK }) + submitTxWithReSign(tx, faucet, { resolveOn: IS_IN_BLOCK }) ) const [balanceAfter, balanceIdent] = await Promise.all([ getBalance(faucet.address), @@ -101,7 +102,7 @@ describe('When there are haves and have-nots', () => { stormyD.address, MIN_TRANSACTION ).then((tx) => - Blockchain.submitTxWithReSign(tx, richieRich, { resolveOn: IS_IN_BLOCK }) + submitTxWithReSign(tx, richieRich, { resolveOn: IS_IN_BLOCK }) ) const balanceTo = await getBalance(stormyD.address) expect(balanceTo.toNumber()).toBe(MIN_TRANSACTION.toNumber()) @@ -111,7 +112,7 @@ describe('When there are haves and have-nots', () => { const originalBalance = await getBalance(stormyD.address) await expect( makeTransfer(bobbyBroke, stormyD.address, MIN_TRANSACTION).then((tx) => - Blockchain.submitTxWithReSign(tx, bobbyBroke, { + submitTxWithReSign(tx, bobbyBroke, { resolveOn: IS_IN_BLOCK, }) ) @@ -128,7 +129,7 @@ describe('When there are haves and have-nots', () => { const RichieBalance = await getBalance(richieRich.address) await expect( makeTransfer(richieRich, bobbyBroke.address, RichieBalance).then((tx) => - Blockchain.submitTxWithReSign(tx, richieRich, { + submitTxWithReSign(tx, richieRich, { resolveOn: IS_IN_BLOCK, }) ) @@ -145,10 +146,10 @@ describe('When there are haves and have-nots', () => { const listener = jest.fn() listenToBalanceChanges(faucet.address, listener) await makeTransfer(faucet, richieRich.address, MIN_TRANSACTION).then((tx) => - Blockchain.submitTxWithReSign(tx, faucet, { resolveOn: IS_READY }) + submitTxWithReSign(tx, faucet, { resolveOn: IS_READY }) ) await makeTransfer(faucet, stormyD.address, MIN_TRANSACTION).then((tx) => - Blockchain.submitTxWithReSign(tx, faucet, { resolveOn: IS_IN_BLOCK }) + submitTxWithReSign(tx, faucet, { resolveOn: IS_IN_BLOCK }) ) expect(listener).toBeCalledWith( @@ -164,10 +165,10 @@ describe('When there are haves and have-nots', () => { listenToBalanceChanges(faucet.address, listener) await Promise.all([ makeTransfer(faucet, richieRich.address, MIN_TRANSACTION).then((tx) => - Blockchain.submitTxWithReSign(tx, faucet, { resolveOn: IS_IN_BLOCK }) + submitTxWithReSign(tx, faucet, { resolveOn: IS_IN_BLOCK }) ), makeTransfer(faucet, stormyD.address, MIN_TRANSACTION).then((tx) => - Blockchain.submitTxWithReSign(tx, faucet, { resolveOn: IS_IN_BLOCK }) + submitTxWithReSign(tx, faucet, { resolveOn: IS_IN_BLOCK }) ), ]) expect(listener).toBeCalledWith( diff --git a/src/__integrationtests__/Ctypes.spec.ts b/src/__integrationtests__/Ctypes.spec.ts index 3b1641b1e..31708710b 100644 --- a/src/__integrationtests__/Ctypes.spec.ts +++ b/src/__integrationtests__/Ctypes.spec.ts @@ -5,10 +5,8 @@ */ import { Identity } from '..' -import Blockchain, { - IS_IN_BLOCK, - IBlockchainApi, -} from '../blockchain/Blockchain' +import { IBlockchainApi } from '../blockchain/Blockchain' +import { IS_IN_BLOCK, submitTxWithReSign } from '../blockchain/Blockchain.utils' import getCached, { DEFAULT_WS_ADDRESS } from '../blockchainApiConnection' import CType from '../ctype/CType' import { getOwner } from '../ctype/CType.chain' @@ -49,7 +47,7 @@ describe('When there is an CtypeCreator and a verifier', () => { ) await expect( ctype.store(bobbyBroke).then((tx) => - Blockchain.submitTxWithReSign(tx, bobbyBroke, { + submitTxWithReSign(tx, bobbyBroke, { resolveOn: IS_IN_BLOCK, }) ) @@ -60,7 +58,7 @@ describe('When there is an CtypeCreator and a verifier', () => { it('should be possible to create a claim type', async () => { const ctype = makeCType() await ctype.store(ctypeCreator).then((tx) => - Blockchain.submitTxWithReSign(tx, ctypeCreator, { + submitTxWithReSign(tx, ctypeCreator, { resolveOn: IS_IN_BLOCK, }) ) @@ -75,13 +73,13 @@ describe('When there is an CtypeCreator and a verifier', () => { it('should not be possible to create a claim type that exists', async () => { const ctype = makeCType() await ctype.store(ctypeCreator).then((tx) => - Blockchain.submitTxWithReSign(tx, ctypeCreator, { + submitTxWithReSign(tx, ctypeCreator, { resolveOn: IS_IN_BLOCK, }) ) await expect( ctype.store(ctypeCreator).then((tx) => - Blockchain.submitTxWithReSign(tx, ctypeCreator, { + submitTxWithReSign(tx, ctypeCreator, { resolveOn: IS_IN_BLOCK, }) ) diff --git a/src/__integrationtests__/Delegation.spec.ts b/src/__integrationtests__/Delegation.spec.ts index b95888278..ceb4d9f31 100644 --- a/src/__integrationtests__/Delegation.spec.ts +++ b/src/__integrationtests__/Delegation.spec.ts @@ -7,11 +7,12 @@ import { cryptoWaitReady } from '@polkadot/util-crypto' import { Identity } from '..' import Attestation from '../attestation/Attestation' -import Blockchain, { +import { IBlockchainApi } from '../blockchain/Blockchain' +import { IS_IN_BLOCK, IS_READY, - IBlockchainApi, -} from '../blockchain/Blockchain' + submitTxWithReSign, +} from '../blockchain/Blockchain.utils' import getCached, { DEFAULT_WS_ADDRESS } from '../blockchainApiConnection' import Claim from '../claim/Claim' import Credential from '../credential/Credential' @@ -52,7 +53,7 @@ describe('when there is an account hierarchy', () => { if (!(await CtypeOnChain(DriversLicense))) { await DriversLicense.store(attester).then((tx) => - Blockchain.submitTxWithReSign(tx, attester, { resolveOn: IS_READY }) + submitTxWithReSign(tx, attester, { resolveOn: IS_READY }) ) } }, 30_000) @@ -65,9 +66,7 @@ describe('when there is an account hierarchy', () => { ) await rootNode .store(uncleSam) - .then((tx) => - Blockchain.submitTxWithReSign(tx, uncleSam, { resolveOn: IS_READY }) - ) + .then((tx) => submitTxWithReSign(tx, uncleSam, { resolveOn: IS_READY })) const delegatedNode = new DelegationNode( UUID.generate(), rootNode.id, @@ -79,7 +78,7 @@ describe('when there is an account hierarchy', () => { await delegatedNode .store(uncleSam, HashSignedByDelegate) .then((tx) => - Blockchain.submitTxWithReSign(tx, uncleSam, { resolveOn: IS_IN_BLOCK }) + submitTxWithReSign(tx, uncleSam, { resolveOn: IS_IN_BLOCK }) ) await Promise.all([ expect(rootNode.verify()).resolves.toBeTruthy(), @@ -108,11 +107,9 @@ describe('when there is an account hierarchy', () => { HashSignedByDelegate = attester.signStr(delegatedNode.generateHash()) await rootNode .store(uncleSam) - .then((tx) => - Blockchain.submitTxWithReSign(tx, uncleSam, { resolveOn: IS_READY }) - ) + .then((tx) => submitTxWithReSign(tx, uncleSam, { resolveOn: IS_READY })) await delegatedNode.store(uncleSam, HashSignedByDelegate).then((tx) => - Blockchain.submitTxWithReSign(tx, uncleSam, { + submitTxWithReSign(tx, uncleSam, { resolveOn: IS_IN_BLOCK, }) ) @@ -145,7 +142,7 @@ describe('when there is an account hierarchy', () => { attester.getPublicIdentity() ) await attestation.store(attester).then((tx) => - Blockchain.submitTxWithReSign(tx, attester, { + submitTxWithReSign(tx, attester, { resolveOn: IS_IN_BLOCK, }) ) @@ -160,7 +157,7 @@ describe('when there is an account hierarchy', () => { // revoke attestation through root await attClaim.attestation.revoke(uncleSam).then((tx) => - Blockchain.submitTxWithReSign(tx, uncleSam, { + submitTxWithReSign(tx, uncleSam, { resolveOn: IS_IN_BLOCK, }) ) diff --git a/src/__integrationtests__/ErrorHandler.spec.ts b/src/__integrationtests__/ErrorHandler.spec.ts index c2100f184..453888d6a 100644 --- a/src/__integrationtests__/ErrorHandler.spec.ts +++ b/src/__integrationtests__/ErrorHandler.spec.ts @@ -7,7 +7,7 @@ import BN from 'bn.js' import { Attestation, IBlockchainApi } from '..' import { makeTransfer } from '../balance/Balance.chain' -import Blockchain, { IS_IN_BLOCK } from '../blockchain/Blockchain' +import { IS_IN_BLOCK, submitTxWithReSign } from '../blockchain/Blockchain.utils' import { DEFAULT_WS_ADDRESS, getCached } from '../blockchainApiConnection' import { ERROR_CTYPE_NOT_FOUND, ERROR_UNKNOWN } from '../errorhandling' import Identity from '../identity' @@ -24,7 +24,7 @@ it('records an unknown extrinsic error when transferring less than the existenti const to = await Identity.buildFromMnemonic('') await expect( makeTransfer(alice, to.address, new BN(1)).then((tx) => - Blockchain.submitTxWithReSign(tx, alice, { resolveOn: IS_IN_BLOCK }) + submitTxWithReSign(tx, alice, { resolveOn: IS_IN_BLOCK }) ) ).rejects.toThrow(ERROR_UNKNOWN) }, 30_000) @@ -41,7 +41,7 @@ it('records an extrinsic error when ctype does not exist', async () => { }) const tx = await attestation.store(alice) await expect( - Blockchain.submitTxWithReSign(tx, alice, { resolveOn: IS_IN_BLOCK }) + submitTxWithReSign(tx, alice, { resolveOn: IS_IN_BLOCK }) ).rejects.toThrow(ERROR_CTYPE_NOT_FOUND) }, 30_000) diff --git a/src/actor/Attester.ts b/src/actor/Attester.ts index 588d2e018..9c85c7917 100644 --- a/src/actor/Attester.ts +++ b/src/actor/Attester.ts @@ -1,5 +1,5 @@ import * as gabi from '@kiltprotocol/portablegabi' -import Blockchain from '../blockchain/Blockchain' +import { BlockchainUtils } from '../blockchain' import Attestation from '../attestation/Attestation' import { getCached } from '../blockchainApiConnection' import { @@ -92,7 +92,7 @@ export async function issueAttestation( } await attestation .store(attester) - .then((tx) => Blockchain.submitTxWithReSign(tx, attester)) + .then((tx) => BlockchainUtils.submitTxWithReSign(tx, attester)) const revocationHandle: IRevocationHandle = { witness, diff --git a/src/balance/Balance.spec.ts b/src/balance/Balance.spec.ts index c789f0926..f875a3364 100644 --- a/src/balance/Balance.spec.ts +++ b/src/balance/Balance.spec.ts @@ -9,7 +9,7 @@ import { makeTransfer, } from './Balance.chain' import TYPE_REGISTRY from '../blockchainApiConnection/__mocks__/BlockchainQuery' -import Blockchain from '../blockchain' +import { BlockchainUtils } from '../blockchain' import BalanceUtils from './Balance.utils' jest.mock('../blockchainApiConnection/BlockchainApiConnection') @@ -68,7 +68,7 @@ describe('Balance', () => { alice, bob.address, new BN(100) - ).then((tx) => Blockchain.submitTxWithReSign(tx, alice)) + ).then((tx) => BlockchainUtils.submitTxWithReSign(tx, alice)) expect(status).toBeInstanceOf(SubmittableResult) expect(status.isFinalized).toBeTruthy() }) @@ -84,7 +84,7 @@ describe('Balance', () => { bob.address, amount, exponent - ).then((tx) => Blockchain.submitTxWithReSign(tx, alice)) + ).then((tx) => BlockchainUtils.submitTxWithReSign(tx, alice)) expect(blockchainApi.tx.balances.transfer).toHaveBeenCalledWith( bob.address, expectedAmount diff --git a/src/blockchain/Blockchain.spec.ts b/src/blockchain/Blockchain.spec.ts index 0f241a68a..4f6357594 100644 --- a/src/blockchain/Blockchain.spec.ts +++ b/src/blockchain/Blockchain.spec.ts @@ -9,7 +9,8 @@ import getCached from '../blockchainApiConnection/BlockchainApiConnection' import TYPE_REGISTRY from '../blockchainApiConnection/__mocks__/BlockchainQuery' import { ERROR_TRANSACTION_RECOVERABLE } from '../errorhandling/SDKErrors' import Identity from '../identity/Identity' -import Blockchain, { +import Blockchain from './Blockchain' +import { EXTRINSIC_FAILED, IS_ERROR, IS_FINALIZED, @@ -17,7 +18,7 @@ import Blockchain, { parseSubscriptionOptions, ResultEvaluator, submitSignedTx, -} from './Blockchain' +} from './Blockchain.utils' jest.mock('../blockchainApiConnection/BlockchainApiConnection') diff --git a/src/blockchain/Blockchain.ts b/src/blockchain/Blockchain.ts index d355a3cd0..661053089 100644 --- a/src/blockchain/Blockchain.ts +++ b/src/blockchain/Blockchain.ts @@ -16,20 +16,14 @@ import { AnyJson, Codec } from '@polkadot/types/types' import { Text } from '@polkadot/types' import { SignerPayloadJSON } from '@polkadot/types/types/extrinsic' import BN from 'bn.js' -import { - ERROR_TRANSACTION_RECOVERABLE, - ERROR_TRANSACTION_USURPED, -} from '../errorhandling/SDKErrors' -import { - Evaluator, - makeSubscriptionPromise, - TerminationOptions, -} from '../util/SubscriptionPromise' +import { ERROR_TRANSACTION_RECOVERABLE } from '../errorhandling/SDKErrors' import { factory as LoggerFactory } from '../config/ConfigLog' -import { ErrorHandler } from '../errorhandling' -import { ERROR_UNKNOWN as UNKNOWN_EXTRINSIC_ERROR } from '../errorhandling/ExtrinsicError' import Identity from '../identity/Identity' -import getCached from '../blockchainApiConnection' +import { + parseSubscriptionOptions, + submitSignedTx, + SubscriptionPromiseOptions, +} from './Blockchain.utils' const log = LoggerFactory.getLogger('Blockchain') @@ -39,9 +33,6 @@ export type Stats = { nodeVersion: string } -export type ResultEvaluator = Evaluator -export type ErrorEvaluator = Evaluator -export type SubscriptionPromiseOptions = TerminationOptions export interface IBlockchainApi { api: ApiPromise portablegabi: gabi.Blockchain @@ -68,93 +59,6 @@ export interface IBlockchainApi { tx: SubmittableExtrinsic ): Promise } -const TxOutdated = '1010: Invalid Transaction: Transaction is outdated' -const TxPriority = '1014: Priority is too low:' -const TxAlreadyImported = 'Transaction Already' - -export const IS_RELEVANT_ERROR: ErrorEvaluator = (err: Error) => { - return new RegExp( - `${TxAlreadyImported}|${TxOutdated}|${TxPriority}`, - 'g' - ).test(err.message) -} -export const IS_READY: ResultEvaluator = (result) => result.status.isReady -export const IS_IN_BLOCK: ResultEvaluator = (result) => result.isInBlock -export const EXTRINSIC_EXECUTED: ResultEvaluator = (result) => - ErrorHandler.extrinsicSuccessful(result) -export const IS_FINALIZED: ResultEvaluator = (result) => result.isFinalized -export const IS_USURPED: ResultEvaluator = (result) => - result.status.isUsurped && ERROR_TRANSACTION_USURPED() -export const IS_ERROR: ResultEvaluator = (result) => { - return ( - (result.status.isDropped && Error('isDropped')) || - (result.status.isInvalid && Error('isInvalid')) || - (result.status.isFinalityTimeout && Error('isFinalityTimeout')) - ) -} -export const EXTRINSIC_FAILED: ResultEvaluator = (result) => - ErrorHandler.extrinsicFailed(result) && - (ErrorHandler.getExtrinsicError(result) || UNKNOWN_EXTRINSIC_ERROR) - -/** - * Parses potentially incomplete or undefined options and returns complete [[SubscriptionPromiseOptions]]. - * - * @param opts Potentially undefined or partial [[SubscriptionPromiseOptions]] . - * @returns Complete [[SubscriptionPromiseOptions]], with potentially defaulted values. - */ -export function parseSubscriptionOptions( - opts?: Partial -): SubscriptionPromiseOptions { - const defaults = { - resolveOn: IS_FINALIZED, - rejectOn: (result: SubmittableResult) => - IS_ERROR(result) || EXTRINSIC_FAILED(result) || IS_USURPED(result), - } - return { - ...defaults, - ...opts, - } -} - -/** - * [ASYNC] Submits a signed [[SubmittableExtrinsic]] and attaches a callback to monitor the inclusion status of the transaction - * and possible errors in the execution of extrinsics. Returns a promise to that end which by default resolves upon - * finalization and rejects any errors occur during submission or execution of extrinsics. This behavior can be adjusted via optional parameters. - * - * Transaction fees will apply whenever a transaction fee makes it into a block, even if extrinsics fail to execute correctly! - * - * @param tx The [[SubmittableExtrinsic]] to be submitted. Most transactions need to be signed, this must be done beforehand. - * @param opts [[SubscriptionPromiseOptions]]: Criteria for resolving/rejecting the promise. - * @returns A promise which can be used to track transaction status. - * If resolved, this promise returns [[SubmittableResult]] that has led to its resolution. - */ -export async function submitSignedTx( - tx: SubmittableExtrinsic, - opts: SubscriptionPromiseOptions -): Promise { - log.info(`Submitting ${tx.method}`) - const { promise, subscription } = makeSubscriptionPromise(opts) - - const unsubscribe = await tx - .send(subscription) - .catch(async (reason: Error) => { - if (IS_RELEVANT_ERROR(reason)) { - return Promise.reject(ERROR_TRANSACTION_RECOVERABLE()) - } - return Promise.reject(reason) - }) - - const result = await promise - .catch(async (reason: Error) => { - if (reason.message === ERROR_TRANSACTION_USURPED().message) { - return Promise.reject(ERROR_TRANSACTION_RECOVERABLE()) - } - return Promise.reject(reason) - }) - .finally(() => unsubscribe()) - - return result -} // Code taken from // https://polkadot.js.org/api/api/classes/_promise_index_.apipromise.html @@ -166,24 +70,6 @@ export default class Blockchain implements IBlockchainApi { return [] } - /** - * [STATIC] [ASYNC] Reroute of class method. - * - * @param tx The [[SubmittableExtrinsic]] to be submitted. Most transactions need to be signed, this must be done beforehand. - * @param identity The [[Identity]] to re-sign the tx on recoverable error. - * @param opts Optional partial criteria for resolving/rejecting the promise. - * @returns A promise which can be used to track transaction status. - * If resolved, this promise returns [[SubmittableResult]] that has led to its resolution. - */ - public static async submitTxWithReSign( - tx: SubmittableExtrinsic, - identity?: Identity, - opts?: Partial - ): Promise { - const chain = await getCached() - return chain.submitTxWithReSign(tx, identity, opts) - } - public api: ApiPromise public readonly portablegabi: gabi.Blockchain private accountNonces: Map diff --git a/src/blockchain/Blockchain.utils.ts b/src/blockchain/Blockchain.utils.ts new file mode 100644 index 000000000..2fefee14b --- /dev/null +++ b/src/blockchain/Blockchain.utils.ts @@ -0,0 +1,134 @@ +/** + * @packageDocumentation + * @module BlockchchainUtils + * @preferred + */ + +import { SubmittableResult } from '@polkadot/api' +import { SubmittableExtrinsic } from '@polkadot/api/promise/types' +import { + ERROR_TRANSACTION_RECOVERABLE, + ERROR_TRANSACTION_USURPED, +} from '../errorhandling/SDKErrors' +import { + Evaluator, + makeSubscriptionPromise, + TerminationOptions, +} from '../util/SubscriptionPromise' +import { ErrorHandler } from '../errorhandling' +import { ERROR_UNKNOWN as UNKNOWN_EXTRINSIC_ERROR } from '../errorhandling/ExtrinsicError' +import { factory as LoggerFactory } from '../config/ConfigLog' +import Identity from '../identity/Identity' +import getCached from '../blockchainApiConnection' + +export type ResultEvaluator = Evaluator +export type ErrorEvaluator = Evaluator +export type SubscriptionPromiseOptions = TerminationOptions + +const log = LoggerFactory.getLogger('Blockchain') + +const TxOutdated = '1010: Invalid Transaction: Transaction is outdated' +const TxPriority = '1014: Priority is too low:' +const TxAlreadyImported = 'Transaction Already' + +export const IS_RELEVANT_ERROR: ErrorEvaluator = (err: Error) => { + return new RegExp( + `${TxAlreadyImported}|${TxOutdated}|${TxPriority}`, + 'g' + ).test(err.message) +} +export const IS_READY: ResultEvaluator = (result) => result.status.isReady +export const IS_IN_BLOCK: ResultEvaluator = (result) => result.isInBlock +export const EXTRINSIC_EXECUTED: ResultEvaluator = (result) => + ErrorHandler.extrinsicSuccessful(result) +export const IS_FINALIZED: ResultEvaluator = (result) => result.isFinalized +export const IS_USURPED: ResultEvaluator = (result) => + result.status.isUsurped && ERROR_TRANSACTION_USURPED() +export const IS_ERROR: ResultEvaluator = (result) => { + return ( + (result.status.isDropped && Error('isDropped')) || + (result.status.isInvalid && Error('isInvalid')) || + (result.status.isFinalityTimeout && Error('isFinalityTimeout')) + ) +} +export const EXTRINSIC_FAILED: ResultEvaluator = (result) => + ErrorHandler.extrinsicFailed(result) && + (ErrorHandler.getExtrinsicError(result) || UNKNOWN_EXTRINSIC_ERROR) + +/** + * Parses potentially incomplete or undefined options and returns complete [[SubscriptionPromiseOptions]]. + * + * @param opts Potentially undefined or partial [[SubscriptionPromiseOptions]] . + * @returns Complete [[SubscriptionPromiseOptions]], with potentially defaulted values. + */ +export function parseSubscriptionOptions( + opts?: Partial +): SubscriptionPromiseOptions { + const defaults = { + resolveOn: IS_FINALIZED, + rejectOn: (result: SubmittableResult) => + IS_ERROR(result) || EXTRINSIC_FAILED(result) || IS_USURPED(result), + } + return { + ...defaults, + ...opts, + } +} + +/** + * [ASYNC] Submits a signed [[SubmittableExtrinsic]] and attaches a callback to monitor the inclusion status of the transaction + * and possible errors in the execution of extrinsics. Returns a promise to that end which by default resolves upon + * finalization and rejects any errors occur during submission or execution of extrinsics. This behavior can be adjusted via optional parameters. + * + * Transaction fees will apply whenever a transaction fee makes it into a block, even if extrinsics fail to execute correctly! + * + * @param tx The [[SubmittableExtrinsic]] to be submitted. Most transactions need to be signed, this must be done beforehand. + * @param opts [[SubscriptionPromiseOptions]]: Criteria for resolving/rejecting the promise. + * @returns A promise which can be used to track transaction status. + * If resolved, this promise returns [[SubmittableResult]] that has led to its resolution. + */ +export async function submitSignedTx( + tx: SubmittableExtrinsic, + opts: SubscriptionPromiseOptions +): Promise { + log.info(`Submitting ${tx.method}`) + const { promise, subscription } = makeSubscriptionPromise(opts) + + const unsubscribe = await tx + .send(subscription) + .catch(async (reason: Error) => { + if (IS_RELEVANT_ERROR(reason)) { + return Promise.reject(ERROR_TRANSACTION_RECOVERABLE()) + } + return Promise.reject(reason) + }) + + const result = await promise + .catch(async (reason: Error) => { + if (reason.message === ERROR_TRANSACTION_USURPED().message) { + return Promise.reject(ERROR_TRANSACTION_RECOVERABLE()) + } + return Promise.reject(reason) + }) + .finally(() => unsubscribe()) + + return result +} + +/** + * [STATIC] [ASYNC] Reroute of class method. + * + * @param tx The [[SubmittableExtrinsic]] to be submitted. Most transactions need to be signed, this must be done beforehand. + * @param identity The [[Identity]] to re-sign the tx on recoverable error. + * @param opts Optional partial criteria for resolving/rejecting the promise. + * @returns A promise which can be used to track transaction status. + * If resolved, this promise returns [[SubmittableResult]] that has led to its resolution. + */ +export async function submitTxWithReSign( + tx: SubmittableExtrinsic, + identity?: Identity, + opts?: Partial +): Promise { + const chain = await getCached() + return chain.submitTxWithReSign(tx, identity, opts) +} diff --git a/src/blockchain/index.ts b/src/blockchain/index.ts index 0b9b364e2..982857aba 100644 --- a/src/blockchain/index.ts +++ b/src/blockchain/index.ts @@ -4,3 +4,4 @@ */ export { default, IBlockchainApi } from './Blockchain' +export * as BlockchainUtils from './Blockchain.utils' diff --git a/src/ctype/CType.spec.ts b/src/ctype/CType.spec.ts index 81591c360..a8ab2a688 100644 --- a/src/ctype/CType.spec.ts +++ b/src/ctype/CType.spec.ts @@ -1,7 +1,7 @@ import { SubmittableResult } from '@polkadot/api' import { TypeRegistry } from '@polkadot/types' import { Option } from '@polkadot/types/codec' -import Blockchain from '../blockchain' +import { BlockchainUtils } from '../blockchain' import Claim from '../claim/Claim' import { ERROR_ADDRESS_INVALID, @@ -102,7 +102,7 @@ describe('CType', () => { const ctype = CType.fromSchema(ctypeModel, identityAlice.address) const tx = await ctype.store(identityAlice) - const result = await Blockchain.submitTxWithReSign(tx, identityAlice) + const result = await BlockchainUtils.submitTxWithReSign(tx, identityAlice) expect(result).toBeInstanceOf(SubmittableResult) expect(result.isFinalized).toBeTruthy() expect(result.isCompleted).toBeTruthy() diff --git a/src/delegation/DelegationNode.spec.ts b/src/delegation/DelegationNode.spec.ts index 8684fa91b..cea849842 100644 --- a/src/delegation/DelegationNode.spec.ts +++ b/src/delegation/DelegationNode.spec.ts @@ -1,4 +1,4 @@ -import Blockchain from '../blockchain' +import { BlockchainUtils } from '../blockchain' import TYPE_REGISTRY, { mockChainQueryReturn, } from '../blockchainApiConnection/__mocks__/BlockchainQuery' @@ -93,7 +93,7 @@ describe('Delegation', () => { ) const revokeStatus = await aDelegationNode .revoke(identityAlice) - .then((tx) => Blockchain.submitTxWithReSign(tx, identityAlice)) + .then((tx) => BlockchainUtils.submitTxWithReSign(tx, identityAlice)) expect(revokeStatus).toBeDefined() }) diff --git a/src/delegation/DelegationRootNode.spec.ts b/src/delegation/DelegationRootNode.spec.ts index 89cc2f373..ace12ba5b 100644 --- a/src/delegation/DelegationRootNode.spec.ts +++ b/src/delegation/DelegationRootNode.spec.ts @@ -1,5 +1,5 @@ import { Crypto, Identity } from '..' -import Blockchain from '../blockchain' +import { BlockchainUtils } from '../blockchain' import getCached from '../blockchainApiConnection' import { mockChainQueryReturn } from '../blockchainApiConnection/__mocks__/BlockchainQuery' import DelegationRootNode from './DelegationRootNode' @@ -42,7 +42,7 @@ describe('Delegation', () => { ) await rootDelegation .store(identityAlice) - .then((tx) => Blockchain.submitTxWithReSign(tx, identityAlice)) + .then((tx) => BlockchainUtils.submitTxWithReSign(tx, identityAlice)) const rootNode = await DelegationRootNode.query(ROOT_IDENTIFIER) if (rootNode) { @@ -105,7 +105,7 @@ describe('Delegation', () => { ) const revokeStatus = await aDelegationRootNode .revoke(identityAlice) - .then((tx) => Blockchain.submitTxWithReSign(tx, identityAlice)) + .then((tx) => BlockchainUtils.submitTxWithReSign(tx, identityAlice)) expect(blockchain.api.tx.delegation.revokeRoot).toBeCalledWith('myRootId') expect(revokeStatus).toBeDefined() }) diff --git a/src/did/Did.spec.ts b/src/did/Did.spec.ts index 56f978b42..4e64e6e01 100644 --- a/src/did/Did.spec.ts +++ b/src/did/Did.spec.ts @@ -1,6 +1,6 @@ import { U8aFixed } from '@polkadot/types' import { Did, IDid } from '..' -import blockchain from '../blockchain' +import { BlockchainUtils } from '../blockchain' import TYPE_REGISTRY, { mockChainQueryReturn, } from '../blockchainApiConnection/__mocks__/BlockchainQuery' @@ -75,7 +75,7 @@ describe('DID', () => { const did = Did.fromIdentity(alice, 'http://myDID.kilt.io') const tx = await did.store(alice) await expect( - blockchain.submitTxWithReSign(tx, alice) + BlockchainUtils.submitTxWithReSign(tx, alice) ).resolves.toHaveProperty('isFinalized', true) }) diff --git a/src/identity/AttesterIdentity.ts b/src/identity/AttesterIdentity.ts index 701ed68bf..09d22bf3c 100644 --- a/src/identity/AttesterIdentity.ts +++ b/src/identity/AttesterIdentity.ts @@ -8,7 +8,6 @@ import * as gabi from '@kiltprotocol/portablegabi' import { KeyringPair } from '@polkadot/keyring/types' import * as u8aUtil from '@polkadot/util/u8a' -import Blockchain from '../blockchain/Blockchain' import Attestation from '../attestation/Attestation' import getCached from '../blockchainApiConnection' import { @@ -20,6 +19,7 @@ import { IRevocationHandle } from '../types/Attestation' import IRequestForAttestation from '../types/RequestForAttestation' import Identity, { IdentityBuildOptions } from './Identity' import PublicAttesterIdentity from './PublicAttesterIdentity' +import { BlockchainUtils } from '../blockchain' const ONE_YEAR_MS = 365 * 24 * 60 * 60 * 1000 const DEFAULT_MAX_ATTRIBUTES = 70 @@ -331,6 +331,6 @@ export default class AttesterIdentity extends Identity { } await new Attestation(handle.attestation) .revoke(this) - .then((tx) => Blockchain.submitTxWithReSign(tx, this)) + .then((tx) => BlockchainUtils.submitTxWithReSign(tx, this)) } } diff --git a/src/index.ts b/src/index.ts index a98ea8325..55d6113ae 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,7 +7,7 @@ import { Attester, Claimer, Verifier } from './actor' import Attestation, { AttestationUtils } from './attestation' import AttestedClaim, { AttestedClaimUtils } from './attestedclaim' import { Balance, BalanceUtils } from './balance' -import Blockchain, { IBlockchainApi } from './blockchain' +import Blockchain, { IBlockchainApi, BlockchainUtils } from './blockchain' import * as BlockchainApiConnection from './blockchainApiConnection' import Claim, { ClaimUtils } from './claim' import Credential from './credential' @@ -65,6 +65,7 @@ export { UUID } from './util' export { Blockchain, IBlockchainApi, + BlockchainUtils, BlockchainApiConnection, Balance, BalanceUtils,