From 486d378dc226572dec6d5156744551a2e693f0b4 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 18 Mar 2019 10:05:21 +0000 Subject: [PATCH 01/83] Added #4 check if an aggregate complete transaction has all cosignatories --- src/model/transaction/AggregateTransaction.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/model/transaction/AggregateTransaction.ts b/src/model/transaction/AggregateTransaction.ts index 1557012fa6..1e8f0efe50 100644 --- a/src/model/transaction/AggregateTransaction.ts +++ b/src/model/transaction/AggregateTransaction.ts @@ -147,4 +147,17 @@ export class AggregateTransaction extends Transaction { || (this.signer !== undefined && this.signer.equals(publicAccount)); } + /** + * Check if an aggregate complete transaction has all cosignatories + * @returns {boolean} + */ + public isComplete(): boolean { + let isCompleted = false; + this.innerTransactions.forEach((innerTransaction) => { + if (!isCompleted) { + isCompleted = this.cosignatures.find((cosignature) => cosignature.signer.equals(innerTransaction.signer)) !== undefined; + } + }); + return isCompleted; + } } From f4c9cbb701b5b2bbbbb79b4567602938818d1ba0 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Tue, 5 Mar 2019 13:17:34 +0000 Subject: [PATCH 02/83] initlal checkin. Added maFee to transaction models --- src/model/transaction/AddressAliasTransaction.ts | 10 ++++++---- src/model/transaction/AggregateTransaction.ts | 16 ++++++++++------ src/model/transaction/AliasTransaction.ts | 8 ++++++-- src/model/transaction/LockFundsTransaction.ts | 12 +++++++----- .../ModifyMultisigAccountTransaction.ts | 12 +++++++----- src/model/transaction/MosaicAliasTransaction.ts | 12 +++++++----- .../transaction/MosaicDefinitionTransaction.ts | 15 ++++++++------- src/model/transaction/Transaction.ts | 8 ++++---- 8 files changed, 55 insertions(+), 38 deletions(-) diff --git a/src/model/transaction/AddressAliasTransaction.ts b/src/model/transaction/AddressAliasTransaction.ts index 48683ef893..0970b9f02b 100644 --- a/src/model/transaction/AddressAliasTransaction.ts +++ b/src/model/transaction/AddressAliasTransaction.ts @@ -36,6 +36,7 @@ export class AddressAliasTransaction extends Transaction { /** * Create a mosaic supply change transaction object * @param deadline - The deadline to include the transaction. + * @param maxFee - Max fee defined by the sender * @param actionType - The namespace id. * @param namespaceId - The namespace id. * @param mosaicId - The mosaic id. @@ -43,6 +44,7 @@ export class AddressAliasTransaction extends Transaction { * @returns {AddressAliasTransaction} */ public static create(deadline: Deadline, + maxFee: UInt64, actionType: AliasActionType, namespaceId: NamespaceId, address: Address, @@ -50,7 +52,7 @@ export class AddressAliasTransaction extends Transaction { return new AddressAliasTransaction(networkType, TransactionVersion.ADDRESS_ALIAS, deadline, - new UInt64([0, 0]), + maxFee, actionType, namespaceId, address, @@ -61,7 +63,7 @@ export class AddressAliasTransaction extends Transaction { * @param networkType * @param version * @param deadline - * @param fee + * @param maxFee * @param actionType * @param namespaceId * @param address @@ -72,7 +74,7 @@ export class AddressAliasTransaction extends Transaction { constructor(networkType: NetworkType, version: number, deadline: Deadline, - fee: UInt64, + maxFee: UInt64, /** * The alias action type. */ @@ -98,7 +100,7 @@ export class AddressAliasTransaction extends Transaction { protected buildTransaction(): VerifiableTransaction { return new AddressAliasTransactionLibrary.Builder() .addDeadline(this.deadline.toDTO()) - .addFee(this.fee.toDTO()) + .addFee(this.maxFee.toDTO()) .addVersion(this.versionToDTO()) .addActionType(this.actionType) .addNamespaceId(this.namespaceId.id.toDTO()) diff --git a/src/model/transaction/AggregateTransaction.ts b/src/model/transaction/AggregateTransaction.ts index 1557012fa6..760fd1ad82 100644 --- a/src/model/transaction/AggregateTransaction.ts +++ b/src/model/transaction/AggregateTransaction.ts @@ -38,7 +38,7 @@ export class AggregateTransaction extends Transaction { * @param type * @param version * @param deadline - * @param fee + * @param maxFee * @param innerTransactions * @param cosignatures * @param signature @@ -49,7 +49,7 @@ export class AggregateTransaction extends Transaction { type: number, version: number, deadline: Deadline, - fee: UInt64, + maxFee: UInt64, /** * The array of innerTransactions included in the aggregate transaction. */ @@ -61,18 +61,20 @@ export class AggregateTransaction extends Transaction { signature?: string, signer?: PublicAccount, transactionInfo?: TransactionInfo) { - super(type, networkType, version, deadline, fee, signature, signer, transactionInfo); + super(type, networkType, version, deadline, maxFee, signature, signer, transactionInfo); } /** * Create an aggregate complete transaction object * @param deadline - The deadline to include the transaction. + * @param {maxFee} maxFee * @param innerTransactions - The array of inner innerTransactions. * @param networkType - The network type. * @param cosignatures * @returns {AggregateTransaction} */ public static createComplete(deadline: Deadline, + maxFee: UInt64, innerTransactions: InnerTransaction[], networkType: NetworkType, cosignatures: AggregateTransactionCosignature[]): AggregateTransaction { @@ -80,7 +82,7 @@ export class AggregateTransaction extends Transaction { TransactionType.AGGREGATE_COMPLETE, TransactionVersion.AGGREGATE_COMPLETE, deadline, - new UInt64([0, 0]), + maxFee, innerTransactions, cosignatures, ); @@ -89,12 +91,14 @@ export class AggregateTransaction extends Transaction { /** * Create an aggregate bonded transaction object * @param {Deadline} deadline + * @param {maxFee} maxFee * @param {InnerTransaction[]} innerTransactions * @param {NetworkType} networkType * @param {AggregateTransactionCosignature[]} cosignatures * @return {AggregateTransaction} */ public static createBonded(deadline: Deadline, + maxFee: UInt64, innerTransactions: InnerTransaction[], networkType: NetworkType, cosignatures: AggregateTransactionCosignature[] = []): AggregateTransaction { @@ -102,7 +106,7 @@ export class AggregateTransaction extends Transaction { TransactionType.AGGREGATE_BONDED, TransactionVersion.AGGREGATE_BONDED, deadline, - new UInt64([0, 0]), + maxFee, innerTransactions, cosignatures, ); @@ -116,7 +120,7 @@ export class AggregateTransaction extends Transaction { return new AggregateTransactionLibrary.Builder() .addDeadline(this.deadline.toDTO()) .addType(this.type) - .addFee(this.fee.toDTO()) + .addFee(this.maxFee.toDTO()) .addVersion(this.versionToDTO()) .addTransactions(this.innerTransactions.map((transaction) => { return transaction.aggregateTransaction(); diff --git a/src/model/transaction/AliasTransaction.ts b/src/model/transaction/AliasTransaction.ts index acf60748a3..06a1e1e843 100644 --- a/src/model/transaction/AliasTransaction.ts +++ b/src/model/transaction/AliasTransaction.ts @@ -35,6 +35,7 @@ export abstract class AliasTransaction extends Transaction { /** * Create an address alias transaction object * @param deadline - The deadline to include the transaction. + * @param maxFee - Max fee defined by the sender * @param aliasAction - The namespace id. * @param namespaceId - The namespace id. * @param address - The address. @@ -42,16 +43,18 @@ export abstract class AliasTransaction extends Transaction { * @returns {AddressAliasTransaction} */ public static createForAddress(deadline: Deadline, + maxFee: UInt64, aliasAction: AliasActionType, namespaceId: NamespaceId, address: Address, networkType: NetworkType): AliasTransaction { - return AddressAliasTransaction.create(deadline, aliasAction, namespaceId, address, networkType); + return AddressAliasTransaction.create(deadline, maxFee, aliasAction, namespaceId, address, networkType); } /** * Create a mosaic alias transaction object * @param deadline - The deadline to include the transaction. + * @param maxFee - Max fee defined by the sender * @param aliasAction - The namespace id. * @param namespaceId - The namespace id. * @param mosaicId - The mosaic id. @@ -59,11 +62,12 @@ export abstract class AliasTransaction extends Transaction { * @returns {MosaicAliasTransaction} */ public static createForMosaic(deadline: Deadline, + maxFee: UInt64, aliasAction: AliasActionType, namespaceId: NamespaceId, mosaicId: MosaicId, networkType: NetworkType): AliasTransaction { - return MosaicAliasTransaction.create(deadline, aliasAction, namespaceId, mosaicId, networkType); + return MosaicAliasTransaction.create(deadline, maxFee, aliasAction, namespaceId, mosaicId, networkType); } } diff --git a/src/model/transaction/LockFundsTransaction.ts b/src/model/transaction/LockFundsTransaction.ts index 7e676b0e48..0ca7c65a90 100644 --- a/src/model/transaction/LockFundsTransaction.ts +++ b/src/model/transaction/LockFundsTransaction.ts @@ -42,12 +42,14 @@ export class LockFundsTransaction extends Transaction { /** * Create a Lock funds transaction object * @param deadline - The deadline to include the transaction. + * @param maxFee - Max fee defined by the sender * @param mosaic - The locked mosaic. * @param duration - The funds lock duration. * @param signedTransaction - The signed transaction for which funds are locked. * @param networkType - The network type. */ public static create(deadline: Deadline, + maxFee: UInt64, mosaic: Mosaic, duration: UInt64, signedTransaction: SignedTransaction, @@ -56,7 +58,7 @@ export class LockFundsTransaction extends Transaction { networkType, TransactionVersion.LOCK, deadline, - UInt64.fromUint(0), + maxFee, mosaic, duration, signedTransaction, @@ -67,7 +69,7 @@ export class LockFundsTransaction extends Transaction { * @param networkType * @param version * @param deadline - * @param fee + * @param maxFee * @param mosaic * @param duration * @param signedTransaction @@ -78,7 +80,7 @@ export class LockFundsTransaction extends Transaction { constructor(networkType: NetworkType, version: number, deadline: Deadline, - fee: UInt64, + maxFee: UInt64, /** * The locked mosaic. */ @@ -91,7 +93,7 @@ export class LockFundsTransaction extends Transaction { signature?: string, signer?: PublicAccount, transactionInfo?: TransactionInfo) { - super(TransactionType.LOCK, networkType, version, deadline, fee, signature, signer, transactionInfo); + super(TransactionType.LOCK, networkType, version, deadline, maxFee, signature, signer, transactionInfo); this.hash = signedTransaction.hash; if (signedTransaction.type !== TransactionType.AGGREGATE_BONDED) { throw new Error('Signed transaction must be Aggregate Bonded Transaction'); @@ -106,7 +108,7 @@ export class LockFundsTransaction extends Transaction { return new HashLockTransaction.Builder() .addDeadline(this.deadline.toDTO()) .addType(this.type) - .addFee(this.fee.toDTO()) + .addFee(this.maxFee.toDTO()) .addVersion(this.versionToDTO()) .addMosaicId(this.mosaic.id.id.toDTO()) .addMosaicAmount(this.mosaic.amount.toDTO()) diff --git a/src/model/transaction/ModifyMultisigAccountTransaction.ts b/src/model/transaction/ModifyMultisigAccountTransaction.ts index 5f489a83c1..9ca6468549 100644 --- a/src/model/transaction/ModifyMultisigAccountTransaction.ts +++ b/src/model/transaction/ModifyMultisigAccountTransaction.ts @@ -36,6 +36,7 @@ export class ModifyMultisigAccountTransaction extends Transaction { /** * Create a modify multisig account transaction object * @param deadline - The deadline to include the transaction. + * @param maxFee - Max fee defined by the sender * @param minApprovalDelta - The min approval relative change. * @param minRemovalDelta - The min removal relative change. * @param modifications - The array of modifications. @@ -43,6 +44,7 @@ export class ModifyMultisigAccountTransaction extends Transaction { * @returns {ModifyMultisigAccountTransaction} */ public static create(deadline: Deadline, + maxFee: UInt64, minApprovalDelta: number, minRemovalDelta: number, modifications: MultisigCosignatoryModification[], @@ -50,7 +52,7 @@ export class ModifyMultisigAccountTransaction extends Transaction { return new ModifyMultisigAccountTransaction(networkType, TransactionVersion.MODIFY_MULTISIG_ACCOUNT, deadline, - new UInt64([0, 0]), + maxFee, minApprovalDelta, minRemovalDelta, modifications); @@ -60,7 +62,7 @@ export class ModifyMultisigAccountTransaction extends Transaction { * @param networkType * @param version * @param deadline - * @param fee + * @param maxFee * @param minApprovalDelta * @param minRemovalDelta * @param modifications @@ -71,7 +73,7 @@ export class ModifyMultisigAccountTransaction extends Transaction { constructor(networkType: NetworkType, version: number, deadline: Deadline, - fee: UInt64, + maxFee: UInt64, /** * The number of signatures needed to approve a transaction. * If we are modifying and existing multi-signature account this indicates the relative change of the minimum cosignatories. @@ -89,7 +91,7 @@ export class ModifyMultisigAccountTransaction extends Transaction { signature?: string, signer?: PublicAccount, transactionInfo?: TransactionInfo) { - super(TransactionType.MODIFY_MULTISIG_ACCOUNT, networkType, version, deadline, fee, signature, signer, transactionInfo); + super(TransactionType.MODIFY_MULTISIG_ACCOUNT, networkType, version, deadline, maxFee, signature, signer, transactionInfo); } /** @@ -99,7 +101,7 @@ export class ModifyMultisigAccountTransaction extends Transaction { protected buildTransaction(): VerifiableTransaction { return new ModifyMultisigAccountTransactionLibrary.Builder() .addDeadline(this.deadline.toDTO()) - .addFee(this.fee.toDTO()) + .addFee(this.maxFee.toDTO()) .addVersion(this.versionToDTO()) .addMinApprovalDelta(this.minApprovalDelta) .addMinRemovalDelta(this.minRemovalDelta) diff --git a/src/model/transaction/MosaicAliasTransaction.ts b/src/model/transaction/MosaicAliasTransaction.ts index fb5404f6f6..c44e4ee9ea 100644 --- a/src/model/transaction/MosaicAliasTransaction.ts +++ b/src/model/transaction/MosaicAliasTransaction.ts @@ -36,6 +36,7 @@ export class MosaicAliasTransaction extends Transaction { /** * Create a mosaic supply change transaction object * @param deadline - The deadline to include the transaction. + * @param maxFee - Max fee defined by the sender * @param actionType - The namespace id. * @param namespaceId - The namespace id. * @param mosaicId - The mosaic id. @@ -43,6 +44,7 @@ export class MosaicAliasTransaction extends Transaction { * @returns {MosaicAliasTransaction} */ public static create(deadline: Deadline, + maxFee: UInt64, actionType: AliasActionType, namespaceId: NamespaceId, mosaicId: MosaicId, @@ -50,7 +52,7 @@ export class MosaicAliasTransaction extends Transaction { return new MosaicAliasTransaction(networkType, TransactionVersion.MOSAIC_ALIAS, deadline, - new UInt64([0, 0]), + maxFee, actionType, namespaceId, mosaicId, @@ -61,7 +63,7 @@ export class MosaicAliasTransaction extends Transaction { * @param networkType * @param version * @param deadline - * @param fee + * @param maxFee * @param actionType * @param namespaceId * @param mosaicId @@ -72,7 +74,7 @@ export class MosaicAliasTransaction extends Transaction { constructor(networkType: NetworkType, version: number, deadline: Deadline, - fee: UInt64, + maxFee: UInt64, /** * The alias action type. */ @@ -88,7 +90,7 @@ export class MosaicAliasTransaction extends Transaction { signature?: string, signer?: PublicAccount, transactionInfo?: TransactionInfo) { - super(TransactionType.MOSAIC_ALIAS, networkType, version, deadline, fee, signature, signer, transactionInfo); + super(TransactionType.MOSAIC_ALIAS, networkType, version, deadline, maxFee, signature, signer, transactionInfo); } /** @@ -98,7 +100,7 @@ export class MosaicAliasTransaction extends Transaction { protected buildTransaction(): VerifiableTransaction { return new MosaicAliasTransactionLibrary.Builder() .addDeadline(this.deadline.toDTO()) - .addFee(this.fee.toDTO()) + .addFee(this.maxFee.toDTO()) .addVersion(this.versionToDTO()) .addActionType(this.actionType) .addNamespaceId(this.namespaceId.id.toDTO()) diff --git a/src/model/transaction/MosaicDefinitionTransaction.ts b/src/model/transaction/MosaicDefinitionTransaction.ts index 3adaf444a4..34643be588 100644 --- a/src/model/transaction/MosaicDefinitionTransaction.ts +++ b/src/model/transaction/MosaicDefinitionTransaction.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { +import { MosaicCreationTransaction as MosaicDefinitionTransactionLibrary, mosaicId as mosaicIdLibrary, VerifiableTransaction, @@ -24,7 +24,6 @@ import { NetworkType } from '../blockchain/NetworkType'; import { MosaicId } from '../mosaic/MosaicId'; import { MosaicNonce } from '../mosaic/MosaicNonce'; import { MosaicProperties } from '../mosaic/MosaicProperties'; -import { NamespaceId } from '../namespace/NamespaceId'; import { UInt64 } from '../UInt64'; import { Deadline } from './Deadline'; import { Transaction } from './Transaction'; @@ -41,6 +40,7 @@ export class MosaicDefinitionTransaction extends Transaction { /** * Create a mosaic creation transaction object * @param deadline - The deadline to include the transaction. + * @param maxFee - Max fee defined by the sender * @param nonce - The mosaic nonce ex: MosaicNonce.createRandom(). * @param mosaicId - The mosaic id ex: new MosaicId([481110499, 231112638]). * @param mosaicProperties - The mosaic properties. @@ -48,6 +48,7 @@ export class MosaicDefinitionTransaction extends Transaction { * @returns {MosaicDefinitionTransaction} */ public static create(deadline: Deadline, + maxFee: UInt64, nonce: MosaicNonce, mosaicId: MosaicId, mosaicProperties: MosaicProperties, @@ -55,7 +56,7 @@ export class MosaicDefinitionTransaction extends Transaction { return new MosaicDefinitionTransaction(networkType, TransactionVersion.MOSAIC_DEFINITION, deadline, - new UInt64([0, 0]), + maxFee, nonce, mosaicId, mosaicProperties, @@ -66,7 +67,7 @@ export class MosaicDefinitionTransaction extends Transaction { * @param networkType * @param version * @param deadline - * @param fee + * @param maxFee * @param mosaicNonce * @param mosaicId * @param mosaicProperties @@ -77,7 +78,7 @@ export class MosaicDefinitionTransaction extends Transaction { constructor(networkType: NetworkType, version: number, deadline: Deadline, - fee: UInt64, + maxFee: UInt64, /** * The mosaic nonce. */ @@ -93,7 +94,7 @@ export class MosaicDefinitionTransaction extends Transaction { signature?: string, signer?: PublicAccount, transactionInfo?: TransactionInfo) { - super(TransactionType.MOSAIC_DEFINITION, networkType, version, deadline, fee, signature, signer, transactionInfo); + super(TransactionType.MOSAIC_DEFINITION, networkType, version, deadline, maxFee, signature, signer, transactionInfo); } /** @@ -103,7 +104,7 @@ export class MosaicDefinitionTransaction extends Transaction { protected buildTransaction(): VerifiableTransaction { let mosaicDefinitionTransaction = new MosaicDefinitionTransactionLibrary.Builder() .addDeadline(this.deadline.toDTO()) - .addFee(this.fee.toDTO()) + .addFee(this.maxFee.toDTO()) .addVersion(this.versionToDTO()) .addDivisibility(this.mosaicProperties.divisibility) .addDuration(this.mosaicProperties.duration.toDTO()) diff --git a/src/model/transaction/Transaction.ts b/src/model/transaction/Transaction.ts index dfa4cdde95..eca739053e 100644 --- a/src/model/transaction/Transaction.ts +++ b/src/model/transaction/Transaction.ts @@ -37,7 +37,7 @@ export abstract class Transaction { * @param networkType * @param version * @param deadline - * @param fee + * @param maxFee * @param signature * @param signer * @param transactionInfo @@ -59,10 +59,10 @@ export abstract class Transaction { */ public readonly deadline: Deadline, /** - * The fee for the transaction. The higher the fee, the higher the priority of the transaction. - * Transactions with high priority get included in a block before transactions with lower priority. + * A sender of a transaction must specify during the transaction definition a max_fee, + * meaning the maximum fee the account allows to spend for this transaction. */ - public readonly fee: UInt64, + public readonly maxFee: UInt64, /** * The transaction signature (missing if part of an aggregate transaction). */ From 882971d6c0c255e363ea5e36956662c4f9b7ebfc Mon Sep 17 00:00:00 2001 From: Greg S Date: Tue, 5 Mar 2019 16:32:05 +0100 Subject: [PATCH 03/83] WIP #53: rewrite all transaction classes to add maxFee in create methods + rewrite tests to add 0 maxFee --- e2e/infrastructure/TransactionHttp.spec.ts | 57 ++++++++++++++ e2e/infrastructure/TransactionUtils.ts | 5 ++ .../transaction/CreateTransactionFromDTO.ts | 78 +++++++++---------- .../MosaicSupplyChangeTransaction.ts | 12 +-- .../RegisterNamespaceTransaction.ts | 16 ++-- .../transaction/SecretLockTransaction.ts | 12 +-- .../transaction/SecretProofTransaction.ts | 12 +-- src/model/transaction/TransferTransaction.ts | 12 +-- test/infrastructure/TransactionHttp.spec.ts | 3 + .../AddressAliasTransaction.spec.ts | 1 + .../transaction/AggregateTransaction.spec.ts | 16 +++- .../CosignatureTransaction.spec.ts | 2 +- .../transaction/HashLockTransaction.spec.ts | 4 + .../transaction/LockFundsTransaction.spec.ts | 4 + .../ModifyMultisigAccountTransaction.spec.ts | 2 + .../MosaicAliasTransaction.spec.ts | 1 + .../MosaicDefinitionTransaction.spec.ts | 2 + .../MosaicSupplyChangeTransaction.spec.ts | 1 + .../RegisterNamespaceTransaction.spec.ts | 2 + .../transaction/SecretLockTransaction.spec.ts | 8 ++ .../SecretProofTransaction.spec.ts | 9 +++ .../transaction/TransferTransaction.spec.ts | 3 + 22 files changed, 195 insertions(+), 67 deletions(-) diff --git a/e2e/infrastructure/TransactionHttp.spec.ts b/e2e/infrastructure/TransactionHttp.spec.ts index de6cd4aaca..9f70eca25e 100644 --- a/e2e/infrastructure/TransactionHttp.spec.ts +++ b/e2e/infrastructure/TransactionHttp.spec.ts @@ -97,6 +97,7 @@ describe('TransactionHttp', () => { describe('TransferTransaction', () => { const transferTransaction = TransferTransaction.create( Deadline.create(), + new UInt64([0, 0]), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), [NetworkCurrencyMosaic.createRelative(10)], PlainMessage.create('test-message'), @@ -109,6 +110,7 @@ describe('TransactionHttp', () => { }); it('aggregate', (done) => { const aggregateTransaction = AggregateTransaction.createComplete(Deadline.create(), + new UInt64([0, 0]), [transferTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, [], @@ -124,6 +126,7 @@ describe('TransactionHttp', () => { namespaceName = 'root-test-namespace-' + Math.floor(Math.random() * 10000); const registerNamespaceTransaction = RegisterNamespaceTransaction.createRootNamespace( Deadline.create(), + new UInt64([0, 0]), namespaceName, UInt64.fromUint(1000), NetworkType.MIJIN_TEST, @@ -135,11 +138,13 @@ describe('TransactionHttp', () => { it('aggregate', (done) => { const registerNamespaceTransaction = RegisterNamespaceTransaction.createRootNamespace( Deadline.create(), + new UInt64([0, 0]), 'root-test-namespace-' + Math.floor(Math.random() * 10000), UInt64.fromUint(1000), NetworkType.MIJIN_TEST, ); const aggregateTransaction = AggregateTransaction.createComplete(Deadline.create(), + new UInt64([0, 0]), [registerNamespaceTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -152,6 +157,7 @@ describe('TransactionHttp', () => { it('standalone', (done) => { const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( Deadline.create(), + new UInt64([0, 0]), new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce new MosaicId(UInt64.fromUint(1).toDTO()), // ID MosaicProperties.create({ @@ -171,6 +177,7 @@ describe('TransactionHttp', () => { it('aggregate', (done) => { const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( Deadline.create(), + new UInt64([0, 0]), new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce new MosaicId(UInt64.fromUint(1).toDTO()), // ID MosaicProperties.create({ @@ -183,6 +190,7 @@ describe('TransactionHttp', () => { NetworkType.MIJIN_TEST, ); const aggregateTransaction = AggregateTransaction.createComplete(Deadline.create(), + new UInt64([0, 0]), [mosaicDefinitionTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -195,6 +203,7 @@ describe('TransactionHttp', () => { it('standalone', (done) => { const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( Deadline.create(), + new UInt64([0, 0]), mosaicId, MosaicSupplyType.Increase, UInt64.fromUint(10), @@ -207,12 +216,14 @@ describe('TransactionHttp', () => { it('aggregate', (done) => { const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( Deadline.create(), + new UInt64([0, 0]), mosaicId, MosaicSupplyType.Increase, UInt64.fromUint(10), NetworkType.MIJIN_TEST, ); const aggregateTransaction = AggregateTransaction.createComplete(Deadline.create(), + new UInt64([0, 0]), [mosaicSupplyChangeTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -225,6 +236,7 @@ describe('TransactionHttp', () => { it('should sign a ModifyMultisigAccountTransaction with cosignatories', (done) => { const modifyMultisigAccountTransaction = ModifyMultisigAccountTransaction.create( Deadline.create(), + new UInt64([0, 0]), 0, 0, [new MultisigCosignatoryModification( @@ -235,6 +247,7 @@ describe('TransactionHttp', () => { NetworkType.MIJIN_TEST, ); const aggregateTransaction = AggregateTransaction.createBonded(Deadline.create(20, ChronoUnit.MINUTES), + new UInt64([0, 0]), [modifyMultisigAccountTransaction.toAggregate(MultisigAccount.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -245,6 +258,7 @@ describe('TransactionHttp', () => { ); const lockFundsTransaction = LockFundsTransaction.create(Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createRelative(10), UInt64.fromUint(10000), signedTransaction, @@ -265,6 +279,7 @@ describe('TransactionHttp', () => { it('CosignatureTransaction', (done) => { const transferTransaction = TransferTransaction.create( Deadline.create(), + new UInt64([0, 0]), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), [NetworkCurrencyMosaic.createRelative(1)], PlainMessage.create('test-message'), @@ -272,6 +287,7 @@ describe('TransactionHttp', () => { ); const aggregateTransaction = AggregateTransaction.createBonded( Deadline.create(2, ChronoUnit.MINUTES), + new UInt64([0, 0]), [transferTransaction.toAggregate(MultisigAccount.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -280,6 +296,7 @@ describe('TransactionHttp', () => { ); const lockFundsTransaction = LockFundsTransaction.create(Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createRelative(10), UInt64.fromUint(10000), signedTransaction, @@ -310,6 +327,7 @@ describe('TransactionHttp', () => { it('standalone', (done) => { const aggregateTransaction = AggregateTransaction.createBonded( Deadline.create(), + new UInt64([0, 0]), [], NetworkType.MIJIN_TEST, [], @@ -317,6 +335,7 @@ describe('TransactionHttp', () => { const signedTransaction = account.sign(aggregateTransaction); const lockFundsTransaction = LockFundsTransaction.create(Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createRelative(10), UInt64.fromUint(10000), signedTransaction, @@ -328,17 +347,20 @@ describe('TransactionHttp', () => { it('aggregate', (done) => { const aggregateTransaction = AggregateTransaction.createBonded( Deadline.create(), + new UInt64([0, 0]), [], NetworkType.MIJIN_TEST, [], ); const signedTransaction = account.sign(aggregateTransaction); const lockFundsTransaction = LockFundsTransaction.create(Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createRelative(10), UInt64.fromUint(10), signedTransaction, NetworkType.MIJIN_TEST); const aggregateLockFundsTransaction = AggregateTransaction.createComplete(Deadline.create(), + new UInt64([0, 0]), [lockFundsTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -352,6 +374,7 @@ describe('TransactionHttp', () => { it('standalone', (done) => { const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Sha3_256, @@ -366,6 +389,7 @@ describe('TransactionHttp', () => { it('aggregate', (done) => { const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Sha3_256, @@ -374,6 +398,7 @@ describe('TransactionHttp', () => { NetworkType.MIJIN_TEST, ); const aggregateSecretLockTransaction = AggregateTransaction.createComplete(Deadline.create(), + new UInt64([0, 0]), [secretLockTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -385,6 +410,7 @@ describe('TransactionHttp', () => { it('standalone', (done) => { const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Keccak_256, @@ -399,6 +425,7 @@ describe('TransactionHttp', () => { it('aggregate', (done) => { const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Keccak_256, @@ -407,6 +434,7 @@ describe('TransactionHttp', () => { NetworkType.MIJIN_TEST, ); const aggregateSecretLockTransaction = AggregateTransaction.createComplete(Deadline.create(), + new UInt64([0, 0]), [secretLockTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -418,6 +446,7 @@ describe('TransactionHttp', () => { it('standalone', (done) => { const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_160, @@ -432,6 +461,7 @@ describe('TransactionHttp', () => { it('aggregate', (done) => { const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_160, @@ -440,6 +470,7 @@ describe('TransactionHttp', () => { NetworkType.MIJIN_TEST, ); const aggregateSecretLockTransaction = AggregateTransaction.createComplete(Deadline.create(), + new UInt64([0, 0]), [secretLockTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -451,6 +482,7 @@ describe('TransactionHttp', () => { it('standalone', (done) => { const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_256, @@ -465,6 +497,7 @@ describe('TransactionHttp', () => { it('aggregate', (done) => { const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_256, @@ -473,6 +506,7 @@ describe('TransactionHttp', () => { NetworkType.MIJIN_TEST, ); const aggregateSecretLockTransaction = AggregateTransaction.createComplete(Deadline.create(), + new UInt64([0, 0]), [secretLockTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -489,6 +523,7 @@ describe('TransactionHttp', () => { const proof = convert.uint8ToHex(secretSeed); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Sha3_256, @@ -499,6 +534,7 @@ describe('TransactionHttp', () => { validateTransactionAnnounceCorrectly(account.address, () => { const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), + new UInt64([0, 0]), HashType.Op_Sha3_256, secret, proof, @@ -516,6 +552,7 @@ describe('TransactionHttp', () => { const proof = convert.uint8ToHex(secretSeed); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Sha3_256, @@ -526,12 +563,14 @@ describe('TransactionHttp', () => { validateTransactionAnnounceCorrectly(account.address, () => { const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), + new UInt64([0, 0]), HashType.Op_Sha3_256, secret, proof, NetworkType.MIJIN_TEST, ); const aggregateSecretProofTransaction = AggregateTransaction.createComplete(Deadline.create(), + new UInt64([0, 0]), [secretProofTransaction.toAggregate(account2.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -549,6 +588,7 @@ describe('TransactionHttp', () => { const proof = convert.uint8ToHex(secretSeed); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Keccak_256, @@ -559,6 +599,7 @@ describe('TransactionHttp', () => { validateTransactionAnnounceCorrectly(account.address, () => { const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), + new UInt64([0, 0]), HashType.Op_Keccak_256, secret, proof, @@ -576,6 +617,7 @@ describe('TransactionHttp', () => { const proof = convert.uint8ToHex(secretSeed); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Keccak_256, @@ -586,12 +628,14 @@ describe('TransactionHttp', () => { validateTransactionAnnounceCorrectly(account.address, () => { const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), + new UInt64([0, 0]), HashType.Op_Keccak_256, secret, proof, NetworkType.MIJIN_TEST, ); const aggregateSecretProofTransaction = AggregateTransaction.createComplete(Deadline.create(), + new UInt64([0, 0]), [secretProofTransaction.toAggregate(account2.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -608,6 +652,7 @@ describe('TransactionHttp', () => { const proof = convert.uint8ToHex(secretSeed); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_160, @@ -618,6 +663,7 @@ describe('TransactionHttp', () => { validateTransactionAnnounceCorrectly(account.address, () => { const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), + new UInt64([0, 0]), HashType.Op_Hash_160, secret, proof, @@ -635,6 +681,7 @@ describe('TransactionHttp', () => { const proof = convert.uint8ToHex(secretSeed); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_160, @@ -645,12 +692,14 @@ describe('TransactionHttp', () => { validateTransactionAnnounceCorrectly(account.address, () => { const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), + new UInt64([0, 0]), HashType.Op_Hash_160, secret, proof, NetworkType.MIJIN_TEST, ); const aggregateSecretProofTransaction = AggregateTransaction.createComplete(Deadline.create(), + new UInt64([0, 0]), [secretProofTransaction.toAggregate(account2.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -667,6 +716,7 @@ describe('TransactionHttp', () => { const proof = convert.uint8ToHex(secretSeed); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_256, @@ -677,6 +727,7 @@ describe('TransactionHttp', () => { validateTransactionAnnounceCorrectly(account.address, () => { const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), + new UInt64([0, 0]), HashType.Op_Hash_256, secret, proof, @@ -694,6 +745,7 @@ describe('TransactionHttp', () => { const proof = convert.uint8ToHex(secretSeed); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_256, @@ -704,12 +756,14 @@ describe('TransactionHttp', () => { validateTransactionAnnounceCorrectly(account.address, () => { const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), + new UInt64([0, 0]), HashType.Op_Hash_256, secret, proof, NetworkType.MIJIN_TEST, ); const aggregateSecretProofTransaction = AggregateTransaction.createComplete(Deadline.create(), + new UInt64([0, 0]), [secretProofTransaction.toAggregate(account2.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -828,6 +882,7 @@ describe('TransactionHttp', () => { const tx = TransferTransaction.create( Deadline.create(), + new UInt64([0, 0]), Address.createFromRawAddress('SAGY2PTFX4T2XYKYXTJXYCTQRP3FESQH5MEQI2RQ'), [], PlainMessage.create('Hi'), @@ -835,6 +890,7 @@ describe('TransactionHttp', () => { ); const aggTx = AggregateTransaction.createComplete( Deadline.create(), + new UInt64([0, 0]), [ tx.toAggregate(signerAccount.publicAccount), ], @@ -857,6 +913,7 @@ describe('TransactionHttp', () => { const tx = TransferTransaction.create( Deadline.create(), + new UInt64([0, 0]), Address.createFromRawAddress('SAGY2PTFX4T2XYKYXTJXYCTQRP3FESQH5MEQI2RQ'), [NetworkCurrencyMosaic.createRelative(10)], EmptyMessage, diff --git a/e2e/infrastructure/TransactionUtils.ts b/e2e/infrastructure/TransactionUtils.ts index bf0bbf23cb..32dbd2c301 100644 --- a/e2e/infrastructure/TransactionUtils.ts +++ b/e2e/infrastructure/TransactionUtils.ts @@ -28,6 +28,7 @@ import { MultisigCosignatoryModification } from '../../src/model/transaction/Mul import { MultisigCosignatoryModificationType } from '../../src/model/transaction/MultisigCosignatoryModificationType'; import {PlainMessage} from '../../src/model/transaction/PlainMessage'; import {TransferTransaction} from '../../src/model/transaction/TransferTransaction'; +import {UInt64} from '../../src/model/UInt64'; import {CosignatoryAccount, MultisigAccount, NIS2_URL, TestingAccount} from '../../test/conf/conf.spec'; export class TransactionUtils { @@ -37,6 +38,7 @@ export class TransactionUtils { const account = TestingAccount; const transferTransaction = TransferTransaction.create( Deadline.create(), + new UInt64([0, 0]), recipient, [], PlainMessage.create('test-message'), @@ -50,6 +52,7 @@ export class TransactionUtils { const account = TestingAccount; const transferTransaction = TransferTransaction.create( Deadline.create(), + new UInt64([0, 0]), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), [NetworkCurrencyMosaic.createRelative(100000000000)], PlainMessage.create('test-message'), @@ -62,6 +65,7 @@ export class TransactionUtils { public static createAggregateBoundedTransactionAndAnnounce(transactionHttp: TransactionHttp = new TransactionHttp(NIS2_URL)) { const transferTransaction = TransferTransaction.create( Deadline.create(), + new UInt64([0, 0]), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), [NetworkCurrencyMosaic.createRelative(100000000000)], PlainMessage.create('test-message'), @@ -70,6 +74,7 @@ export class TransactionUtils { const aggregateTransaction = AggregateTransaction.createBonded( Deadline.create(2, ChronoUnit.MINUTES), + new UInt64([0, 0]), [transferTransaction.toAggregate(MultisigAccount.publicAccount)], NetworkType.MIJIN_TEST, [], diff --git a/src/infrastructure/transaction/CreateTransactionFromDTO.ts b/src/infrastructure/transaction/CreateTransactionFromDTO.ts index ef172162c0..ee8319795c 100644 --- a/src/infrastructure/transaction/CreateTransactionFromDTO.ts +++ b/src/infrastructure/transaction/CreateTransactionFromDTO.ts @@ -68,7 +68,7 @@ export const CreateTransactionFromDTO = (transactionDTO): Transaction => { innerTransactionDTO.meta.aggregateHash, innerTransactionDTO.meta.aggregateId, ) : undefined; - innerTransactionDTO.transaction.fee = transactionDTO.transaction.fee; + innerTransactionDTO.transaction.maxFee = transactionDTO.transaction.maxFee; innerTransactionDTO.transaction.deadline = transactionDTO.transaction.deadline; innerTransactionDTO.transaction.signature = transactionDTO.transaction.signature; return CreateStandaloneTransactionFromDTO(innerTransactionDTO.transaction, aggregateTransactionInfo); @@ -78,7 +78,7 @@ export const CreateTransactionFromDTO = (transactionDTO): Transaction => { transactionDTO.transaction.type, extractTransactionVersion(transactionDTO.transaction.version), Deadline.createFromDTO(transactionDTO.transaction.deadline), - new UInt64(transactionDTO.transaction.fee || [0, 0]), + new UInt64(transactionDTO.transaction.maxFee || [0, 0]), innerTransactions, transactionDTO.transaction.cosignatures ? transactionDTO.transaction.cosignatures .map((aggregateCosignatureDTO) => { @@ -122,7 +122,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + new UInt64(transactionDTO.maxFee || [0, 0]), extractRecipient(transactionDTO.recipient), extractMosaics(transactionDTO.mosaics), transactionDTO.message !== undefined ? @@ -136,7 +136,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + new UInt64(transactionDTO.maxFee || [0, 0]), transactionDTO.namespaceType, transactionDTO.name, new NamespaceId(transactionDTO.namespaceId), @@ -151,37 +151,37 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + new UInt64(transactionDTO.maxFee || [0, 0]), transactionDTO.nonce, new MosaicId(transactionDTO.mosaicId), new MosaicProperties( new UInt64(transactionDTO.properties[0].value), (new UInt64(transactionDTO.properties[1].value)).compact(), - new UInt64(transactionDTO.properties.length === 3 ? transactionDTO.properties[2].value : [0, 0]), - ), - transactionDTO.signature, - PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)), - transactionInfo, - ); - } else if (transactionDTO.type === TransactionType.MOSAIC_SUPPLY_CHANGE) { - return new MosaicSupplyChangeTransaction( - extractNetworkType(transactionDTO.version), - extractTransactionVersion(transactionDTO.version), - Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), - new MosaicId(transactionDTO.mosaicId), - transactionDTO.direction, - new UInt64(transactionDTO.delta), - transactionDTO.signature, - PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)), - transactionInfo, - ); - } else if (transactionDTO.type === TransactionType.MODIFY_MULTISIG_ACCOUNT) { - return new ModifyMultisigAccountTransaction( - extractNetworkType(transactionDTO.version), - extractTransactionVersion(transactionDTO.version), - Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + new UInt64(transactionDTO.properties.length === 3 ? transactionDTO.properties[2].value : [0, 0]), + ), + transactionDTO.signature, + PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)), + transactionInfo, + ); +} else if (transactionDTO.type === TransactionType.MOSAIC_SUPPLY_CHANGE) { + return new MosaicSupplyChangeTransaction( + extractNetworkType(transactionDTO.version), + extractTransactionVersion(transactionDTO.version), + Deadline.createFromDTO(transactionDTO.deadline), + new UInt64(transactionDTO.maxFee || [0, 0]), + new MosaicId(transactionDTO.mosaicId), + transactionDTO.direction, + new UInt64(transactionDTO.delta), + transactionDTO.signature, + PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)), + transactionInfo, + ); +} else if (transactionDTO.type === TransactionType.MODIFY_MULTISIG_ACCOUNT) { + return new ModifyMultisigAccountTransaction( + extractNetworkType(transactionDTO.version), + extractTransactionVersion(transactionDTO.version), + Deadline.createFromDTO(transactionDTO.deadline), + new UInt64(transactionDTO.maxFee || [0, 0]), transactionDTO.minApprovalDelta, transactionDTO.minRemovalDelta, transactionDTO.modifications ? transactionDTO.modifications.map((modificationDTO) => new MultisigCosignatoryModification( @@ -198,7 +198,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr networkType, extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + new UInt64(transactionDTO.maxFee || [0, 0]), new Mosaic(new MosaicId(transactionDTO.mosaicId), new UInt64(transactionDTO.amount)), new UInt64(transactionDTO.duration), new SignedTransaction('', transactionDTO.hash, '', TransactionType.AGGREGATE_BONDED, networkType), @@ -211,7 +211,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + new UInt64(transactionDTO.maxFee || [0, 0]), new Mosaic(new MosaicId(transactionDTO.mosaicId), new UInt64(transactionDTO.amount)), new UInt64(transactionDTO.duration), transactionDTO.hashAlgorithm, @@ -226,7 +226,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + new UInt64(transactionDTO.maxFee || [0, 0]), transactionDTO.hashAlgorithm, transactionDTO.secret, transactionDTO.proof, @@ -239,7 +239,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + new UInt64(transactionDTO.maxFee || [0, 0]), transactionDTO.aliasAction, new NamespaceId(transactionDTO.namespaceId), new MosaicId(transactionDTO.mosaicId), @@ -252,7 +252,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + new UInt64(transactionDTO.maxFee || [0, 0]), transactionDTO.aliasAction, new NamespaceId(transactionDTO.namespaceId), extractRecipient(transactionDTO.address) as Address, @@ -265,7 +265,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + new UInt64(transactionDTO.maxFee || [0, 0]), transactionDTO.propertyType, transactionDTO.modifications ? transactionDTO.modifications.map((modificationDTO) => new AccountPropertyModification( modificationDTO.modificationType, @@ -280,7 +280,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + new UInt64(transactionDTO.maxFee || [0, 0]), transactionDTO.propertyType, transactionDTO.modifications ? transactionDTO.modifications.map((modificationDTO) => new AccountPropertyModification( modificationDTO.modificationType, @@ -295,7 +295,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + new UInt64(transactionDTO.maxFee || [0, 0]), transactionDTO.propertyType, transactionDTO.modifications ? transactionDTO.modifications.map((modificationDTO) => new AccountPropertyModification( modificationDTO.modificationType, @@ -310,7 +310,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + new UInt64(transactionDTO.maxFee || [0, 0]), transactionDTO.remoteAccountKey, transactionDTO.linkAction, transactionDTO.signature, diff --git a/src/model/transaction/MosaicSupplyChangeTransaction.ts b/src/model/transaction/MosaicSupplyChangeTransaction.ts index b5db90e8e5..6e584d8d9a 100644 --- a/src/model/transaction/MosaicSupplyChangeTransaction.ts +++ b/src/model/transaction/MosaicSupplyChangeTransaction.ts @@ -35,6 +35,7 @@ export class MosaicSupplyChangeTransaction extends Transaction { /** * Create a mosaic supply change transaction object * @param deadline - The deadline to include the transaction. + * @param maxFee - Max fee defined by the sender * @param mosaicId - The mosaic id. * @param direction - The supply type. * @param delta - The supply change in units for the mosaic. @@ -42,6 +43,7 @@ export class MosaicSupplyChangeTransaction extends Transaction { * @returns {MosaicSupplyChangeTransaction} */ public static create(deadline: Deadline, + maxFee: UInt64, mosaicId: MosaicId, direction: MosaicSupplyType, delta: UInt64, @@ -49,7 +51,7 @@ export class MosaicSupplyChangeTransaction extends Transaction { return new MosaicSupplyChangeTransaction(networkType, TransactionVersion.MOSAIC_SUPPLY_CHANGE, deadline, - new UInt64([0, 0]), + maxFee, mosaicId, direction, delta, @@ -60,7 +62,7 @@ export class MosaicSupplyChangeTransaction extends Transaction { * @param networkType * @param version * @param deadline - * @param fee + * @param maxFee * @param mosaicId * @param direction * @param delta @@ -71,7 +73,7 @@ export class MosaicSupplyChangeTransaction extends Transaction { constructor(networkType: NetworkType, version: number, deadline: Deadline, - fee: UInt64, + maxFee: UInt64, /** * The mosaic id. */ @@ -87,7 +89,7 @@ export class MosaicSupplyChangeTransaction extends Transaction { signature?: string, signer?: PublicAccount, transactionInfo?: TransactionInfo) { - super(TransactionType.MOSAIC_SUPPLY_CHANGE, networkType, version, deadline, fee, signature, signer, transactionInfo); + super(TransactionType.MOSAIC_SUPPLY_CHANGE, networkType, version, deadline, maxFee, signature, signer, transactionInfo); } /** @@ -97,7 +99,7 @@ export class MosaicSupplyChangeTransaction extends Transaction { protected buildTransaction(): VerifiableTransaction { return new MosaicSupplyChangeTransactionLibrary.Builder() .addDeadline(this.deadline.toDTO()) - .addFee(this.fee.toDTO()) + .addFee(this.maxFee.toDTO()) .addVersion(this.versionToDTO()) .addMosaicId(this.mosaicId.id.toDTO()) .addDirection(this.direction) diff --git a/src/model/transaction/RegisterNamespaceTransaction.ts b/src/model/transaction/RegisterNamespaceTransaction.ts index 4693697120..7e3994d996 100644 --- a/src/model/transaction/RegisterNamespaceTransaction.ts +++ b/src/model/transaction/RegisterNamespaceTransaction.ts @@ -35,19 +35,21 @@ export class RegisterNamespaceTransaction extends Transaction { /** * Create a root namespace object * @param deadline - The deadline to include the transaction. + * @param maxFee - Max fee defined by the sender * @param namespaceName - The namespace name. * @param duration - The duration of the namespace. * @param networkType - The network type. * @returns {RegisterNamespaceTransaction} */ public static createRootNamespace(deadline: Deadline, + maxFee: UInt64, namespaceName: string, duration: UInt64, networkType: NetworkType): RegisterNamespaceTransaction { return new RegisterNamespaceTransaction(networkType, TransactionVersion.REGISTER_NAMESPACE, deadline, - new UInt64([0, 0]), + maxFee, NamespaceType.RootNamespace, namespaceName, new NamespaceId(namespaceName), @@ -58,12 +60,14 @@ export class RegisterNamespaceTransaction extends Transaction { /** * Create a sub namespace object * @param deadline - The deadline to include the transaction. + * @param maxFee - Max fee defined by the sender * @param namespaceName - The namespace name. * @param parentNamespace - The parent namespace name. * @param networkType - The network type. * @returns {RegisterNamespaceTransaction} */ public static createSubNamespace(deadline: Deadline, + maxFee: UInt64, namespaceName: string, parentNamespace: string | NamespaceId, networkType: NetworkType): RegisterNamespaceTransaction { @@ -76,7 +80,7 @@ export class RegisterNamespaceTransaction extends Transaction { return new RegisterNamespaceTransaction(networkType, TransactionVersion.REGISTER_NAMESPACE, deadline, - new UInt64([0, 0]), + maxFee, NamespaceType.SubNamespace, namespaceName, new NamespaceId(subnamespaceNamespaceId(parentNamespace, namespaceName)), @@ -89,7 +93,7 @@ export class RegisterNamespaceTransaction extends Transaction { * @param networkType * @param version * @param deadline - * @param fee + * @param maxFee * @param namespaceType * @param namespaceName * @param namespaceId @@ -102,7 +106,7 @@ export class RegisterNamespaceTransaction extends Transaction { constructor(networkType: NetworkType, version: number, deadline: Deadline, - fee: UInt64, + maxFee: UInt64, /** * The namespace type could be namespace or sub namespace */ @@ -127,7 +131,7 @@ export class RegisterNamespaceTransaction extends Transaction { signature?: string, signer?: PublicAccount, transactionInfo?: TransactionInfo) { - super(TransactionType.REGISTER_NAMESPACE, networkType, version, deadline, fee, signature, signer, transactionInfo); + super(TransactionType.REGISTER_NAMESPACE, networkType, version, deadline, maxFee, signature, signer, transactionInfo); } /** @@ -137,7 +141,7 @@ export class RegisterNamespaceTransaction extends Transaction { protected buildTransaction(): VerifiableTransaction { let registerNamespacetransaction = new RegisterNamespaceTransactionLibrary.Builder() .addDeadline(this.deadline.toDTO()) - .addFee(this.fee.toDTO()) + .addFee(this.maxFee.toDTO()) .addVersion(this.versionToDTO()) .addNamespaceType(this.namespaceType) .addNamespaceId(this.namespaceId.id.toDTO()) diff --git a/src/model/transaction/SecretLockTransaction.ts b/src/model/transaction/SecretLockTransaction.ts index f08fc8d229..2c71b9461f 100644 --- a/src/model/transaction/SecretLockTransaction.ts +++ b/src/model/transaction/SecretLockTransaction.ts @@ -32,6 +32,7 @@ export class SecretLockTransaction extends Transaction { * Create a secret lock transaction object. * * @param deadline - The deadline to include the transaction. + * @param maxFee - Max fee defined by the sender * @param mosaic - The locked mosaic. * @param duration - The funds lock duration. * @param hashType - The hash algorithm secret is generated with. @@ -42,6 +43,7 @@ export class SecretLockTransaction extends Transaction { * @return a SecretLockTransaction instance */ public static create(deadline: Deadline, + maxFee: UInt64, mosaic: Mosaic, duration: UInt64, hashType: HashType, @@ -52,7 +54,7 @@ export class SecretLockTransaction extends Transaction { networkType, TransactionVersion.SECRET_LOCK, deadline, - UInt64.fromUint(0), + maxFee, mosaic, duration, hashType, @@ -65,7 +67,7 @@ export class SecretLockTransaction extends Transaction { * @param networkType * @param version * @param deadline - * @param fee + * @param maxFee * @param mosaic * @param duration * @param hashType @@ -78,7 +80,7 @@ export class SecretLockTransaction extends Transaction { constructor(networkType: NetworkType, version: number, deadline: Deadline, - fee: UInt64, + maxFee: UInt64, /** * The locked mosaic. */ @@ -102,7 +104,7 @@ export class SecretLockTransaction extends Transaction { signature?: string, signer?: PublicAccount, transactionInfo?: TransactionInfo) { - super(TransactionType.SECRET_LOCK, networkType, version, deadline, fee, signature, signer, transactionInfo); + super(TransactionType.SECRET_LOCK, networkType, version, deadline, maxFee, signature, signer, transactionInfo); if (!HashTypeLengthValidator(hashType, this.secret)) { throw new Error('HashType and Secret have incompatible length or not hexadecimal string'); } @@ -116,7 +118,7 @@ export class SecretLockTransaction extends Transaction { return new SecretLockTransactionLibrary.Builder() .addDeadline(this.deadline.toDTO()) .addType(this.type) - .addFee(this.fee.toDTO()) + .addFee(this.maxFee.toDTO()) .addVersion(this.versionToDTO()) .addMosaicId(this.mosaic.id.id.toDTO()) .addMosaicAmount(this.mosaic.amount.toDTO()) diff --git a/src/model/transaction/SecretProofTransaction.ts b/src/model/transaction/SecretProofTransaction.ts index 08b92667f9..d36f69db19 100644 --- a/src/model/transaction/SecretProofTransaction.ts +++ b/src/model/transaction/SecretProofTransaction.ts @@ -30,6 +30,7 @@ export class SecretProofTransaction extends Transaction { * Create a secret proof transaction object. * * @param deadline - The deadline to include the transaction. + * @param maxFee - Max fee defined by the sender * @param hashType - The hash algorithm secret is generated with. * @param secret - The seed proof hashed. * @param proof - The seed proof. @@ -38,6 +39,7 @@ export class SecretProofTransaction extends Transaction { * @return a SecretProofTransaction instance */ public static create(deadline: Deadline, + maxFee: UInt64, hashType: HashType, secret: string, proof: string, @@ -46,7 +48,7 @@ export class SecretProofTransaction extends Transaction { networkType, TransactionVersion.SECRET_PROOF, deadline, - UInt64.fromUint(0), + maxFee, hashType, secret, proof, @@ -57,7 +59,7 @@ export class SecretProofTransaction extends Transaction { * @param networkType * @param version * @param deadline - * @param fee + * @param maxFee * @param hashType * @param secret * @param proof @@ -68,14 +70,14 @@ export class SecretProofTransaction extends Transaction { constructor(networkType: NetworkType, version: number, deadline: Deadline, - fee: UInt64, + maxFee: UInt64, public readonly hashType: HashType, public readonly secret: string, public readonly proof: string, signature?: string, signer?: PublicAccount, transactionInfo?: TransactionInfo) { - super(TransactionType.SECRET_PROOF, networkType, version, deadline, fee, signature, signer, transactionInfo); + super(TransactionType.SECRET_PROOF, networkType, version, deadline, maxFee, signature, signer, transactionInfo); if (!HashTypeLengthValidator(hashType, this.secret)) { throw new Error('HashType and Secret have incompatible length or not hexadecimal string'); } @@ -89,7 +91,7 @@ export class SecretProofTransaction extends Transaction { return new SecretProofTransactionLibrary.Builder() .addDeadline(this.deadline.toDTO()) .addType(this.type) - .addFee(this.fee.toDTO()) + .addFee(this.maxFee.toDTO()) .addVersion(this.versionToDTO()) .addHashAlgorithm(this.hashType) .addSecret(this.secret) diff --git a/src/model/transaction/TransferTransaction.ts b/src/model/transaction/TransferTransaction.ts index cac454588a..b613524b8b 100644 --- a/src/model/transaction/TransferTransaction.ts +++ b/src/model/transaction/TransferTransaction.ts @@ -35,6 +35,7 @@ export class TransferTransaction extends Transaction { /** * Create a transfer transaction object * @param deadline - The deadline to include the transaction. + * @param maxFee - Max fee defined by the sender * @param recipient - The recipient of the transaction. * @param mosaics - The array of mosaics. * @param message - The transaction message. @@ -42,6 +43,7 @@ export class TransferTransaction extends Transaction { * @returns {TransferTransaction} */ public static create(deadline: Deadline, + maxFee: UInt64, recipient: Address | NamespaceId, mosaics: Mosaic[], message: Message, @@ -49,7 +51,7 @@ export class TransferTransaction extends Transaction { return new TransferTransaction(networkType, TransactionVersion.TRANSFER, deadline, - new UInt64([0, 0]), + maxFee, recipient, mosaics, message); @@ -59,7 +61,7 @@ export class TransferTransaction extends Transaction { * @param networkType * @param version * @param deadline - * @param fee + * @param maxFee * @param recipient * @param mosaics * @param message @@ -70,7 +72,7 @@ export class TransferTransaction extends Transaction { constructor(networkType: NetworkType, version: number, deadline: Deadline, - fee: UInt64, + maxFee: UInt64, /** * The address of the recipient. */ @@ -86,7 +88,7 @@ export class TransferTransaction extends Transaction { signature?: string, signer?: PublicAccount, transactionInfo?: TransactionInfo) { - super(TransactionType.TRANSFER, networkType, version, deadline, fee, signature, signer, transactionInfo); + super(TransactionType.TRANSFER, networkType, version, deadline, maxFee, signature, signer, transactionInfo); } /** @@ -112,7 +114,7 @@ export class TransferTransaction extends Transaction { protected buildTransaction(): VerifiableTransaction { return new TransferTransactionLibrary.Builder() .addDeadline(this.deadline.toDTO()) - .addFee(this.fee.toDTO()) + .addFee(this.maxFee.toDTO()) .addVersion(this.versionToDTO()) .addRecipient(this.recipientToString()) .addMosaics(this.mosaics.map((mosaic) => mosaic.toDTO())) diff --git a/test/infrastructure/TransactionHttp.spec.ts b/test/infrastructure/TransactionHttp.spec.ts index 09c72ad659..e252968812 100644 --- a/test/infrastructure/TransactionHttp.spec.ts +++ b/test/infrastructure/TransactionHttp.spec.ts @@ -22,6 +22,7 @@ import {AggregateTransaction} from '../../src/model/transaction/AggregateTransac import {Deadline} from '../../src/model/transaction/Deadline'; import {PlainMessage} from '../../src/model/transaction/PlainMessage'; import {TransferTransaction} from '../../src/model/transaction/TransferTransaction'; +import {UInt64} from '../../src/model/UInt64'; import {NIS2_URL, TestingAccount} from '../conf/conf.spec'; describe('TransactionHttp', () => { @@ -29,6 +30,7 @@ describe('TransactionHttp', () => { it('should return an error when a non aggregate transaction bonded is announced via announceAggregateBonded method', () => { const tx = TransferTransaction.create( Deadline.create(), + new UInt64([0, 0]), Address.createFromRawAddress('SAGY2PTFX4T2XYKYXTJXYCTQRP3FESQH5MEQI2RQ'), [], PlainMessage.create('Hi'), @@ -36,6 +38,7 @@ describe('TransactionHttp', () => { ); const aggTx = AggregateTransaction.createComplete( Deadline.create(), + new UInt64([0, 0]), [ tx.toAggregate(account.publicAccount), ], diff --git a/test/model/transaction/AddressAliasTransaction.spec.ts b/test/model/transaction/AddressAliasTransaction.spec.ts index 68de07ec25..75ec2142b7 100644 --- a/test/model/transaction/AddressAliasTransaction.spec.ts +++ b/test/model/transaction/AddressAliasTransaction.spec.ts @@ -38,6 +38,7 @@ describe('AddressAliasTransaction', () => { const address = Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'); const addressAliasTransaction = AddressAliasTransaction.create( Deadline.create(), + new UInt64([0, 0]), AliasActionType.Link, namespaceId, address, diff --git a/test/model/transaction/AggregateTransaction.spec.ts b/test/model/transaction/AggregateTransaction.spec.ts index 4ecb1f129e..d1bba3e64a 100644 --- a/test/model/transaction/AggregateTransaction.spec.ts +++ b/test/model/transaction/AggregateTransaction.spec.ts @@ -48,6 +48,7 @@ describe('AggregateTransaction', () => { it('should createComplete an AggregateTransaction object with TransferTransaction', () => { const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), + new UInt64([0, 0]), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), [], PlainMessage.create('test-message'), @@ -56,6 +57,7 @@ describe('AggregateTransaction', () => { const aggregateTransaction = AggregateTransaction.createComplete( Deadline.create(), + new UInt64([0, 0]), [transferTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -73,6 +75,7 @@ describe('AggregateTransaction', () => { it('should createComplete an AggregateTransaction object with RegisterNamespaceTransaction', () => { const registerNamespaceTransaction = RegisterNamespaceTransaction.createRootNamespace( Deadline.create(), + new UInt64([0, 0]), 'root-test-namespace', UInt64.fromUint(1000), NetworkType.MIJIN_TEST, @@ -80,6 +83,7 @@ describe('AggregateTransaction', () => { const aggregateTransaction = AggregateTransaction.createComplete( Deadline.create(), + new UInt64([0, 0]), [registerNamespaceTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, [], @@ -99,6 +103,7 @@ describe('AggregateTransaction', () => { it('should createComplete an AggregateTransaction object with MosaicDefinitionTransaction', () => { const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( Deadline.create(), + new UInt64([0, 0]), new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce new MosaicId(UInt64.fromUint(1).toDTO()), // ID MosaicProperties.create({ @@ -113,6 +118,7 @@ describe('AggregateTransaction', () => { const aggregateTransaction = AggregateTransaction.createComplete( Deadline.create(), + new UInt64([0, 0]), [mosaicDefinitionTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, [], @@ -132,6 +138,7 @@ describe('AggregateTransaction', () => { const mosaicId = new MosaicId([2262289484, 3405110546]); const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( Deadline.create(), + new UInt64([0, 0]), mosaicId, MosaicSupplyType.Increase, UInt64.fromUint(10), @@ -140,6 +147,7 @@ describe('AggregateTransaction', () => { const aggregateTransaction = AggregateTransaction.createComplete( Deadline.create(), + new UInt64([0, 0]), [mosaicSupplyChangeTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, [], @@ -158,6 +166,7 @@ describe('AggregateTransaction', () => { it('should createComplete an AggregateTransaction object with ModifyMultisigAccountTransaction', () => { const modifyMultisigAccountTransaction = ModifyMultisigAccountTransaction.create( Deadline.create(), + new UInt64([0, 0]), 2, 1, [new MultisigCosignatoryModification( @@ -174,6 +183,7 @@ describe('AggregateTransaction', () => { ); const aggregateTransaction = AggregateTransaction.createComplete( Deadline.create(), + new UInt64([0, 0]), [modifyMultisigAccountTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, [], @@ -193,12 +203,14 @@ describe('AggregateTransaction', () => { it('should createComplete an AggregateTransaction object with different cosignatories', () => { const transferTransaction = TransferTransaction.create( Deadline.create(), + new UInt64([0, 0]), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), [], PlainMessage.create('test-message'), NetworkType.MIJIN_TEST, ); const aggregateTransaction = AggregateTransaction.createComplete(Deadline.create(), + new UInt64([0, 0]), [transferTransaction.toAggregate(MultisigAccount.publicAccount)], NetworkType.MIJIN_TEST, [], @@ -242,7 +254,7 @@ describe('AggregateTransaction', () => { 3266625578, 11, ], - fee: [ + maxFee: [ 0, 0, ], @@ -298,6 +310,7 @@ describe('AggregateTransaction', () => { it('should have type 0x4141 when it\'s complete', () => { const aggregateTransaction = AggregateTransaction.createComplete( Deadline.create(), + new UInt64([0, 0]), [], NetworkType.MIJIN_TEST, [], @@ -309,6 +322,7 @@ describe('AggregateTransaction', () => { it('should have type 0x4241 when it\'s bonded', () => { const aggregateTransaction = AggregateTransaction.createBonded( Deadline.create(), + new UInt64([0, 0]), [], NetworkType.MIJIN_TEST, ); diff --git a/test/model/transaction/CosignatureTransaction.spec.ts b/test/model/transaction/CosignatureTransaction.spec.ts index 25cc89a4e4..edf8c18c3c 100644 --- a/test/model/transaction/CosignatureTransaction.spec.ts +++ b/test/model/transaction/CosignatureTransaction.spec.ts @@ -53,7 +53,7 @@ describe('CosignatureTransaction', () => { 3266625578, 11, ], - fee: [ + maxFee: [ 0, 0, ], diff --git a/test/model/transaction/HashLockTransaction.spec.ts b/test/model/transaction/HashLockTransaction.spec.ts index c1d946c9ed..f196d89d0e 100644 --- a/test/model/transaction/HashLockTransaction.spec.ts +++ b/test/model/transaction/HashLockTransaction.spec.ts @@ -27,12 +27,14 @@ describe('HashLockTransaction', () => { it('creation with an aggregate bonded tx', () => { const aggregateTransaction = AggregateTransaction.createBonded( Deadline.create(), + new UInt64([0, 0]), [], NetworkType.MIJIN_TEST, [], ); const signedTransaction = account.sign(aggregateTransaction); const transaction = HashLockTransaction.create(Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createRelative(10), UInt64.fromUint(10), signedTransaction, @@ -45,6 +47,7 @@ describe('HashLockTransaction', () => { it('should throw exception if it is not a aggregate bonded tx', () => { const aggregateTransaction = AggregateTransaction.createComplete( Deadline.create(), + new UInt64([0, 0]), [], NetworkType.MIJIN_TEST, [], @@ -52,6 +55,7 @@ describe('HashLockTransaction', () => { const signedTransaction = account.sign(aggregateTransaction); expect(() => { HashLockTransaction.create(Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createRelative(10), UInt64.fromUint(10), signedTransaction, diff --git a/test/model/transaction/LockFundsTransaction.spec.ts b/test/model/transaction/LockFundsTransaction.spec.ts index bf475519e9..a321017d95 100644 --- a/test/model/transaction/LockFundsTransaction.spec.ts +++ b/test/model/transaction/LockFundsTransaction.spec.ts @@ -28,12 +28,14 @@ describe('LockFundsTransaction', () => { it('creation with an aggregate bonded tx', () => { const aggregateTransaction = AggregateTransaction.createBonded( Deadline.create(), + new UInt64([0, 0]), [], NetworkType.MIJIN_TEST, [], ); const signedTransaction = account.sign(aggregateTransaction); const transaction = LockFundsTransaction.create(Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createRelative(10), UInt64.fromUint(10), signedTransaction, @@ -46,6 +48,7 @@ describe('LockFundsTransaction', () => { it('should throw exception if it is not a aggregate bonded tx', () => { const aggregateTransaction = AggregateTransaction.createComplete( Deadline.create(), + new UInt64([0, 0]), [], NetworkType.MIJIN_TEST, [], @@ -53,6 +56,7 @@ describe('LockFundsTransaction', () => { const signedTransaction = account.sign(aggregateTransaction); expect(() => { LockFundsTransaction.create(Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createRelative(10), UInt64.fromUint(10), signedTransaction, diff --git a/test/model/transaction/ModifyMultisigAccountTransaction.spec.ts b/test/model/transaction/ModifyMultisigAccountTransaction.spec.ts index 6a0e7a8589..ea666f97d8 100644 --- a/test/model/transaction/ModifyMultisigAccountTransaction.spec.ts +++ b/test/model/transaction/ModifyMultisigAccountTransaction.spec.ts @@ -22,6 +22,7 @@ import {Deadline} from '../../../src/model/transaction/Deadline'; import {ModifyMultisigAccountTransaction} from '../../../src/model/transaction/ModifyMultisigAccountTransaction'; import {MultisigCosignatoryModification} from '../../../src/model/transaction/MultisigCosignatoryModification'; import {MultisigCosignatoryModificationType} from '../../../src/model/transaction/MultisigCosignatoryModificationType'; +import {UInt64} from '../../../src/model/UInt64'; import {TestingAccount} from '../../conf/conf.spec'; describe('ModifyMultisigAccountTransaction', () => { @@ -34,6 +35,7 @@ describe('ModifyMultisigAccountTransaction', () => { it('should createComplete an ModifyMultisigAccountTransaction object and sign it', () => { const modifyMultisigAccountTransaction = ModifyMultisigAccountTransaction.create( Deadline.create(), + new UInt64([0, 0]), 2, 1, [new MultisigCosignatoryModification( diff --git a/test/model/transaction/MosaicAliasTransaction.spec.ts b/test/model/transaction/MosaicAliasTransaction.spec.ts index e72c5256d2..fbeebc025e 100644 --- a/test/model/transaction/MosaicAliasTransaction.spec.ts +++ b/test/model/transaction/MosaicAliasTransaction.spec.ts @@ -37,6 +37,7 @@ describe('MosaicAliasTransaction', () => { const mosaicId = new MosaicId([2262289484, 3405110546]); const mosaicAliasTransaction = MosaicAliasTransaction.create( Deadline.create(), + new UInt64([0, 0]), AliasActionType.Link, namespaceId, mosaicId, diff --git a/test/model/transaction/MosaicDefinitionTransaction.spec.ts b/test/model/transaction/MosaicDefinitionTransaction.spec.ts index 41799026bc..d6571aa637 100644 --- a/test/model/transaction/MosaicDefinitionTransaction.spec.ts +++ b/test/model/transaction/MosaicDefinitionTransaction.spec.ts @@ -36,6 +36,7 @@ describe('MosaicDefinitionTransaction', () => { it('should createComplete an MosaicDefinitionTransaction object and sign it with flags 7', () => { const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( Deadline.create(), + new UInt64([0, 0]), new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce new MosaicId(UInt64.fromUint(1).toDTO()), // ID MosaicProperties.create({ @@ -68,6 +69,7 @@ describe('MosaicDefinitionTransaction', () => { const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( Deadline.create(), + new UInt64([0, 0]), new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce new MosaicId(UInt64.fromUint(1).toDTO()), // ID MosaicProperties.create({ diff --git a/test/model/transaction/MosaicSupplyChangeTransaction.spec.ts b/test/model/transaction/MosaicSupplyChangeTransaction.spec.ts index ae6e4c99ef..97326f76d4 100644 --- a/test/model/transaction/MosaicSupplyChangeTransaction.spec.ts +++ b/test/model/transaction/MosaicSupplyChangeTransaction.spec.ts @@ -35,6 +35,7 @@ describe('MosaicSupplyChangeTransaction', () => { const mosaicId = new MosaicId([2262289484, 3405110546]); const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( Deadline.create(), + new UInt64([0, 0]), mosaicId, MosaicSupplyType.Increase, UInt64.fromUint(10), diff --git a/test/model/transaction/RegisterNamespaceTransaction.spec.ts b/test/model/transaction/RegisterNamespaceTransaction.spec.ts index 8ff4745066..1971fecfc7 100644 --- a/test/model/transaction/RegisterNamespaceTransaction.spec.ts +++ b/test/model/transaction/RegisterNamespaceTransaction.spec.ts @@ -32,6 +32,7 @@ describe('RegisterNamespaceTransaction', () => { it('should createComplete an root RegisterNamespaceTransaction object and sign it', () => { const registerNamespaceTransaction = RegisterNamespaceTransaction.createRootNamespace( Deadline.create(), + new UInt64([0, 0]), 'root-test-namespace', UInt64.fromUint(1000), NetworkType.MIJIN_TEST, @@ -52,6 +53,7 @@ describe('RegisterNamespaceTransaction', () => { it('should createComplete an sub RegisterNamespaceTransaction object and sign it', () => { const registerNamespaceTransaction = RegisterNamespaceTransaction.createSubNamespace( Deadline.create(), + new UInt64([0, 0]), 'root-test-namespace', 'parent-test-namespace', NetworkType.MIJIN_TEST, diff --git a/test/model/transaction/SecretLockTransaction.spec.ts b/test/model/transaction/SecretLockTransaction.spec.ts index 7166ab790e..743108afc2 100644 --- a/test/model/transaction/SecretLockTransaction.spec.ts +++ b/test/model/transaction/SecretLockTransaction.spec.ts @@ -33,6 +33,7 @@ describe('SecretLockTransaction', () => { const recipient = Address.createFromRawAddress('SDBDG4IT43MPCW2W4CBBCSJJT42AYALQN7A4VVWL'); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Sha3_256, @@ -53,6 +54,7 @@ describe('SecretLockTransaction', () => { const recipient = Address.createFromRawAddress('SDBDG4IT43MPCW2W4CBBCSJJT42AYALQN7A4VVWL'); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Sha3_256, @@ -68,6 +70,7 @@ describe('SecretLockTransaction', () => { const recipient = Address.createFromRawAddress('SDBDG4IT43MPCW2W4CBBCSJJT42AYALQN7A4VVWL'); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Keccak_256, @@ -88,6 +91,7 @@ describe('SecretLockTransaction', () => { const recipient = Address.createFromRawAddress('SDBDG4IT43MPCW2W4CBBCSJJT42AYALQN7A4VVWL'); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Keccak_256, @@ -102,6 +106,7 @@ describe('SecretLockTransaction', () => { const recipient = Address.createFromRawAddress('SDBDG4IT43MPCW2W4CBBCSJJT42AYALQN7A4VVWL'); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_160, @@ -122,6 +127,7 @@ describe('SecretLockTransaction', () => { const recipient = Address.createFromRawAddress('SDBDG4IT43MPCW2W4CBBCSJJT42AYALQN7A4VVWL'); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_160, @@ -136,6 +142,7 @@ describe('SecretLockTransaction', () => { const recipient = Address.createFromRawAddress('SDBDG4IT43MPCW2W4CBBCSJJT42AYALQN7A4VVWL'); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_256, @@ -156,6 +163,7 @@ describe('SecretLockTransaction', () => { const recipient = Address.createFromRawAddress('SDBDG4IT43MPCW2W4CBBCSJJT42AYALQN7A4VVWL'); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), + new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_256, diff --git a/test/model/transaction/SecretProofTransaction.spec.ts b/test/model/transaction/SecretProofTransaction.spec.ts index 1434017965..e5be2125b5 100644 --- a/test/model/transaction/SecretProofTransaction.spec.ts +++ b/test/model/transaction/SecretProofTransaction.spec.ts @@ -21,6 +21,7 @@ import {NetworkType} from '../../../src/model/blockchain/NetworkType'; import {Deadline} from '../../../src/model/transaction/Deadline'; import {HashType} from '../../../src/model/transaction/HashType'; import {SecretProofTransaction} from '../../../src/model/transaction/SecretProofTransaction'; +import {UInt64} from '../../../src/model/UInt64'; describe('SecretProofTransaction', () => { @@ -28,6 +29,7 @@ describe('SecretProofTransaction', () => { const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9DF538782BF199C189DABEAC7'; const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), + new UInt64([0, 0]), HashType.Op_Sha3_256, sha3_256.create().update(convert.hexToUint8(proof)).hex(), proof, @@ -43,6 +45,7 @@ describe('SecretProofTransaction', () => { const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9DF538782BF199C189DABEAC7'; const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), + new UInt64([0, 0]), HashType.Op_Sha3_256, 'non valid hash', proof, @@ -54,6 +57,7 @@ describe('SecretProofTransaction', () => { const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9DF538782BF199C189DABEAC7'; const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), + new UInt64([0, 0]), HashType.Op_Keccak_256, keccak_256.create().update(convert.hexToUint8(proof)).hex(), proof, @@ -69,6 +73,7 @@ describe('SecretProofTransaction', () => { const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9DF538782BF199C189DABEAC7'; const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), + new UInt64([0, 0]), HashType.Op_Keccak_256, 'non valid hash', proof, @@ -80,6 +85,7 @@ describe('SecretProofTransaction', () => { const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9'; const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), + new UInt64([0, 0]), HashType.Op_Hash_160, CryptoJS.RIPEMD160(CryptoJS.SHA256(proof).toString(CryptoJS.enc.Hex)).toString(CryptoJS.enc.Hex), proof, @@ -95,6 +101,7 @@ describe('SecretProofTransaction', () => { const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9DF538782BF199C189DABEAC7'; const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), + new UInt64([0, 0]), HashType.Op_Hash_160, 'non valid hash', proof, @@ -107,6 +114,7 @@ describe('SecretProofTransaction', () => { const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9DF538782BF199C189DABEAC7'; const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), + new UInt64([0, 0]), HashType.Op_Hash_256, CryptoJS.SHA256(CryptoJS.SHA256(proof).toString(CryptoJS.enc.Hex)).toString(CryptoJS.enc.Hex), proof, @@ -122,6 +130,7 @@ describe('SecretProofTransaction', () => { const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9DF538782BF199C189DABEAC7'; const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), + new UInt64([0, 0]), HashType.Op_Hash_256, 'non valid hash', proof, diff --git a/test/model/transaction/TransferTransaction.spec.ts b/test/model/transaction/TransferTransaction.spec.ts index 2fabc317b3..cb3dc03a5e 100644 --- a/test/model/transaction/TransferTransaction.spec.ts +++ b/test/model/transaction/TransferTransaction.spec.ts @@ -23,6 +23,7 @@ import { NamespaceId } from '../../../src/model/namespace/NamespaceId'; import { Deadline } from '../../../src/model/transaction/Deadline'; import { PlainMessage } from '../../../src/model/transaction/PlainMessage'; import { TransferTransaction } from '../../../src/model/transaction/TransferTransaction'; +import {UInt64} from '../../../src/model/UInt64'; import { TestingAccount } from '../../conf/conf.spec'; describe('TransferTransaction', () => { @@ -35,6 +36,7 @@ describe('TransferTransaction', () => { it('should createComplete an TransferTransaction object and sign it without mosaics', () => { const transferTransaction = TransferTransaction.create( Deadline.create(), + new UInt64([0, 0]), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), [], PlainMessage.create('test-message'), @@ -57,6 +59,7 @@ describe('TransferTransaction', () => { it('should createComplete an TransferTransaction object and sign it with mosaics', () => { const transferTransaction = TransferTransaction.create( Deadline.create(), + new UInt64([0, 0]), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), [ NetworkCurrencyMosaic.createRelative(100), From b1905fd226bf1a6e00105a3ff80c66479b0285d3 Mon Sep 17 00:00:00 2001 From: Greg S Date: Mon, 25 Mar 2019 17:38:53 +0100 Subject: [PATCH 04/83] issue #53: make maxFee field optional with default value 0 --- e2e/infrastructure/TransactionHttp.spec.ts | 57 ----------------- e2e/infrastructure/TransactionUtils.ts | 4 -- .../transaction/AccountLinkTransaction.ts | 14 +++-- .../transaction/AccountPropertyTransaction.ts | 61 ++++++++++++++----- .../transaction/AddressAliasTransaction.ts | 8 +-- src/model/transaction/AggregateTransaction.ts | 12 ++-- src/model/transaction/AliasTransaction.ts | 30 ++++++--- src/model/transaction/LockFundsTransaction.ts | 7 ++- ...ModifyAccountPropertyAddressTransaction.ts | 14 +++-- ...ifyAccountPropertyEntityTypeTransaction.ts | 14 +++-- .../ModifyAccountPropertyMosaicTransaction.ts | 14 +++-- .../ModifyMultisigAccountTransaction.ts | 6 +- .../transaction/MosaicAliasTransaction.ts | 6 +- .../MosaicDefinitionTransaction.ts | 6 +- .../MosaicSupplyChangeTransaction.ts | 6 +- .../RegisterNamespaceTransaction.ts | 12 ++-- .../transaction/SecretLockTransaction.ts | 6 +- .../transaction/SecretProofTransaction.ts | 6 +- src/model/transaction/TransferTransaction.ts | 6 +- test/infrastructure/TransactionHttp.spec.ts | 2 - .../AccountLinkTransaction.spec.ts | 26 ++++++++ .../AccountPropertyTransaction.spec.ts | 36 +++++++++++ .../AddressAliasTransaction.spec.ts | 32 +++++++++- .../transaction/AggregateTransaction.spec.ts | 55 ++++++++++++----- .../transaction/HashLockTransaction.spec.ts | 4 -- .../transaction/LockFundsTransaction.spec.ts | 44 +++++++++++-- .../ModifyMultisigAccountTransaction.spec.ts | 46 +++++++++++++- .../MosaicAliasTransaction.spec.ts | 32 +++++++++- .../MosaicDefinitionTransaction.spec.ts | 41 ++++++++++++- .../MosaicSupplyChangeTransaction.spec.ts | 30 ++++++++- .../RegisterNamespaceTransaction.spec.ts | 27 +++++++- .../transaction/SecretLockTransaction.spec.ts | 43 ++++++++++--- .../SecretProofTransaction.spec.ts | 37 ++++++++--- .../transaction/TransferTransaction.spec.ts | 29 ++++++++- 34 files changed, 575 insertions(+), 198 deletions(-) diff --git a/e2e/infrastructure/TransactionHttp.spec.ts b/e2e/infrastructure/TransactionHttp.spec.ts index 9f70eca25e..de6cd4aaca 100644 --- a/e2e/infrastructure/TransactionHttp.spec.ts +++ b/e2e/infrastructure/TransactionHttp.spec.ts @@ -97,7 +97,6 @@ describe('TransactionHttp', () => { describe('TransferTransaction', () => { const transferTransaction = TransferTransaction.create( Deadline.create(), - new UInt64([0, 0]), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), [NetworkCurrencyMosaic.createRelative(10)], PlainMessage.create('test-message'), @@ -110,7 +109,6 @@ describe('TransactionHttp', () => { }); it('aggregate', (done) => { const aggregateTransaction = AggregateTransaction.createComplete(Deadline.create(), - new UInt64([0, 0]), [transferTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, [], @@ -126,7 +124,6 @@ describe('TransactionHttp', () => { namespaceName = 'root-test-namespace-' + Math.floor(Math.random() * 10000); const registerNamespaceTransaction = RegisterNamespaceTransaction.createRootNamespace( Deadline.create(), - new UInt64([0, 0]), namespaceName, UInt64.fromUint(1000), NetworkType.MIJIN_TEST, @@ -138,13 +135,11 @@ describe('TransactionHttp', () => { it('aggregate', (done) => { const registerNamespaceTransaction = RegisterNamespaceTransaction.createRootNamespace( Deadline.create(), - new UInt64([0, 0]), 'root-test-namespace-' + Math.floor(Math.random() * 10000), UInt64.fromUint(1000), NetworkType.MIJIN_TEST, ); const aggregateTransaction = AggregateTransaction.createComplete(Deadline.create(), - new UInt64([0, 0]), [registerNamespaceTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -157,7 +152,6 @@ describe('TransactionHttp', () => { it('standalone', (done) => { const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( Deadline.create(), - new UInt64([0, 0]), new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce new MosaicId(UInt64.fromUint(1).toDTO()), // ID MosaicProperties.create({ @@ -177,7 +171,6 @@ describe('TransactionHttp', () => { it('aggregate', (done) => { const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( Deadline.create(), - new UInt64([0, 0]), new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce new MosaicId(UInt64.fromUint(1).toDTO()), // ID MosaicProperties.create({ @@ -190,7 +183,6 @@ describe('TransactionHttp', () => { NetworkType.MIJIN_TEST, ); const aggregateTransaction = AggregateTransaction.createComplete(Deadline.create(), - new UInt64([0, 0]), [mosaicDefinitionTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -203,7 +195,6 @@ describe('TransactionHttp', () => { it('standalone', (done) => { const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( Deadline.create(), - new UInt64([0, 0]), mosaicId, MosaicSupplyType.Increase, UInt64.fromUint(10), @@ -216,14 +207,12 @@ describe('TransactionHttp', () => { it('aggregate', (done) => { const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( Deadline.create(), - new UInt64([0, 0]), mosaicId, MosaicSupplyType.Increase, UInt64.fromUint(10), NetworkType.MIJIN_TEST, ); const aggregateTransaction = AggregateTransaction.createComplete(Deadline.create(), - new UInt64([0, 0]), [mosaicSupplyChangeTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -236,7 +225,6 @@ describe('TransactionHttp', () => { it('should sign a ModifyMultisigAccountTransaction with cosignatories', (done) => { const modifyMultisigAccountTransaction = ModifyMultisigAccountTransaction.create( Deadline.create(), - new UInt64([0, 0]), 0, 0, [new MultisigCosignatoryModification( @@ -247,7 +235,6 @@ describe('TransactionHttp', () => { NetworkType.MIJIN_TEST, ); const aggregateTransaction = AggregateTransaction.createBonded(Deadline.create(20, ChronoUnit.MINUTES), - new UInt64([0, 0]), [modifyMultisigAccountTransaction.toAggregate(MultisigAccount.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -258,7 +245,6 @@ describe('TransactionHttp', () => { ); const lockFundsTransaction = LockFundsTransaction.create(Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createRelative(10), UInt64.fromUint(10000), signedTransaction, @@ -279,7 +265,6 @@ describe('TransactionHttp', () => { it('CosignatureTransaction', (done) => { const transferTransaction = TransferTransaction.create( Deadline.create(), - new UInt64([0, 0]), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), [NetworkCurrencyMosaic.createRelative(1)], PlainMessage.create('test-message'), @@ -287,7 +272,6 @@ describe('TransactionHttp', () => { ); const aggregateTransaction = AggregateTransaction.createBonded( Deadline.create(2, ChronoUnit.MINUTES), - new UInt64([0, 0]), [transferTransaction.toAggregate(MultisigAccount.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -296,7 +280,6 @@ describe('TransactionHttp', () => { ); const lockFundsTransaction = LockFundsTransaction.create(Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createRelative(10), UInt64.fromUint(10000), signedTransaction, @@ -327,7 +310,6 @@ describe('TransactionHttp', () => { it('standalone', (done) => { const aggregateTransaction = AggregateTransaction.createBonded( Deadline.create(), - new UInt64([0, 0]), [], NetworkType.MIJIN_TEST, [], @@ -335,7 +317,6 @@ describe('TransactionHttp', () => { const signedTransaction = account.sign(aggregateTransaction); const lockFundsTransaction = LockFundsTransaction.create(Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createRelative(10), UInt64.fromUint(10000), signedTransaction, @@ -347,20 +328,17 @@ describe('TransactionHttp', () => { it('aggregate', (done) => { const aggregateTransaction = AggregateTransaction.createBonded( Deadline.create(), - new UInt64([0, 0]), [], NetworkType.MIJIN_TEST, [], ); const signedTransaction = account.sign(aggregateTransaction); const lockFundsTransaction = LockFundsTransaction.create(Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createRelative(10), UInt64.fromUint(10), signedTransaction, NetworkType.MIJIN_TEST); const aggregateLockFundsTransaction = AggregateTransaction.createComplete(Deadline.create(), - new UInt64([0, 0]), [lockFundsTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -374,7 +352,6 @@ describe('TransactionHttp', () => { it('standalone', (done) => { const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Sha3_256, @@ -389,7 +366,6 @@ describe('TransactionHttp', () => { it('aggregate', (done) => { const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Sha3_256, @@ -398,7 +374,6 @@ describe('TransactionHttp', () => { NetworkType.MIJIN_TEST, ); const aggregateSecretLockTransaction = AggregateTransaction.createComplete(Deadline.create(), - new UInt64([0, 0]), [secretLockTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -410,7 +385,6 @@ describe('TransactionHttp', () => { it('standalone', (done) => { const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Keccak_256, @@ -425,7 +399,6 @@ describe('TransactionHttp', () => { it('aggregate', (done) => { const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Keccak_256, @@ -434,7 +407,6 @@ describe('TransactionHttp', () => { NetworkType.MIJIN_TEST, ); const aggregateSecretLockTransaction = AggregateTransaction.createComplete(Deadline.create(), - new UInt64([0, 0]), [secretLockTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -446,7 +418,6 @@ describe('TransactionHttp', () => { it('standalone', (done) => { const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_160, @@ -461,7 +432,6 @@ describe('TransactionHttp', () => { it('aggregate', (done) => { const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_160, @@ -470,7 +440,6 @@ describe('TransactionHttp', () => { NetworkType.MIJIN_TEST, ); const aggregateSecretLockTransaction = AggregateTransaction.createComplete(Deadline.create(), - new UInt64([0, 0]), [secretLockTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -482,7 +451,6 @@ describe('TransactionHttp', () => { it('standalone', (done) => { const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_256, @@ -497,7 +465,6 @@ describe('TransactionHttp', () => { it('aggregate', (done) => { const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_256, @@ -506,7 +473,6 @@ describe('TransactionHttp', () => { NetworkType.MIJIN_TEST, ); const aggregateSecretLockTransaction = AggregateTransaction.createComplete(Deadline.create(), - new UInt64([0, 0]), [secretLockTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -523,7 +489,6 @@ describe('TransactionHttp', () => { const proof = convert.uint8ToHex(secretSeed); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Sha3_256, @@ -534,7 +499,6 @@ describe('TransactionHttp', () => { validateTransactionAnnounceCorrectly(account.address, () => { const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), - new UInt64([0, 0]), HashType.Op_Sha3_256, secret, proof, @@ -552,7 +516,6 @@ describe('TransactionHttp', () => { const proof = convert.uint8ToHex(secretSeed); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Sha3_256, @@ -563,14 +526,12 @@ describe('TransactionHttp', () => { validateTransactionAnnounceCorrectly(account.address, () => { const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), - new UInt64([0, 0]), HashType.Op_Sha3_256, secret, proof, NetworkType.MIJIN_TEST, ); const aggregateSecretProofTransaction = AggregateTransaction.createComplete(Deadline.create(), - new UInt64([0, 0]), [secretProofTransaction.toAggregate(account2.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -588,7 +549,6 @@ describe('TransactionHttp', () => { const proof = convert.uint8ToHex(secretSeed); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Keccak_256, @@ -599,7 +559,6 @@ describe('TransactionHttp', () => { validateTransactionAnnounceCorrectly(account.address, () => { const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), - new UInt64([0, 0]), HashType.Op_Keccak_256, secret, proof, @@ -617,7 +576,6 @@ describe('TransactionHttp', () => { const proof = convert.uint8ToHex(secretSeed); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Keccak_256, @@ -628,14 +586,12 @@ describe('TransactionHttp', () => { validateTransactionAnnounceCorrectly(account.address, () => { const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), - new UInt64([0, 0]), HashType.Op_Keccak_256, secret, proof, NetworkType.MIJIN_TEST, ); const aggregateSecretProofTransaction = AggregateTransaction.createComplete(Deadline.create(), - new UInt64([0, 0]), [secretProofTransaction.toAggregate(account2.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -652,7 +608,6 @@ describe('TransactionHttp', () => { const proof = convert.uint8ToHex(secretSeed); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_160, @@ -663,7 +618,6 @@ describe('TransactionHttp', () => { validateTransactionAnnounceCorrectly(account.address, () => { const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), - new UInt64([0, 0]), HashType.Op_Hash_160, secret, proof, @@ -681,7 +635,6 @@ describe('TransactionHttp', () => { const proof = convert.uint8ToHex(secretSeed); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_160, @@ -692,14 +645,12 @@ describe('TransactionHttp', () => { validateTransactionAnnounceCorrectly(account.address, () => { const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), - new UInt64([0, 0]), HashType.Op_Hash_160, secret, proof, NetworkType.MIJIN_TEST, ); const aggregateSecretProofTransaction = AggregateTransaction.createComplete(Deadline.create(), - new UInt64([0, 0]), [secretProofTransaction.toAggregate(account2.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -716,7 +667,6 @@ describe('TransactionHttp', () => { const proof = convert.uint8ToHex(secretSeed); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_256, @@ -727,7 +677,6 @@ describe('TransactionHttp', () => { validateTransactionAnnounceCorrectly(account.address, () => { const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), - new UInt64([0, 0]), HashType.Op_Hash_256, secret, proof, @@ -745,7 +694,6 @@ describe('TransactionHttp', () => { const proof = convert.uint8ToHex(secretSeed); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_256, @@ -756,14 +704,12 @@ describe('TransactionHttp', () => { validateTransactionAnnounceCorrectly(account.address, () => { const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), - new UInt64([0, 0]), HashType.Op_Hash_256, secret, proof, NetworkType.MIJIN_TEST, ); const aggregateSecretProofTransaction = AggregateTransaction.createComplete(Deadline.create(), - new UInt64([0, 0]), [secretProofTransaction.toAggregate(account2.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -882,7 +828,6 @@ describe('TransactionHttp', () => { const tx = TransferTransaction.create( Deadline.create(), - new UInt64([0, 0]), Address.createFromRawAddress('SAGY2PTFX4T2XYKYXTJXYCTQRP3FESQH5MEQI2RQ'), [], PlainMessage.create('Hi'), @@ -890,7 +835,6 @@ describe('TransactionHttp', () => { ); const aggTx = AggregateTransaction.createComplete( Deadline.create(), - new UInt64([0, 0]), [ tx.toAggregate(signerAccount.publicAccount), ], @@ -913,7 +857,6 @@ describe('TransactionHttp', () => { const tx = TransferTransaction.create( Deadline.create(), - new UInt64([0, 0]), Address.createFromRawAddress('SAGY2PTFX4T2XYKYXTJXYCTQRP3FESQH5MEQI2RQ'), [NetworkCurrencyMosaic.createRelative(10)], EmptyMessage, diff --git a/e2e/infrastructure/TransactionUtils.ts b/e2e/infrastructure/TransactionUtils.ts index 32dbd2c301..48ce119e93 100644 --- a/e2e/infrastructure/TransactionUtils.ts +++ b/e2e/infrastructure/TransactionUtils.ts @@ -38,7 +38,6 @@ export class TransactionUtils { const account = TestingAccount; const transferTransaction = TransferTransaction.create( Deadline.create(), - new UInt64([0, 0]), recipient, [], PlainMessage.create('test-message'), @@ -52,7 +51,6 @@ export class TransactionUtils { const account = TestingAccount; const transferTransaction = TransferTransaction.create( Deadline.create(), - new UInt64([0, 0]), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), [NetworkCurrencyMosaic.createRelative(100000000000)], PlainMessage.create('test-message'), @@ -65,7 +63,6 @@ export class TransactionUtils { public static createAggregateBoundedTransactionAndAnnounce(transactionHttp: TransactionHttp = new TransactionHttp(NIS2_URL)) { const transferTransaction = TransferTransaction.create( Deadline.create(), - new UInt64([0, 0]), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), [NetworkCurrencyMosaic.createRelative(100000000000)], PlainMessage.create('test-message'), @@ -74,7 +71,6 @@ export class TransactionUtils { const aggregateTransaction = AggregateTransaction.createBonded( Deadline.create(2, ChronoUnit.MINUTES), - new UInt64([0, 0]), [transferTransaction.toAggregate(MultisigAccount.publicAccount)], NetworkType.MIJIN_TEST, [], diff --git a/src/model/transaction/AccountLinkTransaction.ts b/src/model/transaction/AccountLinkTransaction.ts index 71d935f26d..a530180d55 100644 --- a/src/model/transaction/AccountLinkTransaction.ts +++ b/src/model/transaction/AccountLinkTransaction.ts @@ -35,16 +35,18 @@ export class AccountLinkTransaction extends Transaction { * @param deadline - The deadline to include the transaction. * @param remoteAccountKey - The public key of the remote account. * @param linkAction - The account link action. + * @param maxFee - (Optional) Max fee defined by the sender * @returns {AccountLinkTransaction} */ public static create(deadline: Deadline, remoteAccountKey: string, linkAction: LinkAction, - networkType: NetworkType): AccountLinkTransaction { + networkType: NetworkType, + maxFee: UInt64 = new UInt64([0, 0])): AccountLinkTransaction { return new AccountLinkTransaction(networkType, TransactionVersion.LINK_ACCOUNT, deadline, - new UInt64([0, 0]), + maxFee, remoteAccountKey, linkAction); } @@ -53,7 +55,7 @@ export class AccountLinkTransaction extends Transaction { * @param networkType * @param version * @param deadline - * @param fee + * @param maxFee * @param remoteAccountKey * @param linkAction * @param signature @@ -63,7 +65,7 @@ export class AccountLinkTransaction extends Transaction { constructor(networkType: NetworkType, version: number, deadline: Deadline, - fee: UInt64, + maxFee: UInt64, /** * The public key of the remote account. */ @@ -75,7 +77,7 @@ export class AccountLinkTransaction extends Transaction { signature?: string, signer?: PublicAccount, transactionInfo?: TransactionInfo) { - super(TransactionType.LINK_ACCOUNT, networkType, version, deadline, fee, signature, signer, transactionInfo); + super(TransactionType.LINK_ACCOUNT, networkType, version, deadline, maxFee, signature, signer, transactionInfo); } /** @@ -85,7 +87,7 @@ export class AccountLinkTransaction extends Transaction { protected buildTransaction(): VerifiableTransaction { return new AccountLinkTransactionLibrary.Builder() .addDeadline(this.deadline.toDTO()) - .addFee(this.fee.toDTO()) + .addFee(this.maxFee.toDTO()) .addVersion(this.versionToDTO()) .addRemoteAccountKey(this.remoteAccountKey) .addLinkAction(this.linkAction) diff --git a/src/model/transaction/AccountPropertyTransaction.ts b/src/model/transaction/AccountPropertyTransaction.ts index a26b580497..a94d38e5b5 100644 --- a/src/model/transaction/AccountPropertyTransaction.ts +++ b/src/model/transaction/AccountPropertyTransaction.ts @@ -19,6 +19,7 @@ import { PropertyModificationType } from '../account/PropertyModificationType'; import { PropertyType } from '../account/PropertyType'; import { NetworkType } from '../blockchain/NetworkType'; import { MosaicId } from '../mosaic/MosaicId'; +import { UInt64 } from '../Uint64'; import { AccountPropertyModification } from './AccountPropertyModification'; import { Deadline } from './Deadline'; import { ModifyAccountPropertyAddressTransaction } from './ModifyAccountPropertyAddressTransaction'; @@ -32,16 +33,26 @@ export class AccountPropertyTransaction { * @param propertyType - Type of account property transaction * @param modification - array of address modifications * @param networkType - The network type. + * @param maxFee - (Optional) Max fee defined by the sender * @returns {ModifyAccountPropertyAddressTransaction} */ - public static createAddressPropertyModificationTransaction(deadline: Deadline, - propertyType: PropertyType, - modifications: Array>, - networkType: NetworkType): ModifyAccountPropertyAddressTransaction { + public static createAddressPropertyModificationTransaction( + deadline: Deadline, + propertyType: PropertyType, + modifications: Array>, + networkType: NetworkType, + maxFee: UInt64 = new UInt64([0, 0]) + ): ModifyAccountPropertyAddressTransaction { if (![PropertyType.AllowAddress, PropertyType.BlockAddress].includes(propertyType)) { throw new Error ('Property type is not allowed.'); } - return ModifyAccountPropertyAddressTransaction.create(deadline, propertyType, modifications, networkType); + return ModifyAccountPropertyAddressTransaction.create( + deadline, + propertyType, + modifications, + networkType, + maxFee + ); } /** @@ -50,16 +61,26 @@ export class AccountPropertyTransaction { * @param propertyType - Type of account property transaction * @param modification - array of mosaic modifications * @param networkType - The network type. + * @param maxFee - (Optional) Max fee defined by the sender * @returns {ModifyAccountPropertyMosaicTransaction} */ - public static createMosaicPropertyModificationTransaction(deadline: Deadline, - propertyType: PropertyType, - modifications: Array>, - networkType: NetworkType): ModifyAccountPropertyMosaicTransaction { + public static createMosaicPropertyModificationTransaction( + deadline: Deadline, + propertyType: PropertyType, + modifications: Array>, + networkType: NetworkType, + maxFee: UInt64 = new UInt64([0, 0]) + ): ModifyAccountPropertyMosaicTransaction { if (![PropertyType.AllowMosaic, PropertyType.BlockMosaic].includes(propertyType)) { throw new Error ('Property type is not allowed.'); } - return ModifyAccountPropertyMosaicTransaction.create(deadline, propertyType, modifications, networkType); + return ModifyAccountPropertyMosaicTransaction.create( + deadline, + propertyType, + modifications, + networkType, + maxFee + ); } /** @@ -68,16 +89,26 @@ export class AccountPropertyTransaction { * @param propertyType - Type of account property transaction * @param modification - array of entity type modifications * @param networkType - The network type. + * @param maxFee - (Optional) Max fee defined by the sender * @returns {ModifyAccountPropertyEntityTypeTransaction} */ - public static createEntityTypePropertyModificationTransaction(deadline: Deadline, - propertyType: PropertyType, - modifications: Array>, - networkType: NetworkType): ModifyAccountPropertyEntityTypeTransaction { + public static createEntityTypePropertyModificationTransaction( + deadline: Deadline, + propertyType: PropertyType, + modifications: Array>, + networkType: NetworkType, + maxFee: UInt64 = new UInt64([0, 0]) + ): ModifyAccountPropertyEntityTypeTransaction { if (![PropertyType.AllowTransaction, PropertyType.BlockTransaction].includes(propertyType)) { throw new Error ('Property type is not allowed.'); } - return ModifyAccountPropertyEntityTypeTransaction.create(deadline, propertyType, modifications, networkType); + return ModifyAccountPropertyEntityTypeTransaction.create( + deadline, + propertyType, + modifications, + networkType, + maxFee + ); } /** diff --git a/src/model/transaction/AddressAliasTransaction.ts b/src/model/transaction/AddressAliasTransaction.ts index 0970b9f02b..bb7502f847 100644 --- a/src/model/transaction/AddressAliasTransaction.ts +++ b/src/model/transaction/AddressAliasTransaction.ts @@ -36,19 +36,19 @@ export class AddressAliasTransaction extends Transaction { /** * Create a mosaic supply change transaction object * @param deadline - The deadline to include the transaction. - * @param maxFee - Max fee defined by the sender * @param actionType - The namespace id. * @param namespaceId - The namespace id. * @param mosaicId - The mosaic id. * @param networkType - The network type. + * @param maxFee - (Optional) Max fee defined by the sender * @returns {AddressAliasTransaction} */ public static create(deadline: Deadline, - maxFee: UInt64, actionType: AliasActionType, namespaceId: NamespaceId, address: Address, - networkType: NetworkType): AddressAliasTransaction { + networkType: NetworkType, + maxFee: UInt64 = new UInt64([0, 0])): AddressAliasTransaction { return new AddressAliasTransaction(networkType, TransactionVersion.ADDRESS_ALIAS, deadline, @@ -90,7 +90,7 @@ export class AddressAliasTransaction extends Transaction { signature?: string, signer?: PublicAccount, transactionInfo?: TransactionInfo) { - super(TransactionType.ADDRESS_ALIAS, networkType, version, deadline, fee, signature, signer, transactionInfo); + super(TransactionType.ADDRESS_ALIAS, networkType, version, deadline, maxFee, signature, signer, transactionInfo); } /** diff --git a/src/model/transaction/AggregateTransaction.ts b/src/model/transaction/AggregateTransaction.ts index 760fd1ad82..86a78e4e33 100644 --- a/src/model/transaction/AggregateTransaction.ts +++ b/src/model/transaction/AggregateTransaction.ts @@ -67,17 +67,17 @@ export class AggregateTransaction extends Transaction { /** * Create an aggregate complete transaction object * @param deadline - The deadline to include the transaction. - * @param {maxFee} maxFee * @param innerTransactions - The array of inner innerTransactions. * @param networkType - The network type. * @param cosignatures + * @param maxFee - (Optional) Max fee defined by the sender * @returns {AggregateTransaction} */ public static createComplete(deadline: Deadline, - maxFee: UInt64, innerTransactions: InnerTransaction[], networkType: NetworkType, - cosignatures: AggregateTransactionCosignature[]): AggregateTransaction { + cosignatures: AggregateTransactionCosignature[], + maxFee: UInt64 = new UInt64([0, 0])): AggregateTransaction { return new AggregateTransaction(networkType, TransactionType.AGGREGATE_COMPLETE, TransactionVersion.AGGREGATE_COMPLETE, @@ -91,17 +91,17 @@ export class AggregateTransaction extends Transaction { /** * Create an aggregate bonded transaction object * @param {Deadline} deadline - * @param {maxFee} maxFee * @param {InnerTransaction[]} innerTransactions * @param {NetworkType} networkType * @param {AggregateTransactionCosignature[]} cosignatures + * @param {UInt64} maxFee - (Optional) Max fee defined by the sender * @return {AggregateTransaction} */ public static createBonded(deadline: Deadline, - maxFee: UInt64, innerTransactions: InnerTransaction[], networkType: NetworkType, - cosignatures: AggregateTransactionCosignature[] = []): AggregateTransaction { + cosignatures: AggregateTransactionCosignature[] = [], + maxFee: UInt64 = new UInt64([0, 0])): AggregateTransaction { return new AggregateTransaction(networkType, TransactionType.AGGREGATE_BONDED, TransactionVersion.AGGREGATE_BONDED, diff --git a/src/model/transaction/AliasTransaction.ts b/src/model/transaction/AliasTransaction.ts index 06a1e1e843..579f9d4969 100644 --- a/src/model/transaction/AliasTransaction.ts +++ b/src/model/transaction/AliasTransaction.ts @@ -35,39 +35,53 @@ export abstract class AliasTransaction extends Transaction { /** * Create an address alias transaction object * @param deadline - The deadline to include the transaction. - * @param maxFee - Max fee defined by the sender * @param aliasAction - The namespace id. * @param namespaceId - The namespace id. * @param address - The address. * @param networkType - The network type. + * @param maxFee - (Optional) Max fee defined by the sender * @returns {AddressAliasTransaction} */ public static createForAddress(deadline: Deadline, - maxFee: UInt64, aliasAction: AliasActionType, namespaceId: NamespaceId, address: Address, - networkType: NetworkType): AliasTransaction { - return AddressAliasTransaction.create(deadline, maxFee, aliasAction, namespaceId, address, networkType); + networkType: NetworkType, + maxFee: UInt64 = new UInt64([0, 0])): AliasTransaction { + return AddressAliasTransaction.create( + deadline, + aliasAction, + namespaceId, + address, + networkType, + maxFee, + ); } /** * Create a mosaic alias transaction object * @param deadline - The deadline to include the transaction. - * @param maxFee - Max fee defined by the sender * @param aliasAction - The namespace id. * @param namespaceId - The namespace id. * @param mosaicId - The mosaic id. * @param networkType - The network type. + * @param maxFee - (Optional) Max fee defined by the sender * @returns {MosaicAliasTransaction} */ public static createForMosaic(deadline: Deadline, - maxFee: UInt64, aliasAction: AliasActionType, namespaceId: NamespaceId, mosaicId: MosaicId, - networkType: NetworkType): AliasTransaction { - return MosaicAliasTransaction.create(deadline, maxFee, aliasAction, namespaceId, mosaicId, networkType); + networkType: NetworkType, + maxFee: UInt64 = new UInt64([0, 0])): AliasTransaction { + return MosaicAliasTransaction.create( + deadline, + aliasAction, + namespaceId, + mosaicId, + networkType, + maxFee, + ); } } diff --git a/src/model/transaction/LockFundsTransaction.ts b/src/model/transaction/LockFundsTransaction.ts index 0ca7c65a90..a83458e580 100644 --- a/src/model/transaction/LockFundsTransaction.ts +++ b/src/model/transaction/LockFundsTransaction.ts @@ -42,18 +42,19 @@ export class LockFundsTransaction extends Transaction { /** * Create a Lock funds transaction object * @param deadline - The deadline to include the transaction. - * @param maxFee - Max fee defined by the sender * @param mosaic - The locked mosaic. * @param duration - The funds lock duration. * @param signedTransaction - The signed transaction for which funds are locked. * @param networkType - The network type. + * @param maxFee - (Optional) Max fee defined by the sender + * @returns {LockFundsTransaction} */ public static create(deadline: Deadline, - maxFee: UInt64, mosaic: Mosaic, duration: UInt64, signedTransaction: SignedTransaction, - networkType: NetworkType): LockFundsTransaction { + networkType: NetworkType, + maxFee: UInt64 = new UInt64([0, 0])): LockFundsTransaction { return new LockFundsTransaction( networkType, TransactionVersion.LOCK, diff --git a/src/model/transaction/ModifyAccountPropertyAddressTransaction.ts b/src/model/transaction/ModifyAccountPropertyAddressTransaction.ts index b275879711..00a47b26b9 100644 --- a/src/model/transaction/ModifyAccountPropertyAddressTransaction.ts +++ b/src/model/transaction/ModifyAccountPropertyAddressTransaction.ts @@ -34,16 +34,18 @@ export class ModifyAccountPropertyAddressTransaction extends Transaction { * @param propertyType - The account property type. * @param modifications - The array of modifications. * @param networkType - The network type. + * @param maxFee - (Optional) Max fee defined by the sender * @returns {ModifyAccountPropertyAddressTransaction} */ public static create(deadline: Deadline, propertyType: PropertyType, modifications: Array>, - networkType: NetworkType): ModifyAccountPropertyAddressTransaction { + networkType: NetworkType, + maxFee: UInt64 = new UInt64([0, 0])): ModifyAccountPropertyAddressTransaction { return new ModifyAccountPropertyAddressTransaction(networkType, TransactionVersion.MODIFY_ACCOUNT_PROPERTY_ADDRESS, deadline, - new UInt64([0, 0]), + maxFee, propertyType, modifications); } @@ -52,7 +54,7 @@ export class ModifyAccountPropertyAddressTransaction extends Transaction { * @param networkType * @param version * @param deadline - * @param fee + * @param maxFee * @param minApprovalDelta * @param minRemovalDelta * @param modifications @@ -63,13 +65,13 @@ export class ModifyAccountPropertyAddressTransaction extends Transaction { constructor(networkType: NetworkType, version: number, deadline: Deadline, - fee: UInt64, + maxFee: UInt64, public readonly propertyType: PropertyType, public readonly modifications: Array>, signature?: string, signer?: PublicAccount, transactionInfo?: TransactionInfo) { - super(TransactionType.MODIFY_ACCOUNT_PROPERTY_ADDRESS, networkType, version, deadline, fee, signature, signer, transactionInfo); + super(TransactionType.MODIFY_ACCOUNT_PROPERTY_ADDRESS, networkType, version, deadline, maxFee, signature, signer, transactionInfo); } /** @@ -79,7 +81,7 @@ export class ModifyAccountPropertyAddressTransaction extends Transaction { protected buildTransaction(): VerifiableTransaction { return new AccountPropertiesAddressTransactionLibrary.Builder() .addDeadline(this.deadline.toDTO()) - .addFee(this.fee.toDTO()) + .addFee(this.maxFee.toDTO()) .addVersion(this.versionToDTO()) .addPropertyType(this.propertyType) .addModifications(this.modifications.map((modification) => modification.toDTO())) diff --git a/src/model/transaction/ModifyAccountPropertyEntityTypeTransaction.ts b/src/model/transaction/ModifyAccountPropertyEntityTypeTransaction.ts index 5143c50724..7a7e920e85 100644 --- a/src/model/transaction/ModifyAccountPropertyEntityTypeTransaction.ts +++ b/src/model/transaction/ModifyAccountPropertyEntityTypeTransaction.ts @@ -35,16 +35,18 @@ export class ModifyAccountPropertyEntityTypeTransaction extends Transaction { * @param propertyType - The account property type. * @param modifications - The array of modifications. * @param networkType - The network type. + * @param maxFee - (Optional) Max fee defined by the sender * @returns {ModifyAccountPropertyEntityTypeTransaction} */ public static create(deadline: Deadline, propertyType: PropertyType, modifications: Array>, - networkType: NetworkType): ModifyAccountPropertyEntityTypeTransaction { + networkType: NetworkType, + maxFee: UInt64 = new UInt64([0, 0])): ModifyAccountPropertyEntityTypeTransaction { return new ModifyAccountPropertyEntityTypeTransaction(networkType, TransactionVersion.MODIFY_ACCOUNT_PROPERTY_ENTITY_TYPE, deadline, - new UInt64([0, 0]), + maxFee, propertyType, modifications); } @@ -53,7 +55,7 @@ export class ModifyAccountPropertyEntityTypeTransaction extends Transaction { * @param networkType * @param version * @param deadline - * @param fee + * @param maxFee * @param minApprovalDelta * @param minRemovalDelta * @param modifications @@ -64,13 +66,13 @@ export class ModifyAccountPropertyEntityTypeTransaction extends Transaction { constructor(networkType: NetworkType, version: number, deadline: Deadline, - fee: UInt64, + maxFee: UInt64, public readonly propertyType: PropertyType, public readonly modifications: Array>, signature?: string, signer?: PublicAccount, transactionInfo?: TransactionInfo) { - super(TransactionType.MODIFY_ACCOUNT_PROPERTY_ENTITY_TYPE, networkType, version, deadline, fee, signature, signer, transactionInfo); + super(TransactionType.MODIFY_ACCOUNT_PROPERTY_ENTITY_TYPE, networkType, version, deadline, maxFee, signature, signer, transactionInfo); } /** @@ -80,7 +82,7 @@ export class ModifyAccountPropertyEntityTypeTransaction extends Transaction { protected buildTransaction(): VerifiableTransaction { return new AccountPropertiesEntityTypeTransactionLibrary.Builder() .addDeadline(this.deadline.toDTO()) - .addFee(this.fee.toDTO()) + .addFee(this.maxFee.toDTO()) .addVersion(this.versionToDTO()) .addPropertyType(this.propertyType) .addModifications(this.modifications.map((modification) => modification.toDTO())) diff --git a/src/model/transaction/ModifyAccountPropertyMosaicTransaction.ts b/src/model/transaction/ModifyAccountPropertyMosaicTransaction.ts index a0fe94eda7..6315c0ddac 100644 --- a/src/model/transaction/ModifyAccountPropertyMosaicTransaction.ts +++ b/src/model/transaction/ModifyAccountPropertyMosaicTransaction.ts @@ -34,16 +34,18 @@ export class ModifyAccountPropertyMosaicTransaction extends Transaction { * @param propertyType - The account property type. * @param modifications - The array of modifications. * @param networkType - The network type. + * @param maxFee - (Optional) Max fee defined by the sender * @returns {ModifyAccountPropertyAddressTransaction} */ public static create(deadline: Deadline, propertyType: PropertyType, modifications: Array>, - networkType: NetworkType): ModifyAccountPropertyMosaicTransaction { + networkType: NetworkType, + maxFee: UInt64 = new UInt64([0, 0])): ModifyAccountPropertyMosaicTransaction { return new ModifyAccountPropertyMosaicTransaction(networkType, TransactionVersion.MODIFY_ACCOUNT_PROPERTY_MOSAIC, deadline, - new UInt64([0, 0]), + maxFee, propertyType, modifications); } @@ -52,7 +54,7 @@ export class ModifyAccountPropertyMosaicTransaction extends Transaction { * @param networkType * @param version * @param deadline - * @param fee + * @param maxFee * @param minApprovalDelta * @param minRemovalDelta * @param modifications @@ -63,13 +65,13 @@ export class ModifyAccountPropertyMosaicTransaction extends Transaction { constructor(networkType: NetworkType, version: number, deadline: Deadline, - fee: UInt64, + maxFee: UInt64, public readonly propertyType: PropertyType, public readonly modifications: Array>, signature?: string, signer?: PublicAccount, transactionInfo?: TransactionInfo) { - super(TransactionType.MODIFY_ACCOUNT_PROPERTY_MOSAIC, networkType, version, deadline, fee, signature, signer, transactionInfo); + super(TransactionType.MODIFY_ACCOUNT_PROPERTY_MOSAIC, networkType, version, deadline, maxFee, signature, signer, transactionInfo); } /** @@ -79,7 +81,7 @@ export class ModifyAccountPropertyMosaicTransaction extends Transaction { protected buildTransaction(): VerifiableTransaction { return new AccountPropertiesMosaicTransactionLibrary.Builder() .addDeadline(this.deadline.toDTO()) - .addFee(this.fee.toDTO()) + .addFee(this.maxFee.toDTO()) .addVersion(this.versionToDTO()) .addPropertyType(this.propertyType) .addModifications(this.modifications.map((modification) => modification.toDTO())) diff --git a/src/model/transaction/ModifyMultisigAccountTransaction.ts b/src/model/transaction/ModifyMultisigAccountTransaction.ts index 9ca6468549..ccda40041e 100644 --- a/src/model/transaction/ModifyMultisigAccountTransaction.ts +++ b/src/model/transaction/ModifyMultisigAccountTransaction.ts @@ -36,19 +36,19 @@ export class ModifyMultisigAccountTransaction extends Transaction { /** * Create a modify multisig account transaction object * @param deadline - The deadline to include the transaction. - * @param maxFee - Max fee defined by the sender * @param minApprovalDelta - The min approval relative change. * @param minRemovalDelta - The min removal relative change. * @param modifications - The array of modifications. * @param networkType - The network type. + * @param maxFee - (Optional) Max fee defined by the sender * @returns {ModifyMultisigAccountTransaction} */ public static create(deadline: Deadline, - maxFee: UInt64, minApprovalDelta: number, minRemovalDelta: number, modifications: MultisigCosignatoryModification[], - networkType: NetworkType): ModifyMultisigAccountTransaction { + networkType: NetworkType, + maxFee: UInt64 = new UInt64([0, 0])): ModifyMultisigAccountTransaction { return new ModifyMultisigAccountTransaction(networkType, TransactionVersion.MODIFY_MULTISIG_ACCOUNT, deadline, diff --git a/src/model/transaction/MosaicAliasTransaction.ts b/src/model/transaction/MosaicAliasTransaction.ts index c44e4ee9ea..49151797c9 100644 --- a/src/model/transaction/MosaicAliasTransaction.ts +++ b/src/model/transaction/MosaicAliasTransaction.ts @@ -36,19 +36,19 @@ export class MosaicAliasTransaction extends Transaction { /** * Create a mosaic supply change transaction object * @param deadline - The deadline to include the transaction. - * @param maxFee - Max fee defined by the sender * @param actionType - The namespace id. * @param namespaceId - The namespace id. * @param mosaicId - The mosaic id. * @param networkType - The network type. + * @param maxFee - (Optional) Max fee defined by the sender * @returns {MosaicAliasTransaction} */ public static create(deadline: Deadline, - maxFee: UInt64, actionType: AliasActionType, namespaceId: NamespaceId, mosaicId: MosaicId, - networkType: NetworkType): MosaicAliasTransaction { + networkType: NetworkType, + maxFee: UInt64 = new UInt64([0, 0])): MosaicAliasTransaction { return new MosaicAliasTransaction(networkType, TransactionVersion.MOSAIC_ALIAS, deadline, diff --git a/src/model/transaction/MosaicDefinitionTransaction.ts b/src/model/transaction/MosaicDefinitionTransaction.ts index 34643be588..6bde92b8b3 100644 --- a/src/model/transaction/MosaicDefinitionTransaction.ts +++ b/src/model/transaction/MosaicDefinitionTransaction.ts @@ -40,19 +40,19 @@ export class MosaicDefinitionTransaction extends Transaction { /** * Create a mosaic creation transaction object * @param deadline - The deadline to include the transaction. - * @param maxFee - Max fee defined by the sender * @param nonce - The mosaic nonce ex: MosaicNonce.createRandom(). * @param mosaicId - The mosaic id ex: new MosaicId([481110499, 231112638]). * @param mosaicProperties - The mosaic properties. * @param networkType - The network type. + * @param maxFee - (Optional) Max fee defined by the sender * @returns {MosaicDefinitionTransaction} */ public static create(deadline: Deadline, - maxFee: UInt64, nonce: MosaicNonce, mosaicId: MosaicId, mosaicProperties: MosaicProperties, - networkType: NetworkType): MosaicDefinitionTransaction { + networkType: NetworkType, + maxFee: UInt64 = new UInt64([0, 0])): MosaicDefinitionTransaction { return new MosaicDefinitionTransaction(networkType, TransactionVersion.MOSAIC_DEFINITION, deadline, diff --git a/src/model/transaction/MosaicSupplyChangeTransaction.ts b/src/model/transaction/MosaicSupplyChangeTransaction.ts index 6e584d8d9a..b969e17f92 100644 --- a/src/model/transaction/MosaicSupplyChangeTransaction.ts +++ b/src/model/transaction/MosaicSupplyChangeTransaction.ts @@ -35,19 +35,19 @@ export class MosaicSupplyChangeTransaction extends Transaction { /** * Create a mosaic supply change transaction object * @param deadline - The deadline to include the transaction. - * @param maxFee - Max fee defined by the sender * @param mosaicId - The mosaic id. * @param direction - The supply type. * @param delta - The supply change in units for the mosaic. * @param networkType - The network type. + * @param maxFee - (Optional) Max fee defined by the sender * @returns {MosaicSupplyChangeTransaction} */ public static create(deadline: Deadline, - maxFee: UInt64, mosaicId: MosaicId, direction: MosaicSupplyType, delta: UInt64, - networkType: NetworkType): MosaicSupplyChangeTransaction { + networkType: NetworkType, + maxFee: UInt64 = new UInt64([0, 0])): MosaicSupplyChangeTransaction { return new MosaicSupplyChangeTransaction(networkType, TransactionVersion.MOSAIC_SUPPLY_CHANGE, deadline, diff --git a/src/model/transaction/RegisterNamespaceTransaction.ts b/src/model/transaction/RegisterNamespaceTransaction.ts index 7e3994d996..8381e00cd5 100644 --- a/src/model/transaction/RegisterNamespaceTransaction.ts +++ b/src/model/transaction/RegisterNamespaceTransaction.ts @@ -35,17 +35,17 @@ export class RegisterNamespaceTransaction extends Transaction { /** * Create a root namespace object * @param deadline - The deadline to include the transaction. - * @param maxFee - Max fee defined by the sender * @param namespaceName - The namespace name. * @param duration - The duration of the namespace. * @param networkType - The network type. + * @param maxFee - (Optional) Max fee defined by the sender * @returns {RegisterNamespaceTransaction} */ public static createRootNamespace(deadline: Deadline, - maxFee: UInt64, namespaceName: string, duration: UInt64, - networkType: NetworkType): RegisterNamespaceTransaction { + networkType: NetworkType, + maxFee: UInt64 = new UInt64([0, 0])): RegisterNamespaceTransaction { return new RegisterNamespaceTransaction(networkType, TransactionVersion.REGISTER_NAMESPACE, deadline, @@ -60,17 +60,17 @@ export class RegisterNamespaceTransaction extends Transaction { /** * Create a sub namespace object * @param deadline - The deadline to include the transaction. - * @param maxFee - Max fee defined by the sender * @param namespaceName - The namespace name. * @param parentNamespace - The parent namespace name. * @param networkType - The network type. + * @param maxFee - (Optional) Max fee defined by the sender * @returns {RegisterNamespaceTransaction} */ public static createSubNamespace(deadline: Deadline, - maxFee: UInt64, namespaceName: string, parentNamespace: string | NamespaceId, - networkType: NetworkType): RegisterNamespaceTransaction { + networkType: NetworkType, + maxFee: UInt64 = new UInt64([0, 0])): RegisterNamespaceTransaction { let parentId: NamespaceId; if (typeof parentNamespace === 'string') { parentId = new NamespaceId(subnamespaceParentId(parentNamespace, namespaceName)); diff --git a/src/model/transaction/SecretLockTransaction.ts b/src/model/transaction/SecretLockTransaction.ts index 2c71b9461f..4a4db900bc 100644 --- a/src/model/transaction/SecretLockTransaction.ts +++ b/src/model/transaction/SecretLockTransaction.ts @@ -32,24 +32,24 @@ export class SecretLockTransaction extends Transaction { * Create a secret lock transaction object. * * @param deadline - The deadline to include the transaction. - * @param maxFee - Max fee defined by the sender * @param mosaic - The locked mosaic. * @param duration - The funds lock duration. * @param hashType - The hash algorithm secret is generated with. * @param secret - The proof hashed. * @param recipient - The recipient of the funds. * @param networkType - The network type. + * @param maxFee - (Optional) Max fee defined by the sender * * @return a SecretLockTransaction instance */ public static create(deadline: Deadline, - maxFee: UInt64, mosaic: Mosaic, duration: UInt64, hashType: HashType, secret: string, recipient: Address, - networkType: NetworkType): SecretLockTransaction { + networkType: NetworkType, + maxFee: UInt64 = new UInt64([0, 0])): SecretLockTransaction { return new SecretLockTransaction( networkType, TransactionVersion.SECRET_LOCK, diff --git a/src/model/transaction/SecretProofTransaction.ts b/src/model/transaction/SecretProofTransaction.ts index d36f69db19..991f5747e5 100644 --- a/src/model/transaction/SecretProofTransaction.ts +++ b/src/model/transaction/SecretProofTransaction.ts @@ -30,20 +30,20 @@ export class SecretProofTransaction extends Transaction { * Create a secret proof transaction object. * * @param deadline - The deadline to include the transaction. - * @param maxFee - Max fee defined by the sender * @param hashType - The hash algorithm secret is generated with. * @param secret - The seed proof hashed. * @param proof - The seed proof. * @param networkType - The network type. + * @param maxFee - (Optional) Max fee defined by the sender * * @return a SecretProofTransaction instance */ public static create(deadline: Deadline, - maxFee: UInt64, hashType: HashType, secret: string, proof: string, - networkType: NetworkType): SecretProofTransaction { + networkType: NetworkType, + maxFee: UInt64 = new UInt64([0, 0])): SecretProofTransaction { return new SecretProofTransaction( networkType, TransactionVersion.SECRET_PROOF, diff --git a/src/model/transaction/TransferTransaction.ts b/src/model/transaction/TransferTransaction.ts index b613524b8b..a90856937d 100644 --- a/src/model/transaction/TransferTransaction.ts +++ b/src/model/transaction/TransferTransaction.ts @@ -35,19 +35,19 @@ export class TransferTransaction extends Transaction { /** * Create a transfer transaction object * @param deadline - The deadline to include the transaction. - * @param maxFee - Max fee defined by the sender * @param recipient - The recipient of the transaction. * @param mosaics - The array of mosaics. * @param message - The transaction message. * @param networkType - The network type. + * @param maxFee - (Optional) Max fee defined by the sender * @returns {TransferTransaction} */ public static create(deadline: Deadline, - maxFee: UInt64, recipient: Address | NamespaceId, mosaics: Mosaic[], message: Message, - networkType: NetworkType): TransferTransaction { + networkType: NetworkType, + maxFee: UInt64 = new UInt64([0, 0])): TransferTransaction { return new TransferTransaction(networkType, TransactionVersion.TRANSFER, deadline, diff --git a/test/infrastructure/TransactionHttp.spec.ts b/test/infrastructure/TransactionHttp.spec.ts index e252968812..e7dcbc6d6c 100644 --- a/test/infrastructure/TransactionHttp.spec.ts +++ b/test/infrastructure/TransactionHttp.spec.ts @@ -30,7 +30,6 @@ describe('TransactionHttp', () => { it('should return an error when a non aggregate transaction bonded is announced via announceAggregateBonded method', () => { const tx = TransferTransaction.create( Deadline.create(), - new UInt64([0, 0]), Address.createFromRawAddress('SAGY2PTFX4T2XYKYXTJXYCTQRP3FESQH5MEQI2RQ'), [], PlainMessage.create('Hi'), @@ -38,7 +37,6 @@ describe('TransactionHttp', () => { ); const aggTx = AggregateTransaction.createComplete( Deadline.create(), - new UInt64([0, 0]), [ tx.toAggregate(account.publicAccount), ], diff --git a/test/model/transaction/AccountLinkTransaction.spec.ts b/test/model/transaction/AccountLinkTransaction.spec.ts index 632ae9ba8d..e0368e38ef 100644 --- a/test/model/transaction/AccountLinkTransaction.spec.ts +++ b/test/model/transaction/AccountLinkTransaction.spec.ts @@ -20,6 +20,7 @@ import { NetworkType } from '../../../src/model/blockchain/NetworkType'; import { AccountLinkTransaction } from '../../../src/model/transaction/AccountLinkTransaction'; import { Deadline } from '../../../src/model/transaction/Deadline'; import { LinkAction } from '../../../src/model/transaction/LinkAction'; +import { UInt64 } from '../../../src/model/UInt64'; import { TestingAccount } from '../../conf/conf.spec'; describe('AccountLinkTransaction', () => { @@ -29,6 +30,31 @@ describe('AccountLinkTransaction', () => { account = TestingAccount; }); + it('should default maxFee field be set to 0', () => { + const accountLinkTransaction = AccountLinkTransaction.create( + Deadline.create(), + account.publicKey, + LinkAction.Link, + NetworkType.MIJIN_TEST, + ); + + expect(accountLinkTransaction.maxFee.higher).to.be.equal(0); + expect(accountLinkTransaction.maxFee.lower).to.be.equal(0); + }); + + it('should filled maxFee override transaction maxFee', () => { + const accountLinkTransaction = AccountLinkTransaction.create( + Deadline.create(), + account.publicKey, + LinkAction.Link, + NetworkType.MIJIN_TEST, + new UInt64([1, 0]) + ); + + expect(accountLinkTransaction.maxFee.higher).to.be.equal(0); + expect(accountLinkTransaction.maxFee.lower).to.be.equal(1); + }); + it('should create an AccountLinkTransaction object with link action', () => { const accountLinkTransaction = AccountLinkTransaction.create( Deadline.create(), diff --git a/test/model/transaction/AccountPropertyTransaction.spec.ts b/test/model/transaction/AccountPropertyTransaction.spec.ts index f6a5bb473e..2225db94c1 100644 --- a/test/model/transaction/AccountPropertyTransaction.spec.ts +++ b/test/model/transaction/AccountPropertyTransaction.spec.ts @@ -22,6 +22,7 @@ import { PropertyModificationType, PropertyType, TransactionType } from '../../. import {MosaicId} from '../../../src/model/mosaic/MosaicId'; import {AccountPropertyTransaction} from '../../../src/model/transaction/AccountPropertyTransaction'; import {Deadline} from '../../../src/model/transaction/Deadline'; +import {UInt64} from '../../../src/model/UInt64'; import {TestingAccount} from '../../conf/conf.spec'; describe('AccountPropertyTransaction', () => { @@ -61,6 +62,41 @@ describe('AccountPropertyTransaction', () => { expect(entityTypePropertyFilter.value).to.be.equal(entityType); }); + it('should default maxFee field be set to 0', () => { + const address = Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'); + const addressPropertyFilter = AccountPropertyTransaction.createAddressFilter( + PropertyModificationType.Add, + address, + ); + const addressPropertyTransaction = AccountPropertyTransaction.createAddressPropertyModificationTransaction( + Deadline.create(), + PropertyType.AllowAddress, + [addressPropertyFilter], + NetworkType.MIJIN_TEST, + ); + + expect(addressPropertyTransaction.maxFee.higher).to.be.equal(0); + expect(addressPropertyTransaction.maxFee.lower).to.be.equal(0); + }); + + it('should filled maxFee override transaction maxFee', () => { + const address = Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'); + const addressPropertyFilter = AccountPropertyTransaction.createAddressFilter( + PropertyModificationType.Add, + address, + ); + const addressPropertyTransaction = AccountPropertyTransaction.createAddressPropertyModificationTransaction( + Deadline.create(), + PropertyType.AllowAddress, + [addressPropertyFilter], + NetworkType.MIJIN_TEST, + new UInt64([1, 0]) + ); + + expect(addressPropertyTransaction.maxFee.higher).to.be.equal(0); + expect(addressPropertyTransaction.maxFee.lower).to.be.equal(1); + }); + it('should create address property transaction', () => { const address = Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'); diff --git a/test/model/transaction/AddressAliasTransaction.spec.ts b/test/model/transaction/AddressAliasTransaction.spec.ts index 75ec2142b7..e483ea6e32 100644 --- a/test/model/transaction/AddressAliasTransaction.spec.ts +++ b/test/model/transaction/AddressAliasTransaction.spec.ts @@ -33,12 +33,42 @@ describe('AddressAliasTransaction', () => { account = TestingAccount; }); + it('should default maxFee field be set to 0', () => { + const namespaceId = new NamespaceId([33347626, 3779697293]); + const address = Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'); + const addressAliasTransaction = AddressAliasTransaction.create( + Deadline.create(), + AliasActionType.Link, + namespaceId, + address, + NetworkType.MIJIN_TEST, + ); + + expect(addressAliasTransaction.maxFee.higher).to.be.equal(0); + expect(addressAliasTransaction.maxFee.lower).to.be.equal(0); + }); + + it('should filled maxFee override transaction maxFee', () => { + const namespaceId = new NamespaceId([33347626, 3779697293]); + const address = Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'); + const addressAliasTransaction = AddressAliasTransaction.create( + Deadline.create(), + AliasActionType.Link, + namespaceId, + address, + NetworkType.MIJIN_TEST, + new UInt64([1, 0]) + ); + + expect(addressAliasTransaction.maxFee.higher).to.be.equal(0); + expect(addressAliasTransaction.maxFee.lower).to.be.equal(1); + }); + it('should createComplete an AddressAliasTransaction object and sign it', () => { const namespaceId = new NamespaceId([33347626, 3779697293]); const address = Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'); const addressAliasTransaction = AddressAliasTransaction.create( Deadline.create(), - new UInt64([0, 0]), AliasActionType.Link, namespaceId, address, diff --git a/test/model/transaction/AggregateTransaction.spec.ts b/test/model/transaction/AggregateTransaction.spec.ts index d1bba3e64a..06cca5df25 100644 --- a/test/model/transaction/AggregateTransaction.spec.ts +++ b/test/model/transaction/AggregateTransaction.spec.ts @@ -45,10 +45,50 @@ describe('AggregateTransaction', () => { account = TestingAccount; }); + it('should default maxFee field be set to 0', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(account.publicAccount)], + NetworkType.MIJIN_TEST, + [], + ); + + expect(aggregateTransaction.maxFee.higher).to.be.equal(0); + expect(aggregateTransaction.maxFee.lower).to.be.equal(0); + }); + + it('should filled maxFee override transaction maxFee', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(account.publicAccount)], + NetworkType.MIJIN_TEST, + [], + new UInt64([1, 0]) + ); + + expect(aggregateTransaction.maxFee.higher).to.be.equal(0); + expect(aggregateTransaction.maxFee.lower).to.be.equal(1); + }); + it('should createComplete an AggregateTransaction object with TransferTransaction', () => { const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), - new UInt64([0, 0]), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), [], PlainMessage.create('test-message'), @@ -57,7 +97,6 @@ describe('AggregateTransaction', () => { const aggregateTransaction = AggregateTransaction.createComplete( Deadline.create(), - new UInt64([0, 0]), [transferTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, []); @@ -75,7 +114,6 @@ describe('AggregateTransaction', () => { it('should createComplete an AggregateTransaction object with RegisterNamespaceTransaction', () => { const registerNamespaceTransaction = RegisterNamespaceTransaction.createRootNamespace( Deadline.create(), - new UInt64([0, 0]), 'root-test-namespace', UInt64.fromUint(1000), NetworkType.MIJIN_TEST, @@ -83,7 +121,6 @@ describe('AggregateTransaction', () => { const aggregateTransaction = AggregateTransaction.createComplete( Deadline.create(), - new UInt64([0, 0]), [registerNamespaceTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, [], @@ -103,7 +140,6 @@ describe('AggregateTransaction', () => { it('should createComplete an AggregateTransaction object with MosaicDefinitionTransaction', () => { const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( Deadline.create(), - new UInt64([0, 0]), new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce new MosaicId(UInt64.fromUint(1).toDTO()), // ID MosaicProperties.create({ @@ -118,7 +154,6 @@ describe('AggregateTransaction', () => { const aggregateTransaction = AggregateTransaction.createComplete( Deadline.create(), - new UInt64([0, 0]), [mosaicDefinitionTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, [], @@ -138,7 +173,6 @@ describe('AggregateTransaction', () => { const mosaicId = new MosaicId([2262289484, 3405110546]); const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( Deadline.create(), - new UInt64([0, 0]), mosaicId, MosaicSupplyType.Increase, UInt64.fromUint(10), @@ -147,7 +181,6 @@ describe('AggregateTransaction', () => { const aggregateTransaction = AggregateTransaction.createComplete( Deadline.create(), - new UInt64([0, 0]), [mosaicSupplyChangeTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, [], @@ -166,7 +199,6 @@ describe('AggregateTransaction', () => { it('should createComplete an AggregateTransaction object with ModifyMultisigAccountTransaction', () => { const modifyMultisigAccountTransaction = ModifyMultisigAccountTransaction.create( Deadline.create(), - new UInt64([0, 0]), 2, 1, [new MultisigCosignatoryModification( @@ -183,7 +215,6 @@ describe('AggregateTransaction', () => { ); const aggregateTransaction = AggregateTransaction.createComplete( Deadline.create(), - new UInt64([0, 0]), [modifyMultisigAccountTransaction.toAggregate(account.publicAccount)], NetworkType.MIJIN_TEST, [], @@ -203,14 +234,12 @@ describe('AggregateTransaction', () => { it('should createComplete an AggregateTransaction object with different cosignatories', () => { const transferTransaction = TransferTransaction.create( Deadline.create(), - new UInt64([0, 0]), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), [], PlainMessage.create('test-message'), NetworkType.MIJIN_TEST, ); const aggregateTransaction = AggregateTransaction.createComplete(Deadline.create(), - new UInt64([0, 0]), [transferTransaction.toAggregate(MultisigAccount.publicAccount)], NetworkType.MIJIN_TEST, [], @@ -310,7 +339,6 @@ describe('AggregateTransaction', () => { it('should have type 0x4141 when it\'s complete', () => { const aggregateTransaction = AggregateTransaction.createComplete( Deadline.create(), - new UInt64([0, 0]), [], NetworkType.MIJIN_TEST, [], @@ -322,7 +350,6 @@ describe('AggregateTransaction', () => { it('should have type 0x4241 when it\'s bonded', () => { const aggregateTransaction = AggregateTransaction.createBonded( Deadline.create(), - new UInt64([0, 0]), [], NetworkType.MIJIN_TEST, ); diff --git a/test/model/transaction/HashLockTransaction.spec.ts b/test/model/transaction/HashLockTransaction.spec.ts index f196d89d0e..c1d946c9ed 100644 --- a/test/model/transaction/HashLockTransaction.spec.ts +++ b/test/model/transaction/HashLockTransaction.spec.ts @@ -27,14 +27,12 @@ describe('HashLockTransaction', () => { it('creation with an aggregate bonded tx', () => { const aggregateTransaction = AggregateTransaction.createBonded( Deadline.create(), - new UInt64([0, 0]), [], NetworkType.MIJIN_TEST, [], ); const signedTransaction = account.sign(aggregateTransaction); const transaction = HashLockTransaction.create(Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createRelative(10), UInt64.fromUint(10), signedTransaction, @@ -47,7 +45,6 @@ describe('HashLockTransaction', () => { it('should throw exception if it is not a aggregate bonded tx', () => { const aggregateTransaction = AggregateTransaction.createComplete( Deadline.create(), - new UInt64([0, 0]), [], NetworkType.MIJIN_TEST, [], @@ -55,7 +52,6 @@ describe('HashLockTransaction', () => { const signedTransaction = account.sign(aggregateTransaction); expect(() => { HashLockTransaction.create(Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createRelative(10), UInt64.fromUint(10), signedTransaction, diff --git a/test/model/transaction/LockFundsTransaction.spec.ts b/test/model/transaction/LockFundsTransaction.spec.ts index a321017d95..9a5c30dfb7 100644 --- a/test/model/transaction/LockFundsTransaction.spec.ts +++ b/test/model/transaction/LockFundsTransaction.spec.ts @@ -25,17 +25,55 @@ import {TestingAccount} from '../../conf/conf.spec'; describe('LockFundsTransaction', () => { const account = TestingAccount; + + it('should default maxFee field be set to 0', () => { + const aggregateTransaction = AggregateTransaction.createBonded( + Deadline.create(), + [], + NetworkType.MIJIN_TEST, + [], + ); + const signedTransaction = account.sign(aggregateTransaction); + const lockFundsTransaction = LockFundsTransaction.create(Deadline.create(), + NetworkCurrencyMosaic.createRelative(10), + UInt64.fromUint(10), + signedTransaction, + NetworkType.MIJIN_TEST, + ); + + expect(lockFundsTransaction.maxFee.higher).to.be.equal(0); + expect(lockFundsTransaction.maxFee.lower).to.be.equal(0); + }); + + it('should filled maxFee override transaction maxFee', () => { + const aggregateTransaction = AggregateTransaction.createBonded( + Deadline.create(), + [], + NetworkType.MIJIN_TEST, + [], + ); + const signedTransaction = account.sign(aggregateTransaction); + const lockFundsTransaction = LockFundsTransaction.create(Deadline.create(), + NetworkCurrencyMosaic.createRelative(10), + UInt64.fromUint(10), + signedTransaction, + NetworkType.MIJIN_TEST, + new UInt64([1, 0]) + ); + + expect(lockFundsTransaction.maxFee.higher).to.be.equal(0); + expect(lockFundsTransaction.maxFee.lower).to.be.equal(1); + }); + it('creation with an aggregate bonded tx', () => { const aggregateTransaction = AggregateTransaction.createBonded( Deadline.create(), - new UInt64([0, 0]), [], NetworkType.MIJIN_TEST, [], ); const signedTransaction = account.sign(aggregateTransaction); const transaction = LockFundsTransaction.create(Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createRelative(10), UInt64.fromUint(10), signedTransaction, @@ -48,7 +86,6 @@ describe('LockFundsTransaction', () => { it('should throw exception if it is not a aggregate bonded tx', () => { const aggregateTransaction = AggregateTransaction.createComplete( Deadline.create(), - new UInt64([0, 0]), [], NetworkType.MIJIN_TEST, [], @@ -56,7 +93,6 @@ describe('LockFundsTransaction', () => { const signedTransaction = account.sign(aggregateTransaction); expect(() => { LockFundsTransaction.create(Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createRelative(10), UInt64.fromUint(10), signedTransaction, diff --git a/test/model/transaction/ModifyMultisigAccountTransaction.spec.ts b/test/model/transaction/ModifyMultisigAccountTransaction.spec.ts index ea666f97d8..bbff1acb00 100644 --- a/test/model/transaction/ModifyMultisigAccountTransaction.spec.ts +++ b/test/model/transaction/ModifyMultisigAccountTransaction.spec.ts @@ -32,10 +32,54 @@ describe('ModifyMultisigAccountTransaction', () => { account = TestingAccount; }); + it('should default maxFee field be set to 0', () => { + const modifyMultisigAccountTransaction = ModifyMultisigAccountTransaction.create( + Deadline.create(), + 2, + 1, + [new MultisigCosignatoryModification( + MultisigCosignatoryModificationType.Add, + PublicAccount.createFromPublicKey('B0F93CBEE49EEB9953C6F3985B15A4F238E205584D8F924C621CBE4D7AC6EC24', + NetworkType.MIJIN_TEST), + ), + new MultisigCosignatoryModification( + MultisigCosignatoryModificationType.Add, + PublicAccount.createFromPublicKey('B1B5581FC81A6970DEE418D2C2978F2724228B7B36C5C6DF71B0162BB04778B4', + NetworkType.MIJIN_TEST), + )], + NetworkType.MIJIN_TEST, + ); + + expect(modifyMultisigAccountTransaction.maxFee.higher).to.be.equal(0); + expect(modifyMultisigAccountTransaction.maxFee.lower).to.be.equal(0); + }); + + it('should filled maxFee override transaction maxFee', () => { + const modifyMultisigAccountTransaction = ModifyMultisigAccountTransaction.create( + Deadline.create(), + 2, + 1, + [new MultisigCosignatoryModification( + MultisigCosignatoryModificationType.Add, + PublicAccount.createFromPublicKey('B0F93CBEE49EEB9953C6F3985B15A4F238E205584D8F924C621CBE4D7AC6EC24', + NetworkType.MIJIN_TEST), + ), + new MultisigCosignatoryModification( + MultisigCosignatoryModificationType.Add, + PublicAccount.createFromPublicKey('B1B5581FC81A6970DEE418D2C2978F2724228B7B36C5C6DF71B0162BB04778B4', + NetworkType.MIJIN_TEST), + )], + NetworkType.MIJIN_TEST, + new UInt64([1, 0]) + ); + + expect(modifyMultisigAccountTransaction.maxFee.higher).to.be.equal(0); + expect(modifyMultisigAccountTransaction.maxFee.lower).to.be.equal(1); + }); + it('should createComplete an ModifyMultisigAccountTransaction object and sign it', () => { const modifyMultisigAccountTransaction = ModifyMultisigAccountTransaction.create( Deadline.create(), - new UInt64([0, 0]), 2, 1, [new MultisigCosignatoryModification( diff --git a/test/model/transaction/MosaicAliasTransaction.spec.ts b/test/model/transaction/MosaicAliasTransaction.spec.ts index fbeebc025e..7132d9f019 100644 --- a/test/model/transaction/MosaicAliasTransaction.spec.ts +++ b/test/model/transaction/MosaicAliasTransaction.spec.ts @@ -32,12 +32,42 @@ describe('MosaicAliasTransaction', () => { account = TestingAccount; }); + it('should default maxFee field be set to 0', () => { + const namespaceId = new NamespaceId([33347626, 3779697293]); + const mosaicId = new MosaicId([2262289484, 3405110546]); + const mosaicAliasTransaction = MosaicAliasTransaction.create( + Deadline.create(), + AliasActionType.Link, + namespaceId, + mosaicId, + NetworkType.MIJIN_TEST, + ); + + expect(mosaicAliasTransaction.maxFee.higher).to.be.equal(0); + expect(mosaicAliasTransaction.maxFee.lower).to.be.equal(0); + }); + + it('should filled maxFee override transaction maxFee', () => { + const namespaceId = new NamespaceId([33347626, 3779697293]); + const mosaicId = new MosaicId([2262289484, 3405110546]); + const mosaicAliasTransaction = MosaicAliasTransaction.create( + Deadline.create(), + AliasActionType.Link, + namespaceId, + mosaicId, + NetworkType.MIJIN_TEST, + new UInt64([1, 0]) + ); + + expect(mosaicAliasTransaction.maxFee.higher).to.be.equal(0); + expect(mosaicAliasTransaction.maxFee.lower).to.be.equal(1); + }); + it('should createComplete an MosaicAliasTransaction object and sign it', () => { const namespaceId = new NamespaceId([33347626, 3779697293]); const mosaicId = new MosaicId([2262289484, 3405110546]); const mosaicAliasTransaction = MosaicAliasTransaction.create( Deadline.create(), - new UInt64([0, 0]), AliasActionType.Link, namespaceId, mosaicId, diff --git a/test/model/transaction/MosaicDefinitionTransaction.spec.ts b/test/model/transaction/MosaicDefinitionTransaction.spec.ts index d6571aa637..46f22a8696 100644 --- a/test/model/transaction/MosaicDefinitionTransaction.spec.ts +++ b/test/model/transaction/MosaicDefinitionTransaction.spec.ts @@ -33,10 +33,48 @@ describe('MosaicDefinitionTransaction', () => { account = TestingAccount; }); + it('should default maxFee field be set to 0', () => { + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce + new MosaicId(UInt64.fromUint(1).toDTO()), // ID + MosaicProperties.create({ + supplyMutable: true, + transferable: true, + levyMutable: true, + divisibility: 3, + duration: UInt64.fromUint(1000), + }), + NetworkType.MIJIN_TEST, + ); + + expect(mosaicDefinitionTransaction.maxFee.higher).to.be.equal(0); + expect(mosaicDefinitionTransaction.maxFee.lower).to.be.equal(0); + }); + + it('should filled maxFee override transaction maxFee', () => { + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce + new MosaicId(UInt64.fromUint(1).toDTO()), // ID + MosaicProperties.create({ + supplyMutable: true, + transferable: true, + levyMutable: true, + divisibility: 3, + duration: UInt64.fromUint(1000), + }), + NetworkType.MIJIN_TEST, + new UInt64([1, 0]) + ); + + expect(mosaicDefinitionTransaction.maxFee.higher).to.be.equal(0); + expect(mosaicDefinitionTransaction.maxFee.lower).to.be.equal(1); + }); + it('should createComplete an MosaicDefinitionTransaction object and sign it with flags 7', () => { const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( Deadline.create(), - new UInt64([0, 0]), new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce new MosaicId(UInt64.fromUint(1).toDTO()), // ID MosaicProperties.create({ @@ -69,7 +107,6 @@ describe('MosaicDefinitionTransaction', () => { const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( Deadline.create(), - new UInt64([0, 0]), new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce new MosaicId(UInt64.fromUint(1).toDTO()), // ID MosaicProperties.create({ diff --git a/test/model/transaction/MosaicSupplyChangeTransaction.spec.ts b/test/model/transaction/MosaicSupplyChangeTransaction.spec.ts index 97326f76d4..bd223535c9 100644 --- a/test/model/transaction/MosaicSupplyChangeTransaction.spec.ts +++ b/test/model/transaction/MosaicSupplyChangeTransaction.spec.ts @@ -31,11 +31,39 @@ describe('MosaicSupplyChangeTransaction', () => { account = TestingAccount; }); + it('should default maxFee field be set to 0', () => { + const mosaicId = new MosaicId([2262289484, 3405110546]); + const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( + Deadline.create(), + mosaicId, + MosaicSupplyType.Increase, + UInt64.fromUint(10), + NetworkType.MIJIN_TEST, + ); + + expect(mosaicSupplyChangeTransaction.maxFee.higher).to.be.equal(0); + expect(mosaicSupplyChangeTransaction.maxFee.lower).to.be.equal(0); + }); + + it('should filled maxFee override transaction maxFee', () => { + const mosaicId = new MosaicId([2262289484, 3405110546]); + const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( + Deadline.create(), + mosaicId, + MosaicSupplyType.Increase, + UInt64.fromUint(10), + NetworkType.MIJIN_TEST, + new UInt64([1, 0]) + ); + + expect(mosaicSupplyChangeTransaction.maxFee.higher).to.be.equal(0); + expect(mosaicSupplyChangeTransaction.maxFee.lower).to.be.equal(1); + }); + it('should createComplete an MosaicSupplyChangeTransaction object and sign it', () => { const mosaicId = new MosaicId([2262289484, 3405110546]); const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( Deadline.create(), - new UInt64([0, 0]), mosaicId, MosaicSupplyType.Increase, UInt64.fromUint(10), diff --git a/test/model/transaction/RegisterNamespaceTransaction.spec.ts b/test/model/transaction/RegisterNamespaceTransaction.spec.ts index 1971fecfc7..a80d69336f 100644 --- a/test/model/transaction/RegisterNamespaceTransaction.spec.ts +++ b/test/model/transaction/RegisterNamespaceTransaction.spec.ts @@ -29,10 +29,34 @@ describe('RegisterNamespaceTransaction', () => { account = TestingAccount; }); + it('should default maxFee field be set to 0', () => { + const registerNamespaceTransaction = RegisterNamespaceTransaction.createRootNamespace( + Deadline.create(), + 'root-test-namespace', + UInt64.fromUint(1000), + NetworkType.MIJIN_TEST, + ); + + expect(registerNamespaceTransaction.maxFee.higher).to.be.equal(0); + expect(registerNamespaceTransaction.maxFee.lower).to.be.equal(0); + }); + + it('should filled maxFee override transaction maxFee', () => { + const registerNamespaceTransaction = RegisterNamespaceTransaction.createRootNamespace( + Deadline.create(), + 'root-test-namespace', + UInt64.fromUint(1000), + NetworkType.MIJIN_TEST, + new UInt64([1, 0]) + ); + + expect(registerNamespaceTransaction.maxFee.higher).to.be.equal(0); + expect(registerNamespaceTransaction.maxFee.lower).to.be.equal(1); + }); + it('should createComplete an root RegisterNamespaceTransaction object and sign it', () => { const registerNamespaceTransaction = RegisterNamespaceTransaction.createRootNamespace( Deadline.create(), - new UInt64([0, 0]), 'root-test-namespace', UInt64.fromUint(1000), NetworkType.MIJIN_TEST, @@ -53,7 +77,6 @@ describe('RegisterNamespaceTransaction', () => { it('should createComplete an sub RegisterNamespaceTransaction object and sign it', () => { const registerNamespaceTransaction = RegisterNamespaceTransaction.createSubNamespace( Deadline.create(), - new UInt64([0, 0]), 'root-test-namespace', 'parent-test-namespace', NetworkType.MIJIN_TEST, diff --git a/test/model/transaction/SecretLockTransaction.spec.ts b/test/model/transaction/SecretLockTransaction.spec.ts index 743108afc2..b1b4848420 100644 --- a/test/model/transaction/SecretLockTransaction.spec.ts +++ b/test/model/transaction/SecretLockTransaction.spec.ts @@ -28,12 +28,46 @@ import {UInt64} from '../../../src/model/UInt64'; describe('SecretLockTransaction', () => { + it('should default maxFee field be set to 0', () => { + const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9DF538782BF199C189DABEAC7'; + const recipient = Address.createFromRawAddress('SDBDG4IT43MPCW2W4CBBCSJJT42AYALQN7A4VVWL'); + const secretLockTransaction = SecretLockTransaction.create( + Deadline.create(), + NetworkCurrencyMosaic.createAbsolute(10), + UInt64.fromUint(100), + HashType.Op_Sha3_256, + sha3_256.create().update(convert.hexToUint8(proof)).hex(), + recipient, + NetworkType.MIJIN_TEST, + ); + + expect(secretLockTransaction.maxFee.higher).to.be.equal(0); + expect(secretLockTransaction.maxFee.lower).to.be.equal(0); + }); + + it('should filled maxFee override transaction maxFee', () => { + const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9DF538782BF199C189DABEAC7'; + const recipient = Address.createFromRawAddress('SDBDG4IT43MPCW2W4CBBCSJJT42AYALQN7A4VVWL'); + const secretLockTransaction = SecretLockTransaction.create( + Deadline.create(), + NetworkCurrencyMosaic.createAbsolute(10), + UInt64.fromUint(100), + HashType.Op_Sha3_256, + sha3_256.create().update(convert.hexToUint8(proof)).hex(), + recipient, + NetworkType.MIJIN_TEST, + new UInt64([1, 0]) + ); + + expect(secretLockTransaction.maxFee.higher).to.be.equal(0); + expect(secretLockTransaction.maxFee.lower).to.be.equal(1); + }); + it('should be created with HashType: Op_Sha3_256 secret', () => { const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9DF538782BF199C189DABEAC7'; const recipient = Address.createFromRawAddress('SDBDG4IT43MPCW2W4CBBCSJJT42AYALQN7A4VVWL'); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Sha3_256, @@ -54,7 +88,6 @@ describe('SecretLockTransaction', () => { const recipient = Address.createFromRawAddress('SDBDG4IT43MPCW2W4CBBCSJJT42AYALQN7A4VVWL'); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Sha3_256, @@ -70,7 +103,6 @@ describe('SecretLockTransaction', () => { const recipient = Address.createFromRawAddress('SDBDG4IT43MPCW2W4CBBCSJJT42AYALQN7A4VVWL'); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Keccak_256, @@ -91,7 +123,6 @@ describe('SecretLockTransaction', () => { const recipient = Address.createFromRawAddress('SDBDG4IT43MPCW2W4CBBCSJJT42AYALQN7A4VVWL'); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Keccak_256, @@ -106,7 +137,6 @@ describe('SecretLockTransaction', () => { const recipient = Address.createFromRawAddress('SDBDG4IT43MPCW2W4CBBCSJJT42AYALQN7A4VVWL'); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_160, @@ -127,7 +157,6 @@ describe('SecretLockTransaction', () => { const recipient = Address.createFromRawAddress('SDBDG4IT43MPCW2W4CBBCSJJT42AYALQN7A4VVWL'); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_160, @@ -142,7 +171,6 @@ describe('SecretLockTransaction', () => { const recipient = Address.createFromRawAddress('SDBDG4IT43MPCW2W4CBBCSJJT42AYALQN7A4VVWL'); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_256, @@ -163,7 +191,6 @@ describe('SecretLockTransaction', () => { const recipient = Address.createFromRawAddress('SDBDG4IT43MPCW2W4CBBCSJJT42AYALQN7A4VVWL'); const secretLockTransaction = SecretLockTransaction.create( Deadline.create(), - new UInt64([0, 0]), NetworkCurrencyMosaic.createAbsolute(10), UInt64.fromUint(100), HashType.Op_Hash_256, diff --git a/test/model/transaction/SecretProofTransaction.spec.ts b/test/model/transaction/SecretProofTransaction.spec.ts index e5be2125b5..7250081ed2 100644 --- a/test/model/transaction/SecretProofTransaction.spec.ts +++ b/test/model/transaction/SecretProofTransaction.spec.ts @@ -25,11 +25,39 @@ import {UInt64} from '../../../src/model/UInt64'; describe('SecretProofTransaction', () => { + it('should default maxFee field be set to 0', () => { + const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9DF538782BF199C189DABEAC7'; + const secretProofTransaction = SecretProofTransaction.create( + Deadline.create(), + HashType.Op_Sha3_256, + sha3_256.create().update(convert.hexToUint8(proof)).hex(), + proof, + NetworkType.MIJIN_TEST, + ); + + expect(secretProofTransaction.maxFee.higher).to.be.equal(0); + expect(secretProofTransaction.maxFee.lower).to.be.equal(0); + }); + + it('should filled maxFee override transaction maxFee', () => { + const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9DF538782BF199C189DABEAC7'; + const secretProofTransaction = SecretProofTransaction.create( + Deadline.create(), + HashType.Op_Sha3_256, + sha3_256.create().update(convert.hexToUint8(proof)).hex(), + proof, + NetworkType.MIJIN_TEST, + new UInt64([1, 0]) + ); + + expect(secretProofTransaction.maxFee.higher).to.be.equal(0); + expect(secretProofTransaction.maxFee.lower).to.be.equal(1); + }); + it('should be created with HashType: Op_Sha3_256 secret', () => { const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9DF538782BF199C189DABEAC7'; const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), - new UInt64([0, 0]), HashType.Op_Sha3_256, sha3_256.create().update(convert.hexToUint8(proof)).hex(), proof, @@ -45,7 +73,6 @@ describe('SecretProofTransaction', () => { const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9DF538782BF199C189DABEAC7'; const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), - new UInt64([0, 0]), HashType.Op_Sha3_256, 'non valid hash', proof, @@ -57,7 +84,6 @@ describe('SecretProofTransaction', () => { const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9DF538782BF199C189DABEAC7'; const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), - new UInt64([0, 0]), HashType.Op_Keccak_256, keccak_256.create().update(convert.hexToUint8(proof)).hex(), proof, @@ -73,7 +99,6 @@ describe('SecretProofTransaction', () => { const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9DF538782BF199C189DABEAC7'; const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), - new UInt64([0, 0]), HashType.Op_Keccak_256, 'non valid hash', proof, @@ -85,7 +110,6 @@ describe('SecretProofTransaction', () => { const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9'; const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), - new UInt64([0, 0]), HashType.Op_Hash_160, CryptoJS.RIPEMD160(CryptoJS.SHA256(proof).toString(CryptoJS.enc.Hex)).toString(CryptoJS.enc.Hex), proof, @@ -101,7 +125,6 @@ describe('SecretProofTransaction', () => { const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9DF538782BF199C189DABEAC7'; const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), - new UInt64([0, 0]), HashType.Op_Hash_160, 'non valid hash', proof, @@ -114,7 +137,6 @@ describe('SecretProofTransaction', () => { const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9DF538782BF199C189DABEAC7'; const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), - new UInt64([0, 0]), HashType.Op_Hash_256, CryptoJS.SHA256(CryptoJS.SHA256(proof).toString(CryptoJS.enc.Hex)).toString(CryptoJS.enc.Hex), proof, @@ -130,7 +152,6 @@ describe('SecretProofTransaction', () => { const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9DF538782BF199C189DABEAC7'; const secretProofTransaction = SecretProofTransaction.create( Deadline.create(), - new UInt64([0, 0]), HashType.Op_Hash_256, 'non valid hash', proof, diff --git a/test/model/transaction/TransferTransaction.spec.ts b/test/model/transaction/TransferTransaction.spec.ts index cb3dc03a5e..c0bebc0e09 100644 --- a/test/model/transaction/TransferTransaction.spec.ts +++ b/test/model/transaction/TransferTransaction.spec.ts @@ -33,10 +33,36 @@ describe('TransferTransaction', () => { account = TestingAccount; }); + it('should default maxFee field be set to 0', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + expect(transferTransaction.maxFee.higher).to.be.equal(0); + expect(transferTransaction.maxFee.lower).to.be.equal(0); + }); + + it('should filled maxFee override transaction maxFee', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + new UInt64([1, 0]) + ); + + expect(transferTransaction.maxFee.higher).to.be.equal(0); + expect(transferTransaction.maxFee.lower).to.be.equal(1); + }); + it('should createComplete an TransferTransaction object and sign it without mosaics', () => { const transferTransaction = TransferTransaction.create( Deadline.create(), - new UInt64([0, 0]), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), [], PlainMessage.create('test-message'), @@ -59,7 +85,6 @@ describe('TransferTransaction', () => { it('should createComplete an TransferTransaction object and sign it with mosaics', () => { const transferTransaction = TransferTransaction.create( Deadline.create(), - new UInt64([0, 0]), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), [ NetworkCurrencyMosaic.createRelative(100), From 02e45178c6ed3bf64ca6713b1817a176fff0373a Mon Sep 17 00:00:00 2001 From: Greg S Date: Mon, 25 Mar 2019 20:01:06 +0100 Subject: [PATCH 05/83] issue #53: implementation proposal for Transaction.size and type classes overloads --- .../transaction/AccountLinkTransaction.ts | 16 ++++++ .../transaction/AccountPropertyTransaction.ts | 6 +-- .../transaction/AddressAliasTransaction.ts | 17 +++++++ src/model/transaction/AggregateTransaction.ts | 20 ++++++++ src/model/transaction/LockFundsTransaction.ts | 18 +++++++ ...ModifyAccountPropertyAddressTransaction.ts | 21 ++++++++ ...ifyAccountPropertyEntityTypeTransaction.ts | 21 ++++++++ .../ModifyAccountPropertyMosaicTransaction.ts | 21 ++++++++ .../ModifyMultisigAccountTransaction.ts | 22 +++++++++ .../transaction/MosaicAliasTransaction.ts | 17 +++++++ .../MosaicDefinitionTransaction.ts | 21 ++++++++ .../MosaicSupplyChangeTransaction.ts | 17 +++++++ .../RegisterNamespaceTransaction.ts | 23 ++++++++- .../transaction/SecretLockTransaction.ts | 24 ++++++++- .../transaction/SecretProofTransaction.ts | 22 ++++++++- src/model/transaction/Transaction.ts | 17 +++++++ src/model/transaction/TransferTransaction.ts | 24 ++++++++- .../AccountLinkTransaction.spec.ts | 12 +++++ .../AccountPropertyTransaction.spec.ts | 49 +++++++++++++++++++ .../AddressAliasTransaction.spec.ts | 15 ++++++ .../transaction/AggregateTransaction.spec.ts | 22 +++++++++ .../transaction/LockFundsTransaction.spec.ts | 19 +++++++ .../ModifyMultisigAccountTransaction.spec.ts | 17 +++++++ .../MosaicAliasTransaction.spec.ts | 15 ++++++ .../MosaicDefinitionTransaction.spec.ts | 20 ++++++++ .../MosaicSupplyChangeTransaction.spec.ts | 14 ++++++ .../RegisterNamespaceTransaction.spec.ts | 12 +++++ .../transaction/SecretLockTransaction.spec.ts | 17 +++++++ .../SecretProofTransaction.spec.ts | 14 ++++++ test/model/transaction/Transaction.spec.ts | 15 ++++++ .../transaction/TransferTransaction.spec.ts | 15 ++++++ 31 files changed, 576 insertions(+), 7 deletions(-) diff --git a/src/model/transaction/AccountLinkTransaction.ts b/src/model/transaction/AccountLinkTransaction.ts index a530180d55..a837f135de 100644 --- a/src/model/transaction/AccountLinkTransaction.ts +++ b/src/model/transaction/AccountLinkTransaction.ts @@ -80,6 +80,22 @@ export class AccountLinkTransaction extends Transaction { super(TransactionType.LINK_ACCOUNT, networkType, version, deadline, maxFee, signature, signer, transactionInfo); } + /** + * @override Transaction.size() + * @description get the byte size of a AccountLinkTransaction + * @returns {number} + * @memberof AccountLinkTransaction + */ + public get size(): number { + const byteSize = super.size; + + // set static byte size fields + const bytePublicKey = 32; + const byteLinkAction = 1; + + return byteSize + bytePublicKey + byteLinkAction; + } + /** * @internal * @returns {VerifiableTransaction} diff --git a/src/model/transaction/AccountPropertyTransaction.ts b/src/model/transaction/AccountPropertyTransaction.ts index a94d38e5b5..bcfac45480 100644 --- a/src/model/transaction/AccountPropertyTransaction.ts +++ b/src/model/transaction/AccountPropertyTransaction.ts @@ -51,7 +51,7 @@ export class AccountPropertyTransaction { propertyType, modifications, networkType, - maxFee + maxFee, ); } @@ -79,7 +79,7 @@ export class AccountPropertyTransaction { propertyType, modifications, networkType, - maxFee + maxFee, ); } @@ -107,7 +107,7 @@ export class AccountPropertyTransaction { propertyType, modifications, networkType, - maxFee + maxFee, ); } diff --git a/src/model/transaction/AddressAliasTransaction.ts b/src/model/transaction/AddressAliasTransaction.ts index bb7502f847..a02881a8ae 100644 --- a/src/model/transaction/AddressAliasTransaction.ts +++ b/src/model/transaction/AddressAliasTransaction.ts @@ -93,6 +93,23 @@ export class AddressAliasTransaction extends Transaction { super(TransactionType.ADDRESS_ALIAS, networkType, version, deadline, maxFee, signature, signer, transactionInfo); } + /** + * @override Transaction.size() + * @description get the byte size of a AddressAliasTransaction + * @returns {number} + * @memberof AddressAliasTransaction + */ + public get size(): number { + const byteSize = super.size; + + // set static byte size fields + const byteActionType = 1; + const byteNamespaceId = 8; + const byteAddress = 25; + + return byteSize + byteActionType + byteNamespaceId + byteAddress; + } + /** * @internal * @returns {VerifiableTransaction} diff --git a/src/model/transaction/AggregateTransaction.ts b/src/model/transaction/AggregateTransaction.ts index 86a78e4e33..3b7bb1567e 100644 --- a/src/model/transaction/AggregateTransaction.ts +++ b/src/model/transaction/AggregateTransaction.ts @@ -151,4 +151,24 @@ export class AggregateTransaction extends Transaction { || (this.signer !== undefined && this.signer.equals(publicAccount)); } + /** + * @override Transaction.size() + * @description get the byte size of a AggregateTransaction + * @returns {number} + * @memberof AggregateTransaction + */ + public get size(): number { + const byteSize = super.size; + + // set static byte size fields + const byteTransactionsSize = 4; + + // calculate each inner transaction's size + let byteTransactions = 0; + this.innerTransactions.map((transaction) => { + byteTransactions += transaction.size; + }); + + return byteSize + byteTransactionsSize + byteTransactions; + } } diff --git a/src/model/transaction/LockFundsTransaction.ts b/src/model/transaction/LockFundsTransaction.ts index a83458e580..aeafd6328a 100644 --- a/src/model/transaction/LockFundsTransaction.ts +++ b/src/model/transaction/LockFundsTransaction.ts @@ -101,6 +101,24 @@ export class LockFundsTransaction extends Transaction { } } + /** + * @override Transaction.size() + * @description get the byte size of a LockFundsTransaction + * @returns {number} + * @memberof LockFundsTransaction + */ + public get size(): number { + const byteSize = super.size; + + // set static byte size fields + const byteMosaicId = 8; + const byteAmount = 8; + const byteDuration = 8; + const byteHash = 32; + + return byteSize + byteMosaicId + byteAmount + byteDuration + byteHash; + } + /** * @internal * @return {VerifiableTransaction} diff --git a/src/model/transaction/ModifyAccountPropertyAddressTransaction.ts b/src/model/transaction/ModifyAccountPropertyAddressTransaction.ts index 00a47b26b9..2c7874d5d7 100644 --- a/src/model/transaction/ModifyAccountPropertyAddressTransaction.ts +++ b/src/model/transaction/ModifyAccountPropertyAddressTransaction.ts @@ -74,6 +74,27 @@ export class ModifyAccountPropertyAddressTransaction extends Transaction { super(TransactionType.MODIFY_ACCOUNT_PROPERTY_ADDRESS, networkType, version, deadline, maxFee, signature, signer, transactionInfo); } + /** + * @override Transaction.size() + * @description get the byte size of a ModifyAccountPropertyAddressTransaction + * @returns {number} + * @memberof ModifyAccountPropertyAddressTransaction + */ + public get size(): number { + const byteSize = super.size; + + // set static byte size fields + const bytePropertyType = 1; + const byteModificationCount = 1; + + // each modification contains : + // - 1 byte for modificationType + // - 25 bytes for the modification value (address) + const byteModifications = 26 * this.modifications.length; + + return byteSize + bytePropertyType + byteModificationCount + byteModifications; + } + /** * @internal * @returns {VerifiableTransaction} diff --git a/src/model/transaction/ModifyAccountPropertyEntityTypeTransaction.ts b/src/model/transaction/ModifyAccountPropertyEntityTypeTransaction.ts index 7a7e920e85..8fae5b10b4 100644 --- a/src/model/transaction/ModifyAccountPropertyEntityTypeTransaction.ts +++ b/src/model/transaction/ModifyAccountPropertyEntityTypeTransaction.ts @@ -75,6 +75,27 @@ export class ModifyAccountPropertyEntityTypeTransaction extends Transaction { super(TransactionType.MODIFY_ACCOUNT_PROPERTY_ENTITY_TYPE, networkType, version, deadline, maxFee, signature, signer, transactionInfo); } + /** + * @override Transaction.size() + * @description get the byte size of a ModifyAccountPropertyEntityTypeTransaction + * @returns {number} + * @memberof ModifyAccountPropertyEntityTypeTransaction + */ + public get size(): number { + const byteSize = super.size; + + // set static byte size fields + const bytePropertyType = 1; + const byteModificationCount = 1; + + // each modification contains : + // - 1 byte for modificationType + // - 2 bytes for the modification value (transaction type) + const byteModifications = 3 * this.modifications.length; + + return byteSize + bytePropertyType + byteModificationCount + byteModifications; + } + /** * @internal * @returns {VerifiableTransaction} diff --git a/src/model/transaction/ModifyAccountPropertyMosaicTransaction.ts b/src/model/transaction/ModifyAccountPropertyMosaicTransaction.ts index 6315c0ddac..d8bd9e3dfb 100644 --- a/src/model/transaction/ModifyAccountPropertyMosaicTransaction.ts +++ b/src/model/transaction/ModifyAccountPropertyMosaicTransaction.ts @@ -74,6 +74,27 @@ export class ModifyAccountPropertyMosaicTransaction extends Transaction { super(TransactionType.MODIFY_ACCOUNT_PROPERTY_MOSAIC, networkType, version, deadline, maxFee, signature, signer, transactionInfo); } + /** + * @override Transaction.size() + * @description get the byte size of a ModifyAccountPropertyMosaicTransaction + * @returns {number} + * @memberof ModifyAccountPropertyMosaicTransaction + */ + public get size(): number { + const byteSize = super.size; + + // set static byte size fields + const bytePropertyType = 1; + const byteModificationCount = 1; + + // each modification contains : + // - 1 byte for modificationType + // - 8 bytes for the modification value (mosaicId) + const byteModifications = 9 * this.modifications.length; + + return byteSize + bytePropertyType + byteModificationCount + byteModifications; + } + /** * @internal * @returns {VerifiableTransaction} diff --git a/src/model/transaction/ModifyMultisigAccountTransaction.ts b/src/model/transaction/ModifyMultisigAccountTransaction.ts index ccda40041e..2510e8770a 100644 --- a/src/model/transaction/ModifyMultisigAccountTransaction.ts +++ b/src/model/transaction/ModifyMultisigAccountTransaction.ts @@ -94,6 +94,28 @@ export class ModifyMultisigAccountTransaction extends Transaction { super(TransactionType.MODIFY_MULTISIG_ACCOUNT, networkType, version, deadline, maxFee, signature, signer, transactionInfo); } + /** + * @override Transaction.size() + * @description get the byte size of a ModifyMultisigAccountTransaction + * @returns {number} + * @memberof ModifyMultisigAccountTransaction + */ + public get size(): number { + const byteSize = super.size; + + // set static byte size fields + const byteRemovalDelta = 1; + const byteApprovalDelta = 1; + const byteNumModifications = 1; + + // each modification contains : + // - 1 byte for modificationType + // - 32 bytes for cosignatoryPublicKey + const byteModifications = 33 * this.modifications.length + + return byteSize + byteRemovalDelta + byteApprovalDelta + byteNumModifications + byteModifications; + } + /** * @internal * @returns {VerifiableTransaction} diff --git a/src/model/transaction/MosaicAliasTransaction.ts b/src/model/transaction/MosaicAliasTransaction.ts index 49151797c9..b80f047548 100644 --- a/src/model/transaction/MosaicAliasTransaction.ts +++ b/src/model/transaction/MosaicAliasTransaction.ts @@ -93,6 +93,23 @@ export class MosaicAliasTransaction extends Transaction { super(TransactionType.MOSAIC_ALIAS, networkType, version, deadline, maxFee, signature, signer, transactionInfo); } + /** + * @override Transaction.size() + * @description get the byte size of a MosaicAliasTransaction + * @returns {number} + * @memberof MosaicAliasTransaction + */ + public get size(): number { + const byteSize = super.size; + + // set static byte size fields + const byteType = 1; + const byteNamespaceId = 8; + const byteMosaicId = 8; + + return byteSize + byteType + byteNamespaceId + byteMosaicId; + } + /** * @internal * @returns {VerifiableTransaction} diff --git a/src/model/transaction/MosaicDefinitionTransaction.ts b/src/model/transaction/MosaicDefinitionTransaction.ts index 6bde92b8b3..9443d08c26 100644 --- a/src/model/transaction/MosaicDefinitionTransaction.ts +++ b/src/model/transaction/MosaicDefinitionTransaction.ts @@ -97,6 +97,27 @@ export class MosaicDefinitionTransaction extends Transaction { super(TransactionType.MOSAIC_DEFINITION, networkType, version, deadline, maxFee, signature, signer, transactionInfo); } + /** + * @override Transaction.size() + * @description get the byte size of a MosaicDefinitionTransaction + * @returns {number} + * @memberof MosaicDefinitionTransaction + */ + public get size(): number { + const byteSize = super.size; + + // set static byte size fields + const byteNonce = 4; + const byteMosaicId = 8; + const byteNumProps = 1; + const byteFlags = 1; + const byteDivisibility = 1; + const byteDurationSize = 1; + const byteDuration = 8; + + return byteSize + byteNonce + byteMosaicId + byteNumProps + byteFlags + byteDivisibility + byteDurationSize + byteDuration; + } + /** * @internal * @returns {VerifiableTransaction} diff --git a/src/model/transaction/MosaicSupplyChangeTransaction.ts b/src/model/transaction/MosaicSupplyChangeTransaction.ts index b969e17f92..4518f21edd 100644 --- a/src/model/transaction/MosaicSupplyChangeTransaction.ts +++ b/src/model/transaction/MosaicSupplyChangeTransaction.ts @@ -92,6 +92,23 @@ export class MosaicSupplyChangeTransaction extends Transaction { super(TransactionType.MOSAIC_SUPPLY_CHANGE, networkType, version, deadline, maxFee, signature, signer, transactionInfo); } + /** + * @override Transaction.size() + * @description get the byte size of a MosaicSupplyChangeTransaction + * @returns {number} + * @memberof MosaicSupplyChangeTransaction + */ + public get size(): number { + const byteSize = super.size; + + // set static byte size fields + const byteMosaicId = 8; + const byteDirection = 1; + const byteDelta = 8; + + return byteSize + byteMosaicId + byteDirection + byteDelta; + } + /** * @internal * @returns {VerifiableTransaction} diff --git a/src/model/transaction/RegisterNamespaceTransaction.ts b/src/model/transaction/RegisterNamespaceTransaction.ts index 8381e00cd5..1d1fbcf9ee 100644 --- a/src/model/transaction/RegisterNamespaceTransaction.ts +++ b/src/model/transaction/RegisterNamespaceTransaction.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { NamespaceCreationTransaction as RegisterNamespaceTransactionLibrary, subnamespaceNamespaceId, subnamespaceParentId, VerifiableTransaction } from 'nem2-library'; +import { convert, NamespaceCreationTransaction as RegisterNamespaceTransactionLibrary, subnamespaceNamespaceId, subnamespaceParentId, VerifiableTransaction } from 'nem2-library'; import { PublicAccount } from '../account/PublicAccount'; import { NetworkType } from '../blockchain/NetworkType'; import { NamespaceId } from '../namespace/NamespaceId'; @@ -134,6 +134,27 @@ export class RegisterNamespaceTransaction extends Transaction { super(TransactionType.REGISTER_NAMESPACE, networkType, version, deadline, maxFee, signature, signer, transactionInfo); } + /** + * @override Transaction.size() + * @description get the byte size of a RegisterNamespaceTransaction + * @returns {number} + * @memberof RegisterNamespaceTransaction + */ + public get size(): number { + const byteSize = super.size; + + // set static byte size fields + const byteType = 1; + const byteDurationParentId = 8; + const byteNamespaceId = 8; + const byteNameSize = 1; + + // convert name to uint8 + const byteName = convert.utf8ToHex(this.namespaceName).length / 2; + + return byteSize + byteType + byteDurationParentId + byteNamespaceId + byteNameSize + byteName; + } + /** * @internal * @returns {VerifiableTransaction} diff --git a/src/model/transaction/SecretLockTransaction.ts b/src/model/transaction/SecretLockTransaction.ts index 4a4db900bc..2d66cd3612 100644 --- a/src/model/transaction/SecretLockTransaction.ts +++ b/src/model/transaction/SecretLockTransaction.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { SecretLockTransaction as SecretLockTransactionLibrary, VerifiableTransaction } from 'nem2-library'; +import { convert, SecretLockTransaction as SecretLockTransactionLibrary, VerifiableTransaction } from 'nem2-library'; import { Address } from '../account/Address'; import { PublicAccount } from '../account/PublicAccount'; import { NetworkType } from '../blockchain/NetworkType'; @@ -110,6 +110,28 @@ export class SecretLockTransaction extends Transaction { } } + /** + * @override Transaction.size() + * @description get the byte size of a SecretLockTransaction + * @returns {number} + * @memberof SecretLockTransaction + */ + public get size(): number { + const byteSize = super.size; + + // set static byte size fields + const byteMosaicId = 8; + const byteAmount = 8; + const byteDuration = 8; + const byteAlgorithm = 1; + const byteRecipient = 25; + + // convert secret to uint8 + const byteSecret = convert.hexToUint8(this.secret).length; + + return byteSize + byteMosaicId + byteAmount + byteDuration + byteAlgorithm + byteRecipient + byteSecret; + } + /** * @internal * @returns {VerifiableTransaction} diff --git a/src/model/transaction/SecretProofTransaction.ts b/src/model/transaction/SecretProofTransaction.ts index 991f5747e5..5149b0c9c3 100644 --- a/src/model/transaction/SecretProofTransaction.ts +++ b/src/model/transaction/SecretProofTransaction.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { SecretProofTransaction as SecretProofTransactionLibrary, VerifiableTransaction } from 'nem2-library'; +import { convert, SecretProofTransaction as SecretProofTransactionLibrary, VerifiableTransaction } from 'nem2-library'; import { PublicAccount } from '../account/PublicAccount'; import { NetworkType } from '../blockchain/NetworkType'; import { UInt64 } from '../UInt64'; @@ -83,6 +83,26 @@ export class SecretProofTransaction extends Transaction { } } + /** + * @override Transaction.size() + * @description get the byte size of a SecretProofTransaction + * @returns {number} + * @memberof SecretProofTransaction + */ + public get size(): number { + const byteSize = super.size; + + // hash algorithm and proof size static byte size + const byteAlgorithm = 1; + const byteProofSize = 2; + + // convert secret and proof to uint8 + const byteSecret = convert.hexToUint8(this.secret).length; + const byteProof = convert.hexToUint8(this.proof).length; + + return byteSize + byteAlgorithm + byteSecret + byteProofSize + byteProof; + } + /** * @internal * @returns {VerifiableTransaction} diff --git a/src/model/transaction/Transaction.ts b/src/model/transaction/Transaction.ts index eca739053e..1255b89bd3 100644 --- a/src/model/transaction/Transaction.ts +++ b/src/model/transaction/Transaction.ts @@ -173,4 +173,21 @@ export abstract class Transaction { } throw new Error('an Announced transaction can\'t be modified'); } + + /** + * @description get the byte size of a transaction + * @returns {number} + * @memberof Transaction + */ + public get size(): number { + const byteSize = 4 // size + + 64 // signature + + 32 // signer + + 2 // version + + 2 // type + + 8 // maxFee + + 8; // deadline + + return byteSize; + } } diff --git a/src/model/transaction/TransferTransaction.ts b/src/model/transaction/TransferTransaction.ts index a90856937d..5994c63f81 100644 --- a/src/model/transaction/TransferTransaction.ts +++ b/src/model/transaction/TransferTransaction.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { TransferTransaction as TransferTransactionLibrary, VerifiableTransaction } from 'nem2-library'; +import { convert, TransferTransaction as TransferTransactionLibrary, VerifiableTransaction } from 'nem2-library'; import { Address } from '../account/Address'; import { PublicAccount } from '../account/PublicAccount'; import { NetworkType } from '../blockchain/NetworkType'; @@ -107,6 +107,28 @@ export class TransferTransaction extends Transaction { return (this.recipient as Address).plain(); } + /** + * @override Transaction.size() + * @description get the byte size of a TransferTransaction + * @returns {number} + * @memberof TransferTransaction + */ + public get size(): number { + const byteSize = super.size; + + // recipient and number of mosaics are static byte size + const byteRecipient = 25; + const byteNumMosaics = 2; + + // read message payload size + const bytePayload = convert.hexToUint8(convert.utf8ToHex(this.message.payload)).length; + + // mosaicId / namespaceId are written on 8 bytes + const byteMosaics = 8 * this.mosaics.length; + + return byteSize + byteRecipient + byteNumMosaics + bytePayload + byteMosaics; + } + /** * @internal * @returns {VerifiableTransaction} diff --git a/test/model/transaction/AccountLinkTransaction.spec.ts b/test/model/transaction/AccountLinkTransaction.spec.ts index e0368e38ef..513452e816 100644 --- a/test/model/transaction/AccountLinkTransaction.spec.ts +++ b/test/model/transaction/AccountLinkTransaction.spec.ts @@ -92,4 +92,16 @@ describe('AccountLinkTransaction', () => { signedTransaction.payload.length, )).to.be.equal('C2F93346E27CE6AD1A9F8F5E3066F8326593A406BDF357ACB041E2F9AB402EFE01'); }); + + describe('size', () => { + it('should return 153 for AccountLinkTransaction byte size', () => { + const accountLinkTransaction = AccountLinkTransaction.create( + Deadline.create(), + account.publicKey, + LinkAction.Unlink, + NetworkType.MIJIN_TEST, + ); + expect(accountLinkTransaction.size).to.be.equal(153); + }); + }); }); diff --git a/test/model/transaction/AccountPropertyTransaction.spec.ts b/test/model/transaction/AccountPropertyTransaction.spec.ts index 2225db94c1..71cf21c198 100644 --- a/test/model/transaction/AccountPropertyTransaction.spec.ts +++ b/test/model/transaction/AccountPropertyTransaction.spec.ts @@ -62,6 +62,55 @@ describe('AccountPropertyTransaction', () => { expect(entityTypePropertyFilter.value).to.be.equal(entityType); }); + describe('size', () => { + it('should return 148 for ModifyAccountPropertyAddressTransaction transaction byte size with 1 modification', () => { + const address = Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'); + const addressPropertyFilter = AccountPropertyTransaction.createAddressFilter( + PropertyModificationType.Add, + address, + ); + const addressPropertyTransaction = AccountPropertyTransaction.createAddressPropertyModificationTransaction( + Deadline.create(), + PropertyType.AllowAddress, + [addressPropertyFilter], + NetworkType.MIJIN_TEST, + ); + + expect(addressPropertyTransaction.size).to.be.equal(148); + }); + + it('should return 131 for ModifyAccountPropertyMosaicTransaction transaction byte size with 1 modification', () => { + const mosaicId = new MosaicId([2262289484, 3405110546]); + const mosaicPropertyFilter = AccountPropertyTransaction.createMosaicFilter( + PropertyModificationType.Add, + mosaicId, + ); + const mosaicPropertyTransaction = AccountPropertyTransaction.createMosaicPropertyModificationTransaction( + Deadline.create(), + PropertyType.AllowMosaic, + [mosaicPropertyFilter], + NetworkType.MIJIN_TEST, + ); + expect(mosaicPropertyTransaction.size).to.be.equal(131); + }); + + it('should return 125 for ModifyAccountPropertyEntityTypeTransaction transaction byte size with 1 modification', () => { + const entityType = TransactionType.ADDRESS_ALIAS; + const entityTypePropertyFilter = AccountPropertyTransaction.createEntityTypeFilter( + PropertyModificationType.Add, + entityType, + ); + const entityTypePropertyTransaction = AccountPropertyTransaction.createEntityTypePropertyModificationTransaction( + Deadline.create(), + PropertyType.AllowTransaction, + [entityTypePropertyFilter], + NetworkType.MIJIN_TEST, + ); + expect(entityTypePropertyTransaction.size).to.be.equal(125); + }); + }); + + it('should default maxFee field be set to 0', () => { const address = Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'); const addressPropertyFilter = AccountPropertyTransaction.createAddressFilter( diff --git a/test/model/transaction/AddressAliasTransaction.spec.ts b/test/model/transaction/AddressAliasTransaction.spec.ts index e483ea6e32..88f2291f27 100644 --- a/test/model/transaction/AddressAliasTransaction.spec.ts +++ b/test/model/transaction/AddressAliasTransaction.spec.ts @@ -88,4 +88,19 @@ describe('AddressAliasTransaction', () => { )).to.be.equal('002AD8FC018D9A49E19050B9837EFAB4BBE8A4B9BB32D812F9885C00D8FC1650E142'); }); + + describe('size', () => { + it('should return 154 for AggregateTransaction byte size with TransferTransaction with 1 mosaic and message NEM', () => { + const namespaceId = new NamespaceId([33347626, 3779697293]); + const address = Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'); + const addressAliasTransaction = AddressAliasTransaction.create( + Deadline.create(), + AliasActionType.Link, + namespaceId, + address, + NetworkType.MIJIN_TEST, + ); + expect(addressAliasTransaction.size).to.be.equal(154); + }); + }); }); diff --git a/test/model/transaction/AggregateTransaction.spec.ts b/test/model/transaction/AggregateTransaction.spec.ts index 06cca5df25..7048b25cbb 100644 --- a/test/model/transaction/AggregateTransaction.spec.ts +++ b/test/model/transaction/AggregateTransaction.spec.ts @@ -25,6 +25,7 @@ import {MosaicId} from '../../../src/model/mosaic/MosaicId'; import {MosaicNonce} from '../../../src/model/mosaic/MosaicNonce'; import {MosaicProperties} from '../../../src/model/mosaic/MosaicProperties'; import {MosaicSupplyType} from '../../../src/model/mosaic/MosaicSupplyType'; +import { NetworkCurrencyMosaic } from '../../../src/model/mosaic/NetworkCurrencyMosaic'; import {AggregateTransaction} from '../../../src/model/transaction/AggregateTransaction'; import {Deadline} from '../../../src/model/transaction/Deadline'; import {ModifyMultisigAccountTransaction} from '../../../src/model/transaction/ModifyMultisigAccountTransaction'; @@ -380,4 +381,25 @@ describe('AggregateTransaction', () => { []); }).to.throw(Error, 'Inner transaction cannot be an aggregated transaction.'); }); + + describe('size', () => { + it('should return 282 for AggregateTransaction byte size with TransferTransaction with 1 mosaic and message NEM', () => { + const transaction = TransferTransaction.create( + Deadline.create(), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [ + NetworkCurrencyMosaic.createRelative(100), + ], + PlainMessage.create('NEM'), + NetworkType.MIJIN_TEST, + ); + const aggregateTransaction = AggregateTransaction.createBonded( + Deadline.create(), + [transaction.toAggregate(account.publicAccount)], + NetworkType.MIJIN_TEST, + [], + ); + expect(aggregateTransaction.size).to.be.equal(120 + 4 + 158); + }); + }); }); diff --git a/test/model/transaction/LockFundsTransaction.spec.ts b/test/model/transaction/LockFundsTransaction.spec.ts index 9a5c30dfb7..6adeb0cff4 100644 --- a/test/model/transaction/LockFundsTransaction.spec.ts +++ b/test/model/transaction/LockFundsTransaction.spec.ts @@ -99,4 +99,23 @@ describe('LockFundsTransaction', () => { NetworkType.MIJIN_TEST); }).to.throw(Error); }); + + describe('size', () => { + it('should return 176 for LockFundsTransaction transaction byte size', () => { + const aggregateTransaction = AggregateTransaction.createBonded( + Deadline.create(), + [], + NetworkType.MIJIN_TEST, + [], + ); + const signedTransaction = account.sign(aggregateTransaction); + const lockFundsTransaction = LockFundsTransaction.create(Deadline.create(), + NetworkCurrencyMosaic.createRelative(10), + UInt64.fromUint(10), + signedTransaction, + NetworkType.MIJIN_TEST, + ); + expect(lockFundsTransaction.size).to.be.equal(176); + }); + }); }); diff --git a/test/model/transaction/ModifyMultisigAccountTransaction.spec.ts b/test/model/transaction/ModifyMultisigAccountTransaction.spec.ts index bbff1acb00..559969c4e1 100644 --- a/test/model/transaction/ModifyMultisigAccountTransaction.spec.ts +++ b/test/model/transaction/ModifyMultisigAccountTransaction.spec.ts @@ -119,4 +119,21 @@ describe('ModifyMultisigAccountTransaction', () => { '6EC2400B1B5581FC81A6970DEE418D2C2978F2724228B7B36C5C6DF71B0162BB04778B4'); }); + + describe('size', () => { + it('should return 156 for ModifyMultisigAccountTransaction transaction byte size with 1 modification', () => { + const modifyMultisigAccountTransaction = ModifyMultisigAccountTransaction.create( + Deadline.create(), + 1, + 1, + [new MultisigCosignatoryModification( + MultisigCosignatoryModificationType.Add, + PublicAccount.createFromPublicKey('B0F93CBEE49EEB9953C6F3985B15A4F238E205584D8F924C621CBE4D7AC6EC24', + NetworkType.MIJIN_TEST), + )], + NetworkType.MIJIN_TEST, + ); + expect(modifyMultisigAccountTransaction.size).to.be.equal(156); + }); + }); }); diff --git a/test/model/transaction/MosaicAliasTransaction.spec.ts b/test/model/transaction/MosaicAliasTransaction.spec.ts index 7132d9f019..2c77986550 100644 --- a/test/model/transaction/MosaicAliasTransaction.spec.ts +++ b/test/model/transaction/MosaicAliasTransaction.spec.ts @@ -88,4 +88,19 @@ describe('MosaicAliasTransaction', () => { )).to.be.equal('002AD8FC018D9A49E14CCCD78612DDF5CA'); }); + + describe('size', () => { + it('should return 137 for MosaicAliasTransaction transaction byte size', () => { + const namespaceId = new NamespaceId([33347626, 3779697293]); + const mosaicId = new MosaicId([2262289484, 3405110546]); + const mosaicAliasTransaction = MosaicAliasTransaction.create( + Deadline.create(), + AliasActionType.Link, + namespaceId, + mosaicId, + NetworkType.MIJIN_TEST, + ); + expect(mosaicAliasTransaction.size).to.be.equal(137); + }); + }); }); diff --git a/test/model/transaction/MosaicDefinitionTransaction.spec.ts b/test/model/transaction/MosaicDefinitionTransaction.spec.ts index 46f22a8696..23ae1bc416 100644 --- a/test/model/transaction/MosaicDefinitionTransaction.spec.ts +++ b/test/model/transaction/MosaicDefinitionTransaction.spec.ts @@ -134,4 +134,24 @@ describe('MosaicDefinitionTransaction', () => { )).to.be.equal('E6DE84B8010000000000000001000302E803000000000000'); }); + + + describe('size', () => { + it('should return 144 for MosaicDefinition transaction byte size', () => { + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce + new MosaicId(UInt64.fromUint(1).toDTO()), // ID + MosaicProperties.create({ + supplyMutable: true, + transferable: true, + levyMutable: true, + divisibility: 3, + duration: UInt64.fromUint(1000), + }), + NetworkType.MIJIN_TEST, + ); + expect(mosaicDefinitionTransaction.size).to.be.equal(144); + }); + }); }); diff --git a/test/model/transaction/MosaicSupplyChangeTransaction.spec.ts b/test/model/transaction/MosaicSupplyChangeTransaction.spec.ts index bd223535c9..99f7438f1e 100644 --- a/test/model/transaction/MosaicSupplyChangeTransaction.spec.ts +++ b/test/model/transaction/MosaicSupplyChangeTransaction.spec.ts @@ -84,4 +84,18 @@ describe('MosaicSupplyChangeTransaction', () => { )).to.be.equal('4CCCD78612DDF5CA010A00000000000000'); }); + + describe('size', () => { + it('should return 137 for MosaicSupplyChange transaction byte size', () => { + const mosaicId = new MosaicId([2262289484, 3405110546]); + const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( + Deadline.create(), + mosaicId, + MosaicSupplyType.Increase, + UInt64.fromUint(10), + NetworkType.MIJIN_TEST, + ); + expect(mosaicSupplyChangeTransaction.size).to.be.equal(137); + }); + }); }); diff --git a/test/model/transaction/RegisterNamespaceTransaction.spec.ts b/test/model/transaction/RegisterNamespaceTransaction.spec.ts index a80d69336f..fb2ca22c98 100644 --- a/test/model/transaction/RegisterNamespaceTransaction.spec.ts +++ b/test/model/transaction/RegisterNamespaceTransaction.spec.ts @@ -90,4 +90,16 @@ describe('RegisterNamespaceTransaction', () => { )).to.be.equal('014DF55E7F6D8FB7FF924207DF2CA1BBF313726F6F742D746573742D6E616D657370616365'); }); + + describe('size', () => { + it('should return 176 for RegisterNamespaceTransaction with name of 19 bytes', () => { + const registerNamespaceTransaction = RegisterNamespaceTransaction.createRootNamespace( + Deadline.create(), + 'root-test-namespace', + UInt64.fromUint(1000), + NetworkType.MIJIN_TEST, + ); + expect(registerNamespaceTransaction.size).to.be.equal(157); + }); + }); }); diff --git a/test/model/transaction/SecretLockTransaction.spec.ts b/test/model/transaction/SecretLockTransaction.spec.ts index b1b4848420..9795d7f5ae 100644 --- a/test/model/transaction/SecretLockTransaction.spec.ts +++ b/test/model/transaction/SecretLockTransaction.spec.ts @@ -200,4 +200,21 @@ describe('SecretLockTransaction', () => { ); }).to.throw(Error); }); + + describe('size', () => { + it('should return 202 for SecretLockTransaction with proof of 32 bytes', () => { + const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9DF538782BF199C189DABEAC7'; + const recipient = Address.createFromRawAddress('SDBDG4IT43MPCW2W4CBBCSJJT42AYALQN7A4VVWL'); + const secretLockTransaction = SecretLockTransaction.create( + Deadline.create(), + NetworkCurrencyMosaic.createAbsolute(10), + UInt64.fromUint(100), + HashType.Op_Hash_256, + CryptoJS.SHA256(CryptoJS.SHA256(proof).toString(CryptoJS.enc.Hex)).toString(CryptoJS.enc.Hex), + recipient, + NetworkType.MIJIN_TEST, + ); + expect(secretLockTransaction.size).to.be.equal(202); + }); + }); }); diff --git a/test/model/transaction/SecretProofTransaction.spec.ts b/test/model/transaction/SecretProofTransaction.spec.ts index 7250081ed2..1a4258c299 100644 --- a/test/model/transaction/SecretProofTransaction.spec.ts +++ b/test/model/transaction/SecretProofTransaction.spec.ts @@ -159,4 +159,18 @@ describe('SecretProofTransaction', () => { ); }).to.throw(Error); }); + + describe('size', () => { + it('should return 167 for SecretProofTransaction with proof and secret both 32 bytes', () => { + const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9DF538782BF199C189DABEAC7'; + const secretProofTransaction = SecretProofTransaction.create( + Deadline.create(), + HashType.Op_Hash_256, + CryptoJS.SHA256(CryptoJS.SHA256(proof).toString(CryptoJS.enc.Hex)).toString(CryptoJS.enc.Hex), + proof, + NetworkType.MIJIN_TEST, + ); + expect(secretProofTransaction.size).to.be.equal(187); + }); + }); }); diff --git a/test/model/transaction/Transaction.spec.ts b/test/model/transaction/Transaction.spec.ts index 8dd49fe8e5..547c0250dc 100644 --- a/test/model/transaction/Transaction.spec.ts +++ b/test/model/transaction/Transaction.spec.ts @@ -177,6 +177,21 @@ describe('Transaction', () => { }).to.throw(Error, 'Inner transaction cannot be an aggregated transaction.'); }); }); + + describe('size', () => { + it('should return 120 for base transaction size', () => { + const transaction = new FakeTransaction(TransactionType.TRANSFER, + NetworkType.MIJIN_TEST, + 1, + Deadline.create(), + UInt64.fromUint(0), + undefined, + undefined, + new TransactionInfo(UInt64.fromUint(100), 1, 'id_hash', 'hash', 'hash'), + ); + expect(transaction.size).to.be.equal(120); + }); + }); }); class FakeTransaction extends Transaction { diff --git a/test/model/transaction/TransferTransaction.spec.ts b/test/model/transaction/TransferTransaction.spec.ts index c0bebc0e09..211c7677fb 100644 --- a/test/model/transaction/TransferTransaction.spec.ts +++ b/test/model/transaction/TransferTransaction.spec.ts @@ -178,4 +178,19 @@ describe('TransferTransaction', () => { 290, )).to.be.equal('9151776168D24257D800000000000000000000000000000000'); }); + + describe('size', () => { + it('should return 158 for TransferTransaction with 1 mosaic and message NEM', () => { + const transaction = TransferTransaction.create( + Deadline.create(), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [ + NetworkCurrencyMosaic.createRelative(100), + ], + PlainMessage.create('NEM'), + NetworkType.MIJIN_TEST, + ); + expect(transaction.size).to.be.equal(158); + }); + }); }); From 7db599574f0fc0827b201191d5ec701f82623e2e Mon Sep 17 00:00:00 2001 From: Greg S Date: Mon, 25 Mar 2019 23:16:10 +0100 Subject: [PATCH 06/83] issue #100: fix BlockInfo fields & #53: add TransactionHttp.getTransactionEffectiveFee() --- e2e/infrastructure/TransactionHttp.spec.ts | 11 ++++++++ src/infrastructure/BlockchainHttp.ts | 8 ++++++ src/infrastructure/Listener.ts | 4 +++ src/infrastructure/TransactionHttp.ts | 30 +++++++++++++++++++-- src/infrastructure/TransactionRepository.ts | 7 +++++ src/model/blockchain/BlockInfo.ts | 22 ++++++++++++++- test/model/blockchain/BlockInfo.spec.ts | 12 +++++++++ 7 files changed, 91 insertions(+), 3 deletions(-) diff --git a/e2e/infrastructure/TransactionHttp.spec.ts b/e2e/infrastructure/TransactionHttp.spec.ts index de6cd4aaca..458129ad37 100644 --- a/e2e/infrastructure/TransactionHttp.spec.ts +++ b/e2e/infrastructure/TransactionHttp.spec.ts @@ -874,4 +874,15 @@ describe('TransactionHttp', () => { }); }); }); + + describe('getTransactionEffectiveFee', () => { + it('should return effective paid fee given transactionHash', (done) => { + transactionHttp.getTransactionEffectiveFee(transactionHash) + .subscribe((effectiveFee) => { + expect(effectiveFee).to.not.be.undefined; + expect(effectiveFee).to.be.equal(0); + done(); + }); + }); + }); }); diff --git a/src/infrastructure/BlockchainHttp.ts b/src/infrastructure/BlockchainHttp.ts index 0b1b931798..b5a0870f27 100644 --- a/src/infrastructure/BlockchainHttp.ts +++ b/src/infrastructure/BlockchainHttp.ts @@ -70,8 +70,12 @@ export class BlockchainHttp extends Http implements BlockchainRepository { new UInt64(blockDTO.block.height), new UInt64(blockDTO.block.timestamp), new UInt64(blockDTO.block.difficulty), + blockDTO.block.feeMultiplier, blockDTO.block.previousBlockHash, blockDTO.block.blockTransactionsHash, + blockDTO.block.blockReceiptsHash, + blockDTO.block.stateHash, + PublicAccount.createFromPublicKey(blockDTO.block.beneficiaryPublicKey, networkType), ); })); } @@ -116,8 +120,12 @@ export class BlockchainHttp extends Http implements BlockchainRepository { new UInt64(blockDTO.block.height), new UInt64(blockDTO.block.timestamp), new UInt64(blockDTO.block.difficulty), + blockDTO.block.feeMultiplier, blockDTO.block.previousBlockHash, blockDTO.block.blockTransactionsHash, + blockDTO.block.blockReceiptsHash, + blockDTO.block.stateHash, + PublicAccount.createFromPublicKey(blockDTO.block.beneficiaryPublicKey, networkType), ); }); })); diff --git a/src/infrastructure/Listener.ts b/src/infrastructure/Listener.ts index f11c60b61c..430f905f70 100644 --- a/src/infrastructure/Listener.ts +++ b/src/infrastructure/Listener.ts @@ -135,8 +135,12 @@ export class Listener { new UInt64(message.block.height), new UInt64(message.block.timestamp), new UInt64(message.block.difficulty), + message.block.feeMultiplier, message.block.previousBlockHash, message.block.blockTransactionsHash, + message.block.blockReceiptsHash, + message.block.stateHash, + PublicAccount.createFromPublicKey(message.block.beneficiaryPublicKey, networkType), ), }); } else if (message.status) { diff --git a/src/infrastructure/TransactionHttp.ts b/src/infrastructure/TransactionHttp.ts index 9351063fbc..957420837c 100644 --- a/src/infrastructure/TransactionHttp.ts +++ b/src/infrastructure/TransactionHttp.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -import {TransactionRoutesApi} from 'nem2-library'; +import {BlockchainRoutesApi, TransactionRoutesApi} from 'nem2-library'; import * as requestPromise from 'request-promise-native'; import {from as observableFrom, Observable, throwError as observableThrowError} from 'rxjs'; -import {catchError, map} from 'rxjs/operators'; +import {catchError, map, mergeMap} from 'rxjs/operators'; import {PublicAccount} from '../model/account/PublicAccount'; import {CosignatureSignedTransaction} from '../model/transaction/CosignatureSignedTransaction'; import {Deadline} from '../model/transaction/Deadline'; @@ -44,6 +44,12 @@ export class TransactionHttp extends Http implements TransactionRepository { */ private transactionRoutesApi: TransactionRoutesApi; + /** + * @internal + * Nem2 Library blockchain routes api + */ + private blockchainRoutesApi: BlockchainRoutesApi; + /** * Constructor * @param url @@ -51,6 +57,7 @@ export class TransactionHttp extends Http implements TransactionRepository { constructor(private readonly url: string) { super(url); this.transactionRoutesApi = new TransactionRoutesApi(this.apiClient); + this.blockchainRoutesApi = new BlockchainRoutesApi(this.apiClient); } /** @@ -190,4 +197,23 @@ export class TransactionHttp extends Http implements TransactionRepository { return observableThrowError(err); })); } + + /** + * Gets a transaction's effective paid fee + * @param transactionId - Transaction id or hash. + * @returns Observable + */ + public getTransactionEffectiveFee(transactionId: string): Observable { + return observableFrom(this.transactionRoutesApi.getTransaction(transactionId)).pipe( + mergeMap((transactionDTO) => observableFrom( + this.blockchainRoutesApi.getBlockByHeight(transactionDTO.meta.height)).pipe( + map((blockDTO) => { + // parse transaction for call to `size` overload + const transaction = CreateTransactionFromDTO(transactionDTO); + + // @see https://nemtech.github.io/concepts/transaction.html#fees + // effective_fee = feeMultiplier x transaction::size + return blockDTO.block.feeMultiplier * transaction.size; + })))); + } } diff --git a/src/infrastructure/TransactionRepository.ts b/src/infrastructure/TransactionRepository.ts index 39b4d657fb..8effe51382 100644 --- a/src/infrastructure/TransactionRepository.ts +++ b/src/infrastructure/TransactionRepository.ts @@ -56,6 +56,13 @@ export interface TransactionRepository { */ getTransactionsStatuses(transactionHashes: string[]): Observable; + /** + * Gets a transaction's effective paid fee + * @param transactionId - Transaction id or hash. + * @returns Observable + */ + getTransactionEffectiveFee(transactionId: string): Observable; + /** * Send a signed transaction * @param signedTransaction - Signed transaction diff --git a/src/model/blockchain/BlockInfo.ts b/src/model/blockchain/BlockInfo.ts index dd8eca9f27..1098c515fd 100644 --- a/src/model/blockchain/BlockInfo.ts +++ b/src/model/blockchain/BlockInfo.ts @@ -36,8 +36,12 @@ export class BlockInfo { * @param height * @param timestamp * @param difficulty + * @param feeMultiplier * @param previousBlockHash * @param blockTransactionsHash + * @param blockReceiptsHash + * @param blockStateHash + * @param beneficiaryPublicKey */ constructor(/** * The block hash. @@ -90,6 +94,10 @@ export class BlockInfo { * The POI difficulty to harvest a block. */ public readonly difficulty: UInt64, + /** + * The feeMultiplier defined by the harvester. + */ + public readonly feeMultiplier: number, /** * The last block hash. */ @@ -97,7 +105,19 @@ export class BlockInfo { /** * The block transaction hash. */ - public readonly blockTransactionsHash: string,) { + public readonly blockTransactionsHash: string, + /** + * The block receipt hash. + */ + public readonly blockReceiptsHash: string, + /** + * The state hash. + */ + public readonly stateHash: string, + /** + * The beneficiary public key. + */ + public readonly beneficiaryPublicKey: PublicAccount,) { } } diff --git a/test/model/blockchain/BlockInfo.spec.ts b/test/model/blockchain/BlockInfo.spec.ts index 1af2bd4eb5..60363992a9 100644 --- a/test/model/blockchain/BlockInfo.spec.ts +++ b/test/model/blockchain/BlockInfo.spec.ts @@ -26,12 +26,16 @@ describe('BlockInfo', () => { const blockDTO = { block: { blockTransactionsHash: '702090BA31CEF9E90C62BBDECC0CCCC0F88192B6625839382850357F70DD68A0', + blockReceiptsHash: '702090BA31CEF9E90C62BBDECC0CCCC0F88192B6625839382850357F70DD68A0', + stateHash: '702090BA31CEF9E90C62BBDECC0CCCC0F88192B6625839382850357F70DD68A0', difficulty: new UInt64([ 276447232, 23283 ]), + feeMultiplier: 1, height: new UInt64([ 1, 0 ]), previousBlockHash: '0000000000000000000000000000000000000000000000000000000000000000', signature: '37351C8244AC166BE6664E3FA954E99A3239AC46E51E2B32CEA1C72DD0851100A7731868' + 'E932E1A9BEF8A27D48E1FFEE401E933EB801824373E7537E51733E0F', signer: 'B4F12E7C9F6946091E2CB8B6D3A12B50D17CCBBF646386EA27CE2946A7423DCF', + beneficiaryPublicKey: 'B4F12E7C9F6946091E2CB8B6D3A12B50D17CCBBF646386EA27CE2946A7423DCF', timestamp: new UInt64([ 0, 0 ]), type: 32768, version: 36867, @@ -58,8 +62,12 @@ describe('BlockInfo', () => { blockDTO.block.height, blockDTO.block.timestamp, blockDTO.block.difficulty, + blockDTO.block.feeMultiplier, blockDTO.block.previousBlockHash, blockDTO.block.blockTransactionsHash, + blockDTO.block.blockReceiptsHash, + blockDTO.block.stateHash, + PublicAccount.createFromPublicKey(blockDTO.block.beneficiaryPublicKey, network), ); expect(blockInfo.hash).to.be.equal(blockDTO.meta.hash); @@ -74,8 +82,12 @@ describe('BlockInfo', () => { deepEqual(blockInfo.height, blockDTO.block.height); deepEqual(blockInfo.timestamp, blockDTO.block.timestamp); deepEqual(blockInfo.difficulty, blockDTO.block.difficulty); + expect(blockInfo.feeMultiplier).to.be.equal(blockDTO.block.feeMultiplier); expect(blockInfo.previousBlockHash).to.be.equal(blockDTO.block.previousBlockHash); expect(blockInfo.blockTransactionsHash).to.be.equal(blockDTO.block.blockTransactionsHash); + expect(blockInfo.blockReceiptsHash).to.be.equal(blockDTO.block.blockReceiptsHash); + expect(blockInfo.stateHash).to.be.equal(blockDTO.block.stateHash); + expect(blockInfo.beneficiaryPublicKey.publicKey).to.be.equal(blockDTO.block.beneficiaryPublicKey); }); }); From d85e43540836af1ae7307d4ff34fa8011ebada61 Mon Sep 17 00:00:00 2001 From: Greg S Date: Tue, 26 Mar 2019 02:05:26 +0100 Subject: [PATCH 07/83] PR #101: fix AccountPropertyTransaction import --- src/model/transaction/AccountPropertyTransaction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/transaction/AccountPropertyTransaction.ts b/src/model/transaction/AccountPropertyTransaction.ts index bcfac45480..91be92fba8 100644 --- a/src/model/transaction/AccountPropertyTransaction.ts +++ b/src/model/transaction/AccountPropertyTransaction.ts @@ -19,7 +19,7 @@ import { PropertyModificationType } from '../account/PropertyModificationType'; import { PropertyType } from '../account/PropertyType'; import { NetworkType } from '../blockchain/NetworkType'; import { MosaicId } from '../mosaic/MosaicId'; -import { UInt64 } from '../Uint64'; +import { UInt64 } from '../UInt64'; import { AccountPropertyModification } from './AccountPropertyModification'; import { Deadline } from './Deadline'; import { ModifyAccountPropertyAddressTransaction } from './ModifyAccountPropertyAddressTransaction'; From 7d1d2fbb525e572c072e755da1c133712ffc9131 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 18 Mar 2019 10:05:21 +0000 Subject: [PATCH 08/83] Added #4 check if an aggregate complete transaction has all cosignatories --- src/model/transaction/AggregateTransaction.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/model/transaction/AggregateTransaction.ts b/src/model/transaction/AggregateTransaction.ts index 1557012fa6..1e8f0efe50 100644 --- a/src/model/transaction/AggregateTransaction.ts +++ b/src/model/transaction/AggregateTransaction.ts @@ -147,4 +147,17 @@ export class AggregateTransaction extends Transaction { || (this.signer !== undefined && this.signer.equals(publicAccount)); } + /** + * Check if an aggregate complete transaction has all cosignatories + * @returns {boolean} + */ + public isComplete(): boolean { + let isCompleted = false; + this.innerTransactions.forEach((innerTransaction) => { + if (!isCompleted) { + isCompleted = this.cosignatures.find((cosignature) => cosignature.signer.equals(innerTransaction.signer)) !== undefined; + } + }); + return isCompleted; + } } From 91566e49f30254243e4baece79fde38e61716333 Mon Sep 17 00:00:00 2001 From: Greg S Date: Tue, 26 Mar 2019 17:09:26 +0100 Subject: [PATCH 09/83] PR #101: fixed CreateTransactionFromDTO maxFee fields ; finalized getTransactionEffectiveFee implementation --- src/infrastructure/TransactionHttp.ts | 17 +++-- .../transaction/CreateTransactionFromDTO.ts | 74 +++++++++---------- 2 files changed, 49 insertions(+), 42 deletions(-) diff --git a/src/infrastructure/TransactionHttp.ts b/src/infrastructure/TransactionHttp.ts index 957420837c..226781080b 100644 --- a/src/infrastructure/TransactionHttp.ts +++ b/src/infrastructure/TransactionHttp.ts @@ -25,6 +25,7 @@ import {SignedTransaction} from '../model/transaction/SignedTransaction'; import { SyncAnnounce } from '../model/transaction/SyncAnnounce'; import {Transaction} from '../model/transaction/Transaction'; import {TransactionAnnounceResponse} from '../model/transaction/TransactionAnnounceResponse'; +import {TransactionInfo} from '../model/transaction/TransactionInfo'; import {TransactionStatus} from '../model/transaction/TransactionStatus'; import {TransactionType} from '../model/transaction/TransactionType'; import {UInt64} from '../model/UInt64'; @@ -205,15 +206,21 @@ export class TransactionHttp extends Http implements TransactionRepository { */ public getTransactionEffectiveFee(transactionId: string): Observable { return observableFrom(this.transactionRoutesApi.getTransaction(transactionId)).pipe( - mergeMap((transactionDTO) => observableFrom( - this.blockchainRoutesApi.getBlockByHeight(transactionDTO.meta.height)).pipe( + mergeMap((transactionDTO) => { + // parse transaction to take advantage of `size` getter overload + const transaction = CreateTransactionFromDTO(transactionDTO); + const uintHeight = (transaction.transactionInfo as TransactionInfo).height; + + // now read block details + return observableFrom(this.blockchainRoutesApi.getBlockByHeight(uintHeight.compact())).pipe( map((blockDTO) => { - // parse transaction for call to `size` overload - const transaction = CreateTransactionFromDTO(transactionDTO); // @see https://nemtech.github.io/concepts/transaction.html#fees // effective_fee = feeMultiplier x transaction::size return blockDTO.block.feeMultiplier * transaction.size; - })))); + })); + }), catchError((err) => { + return observableThrowError(err); + })); } } diff --git a/src/infrastructure/transaction/CreateTransactionFromDTO.ts b/src/infrastructure/transaction/CreateTransactionFromDTO.ts index 738fc75112..2cb5f489fd 100644 --- a/src/infrastructure/transaction/CreateTransactionFromDTO.ts +++ b/src/infrastructure/transaction/CreateTransactionFromDTO.ts @@ -122,7 +122,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.maxFee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), extractRecipient(transactionDTO.recipient), extractMosaics(transactionDTO.mosaics), transactionDTO.message !== undefined ? @@ -136,7 +136,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.maxFee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), transactionDTO.namespaceType, transactionDTO.name, new NamespaceId(transactionDTO.namespaceId), @@ -151,37 +151,37 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.maxFee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), transactionDTO.nonce, new MosaicId(transactionDTO.mosaicId), new MosaicProperties( new UInt64(transactionDTO.properties[0].value), (new UInt64(transactionDTO.properties[1].value)).compact(), - new UInt64(transactionDTO.properties.length === 3 ? transactionDTO.properties[2].value : [0, 0]), - ), - transactionDTO.signature, - PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)), - transactionInfo, - ); -} else if (transactionDTO.type === TransactionType.MOSAIC_SUPPLY_CHANGE) { - return new MosaicSupplyChangeTransaction( - extractNetworkType(transactionDTO.version), - extractTransactionVersion(transactionDTO.version), - Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.maxFee || [0, 0]), - new MosaicId(transactionDTO.mosaicId), - transactionDTO.direction, - new UInt64(transactionDTO.delta), - transactionDTO.signature, - PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)), - transactionInfo, - ); -} else if (transactionDTO.type === TransactionType.MODIFY_MULTISIG_ACCOUNT) { - return new ModifyMultisigAccountTransaction( - extractNetworkType(transactionDTO.version), - extractTransactionVersion(transactionDTO.version), - Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.maxFee || [0, 0]), + new UInt64(transactionDTO.properties.length === 3 ? transactionDTO.properties[2].value : [0, 0]), + ), + transactionDTO.signature, + PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)), + transactionInfo, + ); + } else if (transactionDTO.type === TransactionType.MOSAIC_SUPPLY_CHANGE) { + return new MosaicSupplyChangeTransaction( + extractNetworkType(transactionDTO.version), + extractTransactionVersion(transactionDTO.version), + Deadline.createFromDTO(transactionDTO.deadline), + UInt64.fromUint(transactionDTO.maxFee || 0), + new MosaicId(transactionDTO.mosaicId), + transactionDTO.direction, + new UInt64(transactionDTO.delta), + transactionDTO.signature, + PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)), + transactionInfo, + ); + } else if (transactionDTO.type === TransactionType.MODIFY_MULTISIG_ACCOUNT) { + return new ModifyMultisigAccountTransaction( + extractNetworkType(transactionDTO.version), + extractTransactionVersion(transactionDTO.version), + Deadline.createFromDTO(transactionDTO.deadline), + UInt64.fromUint(transactionDTO.maxFee || 0), transactionDTO.minApprovalDelta, transactionDTO.minRemovalDelta, transactionDTO.modifications ? transactionDTO.modifications.map((modificationDTO) => new MultisigCosignatoryModification( @@ -198,7 +198,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr networkType, extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.maxFee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), new Mosaic(new MosaicId(transactionDTO.mosaicId), new UInt64(transactionDTO.amount)), new UInt64(transactionDTO.duration), new SignedTransaction('', transactionDTO.hash, '', TransactionType.AGGREGATE_BONDED, networkType), @@ -211,7 +211,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.maxFee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), new Mosaic(new MosaicId(transactionDTO.mosaicId), new UInt64(transactionDTO.amount)), new UInt64(transactionDTO.duration), transactionDTO.hashAlgorithm, @@ -226,7 +226,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.maxFee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), transactionDTO.hashAlgorithm, transactionDTO.secret, transactionDTO.proof, @@ -239,7 +239,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.maxFee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), transactionDTO.aliasAction, new NamespaceId(transactionDTO.namespaceId), new MosaicId(transactionDTO.mosaicId), @@ -252,7 +252,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.maxFee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), transactionDTO.aliasAction, new NamespaceId(transactionDTO.namespaceId), extractRecipient(transactionDTO.address) as Address, @@ -265,7 +265,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.maxFee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), transactionDTO.propertyType, transactionDTO.modifications ? transactionDTO.modifications.map((modificationDTO) => new AccountPropertyModification( modificationDTO.modificationType, @@ -280,7 +280,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.maxFee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), transactionDTO.propertyType, transactionDTO.modifications ? transactionDTO.modifications.map((modificationDTO) => new AccountPropertyModification( modificationDTO.modificationType, @@ -295,7 +295,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.maxFee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), transactionDTO.propertyType, transactionDTO.modifications ? transactionDTO.modifications.map((modificationDTO) => new AccountPropertyModification( modificationDTO.modificationType, @@ -310,7 +310,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.maxFee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), transactionDTO.remoteAccountKey, transactionDTO.linkAction, transactionDTO.signature, From 41b0b45a4f85441e49e8eaffbf879b0587c26f7b Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 1 Apr 2019 20:39:11 +0100 Subject: [PATCH 10/83] #4 1. Addes service for Aggregated Transaction - isComplete 2. Fixed couple of bugs in TransactionMappings --- .../CreateTransactionFromPayload.ts | 8 +- src/model/transaction/AggregateTransaction.ts | 14 - src/service/AggregatedTransactionService.ts | 127 +++++++++ src/service/service.ts | 1 + .../AggregatedTransactionService.spec.ts | 242 ++++++++++++++++++ 5 files changed, 374 insertions(+), 18 deletions(-) create mode 100644 src/service/AggregatedTransactionService.ts create mode 100644 test/service/AggregatedTransactionService.spec.ts diff --git a/src/infrastructure/transaction/CreateTransactionFromPayload.ts b/src/infrastructure/transaction/CreateTransactionFromPayload.ts index e8b66453ff..9b124a33bf 100644 --- a/src/infrastructure/transaction/CreateTransactionFromPayload.ts +++ b/src/infrastructure/transaction/CreateTransactionFromPayload.ts @@ -377,7 +377,7 @@ const CreateTransaction = (type: number, transactionData: string, networkType: N networkType, consignatureArray ? consignatureArray.map((cosignature) => new AggregateTransactionCosignature( cosignature.substring(0, 64), - PublicAccount.createFromPublicKey(cosignature.substring(64, 192), networkType), + PublicAccount.createFromPublicKey(cosignature.substring(0, 64), networkType), )) : [], ); case TransactionType.AGGREGATE_BONDED: @@ -401,7 +401,7 @@ const CreateTransaction = (type: number, transactionData: string, networkType: N networkType, bondedConsignatureArray ? bondedConsignatureArray.map((cosignature) => new AggregateTransactionCosignature( cosignature.substring(0, 64), - PublicAccount.createFromPublicKey(cosignature.substring(64, 192), networkType), + PublicAccount.createFromPublicKey(cosignature.substring(0, 64), networkType), )) : [], ); default: @@ -457,9 +457,9 @@ const parseInnerTransactionFromBinary = (innerTransactionBinary: string): string while (innerBinary.length) { const payloadSize = parseInt(convert.uint8ToHex(convert.hexToUint8(innerTransactionBinary.substring(0, 8)).reverse()), 16) * 2; - const innerTransaction = innerTransactionBinary.substring(8, 8 + payloadSize); + const innerTransaction = innerTransactionBinary.substring(8, payloadSize); embeddedTransaction.push(innerTransaction); - innerBinary = innerTransactionBinary.substring(8 + payloadSize); + innerBinary = innerBinary.substring(payloadSize); } return embeddedTransaction; }; diff --git a/src/model/transaction/AggregateTransaction.ts b/src/model/transaction/AggregateTransaction.ts index 1e8f0efe50..47312c1e4f 100644 --- a/src/model/transaction/AggregateTransaction.ts +++ b/src/model/transaction/AggregateTransaction.ts @@ -146,18 +146,4 @@ export class AggregateTransaction extends Transaction { return this.cosignatures.find((cosignature) => cosignature.signer.equals(publicAccount)) !== undefined || (this.signer !== undefined && this.signer.equals(publicAccount)); } - - /** - * Check if an aggregate complete transaction has all cosignatories - * @returns {boolean} - */ - public isComplete(): boolean { - let isCompleted = false; - this.innerTransactions.forEach((innerTransaction) => { - if (!isCompleted) { - isCompleted = this.cosignatures.find((cosignature) => cosignature.signer.equals(innerTransaction.signer)) !== undefined; - } - }); - return isCompleted; - } } diff --git a/src/service/AggregatedTransactionService.ts b/src/service/AggregatedTransactionService.ts new file mode 100644 index 0000000000..ccdd72cd3b --- /dev/null +++ b/src/service/AggregatedTransactionService.ts @@ -0,0 +1,127 @@ +/* + * Copyright 2019 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {from as observableFrom , Observable, of as observableOf} from 'rxjs'; +import { map, mergeMap} from 'rxjs/operators'; +import { TransactionMapping } from '../core/utils/TransactionMapping'; +import { AccountHttp } from '../infrastructure/AccountHttp'; +import { MultisigAccountGraphInfo } from '../model/account/MultisigAccountGraphInfo'; +import { AggregateTransaction } from '../model/transaction/AggregateTransaction'; +import { SignedTransaction } from '../model/transaction/SignedTransaction'; + +/** + * Aggregated Transaction service + */ +export class AggregatedTransactionService { + + /** + * Constructor + * @param accountHttp + */ + constructor(private readonly accountHttp: AccountHttp) { + } + + /** + * Check if an aggregate complete transaction has all cosignatories attached + * @param signedTransaction - The signed aggregate transaction (complete) to be verified + * @returns {Observable} + */ + public isComplete(signedTransaction: SignedTransaction): Observable { + const aggregateTransaction = TransactionMapping.createFromPayload(signedTransaction.payload) as AggregateTransaction; + /** + * Include both initiator & cosigners + */ + const signers = (aggregateTransaction.cosignatures.map((cosigner) => cosigner.signer.publicKey)); + if (signedTransaction.signer) { + signers.push(signedTransaction.signer); + } + + return observableFrom(aggregateTransaction.innerTransactions).pipe( + mergeMap((innerTransaction) => this.accountHttp.getMultisigAccountInfo(innerTransaction.signer.address) + .pipe( + /** + * For multisig account, we need to get the graph info in case it has multiple levels + */ + mergeMap((_) => _.minApproval !== 0 && _.minRemoval !== 0 ? + this.accountHttp.getMultisigAccountGraphInfo(_.account.address) + .pipe( + map((graphInfo) => this.validateCosignatories(graphInfo, signers)), + ) : observableOf(true), + ), + ), + ), + ); + } + + /** + * Validate cosignatories against multisig Account(s) + * @param graphInfo - multisig account graph info + * @param cosignatories - array of cosignatories extracted from aggregated transaction + * @returns {boolean} + */ + private validateCosignatories(graphInfo: MultisigAccountGraphInfo, cosignatories: string[]): boolean { + /** + * Validate cosignatories from bottom level to top + */ + const sortedKeys = Array.from(graphInfo.multisigAccounts.keys()).sort((a, b) => b - a); + const cosignatoriesReceived = cosignatories; + let validationResult = true; + + sortedKeys.forEach((key) => { + const multisigInfo = graphInfo.multisigAccounts.get(key); + if (multisigInfo && validationResult) { + multisigInfo.forEach((multisig) => { + if (multisig.minApproval >= 1) { + const matchedCosignatories = this.compareArrays(cosignatoriesReceived, + multisig.cosignatories.map((cosig) => cosig.publicKey)); + + /** + * if minimal signature requirement met at current level, push the multisig account + * into the received signatories array for next level validation. + * Otherwise return validation failed. + */ + if (matchedCosignatories.length >= multisig.minApproval) { + if (cosignatoriesReceived.indexOf(multisig.account.publicKey) === -1) { + cosignatoriesReceived.push(multisig.account.publicKey); + } + } else { + validationResult = false; + } + } + }); + } + }); + + return validationResult; + } + + /** + * Compare two string arrays + * @param array1 - base array + * @param array2 - array to be matched + * @returns {string[]} - array of matched elements + */ + private compareArrays(array1: string[], array2: string[]): string[] { + const results: string[] = []; + array1.forEach((a1) => array2.forEach((a2) => { + if (a1 === a2) { + results.push(a1); + } + })); + + return results; + } +} diff --git a/src/service/service.ts b/src/service/service.ts index 6a9945c1cd..fcf4c84680 100644 --- a/src/service/service.ts +++ b/src/service/service.ts @@ -16,3 +16,4 @@ export * from './NamespaceService'; export * from './MosaicService'; +export * from './AggregatedTransactionService'; diff --git a/test/service/AggregatedTransactionService.spec.ts b/test/service/AggregatedTransactionService.spec.ts new file mode 100644 index 0000000000..0aff3d8f47 --- /dev/null +++ b/test/service/AggregatedTransactionService.spec.ts @@ -0,0 +1,242 @@ +/* + * Copyright 2018 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect } from 'chai'; +import { ChronoUnit } from 'js-joda'; +import {of as observableOf} from 'rxjs'; +import {deepEqual, instance, mock, when} from 'ts-mockito'; +import { AccountHttp } from '../../src/infrastructure/AccountHttp'; +import { Account } from '../../src/model/account/Account'; +import { Address } from '../../src/model/account/Address'; +import { MultisigAccountGraphInfo } from '../../src/model/account/MultisigAccountGraphInfo'; +import { MultisigAccountInfo } from '../../src/model/account/MultisigAccountInfo'; +import {NetworkType} from '../../src/model/blockchain/NetworkType'; +import { AggregateTransaction } from '../../src/model/transaction/AggregateTransaction'; +import { Deadline } from '../../src/model/transaction/Deadline'; +import { PlainMessage } from '../../src/model/transaction/PlainMessage'; +import { TransferTransaction } from '../../src/model/transaction/TransferTransaction'; +import { AggregatedTransactionService } from '../../src/service/AggregatedTransactionService'; + +/** + * For multi level multisig scenario visit: https://github.com/nemtech/nem2-docs/issues/10 + */ +describe('AggregatedTransactionService', () => { + let aggregatedTransactionService: AggregatedTransactionService; + + /** + * Multisig2 Account: SBROWP-7YMG2M-K45RO6-Q7ZPK7-G7GXWQ-JK5VNQ-OSUX + * Public Key: 5E628EA59818D97AA4118780D9A88C5512FCE7A21C195E1574727EFCE5DF7C0D + * Private Key: 22A1D67F8519D1A45BD7116600BB6E857786E816FE0B45E4C5B9FFF3D64BC177 + * + * + * Multisig1 Account: SAK32M-5JQ43R-WYHWEH-WRBCW4-RXERT2-DLASGL-EANS + * Public Key: BFDF2610C5666A626434FE12FB4A9D896D2B9B033F5F84CCEABE82E043A6307E + * Private Key: 8B0622C2CCFC5CCC5A74B500163E3C68F3AD3643DB12932FC931143EAC67280D + */ + + /** + * Test accounts: + * Multisig1 (1/1): Account2, Account3 + * Multisig2 (2/1): Account1, Multisig1 + * Stranger Account: Account4 + */ + + const account1 = Account.createFromPrivateKey('82DB2528834C9926F0FCCE042466B24A266F5B685CB66D2869AF6648C043E950', + NetworkType.MIJIN_TEST); + const multisig1 = Account.createFromPrivateKey('8B0622C2CCFC5CCC5A74B500163E3C68F3AD3643DB12932FC931143EAC67280D', + NetworkType.MIJIN_TEST); + const multisig2 = Account.createFromPrivateKey('22A1D67F8519D1A45BD7116600BB6E857786E816FE0B45E4C5B9FFF3D64BC177', + NetworkType.MIJIN_TEST); + + const account2 = Account.createFromPrivateKey('A4D410270E01CECDCDEADCDE32EC79C8D9CDEA4DCD426CB1EB666EFEF148FBCE', + NetworkType.MIJIN_TEST); + const account3 = Account.createFromPrivateKey('336AB45EE65A6AFFC0E7ADC5342F91E34BACA0B901A1D9C876FA25A1E590077E', + NetworkType.MIJIN_TEST); + + const account4 = Account.createFromPrivateKey('4D8B3756592532753344E11E2B7541317BCCFBBCF4444274CDBF359D2C4AE0F1', + NetworkType.MIJIN_TEST); + before(() => { + const mockedAccountHttp = mock(AccountHttp); + + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account1.address))) + .thenReturn(observableOf(givenAccount1Info())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account4.address))) + .thenReturn(observableOf(givenAccount4Info())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(multisig2.address))) + .thenReturn(observableOf(givenMultisig2AccountInfo())); + when(mockedAccountHttp.getMultisigAccountGraphInfo(deepEqual(multisig2.address))) + .thenReturn(observableOf(givenMultisig2AccountGraphInfo())); + + const accountHttp = instance(mockedAccountHttp); + aggregatedTransactionService = new AggregatedTransactionService(accountHttp); + }); + + it('should return isComplete: true for aggregated complete transaction - 2 levels Multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account2]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + it('should return isComplete: false for aggregated complete transaction - 2 levels Multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, []); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + + it('should return isComplete: false for aggregated complete transaction - 2 levels Multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + + it('should return correct isComplete status for aggregated complete transaction - 2 levels Multisig, multi inner transaction', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const transferTransaction2 = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount), + transferTransaction.toAggregate(account4.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account2]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + it('should return correct isComplete status for aggregated complete transaction - none multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(account4.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signWith(account1); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + function givenMultisig2AccountInfo(): MultisigAccountInfo { + return new MultisigAccountInfo(multisig2.publicAccount, + 2, 1, + [multisig1.publicAccount, + account1.publicAccount], + [], + ); + } + + function givenAccount1Info(): MultisigAccountInfo { + return new MultisigAccountInfo(account1.publicAccount, + 0, 0, + [], + [multisig2.publicAccount], + ); + } + function givenAccount4Info(): MultisigAccountInfo { + return new MultisigAccountInfo(account4.publicAccount, + 0, 0, + [], + [], + ); + } + + function givenMultisig2AccountGraphInfo(): MultisigAccountGraphInfo { + const map = new Map(); + map.set(0, [new MultisigAccountInfo(multisig2.publicAccount, + 2, 1, + [multisig1.publicAccount, + account1.publicAccount], + [], + )]) + .set(1, [new MultisigAccountInfo(multisig1.publicAccount, + 1, 1, + [account2.publicAccount, account3.publicAccount], + [multisig2.publicAccount], + )]); + + return new MultisigAccountGraphInfo(map); + } + +}); From 319ba1de13cdb590938536bb7a0dce53a71b40a6 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 18 Mar 2019 10:05:21 +0000 Subject: [PATCH 11/83] Added #4 check if an aggregate complete transaction has all cosignatories --- src/model/transaction/AggregateTransaction.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/model/transaction/AggregateTransaction.ts b/src/model/transaction/AggregateTransaction.ts index 1557012fa6..1e8f0efe50 100644 --- a/src/model/transaction/AggregateTransaction.ts +++ b/src/model/transaction/AggregateTransaction.ts @@ -147,4 +147,17 @@ export class AggregateTransaction extends Transaction { || (this.signer !== undefined && this.signer.equals(publicAccount)); } + /** + * Check if an aggregate complete transaction has all cosignatories + * @returns {boolean} + */ + public isComplete(): boolean { + let isCompleted = false; + this.innerTransactions.forEach((innerTransaction) => { + if (!isCompleted) { + isCompleted = this.cosignatures.find((cosignature) => cosignature.signer.equals(innerTransaction.signer)) !== undefined; + } + }); + return isCompleted; + } } From b740dee31de4f4bf40fb97fb97960e002ba87686 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 1 Apr 2019 20:39:11 +0100 Subject: [PATCH 12/83] #4 1. Addes service for Aggregated Transaction - isComplete 2. Fixed couple of bugs in TransactionMappings --- .../CreateTransactionFromPayload.ts | 8 +- src/model/transaction/AggregateTransaction.ts | 14 - src/service/AggregatedTransactionService.ts | 127 +++++++++ src/service/service.ts | 1 + .../AggregatedTransactionService.spec.ts | 242 ++++++++++++++++++ 5 files changed, 374 insertions(+), 18 deletions(-) create mode 100644 src/service/AggregatedTransactionService.ts create mode 100644 test/service/AggregatedTransactionService.spec.ts diff --git a/src/infrastructure/transaction/CreateTransactionFromPayload.ts b/src/infrastructure/transaction/CreateTransactionFromPayload.ts index e8b66453ff..9b124a33bf 100644 --- a/src/infrastructure/transaction/CreateTransactionFromPayload.ts +++ b/src/infrastructure/transaction/CreateTransactionFromPayload.ts @@ -377,7 +377,7 @@ const CreateTransaction = (type: number, transactionData: string, networkType: N networkType, consignatureArray ? consignatureArray.map((cosignature) => new AggregateTransactionCosignature( cosignature.substring(0, 64), - PublicAccount.createFromPublicKey(cosignature.substring(64, 192), networkType), + PublicAccount.createFromPublicKey(cosignature.substring(0, 64), networkType), )) : [], ); case TransactionType.AGGREGATE_BONDED: @@ -401,7 +401,7 @@ const CreateTransaction = (type: number, transactionData: string, networkType: N networkType, bondedConsignatureArray ? bondedConsignatureArray.map((cosignature) => new AggregateTransactionCosignature( cosignature.substring(0, 64), - PublicAccount.createFromPublicKey(cosignature.substring(64, 192), networkType), + PublicAccount.createFromPublicKey(cosignature.substring(0, 64), networkType), )) : [], ); default: @@ -457,9 +457,9 @@ const parseInnerTransactionFromBinary = (innerTransactionBinary: string): string while (innerBinary.length) { const payloadSize = parseInt(convert.uint8ToHex(convert.hexToUint8(innerTransactionBinary.substring(0, 8)).reverse()), 16) * 2; - const innerTransaction = innerTransactionBinary.substring(8, 8 + payloadSize); + const innerTransaction = innerTransactionBinary.substring(8, payloadSize); embeddedTransaction.push(innerTransaction); - innerBinary = innerTransactionBinary.substring(8 + payloadSize); + innerBinary = innerBinary.substring(payloadSize); } return embeddedTransaction; }; diff --git a/src/model/transaction/AggregateTransaction.ts b/src/model/transaction/AggregateTransaction.ts index 1e8f0efe50..47312c1e4f 100644 --- a/src/model/transaction/AggregateTransaction.ts +++ b/src/model/transaction/AggregateTransaction.ts @@ -146,18 +146,4 @@ export class AggregateTransaction extends Transaction { return this.cosignatures.find((cosignature) => cosignature.signer.equals(publicAccount)) !== undefined || (this.signer !== undefined && this.signer.equals(publicAccount)); } - - /** - * Check if an aggregate complete transaction has all cosignatories - * @returns {boolean} - */ - public isComplete(): boolean { - let isCompleted = false; - this.innerTransactions.forEach((innerTransaction) => { - if (!isCompleted) { - isCompleted = this.cosignatures.find((cosignature) => cosignature.signer.equals(innerTransaction.signer)) !== undefined; - } - }); - return isCompleted; - } } diff --git a/src/service/AggregatedTransactionService.ts b/src/service/AggregatedTransactionService.ts new file mode 100644 index 0000000000..ccdd72cd3b --- /dev/null +++ b/src/service/AggregatedTransactionService.ts @@ -0,0 +1,127 @@ +/* + * Copyright 2019 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {from as observableFrom , Observable, of as observableOf} from 'rxjs'; +import { map, mergeMap} from 'rxjs/operators'; +import { TransactionMapping } from '../core/utils/TransactionMapping'; +import { AccountHttp } from '../infrastructure/AccountHttp'; +import { MultisigAccountGraphInfo } from '../model/account/MultisigAccountGraphInfo'; +import { AggregateTransaction } from '../model/transaction/AggregateTransaction'; +import { SignedTransaction } from '../model/transaction/SignedTransaction'; + +/** + * Aggregated Transaction service + */ +export class AggregatedTransactionService { + + /** + * Constructor + * @param accountHttp + */ + constructor(private readonly accountHttp: AccountHttp) { + } + + /** + * Check if an aggregate complete transaction has all cosignatories attached + * @param signedTransaction - The signed aggregate transaction (complete) to be verified + * @returns {Observable} + */ + public isComplete(signedTransaction: SignedTransaction): Observable { + const aggregateTransaction = TransactionMapping.createFromPayload(signedTransaction.payload) as AggregateTransaction; + /** + * Include both initiator & cosigners + */ + const signers = (aggregateTransaction.cosignatures.map((cosigner) => cosigner.signer.publicKey)); + if (signedTransaction.signer) { + signers.push(signedTransaction.signer); + } + + return observableFrom(aggregateTransaction.innerTransactions).pipe( + mergeMap((innerTransaction) => this.accountHttp.getMultisigAccountInfo(innerTransaction.signer.address) + .pipe( + /** + * For multisig account, we need to get the graph info in case it has multiple levels + */ + mergeMap((_) => _.minApproval !== 0 && _.minRemoval !== 0 ? + this.accountHttp.getMultisigAccountGraphInfo(_.account.address) + .pipe( + map((graphInfo) => this.validateCosignatories(graphInfo, signers)), + ) : observableOf(true), + ), + ), + ), + ); + } + + /** + * Validate cosignatories against multisig Account(s) + * @param graphInfo - multisig account graph info + * @param cosignatories - array of cosignatories extracted from aggregated transaction + * @returns {boolean} + */ + private validateCosignatories(graphInfo: MultisigAccountGraphInfo, cosignatories: string[]): boolean { + /** + * Validate cosignatories from bottom level to top + */ + const sortedKeys = Array.from(graphInfo.multisigAccounts.keys()).sort((a, b) => b - a); + const cosignatoriesReceived = cosignatories; + let validationResult = true; + + sortedKeys.forEach((key) => { + const multisigInfo = graphInfo.multisigAccounts.get(key); + if (multisigInfo && validationResult) { + multisigInfo.forEach((multisig) => { + if (multisig.minApproval >= 1) { + const matchedCosignatories = this.compareArrays(cosignatoriesReceived, + multisig.cosignatories.map((cosig) => cosig.publicKey)); + + /** + * if minimal signature requirement met at current level, push the multisig account + * into the received signatories array for next level validation. + * Otherwise return validation failed. + */ + if (matchedCosignatories.length >= multisig.minApproval) { + if (cosignatoriesReceived.indexOf(multisig.account.publicKey) === -1) { + cosignatoriesReceived.push(multisig.account.publicKey); + } + } else { + validationResult = false; + } + } + }); + } + }); + + return validationResult; + } + + /** + * Compare two string arrays + * @param array1 - base array + * @param array2 - array to be matched + * @returns {string[]} - array of matched elements + */ + private compareArrays(array1: string[], array2: string[]): string[] { + const results: string[] = []; + array1.forEach((a1) => array2.forEach((a2) => { + if (a1 === a2) { + results.push(a1); + } + })); + + return results; + } +} diff --git a/src/service/service.ts b/src/service/service.ts index 6a9945c1cd..fcf4c84680 100644 --- a/src/service/service.ts +++ b/src/service/service.ts @@ -16,3 +16,4 @@ export * from './NamespaceService'; export * from './MosaicService'; +export * from './AggregatedTransactionService'; diff --git a/test/service/AggregatedTransactionService.spec.ts b/test/service/AggregatedTransactionService.spec.ts new file mode 100644 index 0000000000..0aff3d8f47 --- /dev/null +++ b/test/service/AggregatedTransactionService.spec.ts @@ -0,0 +1,242 @@ +/* + * Copyright 2018 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect } from 'chai'; +import { ChronoUnit } from 'js-joda'; +import {of as observableOf} from 'rxjs'; +import {deepEqual, instance, mock, when} from 'ts-mockito'; +import { AccountHttp } from '../../src/infrastructure/AccountHttp'; +import { Account } from '../../src/model/account/Account'; +import { Address } from '../../src/model/account/Address'; +import { MultisigAccountGraphInfo } from '../../src/model/account/MultisigAccountGraphInfo'; +import { MultisigAccountInfo } from '../../src/model/account/MultisigAccountInfo'; +import {NetworkType} from '../../src/model/blockchain/NetworkType'; +import { AggregateTransaction } from '../../src/model/transaction/AggregateTransaction'; +import { Deadline } from '../../src/model/transaction/Deadline'; +import { PlainMessage } from '../../src/model/transaction/PlainMessage'; +import { TransferTransaction } from '../../src/model/transaction/TransferTransaction'; +import { AggregatedTransactionService } from '../../src/service/AggregatedTransactionService'; + +/** + * For multi level multisig scenario visit: https://github.com/nemtech/nem2-docs/issues/10 + */ +describe('AggregatedTransactionService', () => { + let aggregatedTransactionService: AggregatedTransactionService; + + /** + * Multisig2 Account: SBROWP-7YMG2M-K45RO6-Q7ZPK7-G7GXWQ-JK5VNQ-OSUX + * Public Key: 5E628EA59818D97AA4118780D9A88C5512FCE7A21C195E1574727EFCE5DF7C0D + * Private Key: 22A1D67F8519D1A45BD7116600BB6E857786E816FE0B45E4C5B9FFF3D64BC177 + * + * + * Multisig1 Account: SAK32M-5JQ43R-WYHWEH-WRBCW4-RXERT2-DLASGL-EANS + * Public Key: BFDF2610C5666A626434FE12FB4A9D896D2B9B033F5F84CCEABE82E043A6307E + * Private Key: 8B0622C2CCFC5CCC5A74B500163E3C68F3AD3643DB12932FC931143EAC67280D + */ + + /** + * Test accounts: + * Multisig1 (1/1): Account2, Account3 + * Multisig2 (2/1): Account1, Multisig1 + * Stranger Account: Account4 + */ + + const account1 = Account.createFromPrivateKey('82DB2528834C9926F0FCCE042466B24A266F5B685CB66D2869AF6648C043E950', + NetworkType.MIJIN_TEST); + const multisig1 = Account.createFromPrivateKey('8B0622C2CCFC5CCC5A74B500163E3C68F3AD3643DB12932FC931143EAC67280D', + NetworkType.MIJIN_TEST); + const multisig2 = Account.createFromPrivateKey('22A1D67F8519D1A45BD7116600BB6E857786E816FE0B45E4C5B9FFF3D64BC177', + NetworkType.MIJIN_TEST); + + const account2 = Account.createFromPrivateKey('A4D410270E01CECDCDEADCDE32EC79C8D9CDEA4DCD426CB1EB666EFEF148FBCE', + NetworkType.MIJIN_TEST); + const account3 = Account.createFromPrivateKey('336AB45EE65A6AFFC0E7ADC5342F91E34BACA0B901A1D9C876FA25A1E590077E', + NetworkType.MIJIN_TEST); + + const account4 = Account.createFromPrivateKey('4D8B3756592532753344E11E2B7541317BCCFBBCF4444274CDBF359D2C4AE0F1', + NetworkType.MIJIN_TEST); + before(() => { + const mockedAccountHttp = mock(AccountHttp); + + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account1.address))) + .thenReturn(observableOf(givenAccount1Info())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account4.address))) + .thenReturn(observableOf(givenAccount4Info())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(multisig2.address))) + .thenReturn(observableOf(givenMultisig2AccountInfo())); + when(mockedAccountHttp.getMultisigAccountGraphInfo(deepEqual(multisig2.address))) + .thenReturn(observableOf(givenMultisig2AccountGraphInfo())); + + const accountHttp = instance(mockedAccountHttp); + aggregatedTransactionService = new AggregatedTransactionService(accountHttp); + }); + + it('should return isComplete: true for aggregated complete transaction - 2 levels Multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account2]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + it('should return isComplete: false for aggregated complete transaction - 2 levels Multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, []); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + + it('should return isComplete: false for aggregated complete transaction - 2 levels Multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + + it('should return correct isComplete status for aggregated complete transaction - 2 levels Multisig, multi inner transaction', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const transferTransaction2 = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount), + transferTransaction.toAggregate(account4.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account2]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + it('should return correct isComplete status for aggregated complete transaction - none multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(account4.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signWith(account1); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + function givenMultisig2AccountInfo(): MultisigAccountInfo { + return new MultisigAccountInfo(multisig2.publicAccount, + 2, 1, + [multisig1.publicAccount, + account1.publicAccount], + [], + ); + } + + function givenAccount1Info(): MultisigAccountInfo { + return new MultisigAccountInfo(account1.publicAccount, + 0, 0, + [], + [multisig2.publicAccount], + ); + } + function givenAccount4Info(): MultisigAccountInfo { + return new MultisigAccountInfo(account4.publicAccount, + 0, 0, + [], + [], + ); + } + + function givenMultisig2AccountGraphInfo(): MultisigAccountGraphInfo { + const map = new Map(); + map.set(0, [new MultisigAccountInfo(multisig2.publicAccount, + 2, 1, + [multisig1.publicAccount, + account1.publicAccount], + [], + )]) + .set(1, [new MultisigAccountInfo(multisig1.publicAccount, + 1, 1, + [account2.publicAccount, account3.publicAccount], + [multisig2.publicAccount], + )]); + + return new MultisigAccountGraphInfo(map); + } + +}); From 08c8ce3a71729242aa14609c850582df4aec3c05 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 1 Apr 2019 21:25:50 +0100 Subject: [PATCH 13/83] Updated license year --- test/service/AggregatedTransactionService.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/service/AggregatedTransactionService.spec.ts b/test/service/AggregatedTransactionService.spec.ts index 0aff3d8f47..bc0c11f535 100644 --- a/test/service/AggregatedTransactionService.spec.ts +++ b/test/service/AggregatedTransactionService.spec.ts @@ -1,5 +1,5 @@ /* - * Copyright 2018 NEM + * Copyright 2019 NEM * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From f32982dce21f4cb789ac7d600399720d633c132a Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 18 Mar 2019 10:05:21 +0000 Subject: [PATCH 14/83] Added #4 check if an aggregate complete transaction has all cosignatories --- src/model/transaction/AggregateTransaction.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/model/transaction/AggregateTransaction.ts b/src/model/transaction/AggregateTransaction.ts index 1557012fa6..1e8f0efe50 100644 --- a/src/model/transaction/AggregateTransaction.ts +++ b/src/model/transaction/AggregateTransaction.ts @@ -147,4 +147,17 @@ export class AggregateTransaction extends Transaction { || (this.signer !== undefined && this.signer.equals(publicAccount)); } + /** + * Check if an aggregate complete transaction has all cosignatories + * @returns {boolean} + */ + public isComplete(): boolean { + let isCompleted = false; + this.innerTransactions.forEach((innerTransaction) => { + if (!isCompleted) { + isCompleted = this.cosignatures.find((cosignature) => cosignature.signer.equals(innerTransaction.signer)) !== undefined; + } + }); + return isCompleted; + } } From 41eb60e5c9b20d7f3f4cd06fbf487f9d299ff415 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 1 Apr 2019 20:39:11 +0100 Subject: [PATCH 15/83] #4 1. Addes service for Aggregated Transaction - isComplete 2. Fixed couple of bugs in TransactionMappings --- .../CreateTransactionFromPayload.ts | 8 +- src/model/transaction/AggregateTransaction.ts | 14 - src/service/AggregatedTransactionService.ts | 127 +++++++++ src/service/service.ts | 1 + .../AggregatedTransactionService.spec.ts | 242 ++++++++++++++++++ 5 files changed, 374 insertions(+), 18 deletions(-) create mode 100644 src/service/AggregatedTransactionService.ts create mode 100644 test/service/AggregatedTransactionService.spec.ts diff --git a/src/infrastructure/transaction/CreateTransactionFromPayload.ts b/src/infrastructure/transaction/CreateTransactionFromPayload.ts index e8b66453ff..9b124a33bf 100644 --- a/src/infrastructure/transaction/CreateTransactionFromPayload.ts +++ b/src/infrastructure/transaction/CreateTransactionFromPayload.ts @@ -377,7 +377,7 @@ const CreateTransaction = (type: number, transactionData: string, networkType: N networkType, consignatureArray ? consignatureArray.map((cosignature) => new AggregateTransactionCosignature( cosignature.substring(0, 64), - PublicAccount.createFromPublicKey(cosignature.substring(64, 192), networkType), + PublicAccount.createFromPublicKey(cosignature.substring(0, 64), networkType), )) : [], ); case TransactionType.AGGREGATE_BONDED: @@ -401,7 +401,7 @@ const CreateTransaction = (type: number, transactionData: string, networkType: N networkType, bondedConsignatureArray ? bondedConsignatureArray.map((cosignature) => new AggregateTransactionCosignature( cosignature.substring(0, 64), - PublicAccount.createFromPublicKey(cosignature.substring(64, 192), networkType), + PublicAccount.createFromPublicKey(cosignature.substring(0, 64), networkType), )) : [], ); default: @@ -457,9 +457,9 @@ const parseInnerTransactionFromBinary = (innerTransactionBinary: string): string while (innerBinary.length) { const payloadSize = parseInt(convert.uint8ToHex(convert.hexToUint8(innerTransactionBinary.substring(0, 8)).reverse()), 16) * 2; - const innerTransaction = innerTransactionBinary.substring(8, 8 + payloadSize); + const innerTransaction = innerTransactionBinary.substring(8, payloadSize); embeddedTransaction.push(innerTransaction); - innerBinary = innerTransactionBinary.substring(8 + payloadSize); + innerBinary = innerBinary.substring(payloadSize); } return embeddedTransaction; }; diff --git a/src/model/transaction/AggregateTransaction.ts b/src/model/transaction/AggregateTransaction.ts index 1e8f0efe50..47312c1e4f 100644 --- a/src/model/transaction/AggregateTransaction.ts +++ b/src/model/transaction/AggregateTransaction.ts @@ -146,18 +146,4 @@ export class AggregateTransaction extends Transaction { return this.cosignatures.find((cosignature) => cosignature.signer.equals(publicAccount)) !== undefined || (this.signer !== undefined && this.signer.equals(publicAccount)); } - - /** - * Check if an aggregate complete transaction has all cosignatories - * @returns {boolean} - */ - public isComplete(): boolean { - let isCompleted = false; - this.innerTransactions.forEach((innerTransaction) => { - if (!isCompleted) { - isCompleted = this.cosignatures.find((cosignature) => cosignature.signer.equals(innerTransaction.signer)) !== undefined; - } - }); - return isCompleted; - } } diff --git a/src/service/AggregatedTransactionService.ts b/src/service/AggregatedTransactionService.ts new file mode 100644 index 0000000000..ccdd72cd3b --- /dev/null +++ b/src/service/AggregatedTransactionService.ts @@ -0,0 +1,127 @@ +/* + * Copyright 2019 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {from as observableFrom , Observable, of as observableOf} from 'rxjs'; +import { map, mergeMap} from 'rxjs/operators'; +import { TransactionMapping } from '../core/utils/TransactionMapping'; +import { AccountHttp } from '../infrastructure/AccountHttp'; +import { MultisigAccountGraphInfo } from '../model/account/MultisigAccountGraphInfo'; +import { AggregateTransaction } from '../model/transaction/AggregateTransaction'; +import { SignedTransaction } from '../model/transaction/SignedTransaction'; + +/** + * Aggregated Transaction service + */ +export class AggregatedTransactionService { + + /** + * Constructor + * @param accountHttp + */ + constructor(private readonly accountHttp: AccountHttp) { + } + + /** + * Check if an aggregate complete transaction has all cosignatories attached + * @param signedTransaction - The signed aggregate transaction (complete) to be verified + * @returns {Observable} + */ + public isComplete(signedTransaction: SignedTransaction): Observable { + const aggregateTransaction = TransactionMapping.createFromPayload(signedTransaction.payload) as AggregateTransaction; + /** + * Include both initiator & cosigners + */ + const signers = (aggregateTransaction.cosignatures.map((cosigner) => cosigner.signer.publicKey)); + if (signedTransaction.signer) { + signers.push(signedTransaction.signer); + } + + return observableFrom(aggregateTransaction.innerTransactions).pipe( + mergeMap((innerTransaction) => this.accountHttp.getMultisigAccountInfo(innerTransaction.signer.address) + .pipe( + /** + * For multisig account, we need to get the graph info in case it has multiple levels + */ + mergeMap((_) => _.minApproval !== 0 && _.minRemoval !== 0 ? + this.accountHttp.getMultisigAccountGraphInfo(_.account.address) + .pipe( + map((graphInfo) => this.validateCosignatories(graphInfo, signers)), + ) : observableOf(true), + ), + ), + ), + ); + } + + /** + * Validate cosignatories against multisig Account(s) + * @param graphInfo - multisig account graph info + * @param cosignatories - array of cosignatories extracted from aggregated transaction + * @returns {boolean} + */ + private validateCosignatories(graphInfo: MultisigAccountGraphInfo, cosignatories: string[]): boolean { + /** + * Validate cosignatories from bottom level to top + */ + const sortedKeys = Array.from(graphInfo.multisigAccounts.keys()).sort((a, b) => b - a); + const cosignatoriesReceived = cosignatories; + let validationResult = true; + + sortedKeys.forEach((key) => { + const multisigInfo = graphInfo.multisigAccounts.get(key); + if (multisigInfo && validationResult) { + multisigInfo.forEach((multisig) => { + if (multisig.minApproval >= 1) { + const matchedCosignatories = this.compareArrays(cosignatoriesReceived, + multisig.cosignatories.map((cosig) => cosig.publicKey)); + + /** + * if minimal signature requirement met at current level, push the multisig account + * into the received signatories array for next level validation. + * Otherwise return validation failed. + */ + if (matchedCosignatories.length >= multisig.minApproval) { + if (cosignatoriesReceived.indexOf(multisig.account.publicKey) === -1) { + cosignatoriesReceived.push(multisig.account.publicKey); + } + } else { + validationResult = false; + } + } + }); + } + }); + + return validationResult; + } + + /** + * Compare two string arrays + * @param array1 - base array + * @param array2 - array to be matched + * @returns {string[]} - array of matched elements + */ + private compareArrays(array1: string[], array2: string[]): string[] { + const results: string[] = []; + array1.forEach((a1) => array2.forEach((a2) => { + if (a1 === a2) { + results.push(a1); + } + })); + + return results; + } +} diff --git a/src/service/service.ts b/src/service/service.ts index 6a9945c1cd..fcf4c84680 100644 --- a/src/service/service.ts +++ b/src/service/service.ts @@ -16,3 +16,4 @@ export * from './NamespaceService'; export * from './MosaicService'; +export * from './AggregatedTransactionService'; diff --git a/test/service/AggregatedTransactionService.spec.ts b/test/service/AggregatedTransactionService.spec.ts new file mode 100644 index 0000000000..0aff3d8f47 --- /dev/null +++ b/test/service/AggregatedTransactionService.spec.ts @@ -0,0 +1,242 @@ +/* + * Copyright 2018 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect } from 'chai'; +import { ChronoUnit } from 'js-joda'; +import {of as observableOf} from 'rxjs'; +import {deepEqual, instance, mock, when} from 'ts-mockito'; +import { AccountHttp } from '../../src/infrastructure/AccountHttp'; +import { Account } from '../../src/model/account/Account'; +import { Address } from '../../src/model/account/Address'; +import { MultisigAccountGraphInfo } from '../../src/model/account/MultisigAccountGraphInfo'; +import { MultisigAccountInfo } from '../../src/model/account/MultisigAccountInfo'; +import {NetworkType} from '../../src/model/blockchain/NetworkType'; +import { AggregateTransaction } from '../../src/model/transaction/AggregateTransaction'; +import { Deadline } from '../../src/model/transaction/Deadline'; +import { PlainMessage } from '../../src/model/transaction/PlainMessage'; +import { TransferTransaction } from '../../src/model/transaction/TransferTransaction'; +import { AggregatedTransactionService } from '../../src/service/AggregatedTransactionService'; + +/** + * For multi level multisig scenario visit: https://github.com/nemtech/nem2-docs/issues/10 + */ +describe('AggregatedTransactionService', () => { + let aggregatedTransactionService: AggregatedTransactionService; + + /** + * Multisig2 Account: SBROWP-7YMG2M-K45RO6-Q7ZPK7-G7GXWQ-JK5VNQ-OSUX + * Public Key: 5E628EA59818D97AA4118780D9A88C5512FCE7A21C195E1574727EFCE5DF7C0D + * Private Key: 22A1D67F8519D1A45BD7116600BB6E857786E816FE0B45E4C5B9FFF3D64BC177 + * + * + * Multisig1 Account: SAK32M-5JQ43R-WYHWEH-WRBCW4-RXERT2-DLASGL-EANS + * Public Key: BFDF2610C5666A626434FE12FB4A9D896D2B9B033F5F84CCEABE82E043A6307E + * Private Key: 8B0622C2CCFC5CCC5A74B500163E3C68F3AD3643DB12932FC931143EAC67280D + */ + + /** + * Test accounts: + * Multisig1 (1/1): Account2, Account3 + * Multisig2 (2/1): Account1, Multisig1 + * Stranger Account: Account4 + */ + + const account1 = Account.createFromPrivateKey('82DB2528834C9926F0FCCE042466B24A266F5B685CB66D2869AF6648C043E950', + NetworkType.MIJIN_TEST); + const multisig1 = Account.createFromPrivateKey('8B0622C2CCFC5CCC5A74B500163E3C68F3AD3643DB12932FC931143EAC67280D', + NetworkType.MIJIN_TEST); + const multisig2 = Account.createFromPrivateKey('22A1D67F8519D1A45BD7116600BB6E857786E816FE0B45E4C5B9FFF3D64BC177', + NetworkType.MIJIN_TEST); + + const account2 = Account.createFromPrivateKey('A4D410270E01CECDCDEADCDE32EC79C8D9CDEA4DCD426CB1EB666EFEF148FBCE', + NetworkType.MIJIN_TEST); + const account3 = Account.createFromPrivateKey('336AB45EE65A6AFFC0E7ADC5342F91E34BACA0B901A1D9C876FA25A1E590077E', + NetworkType.MIJIN_TEST); + + const account4 = Account.createFromPrivateKey('4D8B3756592532753344E11E2B7541317BCCFBBCF4444274CDBF359D2C4AE0F1', + NetworkType.MIJIN_TEST); + before(() => { + const mockedAccountHttp = mock(AccountHttp); + + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account1.address))) + .thenReturn(observableOf(givenAccount1Info())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account4.address))) + .thenReturn(observableOf(givenAccount4Info())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(multisig2.address))) + .thenReturn(observableOf(givenMultisig2AccountInfo())); + when(mockedAccountHttp.getMultisigAccountGraphInfo(deepEqual(multisig2.address))) + .thenReturn(observableOf(givenMultisig2AccountGraphInfo())); + + const accountHttp = instance(mockedAccountHttp); + aggregatedTransactionService = new AggregatedTransactionService(accountHttp); + }); + + it('should return isComplete: true for aggregated complete transaction - 2 levels Multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account2]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + it('should return isComplete: false for aggregated complete transaction - 2 levels Multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, []); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + + it('should return isComplete: false for aggregated complete transaction - 2 levels Multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + + it('should return correct isComplete status for aggregated complete transaction - 2 levels Multisig, multi inner transaction', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const transferTransaction2 = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount), + transferTransaction.toAggregate(account4.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account2]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + it('should return correct isComplete status for aggregated complete transaction - none multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(account4.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signWith(account1); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + function givenMultisig2AccountInfo(): MultisigAccountInfo { + return new MultisigAccountInfo(multisig2.publicAccount, + 2, 1, + [multisig1.publicAccount, + account1.publicAccount], + [], + ); + } + + function givenAccount1Info(): MultisigAccountInfo { + return new MultisigAccountInfo(account1.publicAccount, + 0, 0, + [], + [multisig2.publicAccount], + ); + } + function givenAccount4Info(): MultisigAccountInfo { + return new MultisigAccountInfo(account4.publicAccount, + 0, 0, + [], + [], + ); + } + + function givenMultisig2AccountGraphInfo(): MultisigAccountGraphInfo { + const map = new Map(); + map.set(0, [new MultisigAccountInfo(multisig2.publicAccount, + 2, 1, + [multisig1.publicAccount, + account1.publicAccount], + [], + )]) + .set(1, [new MultisigAccountInfo(multisig1.publicAccount, + 1, 1, + [account2.publicAccount, account3.publicAccount], + [multisig2.publicAccount], + )]); + + return new MultisigAccountGraphInfo(map); + } + +}); From 478c8a4e4f2020415a12240c31732c36057279c3 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 1 Apr 2019 21:25:50 +0100 Subject: [PATCH 16/83] Updated license year --- test/service/AggregatedTransactionService.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/service/AggregatedTransactionService.spec.ts b/test/service/AggregatedTransactionService.spec.ts index 0aff3d8f47..bc0c11f535 100644 --- a/test/service/AggregatedTransactionService.spec.ts +++ b/test/service/AggregatedTransactionService.spec.ts @@ -1,5 +1,5 @@ /* - * Copyright 2018 NEM + * Copyright 2019 NEM * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 6267a1936d4a00dfe536c9bd9ef3384eb7dfa490 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Tue, 2 Apr 2019 19:49:26 +0100 Subject: [PATCH 17/83] Fixed #107 - Fixed issues on createFromDTO with JSON payload --- .../transaction/CreateTransactionFromDTO.ts | 104 +++-- .../transaction/SerializeTransactionToJSON.ts | 22 +- src/model/mosaic/MosaicProperties.ts | 14 +- src/model/transaction/Transaction.ts | 7 +- test/core/utils/TransactionMapping.spec.ts | 357 +++++++++++++++++- .../SerializeTransactionToJSON.spec.ts | 84 ++--- 6 files changed, 488 insertions(+), 100 deletions(-) diff --git a/src/infrastructure/transaction/CreateTransactionFromDTO.ts b/src/infrastructure/transaction/CreateTransactionFromDTO.ts index 5bd0a420d1..ad28fd9bba 100644 --- a/src/infrastructure/transaction/CreateTransactionFromDTO.ts +++ b/src/infrastructure/transaction/CreateTransactionFromDTO.ts @@ -30,7 +30,6 @@ import {AggregateTransaction} from '../../model/transaction/AggregateTransaction import {AggregateTransactionCosignature} from '../../model/transaction/AggregateTransactionCosignature'; import {AggregateTransactionInfo} from '../../model/transaction/AggregateTransactionInfo'; import {Deadline} from '../../model/transaction/Deadline'; -import { LinkAction } from '../../model/transaction/LinkAction'; import {LockFundsTransaction} from '../../model/transaction/LockFundsTransaction'; import {ModifyAccountPropertyAddressTransaction} from '../../model/transaction/ModifyAccountPropertyAddressTransaction'; import {ModifyAccountPropertyEntityTypeTransaction} from '../../model/transaction/ModifyAccountPropertyEntityTypeTransaction'; @@ -88,23 +87,24 @@ export const CreateTransactionFromDTO = (transactionDTO): Transaction => { extractNetworkType(transactionDTO.transaction.version))); }) : [], transactionDTO.transaction.signature, - PublicAccount.createFromPublicKey(transactionDTO.transaction.signer, extractNetworkType(transactionDTO.transaction.version)), - new TransactionInfo( + transactionDTO.transaction.signer ? PublicAccount.createFromPublicKey(transactionDTO.transaction.signer, + extractNetworkType(transactionDTO.transaction.version)) : undefined, + transactionDTO.meta ? new TransactionInfo( new UInt64(transactionDTO.meta.height), transactionDTO.meta.index, transactionDTO.meta.id, transactionDTO.meta.hash, transactionDTO.meta.merkleComponentHash, - ), + ) : undefined, ); } else { - const transactionInfo = new TransactionInfo( + const transactionInfo = transactionDTO.meta ? new TransactionInfo( new UInt64(transactionDTO.meta.height), transactionDTO.meta.index, transactionDTO.meta.id, transactionDTO.meta.hash, transactionDTO.meta.merkleComponentHash, - ); + ) : undefined; return CreateStandaloneTransactionFromDTO(transactionDTO.transaction, transactionInfo); } }; @@ -118,6 +118,16 @@ export const CreateTransactionFromDTO = (transactionDTO): Transaction => { */ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Transaction => { if (transactionDTO.type === TransactionType.TRANSFER) { + /** + * Check if message is encoded (from DTO) or is raw (from JSON) + */ + let message = EmptyMessage; + if (transactionDTO.message !== undefined && convert.isHexString(transactionDTO.message.payload)) { + message = PlainMessage.createFromDTO(transactionDTO.message.payload); + } else { + message = PlainMessage.create(transactionDTO.message.payload); + } + return new TransferTransaction( extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), @@ -125,10 +135,10 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr new UInt64(transactionDTO.fee || [0, 0]), extractRecipient(transactionDTO.recipient), extractMosaics(transactionDTO.mosaics), - transactionDTO.message !== undefined ? - PlainMessage.createFromDTO(transactionDTO.message.payload) : EmptyMessage, + message, transactionDTO.signature, - PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)), + transactionDTO.signer ? PublicAccount.createFromPublicKey(transactionDTO.signer, + extractNetworkType(transactionDTO.version)) : undefined, transactionInfo, ); } else if (transactionDTO.type === TransactionType.REGISTER_NAMESPACE) { @@ -143,7 +153,8 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr transactionDTO.namespaceType === 0 ? new UInt64(transactionDTO.duration) : undefined, transactionDTO.namespaceType === 1 ? new NamespaceId(transactionDTO.parentId) : undefined, transactionDTO.signature, - PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)), + transactionDTO.signer ? PublicAccount.createFromPublicKey(transactionDTO.signer, + extractNetworkType(transactionDTO.version)) : undefined, transactionInfo, ); } else if (transactionDTO.type === TransactionType.MOSAIC_DEFINITION) { @@ -160,7 +171,8 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr new UInt64(transactionDTO.properties.length === 3 ? transactionDTO.properties[2].value : [0, 0]), ), transactionDTO.signature, - PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)), + transactionDTO.signer ? PublicAccount.createFromPublicKey(transactionDTO.signer, + extractNetworkType(transactionDTO.version)) : undefined, transactionInfo, ); } else if (transactionDTO.type === TransactionType.MOSAIC_SUPPLY_CHANGE) { @@ -173,7 +185,8 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr transactionDTO.direction, new UInt64(transactionDTO.delta), transactionDTO.signature, - PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)), + transactionDTO.signer ? PublicAccount.createFromPublicKey(transactionDTO.signer, + extractNetworkType(transactionDTO.version)) : undefined, transactionInfo, ); } else if (transactionDTO.type === TransactionType.MODIFY_MULTISIG_ACCOUNT) { @@ -189,7 +202,8 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr PublicAccount.createFromPublicKey(modificationDTO.cosignatoryPublicKey, extractNetworkType(transactionDTO.version)), )) : [], transactionDTO.signature, - PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)), + transactionDTO.signer ? PublicAccount.createFromPublicKey(transactionDTO.signer, + extractNetworkType(transactionDTO.version)) : undefined, transactionInfo, ); } else if (transactionDTO.type === TransactionType.LOCK) { @@ -203,10 +217,11 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr new UInt64(transactionDTO.duration), new SignedTransaction('', transactionDTO.hash, '', TransactionType.AGGREGATE_BONDED, networkType), transactionDTO.signature, - PublicAccount.createFromPublicKey(transactionDTO.signer, networkType), + transactionDTO.signer ? PublicAccount.createFromPublicKey(transactionDTO.signer, networkType) : undefined, transactionInfo, ); } else if (transactionDTO.type === TransactionType.SECRET_LOCK) { + const recipient = transactionDTO.recipient; return new SecretLockTransaction( extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), @@ -216,9 +231,11 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr new UInt64(transactionDTO.duration), transactionDTO.hashAlgorithm, transactionDTO.secret, - Address.createFromEncoded(transactionDTO.recipient), + typeof recipient === 'object' && recipient.hasOwnProperty('address') ? + Address.createFromRawAddress(recipient.address) : Address.createFromEncoded(recipient), transactionDTO.signature, - PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)), + transactionDTO.signer ? PublicAccount.createFromPublicKey(transactionDTO.signer, + extractNetworkType(transactionDTO.version)) : undefined, transactionInfo, ); } else if (transactionDTO.type === TransactionType.SECRET_PROOF) { @@ -231,7 +248,8 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr transactionDTO.secret, transactionDTO.proof, transactionDTO.signature, - PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)), + transactionDTO.signer ? PublicAccount.createFromPublicKey(transactionDTO.signer, + extractNetworkType(transactionDTO.version)) : undefined, transactionInfo, ); } else if (transactionDTO.type === TransactionType.MOSAIC_ALIAS) { @@ -244,7 +262,8 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr new NamespaceId(transactionDTO.namespaceId), new MosaicId(transactionDTO.mosaicId), transactionDTO.signature, - PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)), + transactionDTO.signer ? PublicAccount.createFromPublicKey(transactionDTO.signer, + extractNetworkType(transactionDTO.version)) : undefined, transactionInfo, ); } else if (transactionDTO.type === TransactionType.ADDRESS_ALIAS) { @@ -257,7 +276,8 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr new NamespaceId(transactionDTO.namespaceId), extractRecipient(transactionDTO.address) as Address, transactionDTO.signature, - PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)), + transactionDTO.signer ? PublicAccount.createFromPublicKey(transactionDTO.signer, + extractNetworkType(transactionDTO.version)) : undefined, transactionInfo, ); } else if (transactionDTO.type === TransactionType.MODIFY_ACCOUNT_PROPERTY_ADDRESS) { @@ -272,7 +292,8 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr modificationDTO.value, )) : [], transactionDTO.signature, - PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)), + transactionDTO.signer ? PublicAccount.createFromPublicKey(transactionDTO.signer, + extractNetworkType(transactionDTO.version)) : undefined, transactionInfo, ); } else if (transactionDTO.type === TransactionType.MODIFY_ACCOUNT_PROPERTY_ENTITY_TYPE) { @@ -287,7 +308,8 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr modificationDTO.value, )) : [], transactionDTO.signature, - PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)), + transactionDTO.signer ? PublicAccount.createFromPublicKey(transactionDTO.signer, + extractNetworkType(transactionDTO.version)) : undefined, transactionInfo, ); } else if (transactionDTO.type === TransactionType.MODIFY_ACCOUNT_PROPERTY_MOSAIC) { @@ -302,7 +324,8 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr modificationDTO.value, )) : [], transactionDTO.signature, - PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)), + transactionDTO.signer ? PublicAccount.createFromPublicKey(transactionDTO.signer, + extractNetworkType(transactionDTO.version)) : undefined, transactionInfo, ); } else if (transactionDTO.type === TransactionType.LINK_ACCOUNT) { @@ -314,11 +337,11 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr transactionDTO.remoteAccountKey, transactionDTO.linkAction, transactionDTO.signature, - PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)), + transactionDTO.signer ? PublicAccount.createFromPublicKey(transactionDTO.signer, + extractNetworkType(transactionDTO.version)) : undefined, transactionInfo, ); } - throw new Error('Unimplemented transaction with type ' + transactionDTO.type); }; @@ -349,20 +372,29 @@ const extractTransactionVersion = (version: number): number => { * @param recipient {string} Encoded hexadecimal recipient notation * @return {Address | NamespaceId} */ -const extractRecipient = (recipient: string): Address | NamespaceId => { - // If bit 0 of byte 0 is not set (like in 0x90), then it is a regular address. - // Else (e.g. 0x91) it represents a namespace id which starts at byte 1. - const bit0 = convert.hexToUint8(recipient.substr(1, 2))[0]; +const extractRecipient = (recipient: any): Address | NamespaceId => { + if (typeof recipient === 'string') { + // If bit 0 of byte 0 is not set (like in 0x90), then it is a regular address. + // Else (e.g. 0x91) it represents a namespace id which starts at byte 1. + const bit0 = convert.hexToUint8(recipient.substr(1, 2))[0]; - if ((bit0 & 16) === 16) { - // namespaceId encoded hexadecimal notation provided - // only 8 bytes are relevant to resolve the NamespaceId - const relevantPart = recipient.substr(2, 16); - return NamespaceId.createFromEncoded(relevantPart); - } + if ((bit0 & 16) === 16) { + // namespaceId encoded hexadecimal notation provided + // only 8 bytes are relevant to resolve the NamespaceId + const relevantPart = recipient.substr(2, 16); + return NamespaceId.createFromEncoded(relevantPart); + } - // read address from encoded hexadecimal notation - return Address.createFromEncoded(recipient); + // read address from encoded hexadecimal notation + return Address.createFromEncoded(recipient); + } else if (typeof recipient === 'object') { // Is JSON object + if (recipient.hasOwnProperty('address')) { + return Address.createFromRawAddress(recipient.address); + } + + return new NamespaceId(recipient.id); + } + throw new Error(`Recipient: ${recipient} type is not recognised`); }; /** diff --git a/src/infrastructure/transaction/SerializeTransactionToJSON.ts b/src/infrastructure/transaction/SerializeTransactionToJSON.ts index fcd384ff3f..74133174e2 100644 --- a/src/infrastructure/transaction/SerializeTransactionToJSON.ts +++ b/src/infrastructure/transaction/SerializeTransactionToJSON.ts @@ -47,15 +47,15 @@ export const SerializeTransactionToJSON = (transaction: Transaction): any => { }; case TransactionType.ADDRESS_ALIAS: return { - actionType: (transaction as AddressAliasTransaction).actionType, + aliasAction: (transaction as AddressAliasTransaction).actionType, namespaceId: (transaction as AddressAliasTransaction).namespaceId.toDTO(), address: (transaction as AddressAliasTransaction).address.toDTO(), }; case TransactionType.AGGREGATE_BONDED: case TransactionType.AGGREGATE_COMPLETE: return { - innerTransactions: (transaction as AggregateTransaction).innerTransactions.map((innerTransaction) => { - return SerializeTransactionToJSON(innerTransaction); + transactions: (transaction as AggregateTransaction).innerTransactions.map((innerTransaction) => { + return innerTransaction.toJSON(); }), cosignatures: (transaction as AggregateTransaction).cosignatures.map((cosignature) => { return cosignature.toDTO(); @@ -63,9 +63,10 @@ export const SerializeTransactionToJSON = (transaction: Transaction): any => { }; case TransactionType.LOCK: return { - mosaic: (transaction as LockFundsTransaction).mosaic.toDTO(), + mosaicId: (transaction as LockFundsTransaction).mosaic.id.id, + amount: (transaction as LockFundsTransaction).mosaic.amount.toDTO(), duration: (transaction as LockFundsTransaction).duration.toDTO(), - signedTransaction: (transaction as LockFundsTransaction).hash, + hash: (transaction as LockFundsTransaction).hash, }; case TransactionType.MODIFY_ACCOUNT_PROPERTY_ADDRESS: return { @@ -100,7 +101,7 @@ export const SerializeTransactionToJSON = (transaction: Transaction): any => { }; case TransactionType.MOSAIC_ALIAS: return { - actionType: (transaction as MosaicAliasTransaction).actionType, + aliasAction: (transaction as MosaicAliasTransaction).actionType, namespaceId: (transaction as MosaicAliasTransaction).namespaceId.toDTO(), mosaicId: (transaction as MosaicAliasTransaction).mosaicId.toDTO(), }; @@ -108,7 +109,7 @@ export const SerializeTransactionToJSON = (transaction: Transaction): any => { return { nonce: (transaction as MosaicDefinitionTransaction).nonce.toDTO(), mosaicId: (transaction as MosaicDefinitionTransaction).mosaicId.toDTO(), - mosaicProperties: (transaction as MosaicDefinitionTransaction).mosaicProperties.toDTO(), + properties: (transaction as MosaicDefinitionTransaction).mosaicProperties.toDTO(), }; case TransactionType.MOSAIC_SUPPLY_CHANGE: return { @@ -135,15 +136,16 @@ export const SerializeTransactionToJSON = (transaction: Transaction): any => { return jsonObject; case TransactionType.SECRET_LOCK: return { - mosaic: (transaction as SecretLockTransaction).mosaic.toDTO(), + mosaicId: (transaction as SecretLockTransaction).mosaic.id.id, + amount: (transaction as SecretLockTransaction).mosaic.amount.toDTO(), duration: (transaction as SecretLockTransaction).duration.toDTO(), - hashType: (transaction as SecretLockTransaction).hashType, + hashAlgorithm: (transaction as SecretLockTransaction).hashType, secret: (transaction as SecretLockTransaction).secret, recipient: (transaction as SecretLockTransaction).recipient.toDTO(), }; case TransactionType.SECRET_PROOF: return { - hashType: (transaction as SecretProofTransaction).hashType, + hashAlgorithm: (transaction as SecretProofTransaction).hashType, secret: (transaction as SecretProofTransaction).secret, proof: (transaction as SecretProofTransaction).proof, }; diff --git a/src/model/mosaic/MosaicProperties.ts b/src/model/mosaic/MosaicProperties.ts index 9d56a35fac..dfb7ebdb22 100644 --- a/src/model/mosaic/MosaicProperties.ts +++ b/src/model/mosaic/MosaicProperties.ts @@ -88,12 +88,12 @@ export class MosaicProperties { * Create DTO object */ toDTO() { - return { - supplyMutable: this.supplyMutable, - transferable: this.transferable, - levyMutable: this.levyMutable, - divisibility: this.divisibility, - duration: this.duration.toDTO(), - }; + return [ + {id: 0, value: UInt64.fromUint((this.supplyMutable ? 1 : 0) + + (this.transferable ? 2 : 0) + + (this.levyMutable ? 4 : 0)).toDTO()}, + {id: 1, value: UInt64.fromUint(this.divisibility).toDTO()}, + {id: 2, value: this.duration.toDTO()}, + ]; } } diff --git a/src/model/transaction/Transaction.ts b/src/model/transaction/Transaction.ts index 28421aec1c..ddbaca8137 100644 --- a/src/model/transaction/Transaction.ts +++ b/src/model/transaction/Transaction.ts @@ -194,18 +194,17 @@ export abstract class Transaction { const commonTransactionObject = { type: this.type, networkType: this.networkType, - version: this.version, + version: this.versionToDTO(), fee: this.fee.toDTO(), deadline: this.deadline.toDTO(), signature: this.signature ? this.signature : '', }; if (this.signer) { - Object.assign(commonTransactionObject, {signer: this.signer.toDTO()}); + Object.assign(commonTransactionObject, {signer: this.signer.publicKey}); } const childClassObject = SerializeTransactionToJSON(this); - - return Object.assign(commonTransactionObject, childClassObject); + return {transaction: Object.assign(commonTransactionObject, childClassObject)}; } } diff --git a/test/core/utils/TransactionMapping.spec.ts b/test/core/utils/TransactionMapping.spec.ts index 6091705a33..467f8d7626 100644 --- a/test/core/utils/TransactionMapping.spec.ts +++ b/test/core/utils/TransactionMapping.spec.ts @@ -42,6 +42,7 @@ import { HashType } from '../../../src/model/transaction/HashType'; import { LinkAction } from '../../../src/model/transaction/LinkAction'; import { LockFundsTransaction } from '../../../src/model/transaction/LockFundsTransaction'; import { ModifyAccountPropertyAddressTransaction } from '../../../src/model/transaction/ModifyAccountPropertyAddressTransaction'; +import { ModifyAccountPropertyMosaicTransaction } from '../../../src/model/transaction/ModifyAccountPropertyMosaicTransaction'; import { ModifyMultisigAccountTransaction } from '../../../src/model/transaction/ModifyMultisigAccountTransaction'; import { MosaicAliasTransaction } from '../../../src/model/transaction/MosaicAliasTransaction'; import { MosaicDefinitionTransaction } from '../../../src/model/transaction/MosaicDefinitionTransaction'; @@ -57,7 +58,7 @@ import { TransferTransaction } from '../../../src/model/transaction/TransferTran import { UInt64 } from '../../../src/model/UInt64'; import { TestingAccount } from '../../conf/conf.spec'; -describe('TransactionMapping', () => { +describe('TransactionMapping - createFromPayload', () => { let account: Account; before(() => { @@ -430,3 +431,357 @@ describe('TransactionMapping', () => { expect(transaction.namespaceName).to.be.equal('root-test-namespace'); }); }); + +describe('TransactionMapping - createFromDTO (Transaction.toJSON() feed)', () => { + let account: Account; + + before(() => { + account = TestingAccount; + }); + + it('should create TransferTransaction - Address', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [ + NetworkCurrencyMosaic.createRelative(100), + ], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const transaction = TransactionMapping.createFromDTO(transferTransaction.toJSON()) as TransferTransaction; + + expect((transaction.recipient as Address).plain()).to.be.equal('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'); + expect(transaction.message.payload).to.be.equal('test-message'); + }); + + it('should create TransferTransaction - NamespaceId', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(), + new NamespaceId([33347626, 3779697293]), + [ + NetworkCurrencyMosaic.createRelative(100), + ], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const transaction = TransactionMapping.createFromDTO(transferTransaction.toJSON()) as TransferTransaction; + expect((transaction.recipient as NamespaceId).id.toHex().toUpperCase()).to.be.equal(new UInt64([33347626, 3779697293]).toHex()); + expect(transaction.message.payload).to.be.equal('test-message'); + }); + + it('should create AccountLinkTransaction', () => { + const accountLinkTransaction = AccountLinkTransaction.create( + Deadline.create(), + account.publicKey, + LinkAction.Link, + NetworkType.MIJIN_TEST, + ); + + const transaction = TransactionMapping.createFromDTO(accountLinkTransaction.toJSON()) as AccountLinkTransaction; + + expect(transaction.remoteAccountKey).to.be.equal(account.publicKey); + expect(transaction.linkAction).to.be.equal(LinkAction.Link); + }); + + it('should create AccountPropertyAddressTransaction', () => { + const address = Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'); + const addressPropertyFilter = AccountPropertyTransaction.createAddressFilter( + PropertyModificationType.Add, + address, + ); + const addressPropertyTransaction = AccountPropertyTransaction.createAddressPropertyModificationTransaction( + Deadline.create(), + PropertyType.AllowAddress, + [addressPropertyFilter], + NetworkType.MIJIN_TEST, + ); + + const transaction = + TransactionMapping.createFromDTO(addressPropertyTransaction.toJSON()) as ModifyAccountPropertyAddressTransaction; + + expect(transaction.modifications[0].value).to.be.equal('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'); + expect(transaction.propertyType).to.be.equal(PropertyType.AllowAddress); + expect(transaction.modifications[0].modificationType).to.be.equal(PropertyModificationType.Add); + }); + + it('should create AccountPropertyMosaicTransaction', () => { + const mosaicId = new MosaicId([2262289484, 3405110546]); + const mosaicPropertyFilter = AccountPropertyTransaction.createMosaicFilter( + PropertyModificationType.Add, + mosaicId, + ); + const mosaicPropertyTransaction = AccountPropertyTransaction.createMosaicPropertyModificationTransaction( + Deadline.create(), + PropertyType.AllowMosaic, + [mosaicPropertyFilter], + NetworkType.MIJIN_TEST, + ); + + const transaction = + TransactionMapping.createFromDTO(mosaicPropertyTransaction.toJSON()) as ModifyAccountPropertyMosaicTransaction; + + expect(transaction.type).to.be.equal(TransactionType.MODIFY_ACCOUNT_PROPERTY_MOSAIC); + expect(transaction.propertyType).to.be.equal(PropertyType.AllowMosaic); + expect(transaction.modifications.length).to.be.equal(1); + }); + + it('should create AccountPropertyMosaicTransaction', () => { + const entityType = TransactionType.ADDRESS_ALIAS; + const entityTypePropertyFilter = AccountPropertyTransaction.createEntityTypeFilter( + PropertyModificationType.Add, + entityType, + ); + const entityTypePropertyTransaction = AccountPropertyTransaction.createEntityTypePropertyModificationTransaction( + Deadline.create(), + PropertyType.AllowTransaction, + [entityTypePropertyFilter], + NetworkType.MIJIN_TEST, + ); + + const transaction = + TransactionMapping.createFromDTO(entityTypePropertyTransaction.toJSON()) as ModifyAccountPropertyMosaicTransaction; + + expect(transaction.type).to.be.equal(TransactionType.MODIFY_ACCOUNT_PROPERTY_ENTITY_TYPE); + expect(transaction.propertyType).to.be.equal(PropertyType.AllowTransaction); + expect(transaction.modifications.length).to.be.equal(1); + }); + + it('should create AddressAliasTransaction', () => { + const namespaceId = new NamespaceId([33347626, 3779697293]); + const address = Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'); + const addressAliasTransaction = AddressAliasTransaction.create( + Deadline.create(), + AliasActionType.Link, + namespaceId, + address, + NetworkType.MIJIN_TEST, + ); + + const transaction = + TransactionMapping.createFromDTO(addressAliasTransaction.toJSON()) as AddressAliasTransaction; + + expect(transaction.type).to.be.equal(TransactionType.ADDRESS_ALIAS); + expect(transaction.actionType).to.be.equal(AliasActionType.Link); + }); + + it('should create MosaicAliasTransaction', () => { + const namespaceId = new NamespaceId([33347626, 3779697293]); + const mosaicId = new MosaicId([2262289484, 3405110546]); + const mosaicAliasTransaction = MosaicAliasTransaction.create( + Deadline.create(), + AliasActionType.Link, + namespaceId, + mosaicId, + NetworkType.MIJIN_TEST, + ); + const transaction = + TransactionMapping.createFromDTO(mosaicAliasTransaction.toJSON()) as MosaicAliasTransaction; + + expect(transaction.type).to.be.equal(TransactionType.MOSAIC_ALIAS); + expect(transaction.actionType).to.be.equal(AliasActionType.Link); + + }); + + it('should create MosaicDefinitionTransaction', () => { + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce + new MosaicId(UInt64.fromUint(1).toDTO()), // ID + MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 3, + duration: UInt64.fromUint(1000), + }), + NetworkType.MIJIN_TEST, + ); + + const transaction = + TransactionMapping.createFromDTO(mosaicDefinitionTransaction.toJSON()) as MosaicDefinitionTransaction; + + expect(transaction.type).to.be.equal(TransactionType.MOSAIC_DEFINITION); + expect(transaction.mosaicProperties.supplyMutable).to.be.equal(false); + expect(transaction.mosaicProperties.transferable).to.be.equal(false); + expect(transaction.mosaicProperties.levyMutable).to.be.equal(false); + expect(transaction.mosaicProperties.divisibility).to.be.equal(3); + + }); + + it('should create MosaicSupplyChangeTransaction', () => { + const mosaicId = new MosaicId([2262289484, 3405110546]); + const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( + Deadline.create(), + mosaicId, + MosaicSupplyType.Increase, + UInt64.fromUint(10), + NetworkType.MIJIN_TEST, + ); + + const transaction = + TransactionMapping.createFromDTO(mosaicSupplyChangeTransaction.toJSON()) as MosaicSupplyChangeTransaction; + + expect(transaction.type).to.be.equal(TransactionType.MOSAIC_SUPPLY_CHANGE); + expect(transaction.direction).to.be.equal(MosaicSupplyType.Increase); + + }); + + it('should create SecretLockTransaction', () => { + const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9DF538782BF199C189DABEAC7'; + const recipient = Address.createFromRawAddress('SDBDG4IT43MPCW2W4CBBCSJJT42AYALQN7A4VVWL'); + const secretLockTransaction = SecretLockTransaction.create( + Deadline.create(), + NetworkCurrencyMosaic.createAbsolute(10), + UInt64.fromUint(100), + HashType.Op_Sha3_256, + sha3_256.create().update(convert.hexToUint8(proof)).hex(), + recipient, + NetworkType.MIJIN_TEST, + ); + + const transaction = + TransactionMapping.createFromDTO(secretLockTransaction.toJSON()) as SecretLockTransaction; + + expect(transaction.type).to.be.equal(TransactionType.SECRET_LOCK); + expect(transaction.hashType).to.be.equal(HashType.Op_Sha3_256); + + }); + + it('should create SecretProofTransaction', () => { + const proof = 'B778A39A3663719DFC5E48C9D78431B1E45C2AF9DF538782BF199C189DABEAC7'; + const secretProofTransaction = SecretProofTransaction.create( + Deadline.create(), + HashType.Op_Sha3_256, + sha3_256.create().update(convert.hexToUint8(proof)).hex(), + proof, + NetworkType.MIJIN_TEST, + ); + + const transaction = + TransactionMapping.createFromDTO(secretProofTransaction.toJSON()) as SecretProofTransaction; + + expect(transaction.type).to.be.equal(TransactionType.SECRET_PROOF); + expect(transaction.hashType).to.be.equal(HashType.Op_Sha3_256); + expect(transaction.secret).to.be.equal(sha3_256.create().update(convert.hexToUint8(proof)).hex()); + expect(transaction.proof).to.be.equal(proof); + + }); + + it('should create ModifyMultiSigTransaction', () => { + const modifyMultisigAccountTransaction = ModifyMultisigAccountTransaction.create( + Deadline.create(), + 2, + 1, + [new MultisigCosignatoryModification( + MultisigCosignatoryModificationType.Add, + PublicAccount.createFromPublicKey('B0F93CBEE49EEB9953C6F3985B15A4F238E205584D8F924C621CBE4D7AC6EC24', + NetworkType.MIJIN_TEST), + )], + NetworkType.MIJIN_TEST, + ); + + const transaction = + TransactionMapping.createFromDTO(modifyMultisigAccountTransaction.toJSON()) as ModifyMultisigAccountTransaction; + + expect(transaction.type).to.be.equal(TransactionType.MODIFY_MULTISIG_ACCOUNT); + expect(transaction.minApprovalDelta).to.be.equal(2); + expect(transaction.minRemovalDelta).to.be.equal(1); + }); + + it('should create AggregatedTransaction - Complete', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(account.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const transaction = + TransactionMapping.createFromDTO(aggregateTransaction.toJSON()) as AggregateTransaction; + + expect(transaction.type).to.be.equal(TransactionType.AGGREGATE_COMPLETE); + expect(transaction.innerTransactions.length).to.be.equal(1); + }); + + it('should create AggregatedTransaction - Bonded', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createBonded( + Deadline.create(), + [transferTransaction.toAggregate(account.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const transaction = + TransactionMapping.createFromDTO(aggregateTransaction.toJSON()) as AggregateTransaction; + + expect(transaction.type).to.be.equal(TransactionType.AGGREGATE_BONDED); + expect(transaction.innerTransactions.length).to.be.equal(1); + }); + + it('should create LockFundTransaction', () => { + const aggregateTransaction = AggregateTransaction.createBonded( + Deadline.create(), + [], + NetworkType.MIJIN_TEST, + [], + ); + const signedTransaction = account.sign(aggregateTransaction); + const lockTransaction = LockFundsTransaction.create(Deadline.create(), + NetworkCurrencyMosaic.createRelative(10), + UInt64.fromUint(10), + signedTransaction, + NetworkType.MIJIN_TEST); + + const transaction = + TransactionMapping.createFromDTO(lockTransaction.toJSON()) as LockFundsTransaction; + + expect(transaction.type).to.be.equal(TransactionType.LOCK); + expect(transaction.hash).to.be.equal(signedTransaction.hash); + }); + + it('should create RegisterNamespaceTransaction - Root', () => { + const registerNamespaceTransaction = RegisterNamespaceTransaction.createRootNamespace( + Deadline.create(), + 'root-test-namespace', + UInt64.fromUint(1000), + NetworkType.MIJIN_TEST, + ); + + const transaction = + TransactionMapping.createFromDTO(registerNamespaceTransaction.toJSON()) as RegisterNamespaceTransaction; + + expect(transaction.type).to.be.equal(TransactionType.REGISTER_NAMESPACE); + + }); + + it('should create RegisterNamespaceTransaction - Sub', () => { + const registerNamespaceTransaction = RegisterNamespaceTransaction.createSubNamespace( + Deadline.create(), + 'root-test-namespace', + 'parent-test-namespace', + NetworkType.MIJIN_TEST, + ); + + const transaction = + TransactionMapping.createFromDTO(registerNamespaceTransaction.toJSON()) as RegisterNamespaceTransaction; + + expect(transaction.type).to.be.equal(TransactionType.REGISTER_NAMESPACE); + }); +}); diff --git a/test/infrastructure/SerializeTransactionToJSON.spec.ts b/test/infrastructure/SerializeTransactionToJSON.spec.ts index 4b84a54722..34807cb745 100644 --- a/test/infrastructure/SerializeTransactionToJSON.spec.ts +++ b/test/infrastructure/SerializeTransactionToJSON.spec.ts @@ -74,8 +74,8 @@ describe('SerializeTransactionToJSON', () => { const json = accountLinkTransaction.toJSON(); - expect(json.remoteAccountKey).to.be.equal(account.publicKey); - expect(json.linkAction).to.be.equal(LinkAction.Link); + expect(json.transaction.remoteAccountKey).to.be.equal(account.publicKey); + expect(json.transaction.linkAction).to.be.equal(LinkAction.Link); }); it('should create AccountPropertyAddressTransaction', () => { @@ -93,9 +93,9 @@ describe('SerializeTransactionToJSON', () => { const json = addressPropertyTransaction.toJSON(); - expect(json.type).to.be.equal(TransactionType.MODIFY_ACCOUNT_PROPERTY_ADDRESS); - expect(json.propertyType).to.be.equal(PropertyType.AllowAddress); - expect(json.modifications.length).to.be.equal(1); + expect(json.transaction.type).to.be.equal(TransactionType.MODIFY_ACCOUNT_PROPERTY_ADDRESS); + expect(json.transaction.propertyType).to.be.equal(PropertyType.AllowAddress); + expect(json.transaction.modifications.length).to.be.equal(1); }); it('should create AccountPropertyMosaicTransaction', () => { @@ -113,9 +113,9 @@ describe('SerializeTransactionToJSON', () => { const json = mosaicPropertyTransaction.toJSON(); - expect(json.type).to.be.equal(TransactionType.MODIFY_ACCOUNT_PROPERTY_MOSAIC); - expect(json.propertyType).to.be.equal(PropertyType.AllowMosaic); - expect(json.modifications.length).to.be.equal(1); + expect(json.transaction.type).to.be.equal(TransactionType.MODIFY_ACCOUNT_PROPERTY_MOSAIC); + expect(json.transaction.propertyType).to.be.equal(PropertyType.AllowMosaic); + expect(json.transaction.modifications.length).to.be.equal(1); }); it('should create AccountPropertyMosaicTransaction', () => { @@ -133,9 +133,9 @@ describe('SerializeTransactionToJSON', () => { const json = entityTypePropertyTransaction.toJSON(); - expect(json.type).to.be.equal(TransactionType.MODIFY_ACCOUNT_PROPERTY_ENTITY_TYPE); - expect(json.propertyType).to.be.equal(PropertyType.AllowTransaction); - expect(json.modifications.length).to.be.equal(1); + expect(json.transaction.type).to.be.equal(TransactionType.MODIFY_ACCOUNT_PROPERTY_ENTITY_TYPE); + expect(json.transaction.propertyType).to.be.equal(PropertyType.AllowTransaction); + expect(json.transaction.modifications.length).to.be.equal(1); }); it('should create AddressAliasTransaction', () => { @@ -151,8 +151,8 @@ describe('SerializeTransactionToJSON', () => { const json = addressAliasTransaction.toJSON(); - expect(json.type).to.be.equal(TransactionType.ADDRESS_ALIAS); - expect(json.actionType).to.be.equal(AliasActionType.Link); + expect(json.transaction.type).to.be.equal(TransactionType.ADDRESS_ALIAS); + expect(json.transaction.aliasAction).to.be.equal(AliasActionType.Link); }); it('should create MosaicAliasTransaction', () => { @@ -167,8 +167,8 @@ describe('SerializeTransactionToJSON', () => { ); const json = mosaicAliasTransaction.toJSON(); - expect(json.type).to.be.equal(TransactionType.MOSAIC_ALIAS); - expect(json.actionType).to.be.equal(AliasActionType.Link); + expect(json.transaction.type).to.be.equal(TransactionType.MOSAIC_ALIAS); + expect(json.transaction.aliasAction).to.be.equal(AliasActionType.Link); }); @@ -189,11 +189,11 @@ describe('SerializeTransactionToJSON', () => { const json = mosaicDefinitionTransaction.toJSON(); - expect(json.type).to.be.equal(TransactionType.MOSAIC_DEFINITION); - expect(json.mosaicProperties.supplyMutable).to.be.equal(false); - expect(json.mosaicProperties.transferable).to.be.equal(false); - expect(json.mosaicProperties.levyMutable).to.be.equal(false); - expect(json.mosaicProperties.divisibility).to.be.equal(3); + expect(json.transaction.type).to.be.equal(TransactionType.MOSAIC_DEFINITION); + expect(json.transaction.properties.length).to.be.equal(3); + expect(new UInt64(json.transaction.properties[1].value).compact()).to.be.equal(UInt64.fromUint(3).compact()); + expect(new UInt64(json.transaction.properties[2].value).compact()).to.be.equal(UInt64.fromUint(1000).compact()); + expect(new UInt64(json.transaction.properties[2].value).lower).to.be.equal(UInt64.fromUint(1000).lower); }); @@ -209,8 +209,8 @@ describe('SerializeTransactionToJSON', () => { const json = mosaicSupplyChangeTransaction.toJSON(); - expect(json.type).to.be.equal(TransactionType.MOSAIC_SUPPLY_CHANGE); - expect(json.direction).to.be.equal(MosaicSupplyType.Increase); + expect(json.transaction.type).to.be.equal(TransactionType.MOSAIC_SUPPLY_CHANGE); + expect(json.transaction.direction).to.be.equal(MosaicSupplyType.Increase); }); @@ -227,9 +227,9 @@ describe('SerializeTransactionToJSON', () => { const json = transferTransaction.toJSON(); - expect(json.type).to.be.equal(TransactionType.TRANSFER); - expect(json.message.payload).to.be.equal('test-message'); - expect(json.message.type).to.be.equal(0); + expect(json.transaction.type).to.be.equal(TransactionType.TRANSFER); + expect(json.transaction.message.payload).to.be.equal('test-message'); + expect(json.transaction.message.type).to.be.equal(0); }); @@ -248,8 +248,8 @@ describe('SerializeTransactionToJSON', () => { const json = secretLockTransaction.toJSON(); - expect(json.type).to.be.equal(TransactionType.SECRET_LOCK); - expect(json.hashType).to.be.equal(HashType.Op_Sha3_256); + expect(json.transaction.type).to.be.equal(TransactionType.SECRET_LOCK); + expect(json.transaction.hashAlgorithm).to.be.equal(HashType.Op_Sha3_256); }); @@ -265,10 +265,10 @@ describe('SerializeTransactionToJSON', () => { const json = secretProofTransaction.toJSON(); - expect(json.type).to.be.equal(TransactionType.SECRET_PROOF); - expect(json.hashType).to.be.equal(HashType.Op_Sha3_256); - expect(json.secret).to.be.equal(sha3_256.create().update(convert.hexToUint8(proof)).hex()); - expect(json.proof).to.be.equal(proof); + expect(json.transaction.type).to.be.equal(TransactionType.SECRET_PROOF); + expect(json.transaction.hashAlgorithm).to.be.equal(HashType.Op_Sha3_256); + expect(json.transaction.secret).to.be.equal(sha3_256.create().update(convert.hexToUint8(proof)).hex()); + expect(json.transaction.proof).to.be.equal(proof); }); @@ -287,9 +287,9 @@ describe('SerializeTransactionToJSON', () => { const json = modifyMultisigAccountTransaction.toJSON(); - expect(json.type).to.be.equal(TransactionType.MODIFY_MULTISIG_ACCOUNT); - expect(json.minApprovalDelta).to.be.equal(2); - expect(json.minRemovalDelta).to.be.equal(1); + expect(json.transaction.type).to.be.equal(TransactionType.MODIFY_MULTISIG_ACCOUNT); + expect(json.transaction.minApprovalDelta).to.be.equal(2); + expect(json.transaction.minRemovalDelta).to.be.equal(1); }); it('should create AggregatedTransaction - Complete', () => { @@ -309,8 +309,8 @@ describe('SerializeTransactionToJSON', () => { const json = aggregateTransaction.toJSON(); - expect(json.type).to.be.equal(TransactionType.AGGREGATE_COMPLETE); - expect(json.innerTransactions.length).to.be.equal(1); + expect(json.transaction.type).to.be.equal(TransactionType.AGGREGATE_COMPLETE); + expect(json.transaction.transactions.length).to.be.equal(1); }); it('should create AggregatedTransaction - Bonded', () => { @@ -330,8 +330,8 @@ describe('SerializeTransactionToJSON', () => { const json = aggregateTransaction.toJSON(); - expect(json.type).to.be.equal(TransactionType.AGGREGATE_BONDED); - expect(json.innerTransactions.length).to.be.equal(1); + expect(json.transaction.type).to.be.equal(TransactionType.AGGREGATE_BONDED); + expect(json.transaction.transactions.length).to.be.equal(1); }); it('should create LockFundTransaction', () => { @@ -350,8 +350,8 @@ describe('SerializeTransactionToJSON', () => { const json = lockTransaction.toJSON(); - expect(json.type).to.be.equal(TransactionType.LOCK); - expect(json.signedTransaction).to.be.equal(signedTransaction.hash); + expect(json.transaction.type).to.be.equal(TransactionType.LOCK); + expect(json.transaction.hash).to.be.equal(signedTransaction.hash); }); it('should create RegisterNamespaceTransaction - Root', () => { @@ -364,7 +364,7 @@ describe('SerializeTransactionToJSON', () => { const json = registerNamespaceTransaction.toJSON(); - expect(json.type).to.be.equal(TransactionType.REGISTER_NAMESPACE); + expect(json.transaction.type).to.be.equal(TransactionType.REGISTER_NAMESPACE); }); @@ -378,6 +378,6 @@ describe('SerializeTransactionToJSON', () => { const json = registerNamespaceTransaction.toJSON(); - expect(json.type).to.be.equal(TransactionType.REGISTER_NAMESPACE); + expect(json.transaction.type).to.be.equal(TransactionType.REGISTER_NAMESPACE); }); }); From 3d1d1f6d4aeeee632d12785fe0f4fdcc2ca62d5b Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Wed, 3 Apr 2019 16:12:08 +0100 Subject: [PATCH 18/83] Added check in validate cosignatories for modify multisig account (removal) --- src/service/AggregatedTransactionService.ts | 29 ++++++++++++++++--- .../AggregatedTransactionService.spec.ts | 27 +++++++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/service/AggregatedTransactionService.ts b/src/service/AggregatedTransactionService.ts index ccdd72cd3b..7803b48bae 100644 --- a/src/service/AggregatedTransactionService.ts +++ b/src/service/AggregatedTransactionService.ts @@ -20,7 +20,11 @@ import { TransactionMapping } from '../core/utils/TransactionMapping'; import { AccountHttp } from '../infrastructure/AccountHttp'; import { MultisigAccountGraphInfo } from '../model/account/MultisigAccountGraphInfo'; import { AggregateTransaction } from '../model/transaction/AggregateTransaction'; +import { InnerTransaction } from '../model/transaction/InnerTransaction'; +import { ModifyMultisigAccountTransaction } from '../model/transaction/ModifyMultisigAccountTransaction'; +import { MultisigCosignatoryModificationType } from '../model/transaction/MultisigCosignatoryModificationType'; import { SignedTransaction } from '../model/transaction/SignedTransaction'; +import { TransactionType } from '../model/transaction/TransactionType'; /** * Aggregated Transaction service @@ -58,7 +62,7 @@ export class AggregatedTransactionService { mergeMap((_) => _.minApproval !== 0 && _.minRemoval !== 0 ? this.accountHttp.getMultisigAccountGraphInfo(_.account.address) .pipe( - map((graphInfo) => this.validateCosignatories(graphInfo, signers)), + map((graphInfo) => this.validateCosignatories(graphInfo, signers, innerTransaction)), ) : observableOf(true), ), ), @@ -70,9 +74,12 @@ export class AggregatedTransactionService { * Validate cosignatories against multisig Account(s) * @param graphInfo - multisig account graph info * @param cosignatories - array of cosignatories extracted from aggregated transaction + * @param innerTransaction - the inner transaction of the aggregated transaction * @returns {boolean} */ - private validateCosignatories(graphInfo: MultisigAccountGraphInfo, cosignatories: string[]): boolean { + private validateCosignatories(graphInfo: MultisigAccountGraphInfo, + cosignatories: string[], + innerTransaction: InnerTransaction): boolean { /** * Validate cosignatories from bottom level to top */ @@ -80,11 +87,24 @@ export class AggregatedTransactionService { const cosignatoriesReceived = cosignatories; let validationResult = true; + let isMultisigRemoval = false; + + /** + * Check inner transaction. If remove cosigner from multisig account, + * use minRemoval instead of minApproval for cosignatories validation. + */ + if (innerTransaction.type === TransactionType.MODIFY_MULTISIG_ACCOUNT) { + if ((innerTransaction as ModifyMultisigAccountTransaction).modifications + .find((modification) => modification.type === MultisigCosignatoryModificationType.Remove) !== undefined) { + isMultisigRemoval = true; + } + } + sortedKeys.forEach((key) => { const multisigInfo = graphInfo.multisigAccounts.get(key); if (multisigInfo && validationResult) { multisigInfo.forEach((multisig) => { - if (multisig.minApproval >= 1) { + if (multisig.minApproval >= 1 && multisig.minRemoval) { // To make sure it is multisig account const matchedCosignatories = this.compareArrays(cosignatoriesReceived, multisig.cosignatories.map((cosig) => cosig.publicKey)); @@ -93,7 +113,8 @@ export class AggregatedTransactionService { * into the received signatories array for next level validation. * Otherwise return validation failed. */ - if (matchedCosignatories.length >= multisig.minApproval) { + if ((matchedCosignatories.length >= multisig.minApproval && !isMultisigRemoval) || + (matchedCosignatories.length >= multisig.minRemoval && isMultisigRemoval)) { if (cosignatoriesReceived.indexOf(multisig.account.publicKey) === -1) { cosignatoriesReceived.push(multisig.account.publicKey); } diff --git a/test/service/AggregatedTransactionService.spec.ts b/test/service/AggregatedTransactionService.spec.ts index bc0c11f535..8ea1432ca6 100644 --- a/test/service/AggregatedTransactionService.spec.ts +++ b/test/service/AggregatedTransactionService.spec.ts @@ -26,6 +26,9 @@ import { MultisigAccountInfo } from '../../src/model/account/MultisigAccountInfo import {NetworkType} from '../../src/model/blockchain/NetworkType'; import { AggregateTransaction } from '../../src/model/transaction/AggregateTransaction'; import { Deadline } from '../../src/model/transaction/Deadline'; +import { ModifyMultisigAccountTransaction } from '../../src/model/transaction/ModifyMultisigAccountTransaction'; +import { MultisigCosignatoryModification } from '../../src/model/transaction/MultisigCosignatoryModification'; +import { MultisigCosignatoryModificationType } from '../../src/model/transaction/MultisigCosignatoryModificationType'; import { PlainMessage } from '../../src/model/transaction/PlainMessage'; import { TransferTransaction } from '../../src/model/transaction/TransferTransaction'; import { AggregatedTransactionService } from '../../src/service/AggregatedTransactionService'; @@ -177,6 +180,30 @@ describe('AggregatedTransactionService', () => { }); }); + it('should use minRemoval for multisig account validation if inner transaction is modify multisig remove', () => { + const modifyMultisigTransaction = ModifyMultisigAccountTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + 1, + 1, + [new MultisigCosignatoryModification( + MultisigCosignatoryModificationType.Remove, + account1.publicAccount, + )], + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [modifyMultisigTransaction.toAggregate(multisig2.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signWith(account2); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + it('should return correct isComplete status for aggregated complete transaction - none multisig', () => { const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), From 7a420e7cafa33c6d7935fa18b9c53387cc59c548 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Thu, 4 Apr 2019 15:03:56 +0100 Subject: [PATCH 19/83] Fixed bug in create transaction from payload --- .../transaction/CreateTransactionFromPayload.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/infrastructure/transaction/CreateTransactionFromPayload.ts b/src/infrastructure/transaction/CreateTransactionFromPayload.ts index 9b124a33bf..7b73d6f6d0 100644 --- a/src/infrastructure/transaction/CreateTransactionFromPayload.ts +++ b/src/infrastructure/transaction/CreateTransactionFromPayload.ts @@ -376,7 +376,7 @@ const CreateTransaction = (type: number, transactionData: string, networkType: N }), networkType, consignatureArray ? consignatureArray.map((cosignature) => new AggregateTransactionCosignature( - cosignature.substring(0, 64), + cosignature.substring(64, 192), PublicAccount.createFromPublicKey(cosignature.substring(0, 64), networkType), )) : [], ); @@ -400,7 +400,7 @@ const CreateTransaction = (type: number, transactionData: string, networkType: N }), networkType, bondedConsignatureArray ? bondedConsignatureArray.map((cosignature) => new AggregateTransactionCosignature( - cosignature.substring(0, 64), + cosignature.substring(64, 192), PublicAccount.createFromPublicKey(cosignature.substring(0, 64), networkType), )) : [], ); From c37da2d89f59673d88007c1845985af86d36cb5a Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Fri, 5 Apr 2019 19:01:52 +0100 Subject: [PATCH 20/83] Added more test cases and fixed a few bugs --- .../CreateTransactionFromPayload.ts | 4 +- src/service/AggregatedTransactionService.ts | 12 +- .../AggregatedTransactionService.spec.ts | 305 +++++++++++++++++- 3 files changed, 309 insertions(+), 12 deletions(-) diff --git a/src/infrastructure/transaction/CreateTransactionFromPayload.ts b/src/infrastructure/transaction/CreateTransactionFromPayload.ts index 7b73d6f6d0..bf36d24a20 100644 --- a/src/infrastructure/transaction/CreateTransactionFromPayload.ts +++ b/src/infrastructure/transaction/CreateTransactionFromPayload.ts @@ -456,8 +456,8 @@ const parseInnerTransactionFromBinary = (innerTransactionBinary: string): string let innerBinary = innerTransactionBinary; while (innerBinary.length) { - const payloadSize = parseInt(convert.uint8ToHex(convert.hexToUint8(innerTransactionBinary.substring(0, 8)).reverse()), 16) * 2; - const innerTransaction = innerTransactionBinary.substring(8, payloadSize); + const payloadSize = parseInt(convert.uint8ToHex(convert.hexToUint8(innerBinary.substring(0, 8)).reverse()), 16) * 2; + const innerTransaction = innerBinary.substring(8, payloadSize); embeddedTransaction.push(innerTransaction); innerBinary = innerBinary.substring(payloadSize); } diff --git a/src/service/AggregatedTransactionService.ts b/src/service/AggregatedTransactionService.ts index 7803b48bae..75a806bf96 100644 --- a/src/service/AggregatedTransactionService.ts +++ b/src/service/AggregatedTransactionService.ts @@ -15,7 +15,7 @@ */ import {from as observableFrom , Observable, of as observableOf} from 'rxjs'; -import { map, mergeMap} from 'rxjs/operators'; +import { flatMap, map, mergeMap, toArray} from 'rxjs/operators'; import { TransactionMapping } from '../core/utils/TransactionMapping'; import { AccountHttp } from '../infrastructure/AccountHttp'; import { MultisigAccountGraphInfo } from '../model/account/MultisigAccountGraphInfo'; @@ -52,7 +52,6 @@ export class AggregatedTransactionService { if (signedTransaction.signer) { signers.push(signedTransaction.signer); } - return observableFrom(aggregateTransaction.innerTransactions).pipe( mergeMap((innerTransaction) => this.accountHttp.getMultisigAccountInfo(innerTransaction.signer.address) .pipe( @@ -63,11 +62,16 @@ export class AggregatedTransactionService { this.accountHttp.getMultisigAccountGraphInfo(_.account.address) .pipe( map((graphInfo) => this.validateCosignatories(graphInfo, signers, innerTransaction)), - ) : observableOf(true), + ) : observableOf(signers.find((s) => s === _.account.publicKey ) !== undefined), ), ), ), - ); + toArray(), + ).pipe( + flatMap((results) => { + return observableOf(results.every((isComplete) => isComplete)); + }), + ); } /** diff --git a/test/service/AggregatedTransactionService.spec.ts b/test/service/AggregatedTransactionService.spec.ts index 8ea1432ca6..23470cb2a1 100644 --- a/test/service/AggregatedTransactionService.spec.ts +++ b/test/service/AggregatedTransactionService.spec.ts @@ -54,6 +54,7 @@ describe('AggregatedTransactionService', () => { * Test accounts: * Multisig1 (1/1): Account2, Account3 * Multisig2 (2/1): Account1, Multisig1 + * Multisig3 (2/2): Account2, Account3 * Stranger Account: Account4 */ @@ -64,6 +65,8 @@ describe('AggregatedTransactionService', () => { const multisig2 = Account.createFromPrivateKey('22A1D67F8519D1A45BD7116600BB6E857786E816FE0B45E4C5B9FFF3D64BC177', NetworkType.MIJIN_TEST); + const multisig3 = Account.createFromPrivateKey('5E7812AB0E709ABC45466034E1A209099F6A12C4698748A63CDCAA9B0DDE1DBD', + NetworkType.MIJIN_TEST); const account2 = Account.createFromPrivateKey('A4D410270E01CECDCDEADCDE32EC79C8D9CDEA4DCD426CB1EB666EFEF148FBCE', NetworkType.MIJIN_TEST); const account3 = Account.createFromPrivateKey('336AB45EE65A6AFFC0E7ADC5342F91E34BACA0B901A1D9C876FA25A1E590077E', @@ -80,14 +83,30 @@ describe('AggregatedTransactionService', () => { .thenReturn(observableOf(givenAccount4Info())); when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(multisig2.address))) .thenReturn(observableOf(givenMultisig2AccountInfo())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(multisig3.address))) + .thenReturn(observableOf(givenMultisig3AccountInfo())); when(mockedAccountHttp.getMultisigAccountGraphInfo(deepEqual(multisig2.address))) .thenReturn(observableOf(givenMultisig2AccountGraphInfo())); + when(mockedAccountHttp.getMultisigAccountGraphInfo(deepEqual(multisig3.address))) + .thenReturn(observableOf(givenMultisig3AccountGraphInfo())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account2.address))) + .thenReturn(observableOf(givenAccount2Info())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account3.address))) + .thenReturn(observableOf(givenAccount3Info())); const accountHttp = instance(mockedAccountHttp); aggregatedTransactionService = new AggregatedTransactionService(accountHttp); }); it('should return isComplete: true for aggregated complete transaction - 2 levels Multisig', () => { + /** + * MLMA + * Alice (account1): normal account + * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) + * Charles (multisig1) - Multisig 1-1 (account2 || account3) + * Given signatories: Account1 && Account4 + * Expecting complete as Bob needs 2 signatures (account1 && (account2 || account3)) + */ const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), @@ -109,6 +128,14 @@ describe('AggregatedTransactionService', () => { }); it('should return isComplete: false for aggregated complete transaction - 2 levels Multisig', () => { + /** + * MLMA + * Alice (account1): normal account + * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) + * Charles (multisig1) - Multisig 1-1 (account2 || account3) + * Given signatories: Account1 && Account4 + * Expecting incomplete as Bob needs 2 signatures (account1 && (account2 || account3)) but only got account1 + */ const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), @@ -130,6 +157,14 @@ describe('AggregatedTransactionService', () => { }); it('should return isComplete: false for aggregated complete transaction - 2 levels Multisig', () => { + /** + * MLMA + * Alice (account1): normal account + * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) + * Charles (multisig1) - Multisig 1-1 (account2 || account3) + * Given signatories: Account1 && Account4 + * Expecting incomplete as Bob needs 2 signatures (account1 && (account2 || account3)) but got account4 + */ const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), @@ -151,9 +186,18 @@ describe('AggregatedTransactionService', () => { }); it('should return correct isComplete status for aggregated complete transaction - 2 levels Multisig, multi inner transaction', () => { + /** + * MLMA - with multiple transaction + * Alice (account1): normal account + * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) + * Charles (multisig1) - Multisig 1-1 (account2 || account3) + * An extra inner transaction to account4 (just to increase the complexity) + * Given signatories: Account1 && Account4 + * Expecting incomplete as Bob needs 2 signatures (account1 && (account2 || account3)) + */ const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), - Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + account2.address, [], PlainMessage.create('test-message'), NetworkType.MIJIN_TEST, @@ -161,7 +205,7 @@ describe('AggregatedTransactionService', () => { const transferTransaction2 = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), - Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + account2.address, [], PlainMessage.create('test-message'), NetworkType.MIJIN_TEST, @@ -170,17 +214,60 @@ describe('AggregatedTransactionService', () => { const aggregateTransaction = AggregateTransaction.createComplete( Deadline.create(), [transferTransaction.toAggregate(multisig2.publicAccount), - transferTransaction.toAggregate(account4.publicAccount)], + transferTransaction2.toAggregate(account4.publicAccount)], NetworkType.MIJIN_TEST, []); + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); - const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account2]); + it('should return correct isComplete status for aggregated complete transaction - 2 levels Multisig, multi inner transaction', () => { + /** + * MLMA - with multiple transaction + * Alice (account1): normal account + * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) + * Charles (multisig1) - Multisig 1-1 (account2 || account3) + * An extra inner transaction to account4 (just to increase the complexity) + * Given signatories: Account1 && Account4 && Account2 + * Expecting complete + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account2.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const transferTransaction2 = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account2.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount), + transferTransaction2.toAggregate(account4.publicAccount)], + NetworkType.MIJIN_TEST, + []); + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4, account2]); aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); it('should use minRemoval for multisig account validation if inner transaction is modify multisig remove', () => { + /** + * If the inner transaction is issued to a multisig account + * and the inner transaction itself is a ModifyMultiSigAccountTransaction - Removal + * The validator should use minRemoval value rather than minApproval value + * to determine if the act is complete or not + */ const modifyMultisigTransaction = ModifyMultisigAccountTransaction.create( Deadline.create(1, ChronoUnit.HOURS), 1, @@ -197,14 +284,19 @@ describe('AggregatedTransactionService', () => { [modifyMultisigTransaction.toAggregate(multisig2.publicAccount)], NetworkType.MIJIN_TEST, []); - const signedTransaction = aggregateTransaction.signWith(account2); aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); - it('should return correct isComplete status for aggregated complete transaction - none multisig', () => { + it('should return correct isComplete status (false) for aggregated complete transaction - none multisig', () => { + /** + * If the inner transaction is issued to a multisig account + * and the inner transaction itself is a ModifyMultiSigAccountTransaction - Removal + * The validator should use minRemoval value rather than minApproval value + * to determine if the act is complete or not + */ const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), @@ -220,11 +312,176 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signWith(account1); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + + it('should return correct isComplete status (true) for aggregated complete transaction - none multisig', () => { + /** + * ACT + * Alice (account1): normal account + * Bob (account4) - normal account + * Alice initiate the transaction + * Bob sign + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(account4.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signWith(account4); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + it('should return correct isComplete status TRUE - multiple normal account', () => { + /** + * ACT + * Alice: account1 + * Bog: account4 + * An escrow contract is signed by all the participants (normal accounts) + * Given Alice defined the following escrow contract: + * | sender | recipient | type | data | + * | Alice | Bob | send-an-asset | 1 concert.ticket | + * | Bob | Alice | send-an-asset | 20 euros | + * And Bob signs the contract + * And Alice signs the contract + * Then the contract should appear as complete + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account1.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const transferTransaction2 = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account4.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(account4.publicAccount), + transferTransaction2.toAggregate(account1.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); + it('should return correct isComplete status FALSE - multiple normal account', () => { + /** + * ACT + * Alice: account1 + * Bog: account4 + * An escrow contract is signed by all the participants (normal accounts) + * Given Alice defined the following escrow contract: + * | sender | recipient | type | data | + * | Alice | Bob | send-an-asset | 1 concert.ticket | + * | Bob | Alice | send-an-asset | 20 euros | + * And Alice signs the contract + * Then the contract should appear as incomplete + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account1.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const transferTransaction2 = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account4.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(account4.publicAccount), + transferTransaction2.toAggregate(account1.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, []); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + + it('should return correct isComplete status TRUE - multisig Single Level', () => { + /** + * ACT - Multisig single level + * Alice (account1): initiate an transfer to Bob + * Bob (multisig3): is a 2/2 multisig account (account2 && account3) + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account4.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig3.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account2, [account3]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + it('should return correct isComplete status FALSE - multisig Single Level', () => { + /** + * ACT - Multisig single level + * Alice (account1): initiate an transfer to Bob + * Bob (multisig3): is a 2/2 multisig account (account2 && account3) + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account4.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig3.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account2, []); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + function givenMultisig2AccountInfo(): MultisigAccountInfo { return new MultisigAccountInfo(multisig2.publicAccount, 2, 1, @@ -233,6 +490,14 @@ describe('AggregatedTransactionService', () => { [], ); } + function givenMultisig3AccountInfo(): MultisigAccountInfo { + return new MultisigAccountInfo(multisig3.publicAccount, + 2, 2, + [account2.publicAccount, + account3.publicAccount], + [], + ); + } function givenAccount1Info(): MultisigAccountInfo { return new MultisigAccountInfo(account1.publicAccount, @@ -241,6 +506,22 @@ describe('AggregatedTransactionService', () => { [multisig2.publicAccount], ); } + function givenAccount2Info(): MultisigAccountInfo { + return new MultisigAccountInfo(account2.publicAccount, + 0, 0, + [], + [multisig2.publicAccount, + multisig3.publicAccount], + ); + } + function givenAccount3Info(): MultisigAccountInfo { + return new MultisigAccountInfo(account3.publicAccount, + 0, 0, + [], + [multisig2.publicAccount, + multisig3.publicAccount], + ); + } function givenAccount4Info(): MultisigAccountInfo { return new MultisigAccountInfo(account4.publicAccount, 0, 0, @@ -266,4 +547,16 @@ describe('AggregatedTransactionService', () => { return new MultisigAccountGraphInfo(map); } + function givenMultisig3AccountGraphInfo(): MultisigAccountGraphInfo { + const map = new Map(); + map.set(0, [new MultisigAccountInfo(multisig3.publicAccount, + 2, 2, + [account2.publicAccount, + account3.publicAccount], + [], + )]); + + return new MultisigAccountGraphInfo(map); + } + }); From 31a96e2efb4c750246f9b6a0d98b0f03b5e6df62 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Fri, 5 Apr 2019 19:57:12 +0100 Subject: [PATCH 21/83] added extractMessage methods. added extra check on recipient.id --- .../transaction/CreateTransactionFromDTO.ts | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/infrastructure/transaction/CreateTransactionFromDTO.ts b/src/infrastructure/transaction/CreateTransactionFromDTO.ts index ad28fd9bba..a7a216c0b8 100644 --- a/src/infrastructure/transaction/CreateTransactionFromDTO.ts +++ b/src/infrastructure/transaction/CreateTransactionFromDTO.ts @@ -118,16 +118,6 @@ export const CreateTransactionFromDTO = (transactionDTO): Transaction => { */ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Transaction => { if (transactionDTO.type === TransactionType.TRANSFER) { - /** - * Check if message is encoded (from DTO) or is raw (from JSON) - */ - let message = EmptyMessage; - if (transactionDTO.message !== undefined && convert.isHexString(transactionDTO.message.payload)) { - message = PlainMessage.createFromDTO(transactionDTO.message.payload); - } else { - message = PlainMessage.create(transactionDTO.message.payload); - } - return new TransferTransaction( extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), @@ -135,7 +125,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr new UInt64(transactionDTO.fee || [0, 0]), extractRecipient(transactionDTO.recipient), extractMosaics(transactionDTO.mosaics), - message, + extractMessage(transactionDTO.message.payload), transactionDTO.signature, transactionDTO.signer ? PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)) : undefined, @@ -390,9 +380,9 @@ const extractRecipient = (recipient: any): Address | NamespaceId => { } else if (typeof recipient === 'object') { // Is JSON object if (recipient.hasOwnProperty('address')) { return Address.createFromRawAddress(recipient.address); + } else if (recipient.hasOwnProperty('id')) { + return new NamespaceId(recipient.id); } - - return new NamespaceId(recipient.id); } throw new Error(`Recipient: ${recipient} type is not recognised`); }; @@ -428,3 +418,20 @@ const extractMosaics = (mosaics: any): Mosaic[] => { return new Mosaic(new MosaicId(mosaicDTO.id), new UInt64(mosaicDTO.amount)); }); }; + +/** + * Extract message from either JSON payload (unencoded) or DTO (encoded) + * + * @param message - message payload + * @return {PlainMessage} + */ +const extractMessage = (message: any): PlainMessage => { + let plainMessage = EmptyMessage; + if (message !== undefined && convert.isHexString(message)) { + plainMessage = PlainMessage.createFromDTO(message); + } else { + plainMessage = PlainMessage.create(message); + } + + return plainMessage; +}; From c513cc5369cfae7981e31c45326971df23aa9c01 Mon Sep 17 00:00:00 2001 From: dnice Date: Mon, 8 Apr 2019 13:32:20 +0800 Subject: [PATCH 22/83] add EncryptedMessage class --- src/model/account/Account.ts | 20 ++++++ src/model/transaction/EncryptedMessage.ts | 62 +++++++++++++++++++ .../transaction/EncryptedMessage.spec.ts | 58 +++++++++++++++++ 3 files changed, 140 insertions(+) create mode 100644 src/model/transaction/EncryptedMessage.ts create mode 100644 test/model/transaction/EncryptedMessage.spec.ts diff --git a/src/model/account/Account.ts b/src/model/account/Account.ts index 7cacc19d04..b5a1dce8c4 100644 --- a/src/model/account/Account.ts +++ b/src/model/account/Account.ts @@ -19,6 +19,8 @@ import {NetworkType} from '../blockchain/NetworkType'; import {AggregateTransaction} from '../transaction/AggregateTransaction'; import {CosignatureSignedTransaction} from '../transaction/CosignatureSignedTransaction'; import {CosignatureTransaction} from '../transaction/CosignatureTransaction'; +import {EncryptedMessage} from '../transaction/EncryptedMessage'; +import {PlainMessage} from '../transaction/PlainMessage'; import {SignedTransaction} from '../transaction/SignedTransaction'; import {Transaction} from '../transaction/Transaction'; import {Address} from './Address'; @@ -79,7 +81,25 @@ export class Account { const address = Address.createFromPublicKey(convert.uint8ToHex(keyPair.publicKey), networkType); return new Account(address, keyPair); } + /** + * Create a new encrypted Message + * @param message + * @param recipientPublicAccount + * @returns {EncryptedMessage} + */ + public encryptMessage(message: string, recipientPublicAccount: PublicAccount): EncryptedMessage { + return EncryptedMessage.create(message, recipientPublicAccount, this.privateKey); + } + /** + * Decrypts an encrypted message + * @param encryptedMessage + * @param recipientPublicAccount + * @returns {PlainMessage} + */ + public decryptMessage(encryptedMessage: EncryptedMessage, recipientPublicAccount: PublicAccount): PlainMessage { + return EncryptedMessage.decrypt(encryptedMessage, this.privateKey, recipientPublicAccount); + } /** * Account public key. * @return {string} diff --git a/src/model/transaction/EncryptedMessage.ts b/src/model/transaction/EncryptedMessage.ts new file mode 100644 index 0000000000..847aefd170 --- /dev/null +++ b/src/model/transaction/EncryptedMessage.ts @@ -0,0 +1,62 @@ +/* + * Copyright 2019 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {crypto} from 'nem2-library'; +import {PublicAccount} from '../account/PublicAccount'; +import {Message} from './Message'; +import {PlainMessage} from './PlainMessage'; + +/** + * Encrypted Message model + */ +export class EncryptedMessage extends Message { + + public readonly recipientPublicAccount?: PublicAccount; + + constructor(payload: string, + recipientPublicAccount?: PublicAccount){ + super(1,payload); + this.recipientPublicAccount = recipientPublicAccount; + } + + /** + * + * @param message + * @param recipientPublicAccount + * @param privateKey + */ + public static create(message: string, recipientPublicAccount: PublicAccount, privateKey) { + return new EncryptedMessage(crypto.encode(privateKey, recipientPublicAccount.publicKey, message), recipientPublicAccount); + } + + /** + * + * @param payload + */ + public static createFromDTO(payload: string): EncryptedMessage { + return new EncryptedMessage(payload); + } + + /** + * + * @param encryptMessage + * @param privateKey + * @param recipientPublicAccount + */ + public static decrypt(encryptMessage: EncryptedMessage, privateKey, recipientPublicAccount: PublicAccount): PlainMessage { + return new PlainMessage(Message.decodeHex(crypto.decode(privateKey, recipientPublicAccount.publicKey, encryptMessage.payload))); + } +} diff --git a/test/model/transaction/EncryptedMessage.spec.ts b/test/model/transaction/EncryptedMessage.spec.ts new file mode 100644 index 0000000000..1eee4e9c9a --- /dev/null +++ b/test/model/transaction/EncryptedMessage.spec.ts @@ -0,0 +1,58 @@ +/* + * Copyright 2019 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {expect} from 'chai'; +import {Account} from '../../../src/model/account/Account'; + +import {PublicAccount} from '../../../src/model/account/PublicAccount'; +import {NetworkType} from '../../../src/model/blockchain/NetworkType'; +import {EncryptedMessage} from '../../../src/model/transaction/EncryptedMessage'; + + +describe('EncryptedMessage', () => { + + const accountInformation = { + address: 'SCTVW23D2MN5VE4AQ4TZIDZENGNOZXPRPRLIKCF2', + privateKey: '26b64cb10f005e5988a36744ca19e20d835ccc7c105aaa5f3b212da593180930'.toUpperCase(), + publicKey: 'c2f93346e27ce6ad1a9f8f5e3066f8326593a406bdf357acb041e2f9ab402efe'.toUpperCase(), + }; + let recipientPublicAccount:PublicAccount; + + before(() => { + recipientPublicAccount = PublicAccount.createFromPublicKey(accountInformation.publicKey,NetworkType.MIJIN_TEST); + }); + + it("should create a encrypted message from a DTO", () => { + const encryptedMessage = EncryptedMessage.createFromDTO("test transaction"); + expect(encryptedMessage.payload).to.be.equal("test transaction"); + }); + + it("should return encrypted message dto", () => { + const account = Account.createFromPrivateKey(accountInformation.privateKey,NetworkType.MIJIN_TEST); + const publicAccount = PublicAccount.createFromPublicKey(account.publicKey,NetworkType.MIJIN_TEST); + const encryptedMessage = account.encryptMessage("test transaction", publicAccount); + const plainMessage = account.decryptMessage(encryptedMessage, publicAccount); + expect(plainMessage.payload).to.be.equal("test transaction"); + }); + + it("should create an encrypted message from a DTO and decrypt it", () => { + const account = Account.createFromPrivateKey(accountInformation.privateKey,NetworkType.MIJIN_TEST); + const publicAccount = PublicAccount.createFromPublicKey("0414fe7647ec008e533aac98a4bf1c5fbf1d236c75b81fdadf1f5d1042fdd2ff",NetworkType.MIJIN_TEST); + const encryptMessage = EncryptedMessage.createFromDTO("02bb332c0fdd445455117882b2bec5e49f5713860d6b34650d0f769159d021a27518ea03539af8913231b9f80f600daae9291bb100a6d32e36b52a6c457fea287ca9942a32368618fe1fd0c185dbf834"); + const plainMessage = account.decryptMessage(encryptMessage, publicAccount); + expect(plainMessage.payload).to.be.equal("test transaction"); + }); +}); From 90b3d8c9fe985fa41410063a18a8a5cea9ecf742 Mon Sep 17 00:00:00 2001 From: dnice Date: Mon, 8 Apr 2019 15:33:04 +0800 Subject: [PATCH 23/83] add MessageType --- src/model/transaction/EncryptedMessage.ts | 3 ++- src/model/transaction/MessageType.ts | 25 +++++++++++++++++++++++ src/model/transaction/PlainMessage.ts | 3 ++- 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 src/model/transaction/MessageType.ts diff --git a/src/model/transaction/EncryptedMessage.ts b/src/model/transaction/EncryptedMessage.ts index 847aefd170..443af6c125 100644 --- a/src/model/transaction/EncryptedMessage.ts +++ b/src/model/transaction/EncryptedMessage.ts @@ -17,6 +17,7 @@ import {crypto} from 'nem2-library'; import {PublicAccount} from '../account/PublicAccount'; import {Message} from './Message'; +import {MessageType} from './MessageType'; import {PlainMessage} from './PlainMessage'; /** @@ -28,7 +29,7 @@ export class EncryptedMessage extends Message { constructor(payload: string, recipientPublicAccount?: PublicAccount){ - super(1,payload); + super(MessageType.EncryptedMessage,payload); this.recipientPublicAccount = recipientPublicAccount; } diff --git a/src/model/transaction/MessageType.ts b/src/model/transaction/MessageType.ts new file mode 100644 index 0000000000..b37220a9cf --- /dev/null +++ b/src/model/transaction/MessageType.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2018 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * The supply type. Supported supply types are: + * 0: Increase in supply. + * 1: Decrease in supply. + */ +export enum MessageType { + PlainMessage = 0, + EncryptedMessage = 1, +} diff --git a/src/model/transaction/PlainMessage.ts b/src/model/transaction/PlainMessage.ts index 2f611ca473..ee7f2f44de 100644 --- a/src/model/transaction/PlainMessage.ts +++ b/src/model/transaction/PlainMessage.ts @@ -15,6 +15,7 @@ */ import {Message} from './Message'; +import {MessageType} from './MessageType'; /** * The plain message model defines a plain string. When sending it to the network we transform the payload to hex-string. @@ -40,7 +41,7 @@ export class PlainMessage extends Message { * @param payload */ constructor(payload: string) { - super(0, payload); + super(MessageType.PlainMessage, payload); } } From 6da136eee63cda1010c4fded81f768f1093ece72 Mon Sep 17 00:00:00 2001 From: dnice Date: Mon, 8 Apr 2019 15:33:52 +0800 Subject: [PATCH 24/83] add MessageType --- src/model/transaction/MessageType.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/model/transaction/MessageType.ts b/src/model/transaction/MessageType.ts index b37220a9cf..a43da5d6b1 100644 --- a/src/model/transaction/MessageType.ts +++ b/src/model/transaction/MessageType.ts @@ -1,5 +1,5 @@ /* - * Copyright 2018 NEM + * Copyright 2019 NEM * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,9 +15,9 @@ */ /** - * The supply type. Supported supply types are: - * 0: Increase in supply. - * 1: Decrease in supply. + * The Message type. Supported supply types are: + * 0: PlainMessage + * 1: EncryptedMessage. */ export enum MessageType { PlainMessage = 0, From 2260b5f2a55a2ec26d0a5314ec2eff91f19f92a8 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 11:10:16 +0100 Subject: [PATCH 25/83] Renamed PlainMessage.createFromDTO to createFromPayload --- src/infrastructure/transaction/CreateTransactionFromDTO.ts | 2 +- src/infrastructure/transaction/CreateTransactionFromPayload.ts | 2 +- src/model/transaction/PlainMessage.ts | 2 +- test/model/transaction/PlainMessage.spec.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/infrastructure/transaction/CreateTransactionFromDTO.ts b/src/infrastructure/transaction/CreateTransactionFromDTO.ts index a7a216c0b8..d6dfc38312 100644 --- a/src/infrastructure/transaction/CreateTransactionFromDTO.ts +++ b/src/infrastructure/transaction/CreateTransactionFromDTO.ts @@ -428,7 +428,7 @@ const extractMosaics = (mosaics: any): Mosaic[] => { const extractMessage = (message: any): PlainMessage => { let plainMessage = EmptyMessage; if (message !== undefined && convert.isHexString(message)) { - plainMessage = PlainMessage.createFromDTO(message); + plainMessage = PlainMessage.createFromPayload(message); } else { plainMessage = PlainMessage.create(message); } diff --git a/src/infrastructure/transaction/CreateTransactionFromPayload.ts b/src/infrastructure/transaction/CreateTransactionFromPayload.ts index e8b66453ff..af8bd12125 100644 --- a/src/infrastructure/transaction/CreateTransactionFromPayload.ts +++ b/src/infrastructure/transaction/CreateTransactionFromPayload.ts @@ -279,7 +279,7 @@ const CreateTransaction = (type: number, transactionData: string, networkType: N new MosaicId(UInt64.fromHex(reverse(mosaic.substring(0, 16))).toDTO()), UInt64.fromHex(reverse(mosaic.substring(16))), )) : [], - PlainMessage.createFromDTO(transferMessage), + PlainMessage.createFromPayload(transferMessage), networkType, ); case TransactionType.SECRET_LOCK: diff --git a/src/model/transaction/PlainMessage.ts b/src/model/transaction/PlainMessage.ts index 2f611ca473..82eb339edc 100644 --- a/src/model/transaction/PlainMessage.ts +++ b/src/model/transaction/PlainMessage.ts @@ -31,7 +31,7 @@ export class PlainMessage extends Message { /** * @internal */ - public static createFromDTO(payload: string): PlainMessage { + public static createFromPayload(payload: string): PlainMessage { return new PlainMessage(this.decodeHex(payload)); } diff --git a/test/model/transaction/PlainMessage.spec.ts b/test/model/transaction/PlainMessage.spec.ts index 44835830ed..508dee9fe9 100644 --- a/test/model/transaction/PlainMessage.spec.ts +++ b/test/model/transaction/PlainMessage.spec.ts @@ -31,7 +31,7 @@ describe('PlainMessage', () => { it('should createComplete message from payload with static method', () => { const payload = '746573742D6D657373616765'; - const message = PlainMessage.createFromDTO(payload); + const message = PlainMessage.createFromPayload(payload); expect(message.payload).to.be.equal('test-message'); }); From dbb9cc1ebc405bed58b19a2e8a83c4602ea9805c Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 13:22:33 +0100 Subject: [PATCH 26/83] Added #109 Make duration optiuon for mosaic definition --- src/model/mosaic/MosaicInfo.ts | 2 +- src/model/mosaic/MosaicProperties.ts | 14 ++++++++++---- .../transaction/MosaicDefinitionTransaction.ts | 4 ++-- test/core/utils/TransactionMapping.spec.ts | 4 ++-- .../MosaicDefinitionTransaction.spec.ts | 8 ++++---- 5 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/model/mosaic/MosaicInfo.ts b/src/model/mosaic/MosaicInfo.ts index 162e6d7238..999d044e48 100644 --- a/src/model/mosaic/MosaicInfo.ts +++ b/src/model/mosaic/MosaicInfo.ts @@ -81,7 +81,7 @@ export class MosaicInfo { * Mosaic duration * @returns {UInt64} */ - public get duration(): UInt64 { + public get duration(): UInt64 | undefined { return this.properties.duration; } diff --git a/src/model/mosaic/MosaicProperties.ts b/src/model/mosaic/MosaicProperties.ts index 9d56a35fac..399c31c622 100644 --- a/src/model/mosaic/MosaicProperties.ts +++ b/src/model/mosaic/MosaicProperties.ts @@ -59,8 +59,9 @@ export class MosaicProperties { /** * The duration in blocks a mosaic will be available. * After the duration finishes mosaic is inactive and can be renewed. + * Duration is optional when defining the mosaic */ - public readonly duration: UInt64) { + public readonly duration?: UInt64) { let binaryFlags = '00' + (flags.lower >>> 0).toString(2); binaryFlags = binaryFlags.substr(binaryFlags.length - 3, 3); this.supplyMutable = binaryFlags[2] === '1'; @@ -78,7 +79,7 @@ export class MosaicProperties { transferable: boolean, levyMutable: boolean, divisibility: number, - duration: UInt64, + duration?: UInt64, }) { const flags = (params.supplyMutable ? 1 : 0) + (params.transferable ? 2 : 0) + (params.levyMutable ? 4 : 0); return new MosaicProperties(UInt64.fromUint(flags), params.divisibility, params.duration); @@ -88,12 +89,17 @@ export class MosaicProperties { * Create DTO object */ toDTO() { - return { + const dto = { supplyMutable: this.supplyMutable, transferable: this.transferable, levyMutable: this.levyMutable, divisibility: this.divisibility, - duration: this.duration.toDTO(), }; + + if (this.duration) { + Object.assign(dto, {duration: this.duration.toDTO()}); + } + + return dto; } } diff --git a/src/model/transaction/MosaicDefinitionTransaction.ts b/src/model/transaction/MosaicDefinitionTransaction.ts index 3adaf444a4..f1097a32d4 100644 --- a/src/model/transaction/MosaicDefinitionTransaction.ts +++ b/src/model/transaction/MosaicDefinitionTransaction.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { +import { MosaicCreationTransaction as MosaicDefinitionTransactionLibrary, mosaicId as mosaicIdLibrary, VerifiableTransaction, @@ -106,7 +106,7 @@ export class MosaicDefinitionTransaction extends Transaction { .addFee(this.fee.toDTO()) .addVersion(this.versionToDTO()) .addDivisibility(this.mosaicProperties.divisibility) - .addDuration(this.mosaicProperties.duration.toDTO()) + .addDuration(this.mosaicProperties.duration ? this.mosaicProperties.duration.toDTO() : []) .addNonce(this.nonce.toDTO()) .addMosaicId(this.mosaicId.id.toDTO()); diff --git a/test/core/utils/TransactionMapping.spec.ts b/test/core/utils/TransactionMapping.spec.ts index 6091705a33..e2605a6b0c 100644 --- a/test/core/utils/TransactionMapping.spec.ts +++ b/test/core/utils/TransactionMapping.spec.ts @@ -191,8 +191,8 @@ describe('TransactionMapping', () => { const transaction = TransactionMapping.createFromPayload(signedTransaction.payload) as MosaicDefinitionTransaction; - expect(transaction.mosaicProperties.duration.lower).to.be.equal(1000); - expect(transaction.mosaicProperties.duration.higher).to.be.equal(0); + expect(transaction.mosaicProperties.duration!.lower).to.be.equal(1000); + expect(transaction.mosaicProperties.duration! .higher).to.be.equal(0); expect(transaction.mosaicProperties.divisibility).to.be.equal(3); expect(transaction.mosaicProperties.supplyMutable).to.be.equal(false); expect(transaction.mosaicProperties.transferable).to.be.equal(false); diff --git a/test/model/transaction/MosaicDefinitionTransaction.spec.ts b/test/model/transaction/MosaicDefinitionTransaction.spec.ts index 41799026bc..3b575aa758 100644 --- a/test/model/transaction/MosaicDefinitionTransaction.spec.ts +++ b/test/model/transaction/MosaicDefinitionTransaction.spec.ts @@ -48,8 +48,8 @@ describe('MosaicDefinitionTransaction', () => { NetworkType.MIJIN_TEST, ); - expect(mosaicDefinitionTransaction.mosaicProperties.duration.lower).to.be.equal(1000); - expect(mosaicDefinitionTransaction.mosaicProperties.duration.higher).to.be.equal(0); + expect(mosaicDefinitionTransaction.mosaicProperties.duration!.lower).to.be.equal(1000); + expect(mosaicDefinitionTransaction.mosaicProperties.duration!.higher).to.be.equal(0); expect(mosaicDefinitionTransaction.mosaicProperties.divisibility).to.be.equal(3); expect(mosaicDefinitionTransaction.mosaicProperties.supplyMutable).to.be.equal(true); expect(mosaicDefinitionTransaction.mosaicProperties.transferable).to.be.equal(true); @@ -80,8 +80,8 @@ describe('MosaicDefinitionTransaction', () => { NetworkType.MIJIN_TEST, ); - expect(mosaicDefinitionTransaction.mosaicProperties.duration.lower).to.be.equal(1000); - expect(mosaicDefinitionTransaction.mosaicProperties.duration.higher).to.be.equal(0); + expect(mosaicDefinitionTransaction.mosaicProperties.duration!.lower).to.be.equal(1000); + expect(mosaicDefinitionTransaction.mosaicProperties.duration!.higher).to.be.equal(0); expect(mosaicDefinitionTransaction.mosaicProperties.divisibility).to.be.equal(3); expect(mosaicDefinitionTransaction.mosaicProperties.supplyMutable).to.be.equal(false); expect(mosaicDefinitionTransaction.mosaicProperties.transferable).to.be.equal(false); From b7989c445418990157a48b708990e135675c1b0d Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 13:24:43 +0100 Subject: [PATCH 27/83] Fixed bug on mosaic nounce --- src/infrastructure/transaction/SerializeTransactionToJSON.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/infrastructure/transaction/SerializeTransactionToJSON.ts b/src/infrastructure/transaction/SerializeTransactionToJSON.ts index 74133174e2..dac42aa70f 100644 --- a/src/infrastructure/transaction/SerializeTransactionToJSON.ts +++ b/src/infrastructure/transaction/SerializeTransactionToJSON.ts @@ -107,7 +107,7 @@ export const SerializeTransactionToJSON = (transaction: Transaction): any => { }; case TransactionType.MOSAIC_DEFINITION: return { - nonce: (transaction as MosaicDefinitionTransaction).nonce.toDTO(), + nonce: (transaction as MosaicDefinitionTransaction).nonce, mosaicId: (transaction as MosaicDefinitionTransaction).mosaicId.toDTO(), properties: (transaction as MosaicDefinitionTransaction).mosaicProperties.toDTO(), }; From 295bbff0643f47eb3ad247abb5e27311f2b46fa8 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 16:21:51 +0100 Subject: [PATCH 28/83] made mosaic properties duration option to all references --- .../transaction/CreateTransactionFromDTO.ts | 2 +- .../CreateTransactionFromPayload.ts | 2 +- .../transaction/SerializeTransactionToJSON.ts | 2 +- test/core/utils/TransactionMapping.spec.ts | 25 ++++++++++++++++ .../SerializeTransactionToJSON.spec.ts | 24 +++++++++++++++ test/model/mosaic/MosaicInfo.spec.ts | 27 +++++++++++++++++ test/model/mosaic/MosaicProperties.spec.ts | 16 ++++++++++ .../MosaicDefinitionTransaction.spec.ts | 30 ++++++++++++++++++- 8 files changed, 124 insertions(+), 4 deletions(-) diff --git a/src/infrastructure/transaction/CreateTransactionFromDTO.ts b/src/infrastructure/transaction/CreateTransactionFromDTO.ts index 5bd0a420d1..84b25fcb42 100644 --- a/src/infrastructure/transaction/CreateTransactionFromDTO.ts +++ b/src/infrastructure/transaction/CreateTransactionFromDTO.ts @@ -157,7 +157,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr new MosaicProperties( new UInt64(transactionDTO.properties[0].value), (new UInt64(transactionDTO.properties[1].value)).compact(), - new UInt64(transactionDTO.properties.length === 3 ? transactionDTO.properties[2].value : [0, 0]), + new UInt64(transactionDTO.properties.length === 3 ? transactionDTO.properties[2].value : undefined), ), transactionDTO.signature, PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)), diff --git a/src/infrastructure/transaction/CreateTransactionFromPayload.ts b/src/infrastructure/transaction/CreateTransactionFromPayload.ts index e8b66453ff..15dbd84487 100644 --- a/src/infrastructure/transaction/CreateTransactionFromPayload.ts +++ b/src/infrastructure/transaction/CreateTransactionFromPayload.ts @@ -224,7 +224,7 @@ const CreateTransaction = (type: number, transactionData: string, networkType: N transferable: (flags & 2) === 2, levyMutable: (flags & 4) === 4, divisibility: parseInt(convert.uint8ToHex(convert.hexToUint8(divisibility).reverse()), 16), - duration: UInt64.fromHex(reverse(duration)), + duration: duration ? UInt64.fromHex(reverse(duration)) : undefined, }), networkType, ); diff --git a/src/infrastructure/transaction/SerializeTransactionToJSON.ts b/src/infrastructure/transaction/SerializeTransactionToJSON.ts index fcd384ff3f..86e6d2b553 100644 --- a/src/infrastructure/transaction/SerializeTransactionToJSON.ts +++ b/src/infrastructure/transaction/SerializeTransactionToJSON.ts @@ -106,7 +106,7 @@ export const SerializeTransactionToJSON = (transaction: Transaction): any => { }; case TransactionType.MOSAIC_DEFINITION: return { - nonce: (transaction as MosaicDefinitionTransaction).nonce.toDTO(), + nonce: (transaction as MosaicDefinitionTransaction).nonce, mosaicId: (transaction as MosaicDefinitionTransaction).mosaicId.toDTO(), mosaicProperties: (transaction as MosaicDefinitionTransaction).mosaicProperties.toDTO(), }; diff --git a/test/core/utils/TransactionMapping.spec.ts b/test/core/utils/TransactionMapping.spec.ts index e2605a6b0c..1dd6d73735 100644 --- a/test/core/utils/TransactionMapping.spec.ts +++ b/test/core/utils/TransactionMapping.spec.ts @@ -200,6 +200,31 @@ describe('TransactionMapping', () => { }); + it('should create MosaicDefinitionTransaction - without duration', () => { + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce + new MosaicId(UInt64.fromUint(1).toDTO()), // ID + MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 3, + }), + NetworkType.MIJIN_TEST, + ); + + const signedTransaction = mosaicDefinitionTransaction.signWith(account); + + const transaction = TransactionMapping.createFromPayload(signedTransaction.payload) as MosaicDefinitionTransaction; + + expect(transaction.mosaicProperties.divisibility).to.be.equal(3); + expect(transaction.mosaicProperties.supplyMutable).to.be.equal(false); + expect(transaction.mosaicProperties.transferable).to.be.equal(false); + expect(transaction.mosaicProperties.levyMutable).to.be.equal(false); + + }); + it('should create MosaicSupplyChangeTransaction', () => { const mosaicId = new MosaicId([2262289484, 3405110546]); const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( diff --git a/test/infrastructure/SerializeTransactionToJSON.spec.ts b/test/infrastructure/SerializeTransactionToJSON.spec.ts index 4b84a54722..a428f7d9fb 100644 --- a/test/infrastructure/SerializeTransactionToJSON.spec.ts +++ b/test/infrastructure/SerializeTransactionToJSON.spec.ts @@ -197,6 +197,30 @@ describe('SerializeTransactionToJSON', () => { }); + it('should create MosaicDefinitionTransaction without duration', () => { + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce + new MosaicId(UInt64.fromUint(1).toDTO()), // ID + MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 3, + }), + NetworkType.MIJIN_TEST, + ); + + const json = mosaicDefinitionTransaction.toJSON(); + + expect(json.type).to.be.equal(TransactionType.MOSAIC_DEFINITION); + expect(json.mosaicProperties.supplyMutable).to.be.equal(false); + expect(json.mosaicProperties.transferable).to.be.equal(false); + expect(json.mosaicProperties.levyMutable).to.be.equal(false); + expect(json.mosaicProperties.divisibility).to.be.equal(3); + + }); + it('should create MosaicSupplyChangeTransaction', () => { const mosaicId = new MosaicId([2262289484, 3405110546]); const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( diff --git a/test/model/mosaic/MosaicInfo.spec.ts b/test/model/mosaic/MosaicInfo.spec.ts index a46eb459ea..b60de0ca3b 100644 --- a/test/model/mosaic/MosaicInfo.spec.ts +++ b/test/model/mosaic/MosaicInfo.spec.ts @@ -78,6 +78,33 @@ describe('MosaicInfo', () => { }); + it('should createComplete an MosaicInfo object without duration', () => { + const mosaicInfo = new MosaicInfo( + mosaicInfoDTO.meta.id, + mosaicInfoDTO.mosaic.mosaicId, + mosaicInfoDTO.mosaic.supply, + mosaicInfoDTO.mosaic.height, + mosaicInfoDTO.mosaic.owner, + mosaicInfoDTO.mosaic.revision, + new MosaicProperties( + mosaicInfoDTO.mosaic.properties[0], + mosaicInfoDTO.mosaic.properties[1].compact(), + ), + mosaicInfoDTO.mosaic.levy, + ); + + expect(mosaicInfo.metaId).to.be.equal(mosaicInfoDTO.meta.id); + deepEqual(mosaicInfo.mosaicId, mosaicInfoDTO.mosaic.mosaicId); + deepEqual(mosaicInfo.supply, mosaicInfoDTO.mosaic.supply); + deepEqual(mosaicInfo.height, mosaicInfoDTO.mosaic.height); + expect(mosaicInfo.owner).to.be.equal(mosaicInfoDTO.mosaic.owner); + deepEqual(mosaicInfo.revision, mosaicInfoDTO.mosaic.revision); + + expect(mosaicInfo.divisibility).to.be.equal(mosaicInfoDTO.mosaic.properties[1].lower); + deepEqual(mosaicInfo.duration, undefined); + + }); + describe('isSupplyMutable', () => { it('should return true when it\'s mutable', () => { const mosaicInfo = new MosaicInfo( diff --git a/test/model/mosaic/MosaicProperties.spec.ts b/test/model/mosaic/MosaicProperties.spec.ts index 5f711f2da8..f2603b3fa8 100644 --- a/test/model/mosaic/MosaicProperties.spec.ts +++ b/test/model/mosaic/MosaicProperties.spec.ts @@ -69,4 +69,20 @@ describe('MosaicProperties', () => { expect(mosaicProperties.transferable).to.be.equal(false); expect(mosaicProperties.levyMutable).to.be.equal(false); }); + + it('should createComplete an MosaicProperties object without duration', () => { + const mosaicProperties = MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 10, + }); + + expect(mosaicProperties.divisibility).to.be.equal(10); + deepEqual(mosaicProperties.duration, undefined); + + expect(mosaicProperties.supplyMutable).to.be.equal(false); + expect(mosaicProperties.transferable).to.be.equal(false); + expect(mosaicProperties.levyMutable).to.be.equal(false); + }); }); diff --git a/test/model/transaction/MosaicDefinitionTransaction.spec.ts b/test/model/transaction/MosaicDefinitionTransaction.spec.ts index 3b575aa758..5ed7e37610 100644 --- a/test/model/transaction/MosaicDefinitionTransaction.spec.ts +++ b/test/model/transaction/MosaicDefinitionTransaction.spec.ts @@ -24,7 +24,6 @@ import {Deadline} from '../../../src/model/transaction/Deadline'; import {MosaicDefinitionTransaction} from '../../../src/model/transaction/MosaicDefinitionTransaction'; import {UInt64} from '../../../src/model/UInt64'; import {TestingAccount} from '../../conf/conf.spec'; -import {convert, mosaicId, uint64 as uint64_t} from 'nem2-library'; describe('MosaicDefinitionTransaction', () => { let account: Account; @@ -95,4 +94,33 @@ describe('MosaicDefinitionTransaction', () => { )).to.be.equal('E6DE84B8010000000000000001000302E803000000000000'); }); + + it('should createComplete an MosaicDefinitionTransaction object and sign it without duration', () => { + + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce + new MosaicId(UInt64.fromUint(1).toDTO()), // ID + MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 3, + }), + NetworkType.MIJIN_TEST, + ); + + expect(mosaicDefinitionTransaction.mosaicProperties.divisibility).to.be.equal(3); + expect(mosaicDefinitionTransaction.mosaicProperties.supplyMutable).to.be.equal(false); + expect(mosaicDefinitionTransaction.mosaicProperties.transferable).to.be.equal(false); + expect(mosaicDefinitionTransaction.mosaicProperties.levyMutable).to.be.equal(false); + + const signedTransaction = mosaicDefinitionTransaction.signWith(account); + + expect(signedTransaction.payload.substring( + 240, + signedTransaction.payload.length, + )).to.be.equal('E6DE84B80100000000000000000003'); + + }); }); From c5697cae4bb6720a8372f7c432f409c1a4920c8a Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 18:02:11 +0100 Subject: [PATCH 29/83] Updared tslint to disable "no-non-null-assertion" --- tslint.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tslint.json b/tslint.json index aeca64ccdf..f47346aaf9 100644 --- a/tslint.json +++ b/tslint.json @@ -65,7 +65,7 @@ "ignore-params" ], "no-misused-new": true, - "no-non-null-assertion": true, + "no-non-null-assertion": false, "no-shadowed-variable": true, "no-string-literal": false, "no-string-throw": true, @@ -139,6 +139,6 @@ "use-pipe-transform-interface": true, "component-class-suffix": true, "directive-class-suffix": true, - "invoke-injectable": true + "invoke-injectable": true, } } From 8f52525362a2d7e8b0a1a3ed3f2e798710ac4314 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Tue, 9 Apr 2019 12:46:33 +0100 Subject: [PATCH 30/83] Fixed white space issue on TransactionMapping test Fixed trailing comma in tslint --- test/core/utils/TransactionMapping.spec.ts | 2 +- tslint.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/core/utils/TransactionMapping.spec.ts b/test/core/utils/TransactionMapping.spec.ts index 1dd6d73735..300c6f3fe9 100644 --- a/test/core/utils/TransactionMapping.spec.ts +++ b/test/core/utils/TransactionMapping.spec.ts @@ -192,7 +192,7 @@ describe('TransactionMapping', () => { const transaction = TransactionMapping.createFromPayload(signedTransaction.payload) as MosaicDefinitionTransaction; expect(transaction.mosaicProperties.duration!.lower).to.be.equal(1000); - expect(transaction.mosaicProperties.duration! .higher).to.be.equal(0); + expect(transaction.mosaicProperties.duration!.higher).to.be.equal(0); expect(transaction.mosaicProperties.divisibility).to.be.equal(3); expect(transaction.mosaicProperties.supplyMutable).to.be.equal(false); expect(transaction.mosaicProperties.transferable).to.be.equal(false); diff --git a/tslint.json b/tslint.json index f47346aaf9..ded366576a 100644 --- a/tslint.json +++ b/tslint.json @@ -139,6 +139,6 @@ "use-pipe-transform-interface": true, "component-class-suffix": true, "directive-class-suffix": true, - "invoke-injectable": true, + "invoke-injectable": true } } From 2c8be0b753415668facdc9e80969abb48be05c4d Mon Sep 17 00:00:00 2001 From: Greg S Date: Wed, 10 Apr 2019 15:55:12 +0200 Subject: [PATCH 31/83] PR #101: fixed block DTOs with beneficiary field DTO problem --- src/infrastructure/BlockchainHttp.ts | 6 +-- src/infrastructure/Listener.ts | 5 +- .../transaction/CreateTransactionFromDTO.ts | 49 +++++++++++++++++-- src/model/blockchain/BlockInfo.ts | 2 +- test/model/blockchain/BlockInfo.spec.ts | 2 +- 5 files changed, 53 insertions(+), 11 deletions(-) diff --git a/src/infrastructure/BlockchainHttp.ts b/src/infrastructure/BlockchainHttp.ts index b5a0870f27..14d12cff5b 100644 --- a/src/infrastructure/BlockchainHttp.ts +++ b/src/infrastructure/BlockchainHttp.ts @@ -26,7 +26,7 @@ import {UInt64} from '../model/UInt64'; import {BlockchainRepository} from './BlockchainRepository'; import {Http} from './Http'; import {QueryParams} from './QueryParams'; -import {CreateTransactionFromDTO} from './transaction/CreateTransactionFromDTO'; +import {CreateTransactionFromDTO, extractBeneficiary} from './transaction/CreateTransactionFromDTO'; /** * Blockchain http repository. @@ -75,7 +75,7 @@ export class BlockchainHttp extends Http implements BlockchainRepository { blockDTO.block.blockTransactionsHash, blockDTO.block.blockReceiptsHash, blockDTO.block.stateHash, - PublicAccount.createFromPublicKey(blockDTO.block.beneficiaryPublicKey, networkType), + extractBeneficiary(blockDTO, networkType), ); })); } @@ -125,7 +125,7 @@ export class BlockchainHttp extends Http implements BlockchainRepository { blockDTO.block.blockTransactionsHash, blockDTO.block.blockReceiptsHash, blockDTO.block.stateHash, - PublicAccount.createFromPublicKey(blockDTO.block.beneficiaryPublicKey, networkType), + extractBeneficiary(blockDTO, networkType), ); }); })); diff --git a/src/infrastructure/Listener.ts b/src/infrastructure/Listener.ts index 430f905f70..69c7760f21 100644 --- a/src/infrastructure/Listener.ts +++ b/src/infrastructure/Listener.ts @@ -20,6 +20,7 @@ import * as WebSocket from 'ws'; import {Address} from '../model/account/Address'; import {PublicAccount} from '../model/account/PublicAccount'; import {BlockInfo} from '../model/blockchain/BlockInfo'; +import {NetworkType} from '../model/blockchain/NetworkType'; import {NamespaceId} from '../model/namespace/NamespaceId'; import {AggregateTransaction} from '../model/transaction/AggregateTransaction'; import {AggregateTransactionCosignature} from '../model/transaction/AggregateTransactionCosignature'; @@ -33,7 +34,7 @@ import {Transaction} from '../model/transaction/Transaction'; import {TransactionStatusError} from '../model/transaction/TransactionStatusError'; import {TransferTransaction} from '../model/transaction/TransferTransaction'; import {UInt64} from '../model/UInt64'; -import {CreateTransactionFromDTO} from './transaction/CreateTransactionFromDTO'; +import {CreateTransactionFromDTO, extractBeneficiary} from './transaction/CreateTransactionFromDTO'; enum ListenerChannelName { block = 'block', @@ -140,7 +141,7 @@ export class Listener { message.block.blockTransactionsHash, message.block.blockReceiptsHash, message.block.stateHash, - PublicAccount.createFromPublicKey(message.block.beneficiaryPublicKey, networkType), + extractBeneficiary(message, networkType), // passing `message` as `blockDTO` ), }); } else if (message.status) { diff --git a/src/infrastructure/transaction/CreateTransactionFromDTO.ts b/src/infrastructure/transaction/CreateTransactionFromDTO.ts index 2cb5f489fd..14d6d99081 100644 --- a/src/infrastructure/transaction/CreateTransactionFromDTO.ts +++ b/src/infrastructure/transaction/CreateTransactionFromDTO.ts @@ -322,7 +322,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr throw new Error('Unimplemented transaction with type ' + transactionDTO.type); }; -const extractNetworkType = (version: number): NetworkType => { +export const extractNetworkType = (version: number): NetworkType => { const networkType = parseInt(version.toString(16).substr(0, 2), 16); if (networkType === NetworkType.MAIN_NET) { return NetworkType.MAIN_NET; @@ -336,7 +336,7 @@ const extractNetworkType = (version: number): NetworkType => { throw new Error('Unimplemented network type'); }; -const extractTransactionVersion = (version: number): number => { +export const extractTransactionVersion = (version: number): number => { return parseInt(version.toString(16).substr(2, 2), 16); }; @@ -349,7 +349,7 @@ const extractTransactionVersion = (version: number): number => { * @param recipient {string} Encoded hexadecimal recipient notation * @return {Address | NamespaceId} */ -const extractRecipient = (recipient: string): Address | NamespaceId => { +export const extractRecipient = (recipient: string): Address | NamespaceId => { // If bit 0 of byte 0 is not set (like in 0x90), then it is a regular address. // Else (e.g. 0x91) it represents a namespace id which starts at byte 1. const bit0 = convert.hexToUint8(recipient.substr(1, 2))[0]; @@ -374,7 +374,7 @@ const extractRecipient = (recipient: string): Address | NamespaceId => { * @param mosaics {Array | undefined} The DTO array of mosaics (with UInt64 Id notation) * @return {Mosaic[]} */ -const extractMosaics = (mosaics: any): Mosaic[] => { +export const extractMosaics = (mosaics: any): Mosaic[] => { if (mosaics === undefined) { return []; @@ -396,3 +396,44 @@ const extractMosaics = (mosaics: any): Mosaic[] => { return new Mosaic(new MosaicId(mosaicDTO.id), new UInt64(mosaicDTO.amount)); }); }; + +/** + * Extract beneficiary public key from DTO. + * + * @todo Upgrade of catapult-rest WITH catapult-service-bootstrap versioning. + * + * With `cow` upgrade (nemtech/catapult-server@0.3.0.2), `catapult-rest` block DTO + * was updated and latest catapult-service-bootstrap uses the wrong block DTO. + * This will be fixed with next catapult-server upgrade to `dragon`. + * + * :warning It is currently not possible to read the block's beneficiary public key + * except when working with a local instance of `catapult-rest`. + * + * @param beneficiary {string | undefined} The beneficiary public key if set + * @return {Mosaic[]} + */ +export const extractBeneficiary = ( + blockDTO: any, + networkType: NetworkType +): PublicAccount | undefined => { + + let dtoPublicAccount: PublicAccount | undefined; + let dtoFieldValue: string | undefined; + if (blockDTO.beneficiaryPublicKey) { + dtoFieldValue = blockDTO.beneficiaryPublicKey; + } else if (blockDTO.beneficiary) { + dtoFieldValue = blockDTO.beneficiary; + } + + if (! dtoFieldValue) { + return undefined; + } + + try { + // @FIX with latest catapult-service-bootstrap version, catapult-rest still returns + // a `string` formatted copy of the public *when it is set at all*. + dtoPublicAccount = PublicAccount.createFromPublicKey(dtoFieldValue, networkType); + } catch (e) { dtoPublicAccount =  undefined; } + + return dtoPublicAccount; +}; diff --git a/src/model/blockchain/BlockInfo.ts b/src/model/blockchain/BlockInfo.ts index 1098c515fd..0a98a54dfd 100644 --- a/src/model/blockchain/BlockInfo.ts +++ b/src/model/blockchain/BlockInfo.ts @@ -117,7 +117,7 @@ export class BlockInfo { /** * The beneficiary public key. */ - public readonly beneficiaryPublicKey: PublicAccount,) { + public readonly beneficiaryPublicKey?: PublicAccount | undefined) { } } diff --git a/test/model/blockchain/BlockInfo.spec.ts b/test/model/blockchain/BlockInfo.spec.ts index 60363992a9..ba2e9034b9 100644 --- a/test/model/blockchain/BlockInfo.spec.ts +++ b/test/model/blockchain/BlockInfo.spec.ts @@ -87,7 +87,7 @@ describe('BlockInfo', () => { expect(blockInfo.blockTransactionsHash).to.be.equal(blockDTO.block.blockTransactionsHash); expect(blockInfo.blockReceiptsHash).to.be.equal(blockDTO.block.blockReceiptsHash); expect(blockInfo.stateHash).to.be.equal(blockDTO.block.stateHash); - expect(blockInfo.beneficiaryPublicKey.publicKey).to.be.equal(blockDTO.block.beneficiaryPublicKey); + expect((blockInfo.beneficiaryPublicKey as PublicAccount).publicKey).to.be.equal(blockDTO.block.beneficiaryPublicKey); }); }); From 6f9db68ba4b200576efee60e62129725894a77db Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 13:22:33 +0100 Subject: [PATCH 32/83] Added #109 Make duration optiuon for mosaic definition --- src/model/mosaic/MosaicInfo.ts | 2 +- src/model/mosaic/MosaicProperties.ts | 14 ++++++++++---- .../transaction/MosaicDefinitionTransaction.ts | 2 +- test/core/utils/TransactionMapping.spec.ts | 4 ++-- .../MosaicDefinitionTransaction.spec.ts | 8 ++++---- 5 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/model/mosaic/MosaicInfo.ts b/src/model/mosaic/MosaicInfo.ts index 162e6d7238..999d044e48 100644 --- a/src/model/mosaic/MosaicInfo.ts +++ b/src/model/mosaic/MosaicInfo.ts @@ -81,7 +81,7 @@ export class MosaicInfo { * Mosaic duration * @returns {UInt64} */ - public get duration(): UInt64 { + public get duration(): UInt64 | undefined { return this.properties.duration; } diff --git a/src/model/mosaic/MosaicProperties.ts b/src/model/mosaic/MosaicProperties.ts index 9d56a35fac..399c31c622 100644 --- a/src/model/mosaic/MosaicProperties.ts +++ b/src/model/mosaic/MosaicProperties.ts @@ -59,8 +59,9 @@ export class MosaicProperties { /** * The duration in blocks a mosaic will be available. * After the duration finishes mosaic is inactive and can be renewed. + * Duration is optional when defining the mosaic */ - public readonly duration: UInt64) { + public readonly duration?: UInt64) { let binaryFlags = '00' + (flags.lower >>> 0).toString(2); binaryFlags = binaryFlags.substr(binaryFlags.length - 3, 3); this.supplyMutable = binaryFlags[2] === '1'; @@ -78,7 +79,7 @@ export class MosaicProperties { transferable: boolean, levyMutable: boolean, divisibility: number, - duration: UInt64, + duration?: UInt64, }) { const flags = (params.supplyMutable ? 1 : 0) + (params.transferable ? 2 : 0) + (params.levyMutable ? 4 : 0); return new MosaicProperties(UInt64.fromUint(flags), params.divisibility, params.duration); @@ -88,12 +89,17 @@ export class MosaicProperties { * Create DTO object */ toDTO() { - return { + const dto = { supplyMutable: this.supplyMutable, transferable: this.transferable, levyMutable: this.levyMutable, divisibility: this.divisibility, - duration: this.duration.toDTO(), }; + + if (this.duration) { + Object.assign(dto, {duration: this.duration.toDTO()}); + } + + return dto; } } diff --git a/src/model/transaction/MosaicDefinitionTransaction.ts b/src/model/transaction/MosaicDefinitionTransaction.ts index 9443d08c26..851306c3b4 100644 --- a/src/model/transaction/MosaicDefinitionTransaction.ts +++ b/src/model/transaction/MosaicDefinitionTransaction.ts @@ -128,7 +128,7 @@ export class MosaicDefinitionTransaction extends Transaction { .addFee(this.maxFee.toDTO()) .addVersion(this.versionToDTO()) .addDivisibility(this.mosaicProperties.divisibility) - .addDuration(this.mosaicProperties.duration.toDTO()) + .addDuration(this.mosaicProperties.duration ? this.mosaicProperties.duration.toDTO() : []) .addNonce(this.nonce.toDTO()) .addMosaicId(this.mosaicId.id.toDTO()); diff --git a/test/core/utils/TransactionMapping.spec.ts b/test/core/utils/TransactionMapping.spec.ts index 6091705a33..e2605a6b0c 100644 --- a/test/core/utils/TransactionMapping.spec.ts +++ b/test/core/utils/TransactionMapping.spec.ts @@ -191,8 +191,8 @@ describe('TransactionMapping', () => { const transaction = TransactionMapping.createFromPayload(signedTransaction.payload) as MosaicDefinitionTransaction; - expect(transaction.mosaicProperties.duration.lower).to.be.equal(1000); - expect(transaction.mosaicProperties.duration.higher).to.be.equal(0); + expect(transaction.mosaicProperties.duration!.lower).to.be.equal(1000); + expect(transaction.mosaicProperties.duration! .higher).to.be.equal(0); expect(transaction.mosaicProperties.divisibility).to.be.equal(3); expect(transaction.mosaicProperties.supplyMutable).to.be.equal(false); expect(transaction.mosaicProperties.transferable).to.be.equal(false); diff --git a/test/model/transaction/MosaicDefinitionTransaction.spec.ts b/test/model/transaction/MosaicDefinitionTransaction.spec.ts index 23ae1bc416..a70fecf0cb 100644 --- a/test/model/transaction/MosaicDefinitionTransaction.spec.ts +++ b/test/model/transaction/MosaicDefinitionTransaction.spec.ts @@ -87,8 +87,8 @@ describe('MosaicDefinitionTransaction', () => { NetworkType.MIJIN_TEST, ); - expect(mosaicDefinitionTransaction.mosaicProperties.duration.lower).to.be.equal(1000); - expect(mosaicDefinitionTransaction.mosaicProperties.duration.higher).to.be.equal(0); + expect(mosaicDefinitionTransaction.mosaicProperties.duration!.lower).to.be.equal(1000); + expect(mosaicDefinitionTransaction.mosaicProperties.duration!.higher).to.be.equal(0); expect(mosaicDefinitionTransaction.mosaicProperties.divisibility).to.be.equal(3); expect(mosaicDefinitionTransaction.mosaicProperties.supplyMutable).to.be.equal(true); expect(mosaicDefinitionTransaction.mosaicProperties.transferable).to.be.equal(true); @@ -119,8 +119,8 @@ describe('MosaicDefinitionTransaction', () => { NetworkType.MIJIN_TEST, ); - expect(mosaicDefinitionTransaction.mosaicProperties.duration.lower).to.be.equal(1000); - expect(mosaicDefinitionTransaction.mosaicProperties.duration.higher).to.be.equal(0); + expect(mosaicDefinitionTransaction.mosaicProperties.duration!.lower).to.be.equal(1000); + expect(mosaicDefinitionTransaction.mosaicProperties.duration!.higher).to.be.equal(0); expect(mosaicDefinitionTransaction.mosaicProperties.divisibility).to.be.equal(3); expect(mosaicDefinitionTransaction.mosaicProperties.supplyMutable).to.be.equal(false); expect(mosaicDefinitionTransaction.mosaicProperties.transferable).to.be.equal(false); From 8d3ea5dd7877dcacf7430efe60cec2ed78a1ecc3 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 16:21:51 +0100 Subject: [PATCH 33/83] made mosaic properties duration option to all references --- .../transaction/CreateTransactionFromDTO.ts | 2 +- .../CreateTransactionFromPayload.ts | 2 +- .../transaction/SerializeTransactionToJSON.ts | 2 +- test/core/utils/TransactionMapping.spec.ts | 25 ++++++++++++++++ .../SerializeTransactionToJSON.spec.ts | 24 +++++++++++++++ test/model/mosaic/MosaicInfo.spec.ts | 27 +++++++++++++++++ test/model/mosaic/MosaicProperties.spec.ts | 16 ++++++++++ .../MosaicDefinitionTransaction.spec.ts | 30 ++++++++++++++++++- 8 files changed, 124 insertions(+), 4 deletions(-) diff --git a/src/infrastructure/transaction/CreateTransactionFromDTO.ts b/src/infrastructure/transaction/CreateTransactionFromDTO.ts index 14d6d99081..5b3e530cd7 100644 --- a/src/infrastructure/transaction/CreateTransactionFromDTO.ts +++ b/src/infrastructure/transaction/CreateTransactionFromDTO.ts @@ -157,7 +157,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr new MosaicProperties( new UInt64(transactionDTO.properties[0].value), (new UInt64(transactionDTO.properties[1].value)).compact(), - new UInt64(transactionDTO.properties.length === 3 ? transactionDTO.properties[2].value : [0, 0]), + new UInt64(transactionDTO.properties.length === 3 ? transactionDTO.properties[2].value : undefined), ), transactionDTO.signature, PublicAccount.createFromPublicKey(transactionDTO.signer, extractNetworkType(transactionDTO.version)), diff --git a/src/infrastructure/transaction/CreateTransactionFromPayload.ts b/src/infrastructure/transaction/CreateTransactionFromPayload.ts index e8b66453ff..15dbd84487 100644 --- a/src/infrastructure/transaction/CreateTransactionFromPayload.ts +++ b/src/infrastructure/transaction/CreateTransactionFromPayload.ts @@ -224,7 +224,7 @@ const CreateTransaction = (type: number, transactionData: string, networkType: N transferable: (flags & 2) === 2, levyMutable: (flags & 4) === 4, divisibility: parseInt(convert.uint8ToHex(convert.hexToUint8(divisibility).reverse()), 16), - duration: UInt64.fromHex(reverse(duration)), + duration: duration ? UInt64.fromHex(reverse(duration)) : undefined, }), networkType, ); diff --git a/src/infrastructure/transaction/SerializeTransactionToJSON.ts b/src/infrastructure/transaction/SerializeTransactionToJSON.ts index fcd384ff3f..86e6d2b553 100644 --- a/src/infrastructure/transaction/SerializeTransactionToJSON.ts +++ b/src/infrastructure/transaction/SerializeTransactionToJSON.ts @@ -106,7 +106,7 @@ export const SerializeTransactionToJSON = (transaction: Transaction): any => { }; case TransactionType.MOSAIC_DEFINITION: return { - nonce: (transaction as MosaicDefinitionTransaction).nonce.toDTO(), + nonce: (transaction as MosaicDefinitionTransaction).nonce, mosaicId: (transaction as MosaicDefinitionTransaction).mosaicId.toDTO(), mosaicProperties: (transaction as MosaicDefinitionTransaction).mosaicProperties.toDTO(), }; diff --git a/test/core/utils/TransactionMapping.spec.ts b/test/core/utils/TransactionMapping.spec.ts index e2605a6b0c..1dd6d73735 100644 --- a/test/core/utils/TransactionMapping.spec.ts +++ b/test/core/utils/TransactionMapping.spec.ts @@ -200,6 +200,31 @@ describe('TransactionMapping', () => { }); + it('should create MosaicDefinitionTransaction - without duration', () => { + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce + new MosaicId(UInt64.fromUint(1).toDTO()), // ID + MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 3, + }), + NetworkType.MIJIN_TEST, + ); + + const signedTransaction = mosaicDefinitionTransaction.signWith(account); + + const transaction = TransactionMapping.createFromPayload(signedTransaction.payload) as MosaicDefinitionTransaction; + + expect(transaction.mosaicProperties.divisibility).to.be.equal(3); + expect(transaction.mosaicProperties.supplyMutable).to.be.equal(false); + expect(transaction.mosaicProperties.transferable).to.be.equal(false); + expect(transaction.mosaicProperties.levyMutable).to.be.equal(false); + + }); + it('should create MosaicSupplyChangeTransaction', () => { const mosaicId = new MosaicId([2262289484, 3405110546]); const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( diff --git a/test/infrastructure/SerializeTransactionToJSON.spec.ts b/test/infrastructure/SerializeTransactionToJSON.spec.ts index 4b84a54722..a428f7d9fb 100644 --- a/test/infrastructure/SerializeTransactionToJSON.spec.ts +++ b/test/infrastructure/SerializeTransactionToJSON.spec.ts @@ -197,6 +197,30 @@ describe('SerializeTransactionToJSON', () => { }); + it('should create MosaicDefinitionTransaction without duration', () => { + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce + new MosaicId(UInt64.fromUint(1).toDTO()), // ID + MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 3, + }), + NetworkType.MIJIN_TEST, + ); + + const json = mosaicDefinitionTransaction.toJSON(); + + expect(json.type).to.be.equal(TransactionType.MOSAIC_DEFINITION); + expect(json.mosaicProperties.supplyMutable).to.be.equal(false); + expect(json.mosaicProperties.transferable).to.be.equal(false); + expect(json.mosaicProperties.levyMutable).to.be.equal(false); + expect(json.mosaicProperties.divisibility).to.be.equal(3); + + }); + it('should create MosaicSupplyChangeTransaction', () => { const mosaicId = new MosaicId([2262289484, 3405110546]); const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( diff --git a/test/model/mosaic/MosaicInfo.spec.ts b/test/model/mosaic/MosaicInfo.spec.ts index a46eb459ea..b60de0ca3b 100644 --- a/test/model/mosaic/MosaicInfo.spec.ts +++ b/test/model/mosaic/MosaicInfo.spec.ts @@ -78,6 +78,33 @@ describe('MosaicInfo', () => { }); + it('should createComplete an MosaicInfo object without duration', () => { + const mosaicInfo = new MosaicInfo( + mosaicInfoDTO.meta.id, + mosaicInfoDTO.mosaic.mosaicId, + mosaicInfoDTO.mosaic.supply, + mosaicInfoDTO.mosaic.height, + mosaicInfoDTO.mosaic.owner, + mosaicInfoDTO.mosaic.revision, + new MosaicProperties( + mosaicInfoDTO.mosaic.properties[0], + mosaicInfoDTO.mosaic.properties[1].compact(), + ), + mosaicInfoDTO.mosaic.levy, + ); + + expect(mosaicInfo.metaId).to.be.equal(mosaicInfoDTO.meta.id); + deepEqual(mosaicInfo.mosaicId, mosaicInfoDTO.mosaic.mosaicId); + deepEqual(mosaicInfo.supply, mosaicInfoDTO.mosaic.supply); + deepEqual(mosaicInfo.height, mosaicInfoDTO.mosaic.height); + expect(mosaicInfo.owner).to.be.equal(mosaicInfoDTO.mosaic.owner); + deepEqual(mosaicInfo.revision, mosaicInfoDTO.mosaic.revision); + + expect(mosaicInfo.divisibility).to.be.equal(mosaicInfoDTO.mosaic.properties[1].lower); + deepEqual(mosaicInfo.duration, undefined); + + }); + describe('isSupplyMutable', () => { it('should return true when it\'s mutable', () => { const mosaicInfo = new MosaicInfo( diff --git a/test/model/mosaic/MosaicProperties.spec.ts b/test/model/mosaic/MosaicProperties.spec.ts index 5f711f2da8..f2603b3fa8 100644 --- a/test/model/mosaic/MosaicProperties.spec.ts +++ b/test/model/mosaic/MosaicProperties.spec.ts @@ -69,4 +69,20 @@ describe('MosaicProperties', () => { expect(mosaicProperties.transferable).to.be.equal(false); expect(mosaicProperties.levyMutable).to.be.equal(false); }); + + it('should createComplete an MosaicProperties object without duration', () => { + const mosaicProperties = MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 10, + }); + + expect(mosaicProperties.divisibility).to.be.equal(10); + deepEqual(mosaicProperties.duration, undefined); + + expect(mosaicProperties.supplyMutable).to.be.equal(false); + expect(mosaicProperties.transferable).to.be.equal(false); + expect(mosaicProperties.levyMutable).to.be.equal(false); + }); }); diff --git a/test/model/transaction/MosaicDefinitionTransaction.spec.ts b/test/model/transaction/MosaicDefinitionTransaction.spec.ts index a70fecf0cb..4f4f3f61ff 100644 --- a/test/model/transaction/MosaicDefinitionTransaction.spec.ts +++ b/test/model/transaction/MosaicDefinitionTransaction.spec.ts @@ -24,7 +24,6 @@ import {Deadline} from '../../../src/model/transaction/Deadline'; import {MosaicDefinitionTransaction} from '../../../src/model/transaction/MosaicDefinitionTransaction'; import {UInt64} from '../../../src/model/UInt64'; import {TestingAccount} from '../../conf/conf.spec'; -import {convert, mosaicId, uint64 as uint64_t} from 'nem2-library'; describe('MosaicDefinitionTransaction', () => { let account: Account; @@ -154,4 +153,33 @@ describe('MosaicDefinitionTransaction', () => { expect(mosaicDefinitionTransaction.size).to.be.equal(144); }); }); + + it('should createComplete an MosaicDefinitionTransaction object and sign it without duration', () => { + + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce + new MosaicId(UInt64.fromUint(1).toDTO()), // ID + MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 3, + }), + NetworkType.MIJIN_TEST, + ); + + expect(mosaicDefinitionTransaction.mosaicProperties.divisibility).to.be.equal(3); + expect(mosaicDefinitionTransaction.mosaicProperties.supplyMutable).to.be.equal(false); + expect(mosaicDefinitionTransaction.mosaicProperties.transferable).to.be.equal(false); + expect(mosaicDefinitionTransaction.mosaicProperties.levyMutable).to.be.equal(false); + + const signedTransaction = mosaicDefinitionTransaction.signWith(account); + + expect(signedTransaction.payload.substring( + 240, + signedTransaction.payload.length, + )).to.be.equal('E6DE84B80100000000000000000003'); + + }); }); From e3e383459172466bc067e23ad330fa3f77988016 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 18:02:11 +0100 Subject: [PATCH 34/83] Updared tslint to disable "no-non-null-assertion" --- tslint.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tslint.json b/tslint.json index aeca64ccdf..f47346aaf9 100644 --- a/tslint.json +++ b/tslint.json @@ -65,7 +65,7 @@ "ignore-params" ], "no-misused-new": true, - "no-non-null-assertion": true, + "no-non-null-assertion": false, "no-shadowed-variable": true, "no-string-literal": false, "no-string-throw": true, @@ -139,6 +139,6 @@ "use-pipe-transform-interface": true, "component-class-suffix": true, "directive-class-suffix": true, - "invoke-injectable": true + "invoke-injectable": true, } } From 4fe51d7eecf1c5350bd6b245f00ad3b7ac07f3ec Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Tue, 9 Apr 2019 12:46:33 +0100 Subject: [PATCH 35/83] Fixed white space issue on TransactionMapping test Fixed trailing comma in tslint --- test/core/utils/TransactionMapping.spec.ts | 2 +- tslint.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/core/utils/TransactionMapping.spec.ts b/test/core/utils/TransactionMapping.spec.ts index 1dd6d73735..300c6f3fe9 100644 --- a/test/core/utils/TransactionMapping.spec.ts +++ b/test/core/utils/TransactionMapping.spec.ts @@ -192,7 +192,7 @@ describe('TransactionMapping', () => { const transaction = TransactionMapping.createFromPayload(signedTransaction.payload) as MosaicDefinitionTransaction; expect(transaction.mosaicProperties.duration!.lower).to.be.equal(1000); - expect(transaction.mosaicProperties.duration! .higher).to.be.equal(0); + expect(transaction.mosaicProperties.duration!.higher).to.be.equal(0); expect(transaction.mosaicProperties.divisibility).to.be.equal(3); expect(transaction.mosaicProperties.supplyMutable).to.be.equal(false); expect(transaction.mosaicProperties.transferable).to.be.equal(false); diff --git a/tslint.json b/tslint.json index f47346aaf9..ded366576a 100644 --- a/tslint.json +++ b/tslint.json @@ -139,6 +139,6 @@ "use-pipe-transform-interface": true, "component-class-suffix": true, "directive-class-suffix": true, - "invoke-injectable": true, + "invoke-injectable": true } } From eb2e12f0ffa62c1f683f197198ceb0eeb00ca01b Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Wed, 10 Apr 2019 15:13:01 +0100 Subject: [PATCH 36/83] renamed AggregatedTransactionService --- ...vice.ts => AggregateTransactionService.ts} | 2 +- src/service/service.ts | 2 +- ...ts => AggregateTransactionService.spec.ts} | 32 +++++++++---------- 3 files changed, 18 insertions(+), 18 deletions(-) rename src/service/{AggregatedTransactionService.ts => AggregateTransactionService.ts} (99%) rename test/service/{AggregatedTransactionService.spec.ts => AggregateTransactionService.spec.ts} (93%) diff --git a/src/service/AggregatedTransactionService.ts b/src/service/AggregateTransactionService.ts similarity index 99% rename from src/service/AggregatedTransactionService.ts rename to src/service/AggregateTransactionService.ts index 75a806bf96..800d1a1b89 100644 --- a/src/service/AggregatedTransactionService.ts +++ b/src/service/AggregateTransactionService.ts @@ -29,7 +29,7 @@ import { TransactionType } from '../model/transaction/TransactionType'; /** * Aggregated Transaction service */ -export class AggregatedTransactionService { +export class AggregateTransactionService { /** * Constructor diff --git a/src/service/service.ts b/src/service/service.ts index fcf4c84680..c988e13ffb 100644 --- a/src/service/service.ts +++ b/src/service/service.ts @@ -16,4 +16,4 @@ export * from './NamespaceService'; export * from './MosaicService'; -export * from './AggregatedTransactionService'; +export * from './AggregateTransactionService'; diff --git a/test/service/AggregatedTransactionService.spec.ts b/test/service/AggregateTransactionService.spec.ts similarity index 93% rename from test/service/AggregatedTransactionService.spec.ts rename to test/service/AggregateTransactionService.spec.ts index 23470cb2a1..ae2deab99b 100644 --- a/test/service/AggregatedTransactionService.spec.ts +++ b/test/service/AggregateTransactionService.spec.ts @@ -31,13 +31,13 @@ import { MultisigCosignatoryModification } from '../../src/model/transaction/Mul import { MultisigCosignatoryModificationType } from '../../src/model/transaction/MultisigCosignatoryModificationType'; import { PlainMessage } from '../../src/model/transaction/PlainMessage'; import { TransferTransaction } from '../../src/model/transaction/TransferTransaction'; -import { AggregatedTransactionService } from '../../src/service/AggregatedTransactionService'; +import { AggregateTransactionService } from '../../src/service/AggregateTransactionService'; /** * For multi level multisig scenario visit: https://github.com/nemtech/nem2-docs/issues/10 */ -describe('AggregatedTransactionService', () => { - let aggregatedTransactionService: AggregatedTransactionService; +describe('AggregateTransactionService', () => { + let aggregateTransactionService: AggregateTransactionService; /** * Multisig2 Account: SBROWP-7YMG2M-K45RO6-Q7ZPK7-G7GXWQ-JK5VNQ-OSUX @@ -95,7 +95,7 @@ describe('AggregatedTransactionService', () => { .thenReturn(observableOf(givenAccount3Info())); const accountHttp = instance(mockedAccountHttp); - aggregatedTransactionService = new AggregatedTransactionService(accountHttp); + aggregateTransactionService = new AggregateTransactionService(accountHttp); }); it('should return isComplete: true for aggregated complete transaction - 2 levels Multisig', () => { @@ -122,7 +122,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account2]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); @@ -151,7 +151,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, []); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.false; }); }); @@ -180,7 +180,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.false; }); }); @@ -218,7 +218,7 @@ describe('AggregatedTransactionService', () => { NetworkType.MIJIN_TEST, []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.false; }); }); @@ -256,7 +256,7 @@ describe('AggregatedTransactionService', () => { NetworkType.MIJIN_TEST, []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4, account2]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); @@ -285,7 +285,7 @@ describe('AggregatedTransactionService', () => { NetworkType.MIJIN_TEST, []); const signedTransaction = aggregateTransaction.signWith(account2); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); @@ -312,7 +312,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signWith(account1); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.false; }); }); @@ -340,7 +340,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signWith(account4); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); @@ -383,7 +383,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); @@ -425,7 +425,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, []); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.false; }); }); @@ -451,7 +451,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account2, [account3]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); @@ -477,7 +477,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account2, []); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.false; }); }); From 5776201f72278a200cd7cafa2ef3c00abc80c88a Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 1 Apr 2019 20:39:11 +0100 Subject: [PATCH 37/83] #4 1. Addes service for Aggregated Transaction - isComplete 2. Fixed couple of bugs in TransactionMappings --- .../CreateTransactionFromPayload.ts | 8 +- src/service/AggregatedTransactionService.ts | 127 +++++++++ src/service/service.ts | 1 + .../AggregatedTransactionService.spec.ts | 242 ++++++++++++++++++ 4 files changed, 374 insertions(+), 4 deletions(-) create mode 100644 src/service/AggregatedTransactionService.ts create mode 100644 test/service/AggregatedTransactionService.spec.ts diff --git a/src/infrastructure/transaction/CreateTransactionFromPayload.ts b/src/infrastructure/transaction/CreateTransactionFromPayload.ts index e8b66453ff..9b124a33bf 100644 --- a/src/infrastructure/transaction/CreateTransactionFromPayload.ts +++ b/src/infrastructure/transaction/CreateTransactionFromPayload.ts @@ -377,7 +377,7 @@ const CreateTransaction = (type: number, transactionData: string, networkType: N networkType, consignatureArray ? consignatureArray.map((cosignature) => new AggregateTransactionCosignature( cosignature.substring(0, 64), - PublicAccount.createFromPublicKey(cosignature.substring(64, 192), networkType), + PublicAccount.createFromPublicKey(cosignature.substring(0, 64), networkType), )) : [], ); case TransactionType.AGGREGATE_BONDED: @@ -401,7 +401,7 @@ const CreateTransaction = (type: number, transactionData: string, networkType: N networkType, bondedConsignatureArray ? bondedConsignatureArray.map((cosignature) => new AggregateTransactionCosignature( cosignature.substring(0, 64), - PublicAccount.createFromPublicKey(cosignature.substring(64, 192), networkType), + PublicAccount.createFromPublicKey(cosignature.substring(0, 64), networkType), )) : [], ); default: @@ -457,9 +457,9 @@ const parseInnerTransactionFromBinary = (innerTransactionBinary: string): string while (innerBinary.length) { const payloadSize = parseInt(convert.uint8ToHex(convert.hexToUint8(innerTransactionBinary.substring(0, 8)).reverse()), 16) * 2; - const innerTransaction = innerTransactionBinary.substring(8, 8 + payloadSize); + const innerTransaction = innerTransactionBinary.substring(8, payloadSize); embeddedTransaction.push(innerTransaction); - innerBinary = innerTransactionBinary.substring(8 + payloadSize); + innerBinary = innerBinary.substring(payloadSize); } return embeddedTransaction; }; diff --git a/src/service/AggregatedTransactionService.ts b/src/service/AggregatedTransactionService.ts new file mode 100644 index 0000000000..ccdd72cd3b --- /dev/null +++ b/src/service/AggregatedTransactionService.ts @@ -0,0 +1,127 @@ +/* + * Copyright 2019 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {from as observableFrom , Observable, of as observableOf} from 'rxjs'; +import { map, mergeMap} from 'rxjs/operators'; +import { TransactionMapping } from '../core/utils/TransactionMapping'; +import { AccountHttp } from '../infrastructure/AccountHttp'; +import { MultisigAccountGraphInfo } from '../model/account/MultisigAccountGraphInfo'; +import { AggregateTransaction } from '../model/transaction/AggregateTransaction'; +import { SignedTransaction } from '../model/transaction/SignedTransaction'; + +/** + * Aggregated Transaction service + */ +export class AggregatedTransactionService { + + /** + * Constructor + * @param accountHttp + */ + constructor(private readonly accountHttp: AccountHttp) { + } + + /** + * Check if an aggregate complete transaction has all cosignatories attached + * @param signedTransaction - The signed aggregate transaction (complete) to be verified + * @returns {Observable} + */ + public isComplete(signedTransaction: SignedTransaction): Observable { + const aggregateTransaction = TransactionMapping.createFromPayload(signedTransaction.payload) as AggregateTransaction; + /** + * Include both initiator & cosigners + */ + const signers = (aggregateTransaction.cosignatures.map((cosigner) => cosigner.signer.publicKey)); + if (signedTransaction.signer) { + signers.push(signedTransaction.signer); + } + + return observableFrom(aggregateTransaction.innerTransactions).pipe( + mergeMap((innerTransaction) => this.accountHttp.getMultisigAccountInfo(innerTransaction.signer.address) + .pipe( + /** + * For multisig account, we need to get the graph info in case it has multiple levels + */ + mergeMap((_) => _.minApproval !== 0 && _.minRemoval !== 0 ? + this.accountHttp.getMultisigAccountGraphInfo(_.account.address) + .pipe( + map((graphInfo) => this.validateCosignatories(graphInfo, signers)), + ) : observableOf(true), + ), + ), + ), + ); + } + + /** + * Validate cosignatories against multisig Account(s) + * @param graphInfo - multisig account graph info + * @param cosignatories - array of cosignatories extracted from aggregated transaction + * @returns {boolean} + */ + private validateCosignatories(graphInfo: MultisigAccountGraphInfo, cosignatories: string[]): boolean { + /** + * Validate cosignatories from bottom level to top + */ + const sortedKeys = Array.from(graphInfo.multisigAccounts.keys()).sort((a, b) => b - a); + const cosignatoriesReceived = cosignatories; + let validationResult = true; + + sortedKeys.forEach((key) => { + const multisigInfo = graphInfo.multisigAccounts.get(key); + if (multisigInfo && validationResult) { + multisigInfo.forEach((multisig) => { + if (multisig.minApproval >= 1) { + const matchedCosignatories = this.compareArrays(cosignatoriesReceived, + multisig.cosignatories.map((cosig) => cosig.publicKey)); + + /** + * if minimal signature requirement met at current level, push the multisig account + * into the received signatories array for next level validation. + * Otherwise return validation failed. + */ + if (matchedCosignatories.length >= multisig.minApproval) { + if (cosignatoriesReceived.indexOf(multisig.account.publicKey) === -1) { + cosignatoriesReceived.push(multisig.account.publicKey); + } + } else { + validationResult = false; + } + } + }); + } + }); + + return validationResult; + } + + /** + * Compare two string arrays + * @param array1 - base array + * @param array2 - array to be matched + * @returns {string[]} - array of matched elements + */ + private compareArrays(array1: string[], array2: string[]): string[] { + const results: string[] = []; + array1.forEach((a1) => array2.forEach((a2) => { + if (a1 === a2) { + results.push(a1); + } + })); + + return results; + } +} diff --git a/src/service/service.ts b/src/service/service.ts index 6a9945c1cd..fcf4c84680 100644 --- a/src/service/service.ts +++ b/src/service/service.ts @@ -16,3 +16,4 @@ export * from './NamespaceService'; export * from './MosaicService'; +export * from './AggregatedTransactionService'; diff --git a/test/service/AggregatedTransactionService.spec.ts b/test/service/AggregatedTransactionService.spec.ts new file mode 100644 index 0000000000..0aff3d8f47 --- /dev/null +++ b/test/service/AggregatedTransactionService.spec.ts @@ -0,0 +1,242 @@ +/* + * Copyright 2018 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect } from 'chai'; +import { ChronoUnit } from 'js-joda'; +import {of as observableOf} from 'rxjs'; +import {deepEqual, instance, mock, when} from 'ts-mockito'; +import { AccountHttp } from '../../src/infrastructure/AccountHttp'; +import { Account } from '../../src/model/account/Account'; +import { Address } from '../../src/model/account/Address'; +import { MultisigAccountGraphInfo } from '../../src/model/account/MultisigAccountGraphInfo'; +import { MultisigAccountInfo } from '../../src/model/account/MultisigAccountInfo'; +import {NetworkType} from '../../src/model/blockchain/NetworkType'; +import { AggregateTransaction } from '../../src/model/transaction/AggregateTransaction'; +import { Deadline } from '../../src/model/transaction/Deadline'; +import { PlainMessage } from '../../src/model/transaction/PlainMessage'; +import { TransferTransaction } from '../../src/model/transaction/TransferTransaction'; +import { AggregatedTransactionService } from '../../src/service/AggregatedTransactionService'; + +/** + * For multi level multisig scenario visit: https://github.com/nemtech/nem2-docs/issues/10 + */ +describe('AggregatedTransactionService', () => { + let aggregatedTransactionService: AggregatedTransactionService; + + /** + * Multisig2 Account: SBROWP-7YMG2M-K45RO6-Q7ZPK7-G7GXWQ-JK5VNQ-OSUX + * Public Key: 5E628EA59818D97AA4118780D9A88C5512FCE7A21C195E1574727EFCE5DF7C0D + * Private Key: 22A1D67F8519D1A45BD7116600BB6E857786E816FE0B45E4C5B9FFF3D64BC177 + * + * + * Multisig1 Account: SAK32M-5JQ43R-WYHWEH-WRBCW4-RXERT2-DLASGL-EANS + * Public Key: BFDF2610C5666A626434FE12FB4A9D896D2B9B033F5F84CCEABE82E043A6307E + * Private Key: 8B0622C2CCFC5CCC5A74B500163E3C68F3AD3643DB12932FC931143EAC67280D + */ + + /** + * Test accounts: + * Multisig1 (1/1): Account2, Account3 + * Multisig2 (2/1): Account1, Multisig1 + * Stranger Account: Account4 + */ + + const account1 = Account.createFromPrivateKey('82DB2528834C9926F0FCCE042466B24A266F5B685CB66D2869AF6648C043E950', + NetworkType.MIJIN_TEST); + const multisig1 = Account.createFromPrivateKey('8B0622C2CCFC5CCC5A74B500163E3C68F3AD3643DB12932FC931143EAC67280D', + NetworkType.MIJIN_TEST); + const multisig2 = Account.createFromPrivateKey('22A1D67F8519D1A45BD7116600BB6E857786E816FE0B45E4C5B9FFF3D64BC177', + NetworkType.MIJIN_TEST); + + const account2 = Account.createFromPrivateKey('A4D410270E01CECDCDEADCDE32EC79C8D9CDEA4DCD426CB1EB666EFEF148FBCE', + NetworkType.MIJIN_TEST); + const account3 = Account.createFromPrivateKey('336AB45EE65A6AFFC0E7ADC5342F91E34BACA0B901A1D9C876FA25A1E590077E', + NetworkType.MIJIN_TEST); + + const account4 = Account.createFromPrivateKey('4D8B3756592532753344E11E2B7541317BCCFBBCF4444274CDBF359D2C4AE0F1', + NetworkType.MIJIN_TEST); + before(() => { + const mockedAccountHttp = mock(AccountHttp); + + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account1.address))) + .thenReturn(observableOf(givenAccount1Info())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account4.address))) + .thenReturn(observableOf(givenAccount4Info())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(multisig2.address))) + .thenReturn(observableOf(givenMultisig2AccountInfo())); + when(mockedAccountHttp.getMultisigAccountGraphInfo(deepEqual(multisig2.address))) + .thenReturn(observableOf(givenMultisig2AccountGraphInfo())); + + const accountHttp = instance(mockedAccountHttp); + aggregatedTransactionService = new AggregatedTransactionService(accountHttp); + }); + + it('should return isComplete: true for aggregated complete transaction - 2 levels Multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account2]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + it('should return isComplete: false for aggregated complete transaction - 2 levels Multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, []); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + + it('should return isComplete: false for aggregated complete transaction - 2 levels Multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + + it('should return correct isComplete status for aggregated complete transaction - 2 levels Multisig, multi inner transaction', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const transferTransaction2 = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount), + transferTransaction.toAggregate(account4.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account2]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + it('should return correct isComplete status for aggregated complete transaction - none multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(account4.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signWith(account1); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + function givenMultisig2AccountInfo(): MultisigAccountInfo { + return new MultisigAccountInfo(multisig2.publicAccount, + 2, 1, + [multisig1.publicAccount, + account1.publicAccount], + [], + ); + } + + function givenAccount1Info(): MultisigAccountInfo { + return new MultisigAccountInfo(account1.publicAccount, + 0, 0, + [], + [multisig2.publicAccount], + ); + } + function givenAccount4Info(): MultisigAccountInfo { + return new MultisigAccountInfo(account4.publicAccount, + 0, 0, + [], + [], + ); + } + + function givenMultisig2AccountGraphInfo(): MultisigAccountGraphInfo { + const map = new Map(); + map.set(0, [new MultisigAccountInfo(multisig2.publicAccount, + 2, 1, + [multisig1.publicAccount, + account1.publicAccount], + [], + )]) + .set(1, [new MultisigAccountInfo(multisig1.publicAccount, + 1, 1, + [account2.publicAccount, account3.publicAccount], + [multisig2.publicAccount], + )]); + + return new MultisigAccountGraphInfo(map); + } + +}); From 90e805f3ad802bd0b4137894525eb4fb15164f1f Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 1 Apr 2019 21:25:50 +0100 Subject: [PATCH 38/83] Updated license year --- test/service/AggregatedTransactionService.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/service/AggregatedTransactionService.spec.ts b/test/service/AggregatedTransactionService.spec.ts index 0aff3d8f47..bc0c11f535 100644 --- a/test/service/AggregatedTransactionService.spec.ts +++ b/test/service/AggregatedTransactionService.spec.ts @@ -1,5 +1,5 @@ /* - * Copyright 2018 NEM + * Copyright 2019 NEM * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From c33a4c36974ba8689eb342b9c6bf802e5a433c7c Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Wed, 3 Apr 2019 16:12:08 +0100 Subject: [PATCH 39/83] Added check in validate cosignatories for modify multisig account (removal) --- src/service/AggregatedTransactionService.ts | 29 ++++++++++++++++--- .../AggregatedTransactionService.spec.ts | 27 +++++++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/service/AggregatedTransactionService.ts b/src/service/AggregatedTransactionService.ts index ccdd72cd3b..7803b48bae 100644 --- a/src/service/AggregatedTransactionService.ts +++ b/src/service/AggregatedTransactionService.ts @@ -20,7 +20,11 @@ import { TransactionMapping } from '../core/utils/TransactionMapping'; import { AccountHttp } from '../infrastructure/AccountHttp'; import { MultisigAccountGraphInfo } from '../model/account/MultisigAccountGraphInfo'; import { AggregateTransaction } from '../model/transaction/AggregateTransaction'; +import { InnerTransaction } from '../model/transaction/InnerTransaction'; +import { ModifyMultisigAccountTransaction } from '../model/transaction/ModifyMultisigAccountTransaction'; +import { MultisigCosignatoryModificationType } from '../model/transaction/MultisigCosignatoryModificationType'; import { SignedTransaction } from '../model/transaction/SignedTransaction'; +import { TransactionType } from '../model/transaction/TransactionType'; /** * Aggregated Transaction service @@ -58,7 +62,7 @@ export class AggregatedTransactionService { mergeMap((_) => _.minApproval !== 0 && _.minRemoval !== 0 ? this.accountHttp.getMultisigAccountGraphInfo(_.account.address) .pipe( - map((graphInfo) => this.validateCosignatories(graphInfo, signers)), + map((graphInfo) => this.validateCosignatories(graphInfo, signers, innerTransaction)), ) : observableOf(true), ), ), @@ -70,9 +74,12 @@ export class AggregatedTransactionService { * Validate cosignatories against multisig Account(s) * @param graphInfo - multisig account graph info * @param cosignatories - array of cosignatories extracted from aggregated transaction + * @param innerTransaction - the inner transaction of the aggregated transaction * @returns {boolean} */ - private validateCosignatories(graphInfo: MultisigAccountGraphInfo, cosignatories: string[]): boolean { + private validateCosignatories(graphInfo: MultisigAccountGraphInfo, + cosignatories: string[], + innerTransaction: InnerTransaction): boolean { /** * Validate cosignatories from bottom level to top */ @@ -80,11 +87,24 @@ export class AggregatedTransactionService { const cosignatoriesReceived = cosignatories; let validationResult = true; + let isMultisigRemoval = false; + + /** + * Check inner transaction. If remove cosigner from multisig account, + * use minRemoval instead of minApproval for cosignatories validation. + */ + if (innerTransaction.type === TransactionType.MODIFY_MULTISIG_ACCOUNT) { + if ((innerTransaction as ModifyMultisigAccountTransaction).modifications + .find((modification) => modification.type === MultisigCosignatoryModificationType.Remove) !== undefined) { + isMultisigRemoval = true; + } + } + sortedKeys.forEach((key) => { const multisigInfo = graphInfo.multisigAccounts.get(key); if (multisigInfo && validationResult) { multisigInfo.forEach((multisig) => { - if (multisig.minApproval >= 1) { + if (multisig.minApproval >= 1 && multisig.minRemoval) { // To make sure it is multisig account const matchedCosignatories = this.compareArrays(cosignatoriesReceived, multisig.cosignatories.map((cosig) => cosig.publicKey)); @@ -93,7 +113,8 @@ export class AggregatedTransactionService { * into the received signatories array for next level validation. * Otherwise return validation failed. */ - if (matchedCosignatories.length >= multisig.minApproval) { + if ((matchedCosignatories.length >= multisig.minApproval && !isMultisigRemoval) || + (matchedCosignatories.length >= multisig.minRemoval && isMultisigRemoval)) { if (cosignatoriesReceived.indexOf(multisig.account.publicKey) === -1) { cosignatoriesReceived.push(multisig.account.publicKey); } diff --git a/test/service/AggregatedTransactionService.spec.ts b/test/service/AggregatedTransactionService.spec.ts index bc0c11f535..8ea1432ca6 100644 --- a/test/service/AggregatedTransactionService.spec.ts +++ b/test/service/AggregatedTransactionService.spec.ts @@ -26,6 +26,9 @@ import { MultisigAccountInfo } from '../../src/model/account/MultisigAccountInfo import {NetworkType} from '../../src/model/blockchain/NetworkType'; import { AggregateTransaction } from '../../src/model/transaction/AggregateTransaction'; import { Deadline } from '../../src/model/transaction/Deadline'; +import { ModifyMultisigAccountTransaction } from '../../src/model/transaction/ModifyMultisigAccountTransaction'; +import { MultisigCosignatoryModification } from '../../src/model/transaction/MultisigCosignatoryModification'; +import { MultisigCosignatoryModificationType } from '../../src/model/transaction/MultisigCosignatoryModificationType'; import { PlainMessage } from '../../src/model/transaction/PlainMessage'; import { TransferTransaction } from '../../src/model/transaction/TransferTransaction'; import { AggregatedTransactionService } from '../../src/service/AggregatedTransactionService'; @@ -177,6 +180,30 @@ describe('AggregatedTransactionService', () => { }); }); + it('should use minRemoval for multisig account validation if inner transaction is modify multisig remove', () => { + const modifyMultisigTransaction = ModifyMultisigAccountTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + 1, + 1, + [new MultisigCosignatoryModification( + MultisigCosignatoryModificationType.Remove, + account1.publicAccount, + )], + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [modifyMultisigTransaction.toAggregate(multisig2.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signWith(account2); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + it('should return correct isComplete status for aggregated complete transaction - none multisig', () => { const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), From 9c4ae75f55c1835af8e01c83fbae96cbc6771ea5 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Thu, 4 Apr 2019 15:03:56 +0100 Subject: [PATCH 40/83] Fixed bug in create transaction from payload --- .../transaction/CreateTransactionFromPayload.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/infrastructure/transaction/CreateTransactionFromPayload.ts b/src/infrastructure/transaction/CreateTransactionFromPayload.ts index 9b124a33bf..7b73d6f6d0 100644 --- a/src/infrastructure/transaction/CreateTransactionFromPayload.ts +++ b/src/infrastructure/transaction/CreateTransactionFromPayload.ts @@ -376,7 +376,7 @@ const CreateTransaction = (type: number, transactionData: string, networkType: N }), networkType, consignatureArray ? consignatureArray.map((cosignature) => new AggregateTransactionCosignature( - cosignature.substring(0, 64), + cosignature.substring(64, 192), PublicAccount.createFromPublicKey(cosignature.substring(0, 64), networkType), )) : [], ); @@ -400,7 +400,7 @@ const CreateTransaction = (type: number, transactionData: string, networkType: N }), networkType, bondedConsignatureArray ? bondedConsignatureArray.map((cosignature) => new AggregateTransactionCosignature( - cosignature.substring(0, 64), + cosignature.substring(64, 192), PublicAccount.createFromPublicKey(cosignature.substring(0, 64), networkType), )) : [], ); From ffed0a7e9a6819b9372f6edf7d3caa78acde8b75 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Fri, 5 Apr 2019 19:01:52 +0100 Subject: [PATCH 41/83] Added more test cases and fixed a few bugs --- .../CreateTransactionFromPayload.ts | 4 +- src/service/AggregatedTransactionService.ts | 12 +- .../AggregatedTransactionService.spec.ts | 305 +++++++++++++++++- 3 files changed, 309 insertions(+), 12 deletions(-) diff --git a/src/infrastructure/transaction/CreateTransactionFromPayload.ts b/src/infrastructure/transaction/CreateTransactionFromPayload.ts index 7b73d6f6d0..bf36d24a20 100644 --- a/src/infrastructure/transaction/CreateTransactionFromPayload.ts +++ b/src/infrastructure/transaction/CreateTransactionFromPayload.ts @@ -456,8 +456,8 @@ const parseInnerTransactionFromBinary = (innerTransactionBinary: string): string let innerBinary = innerTransactionBinary; while (innerBinary.length) { - const payloadSize = parseInt(convert.uint8ToHex(convert.hexToUint8(innerTransactionBinary.substring(0, 8)).reverse()), 16) * 2; - const innerTransaction = innerTransactionBinary.substring(8, payloadSize); + const payloadSize = parseInt(convert.uint8ToHex(convert.hexToUint8(innerBinary.substring(0, 8)).reverse()), 16) * 2; + const innerTransaction = innerBinary.substring(8, payloadSize); embeddedTransaction.push(innerTransaction); innerBinary = innerBinary.substring(payloadSize); } diff --git a/src/service/AggregatedTransactionService.ts b/src/service/AggregatedTransactionService.ts index 7803b48bae..75a806bf96 100644 --- a/src/service/AggregatedTransactionService.ts +++ b/src/service/AggregatedTransactionService.ts @@ -15,7 +15,7 @@ */ import {from as observableFrom , Observable, of as observableOf} from 'rxjs'; -import { map, mergeMap} from 'rxjs/operators'; +import { flatMap, map, mergeMap, toArray} from 'rxjs/operators'; import { TransactionMapping } from '../core/utils/TransactionMapping'; import { AccountHttp } from '../infrastructure/AccountHttp'; import { MultisigAccountGraphInfo } from '../model/account/MultisigAccountGraphInfo'; @@ -52,7 +52,6 @@ export class AggregatedTransactionService { if (signedTransaction.signer) { signers.push(signedTransaction.signer); } - return observableFrom(aggregateTransaction.innerTransactions).pipe( mergeMap((innerTransaction) => this.accountHttp.getMultisigAccountInfo(innerTransaction.signer.address) .pipe( @@ -63,11 +62,16 @@ export class AggregatedTransactionService { this.accountHttp.getMultisigAccountGraphInfo(_.account.address) .pipe( map((graphInfo) => this.validateCosignatories(graphInfo, signers, innerTransaction)), - ) : observableOf(true), + ) : observableOf(signers.find((s) => s === _.account.publicKey ) !== undefined), ), ), ), - ); + toArray(), + ).pipe( + flatMap((results) => { + return observableOf(results.every((isComplete) => isComplete)); + }), + ); } /** diff --git a/test/service/AggregatedTransactionService.spec.ts b/test/service/AggregatedTransactionService.spec.ts index 8ea1432ca6..23470cb2a1 100644 --- a/test/service/AggregatedTransactionService.spec.ts +++ b/test/service/AggregatedTransactionService.spec.ts @@ -54,6 +54,7 @@ describe('AggregatedTransactionService', () => { * Test accounts: * Multisig1 (1/1): Account2, Account3 * Multisig2 (2/1): Account1, Multisig1 + * Multisig3 (2/2): Account2, Account3 * Stranger Account: Account4 */ @@ -64,6 +65,8 @@ describe('AggregatedTransactionService', () => { const multisig2 = Account.createFromPrivateKey('22A1D67F8519D1A45BD7116600BB6E857786E816FE0B45E4C5B9FFF3D64BC177', NetworkType.MIJIN_TEST); + const multisig3 = Account.createFromPrivateKey('5E7812AB0E709ABC45466034E1A209099F6A12C4698748A63CDCAA9B0DDE1DBD', + NetworkType.MIJIN_TEST); const account2 = Account.createFromPrivateKey('A4D410270E01CECDCDEADCDE32EC79C8D9CDEA4DCD426CB1EB666EFEF148FBCE', NetworkType.MIJIN_TEST); const account3 = Account.createFromPrivateKey('336AB45EE65A6AFFC0E7ADC5342F91E34BACA0B901A1D9C876FA25A1E590077E', @@ -80,14 +83,30 @@ describe('AggregatedTransactionService', () => { .thenReturn(observableOf(givenAccount4Info())); when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(multisig2.address))) .thenReturn(observableOf(givenMultisig2AccountInfo())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(multisig3.address))) + .thenReturn(observableOf(givenMultisig3AccountInfo())); when(mockedAccountHttp.getMultisigAccountGraphInfo(deepEqual(multisig2.address))) .thenReturn(observableOf(givenMultisig2AccountGraphInfo())); + when(mockedAccountHttp.getMultisigAccountGraphInfo(deepEqual(multisig3.address))) + .thenReturn(observableOf(givenMultisig3AccountGraphInfo())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account2.address))) + .thenReturn(observableOf(givenAccount2Info())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account3.address))) + .thenReturn(observableOf(givenAccount3Info())); const accountHttp = instance(mockedAccountHttp); aggregatedTransactionService = new AggregatedTransactionService(accountHttp); }); it('should return isComplete: true for aggregated complete transaction - 2 levels Multisig', () => { + /** + * MLMA + * Alice (account1): normal account + * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) + * Charles (multisig1) - Multisig 1-1 (account2 || account3) + * Given signatories: Account1 && Account4 + * Expecting complete as Bob needs 2 signatures (account1 && (account2 || account3)) + */ const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), @@ -109,6 +128,14 @@ describe('AggregatedTransactionService', () => { }); it('should return isComplete: false for aggregated complete transaction - 2 levels Multisig', () => { + /** + * MLMA + * Alice (account1): normal account + * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) + * Charles (multisig1) - Multisig 1-1 (account2 || account3) + * Given signatories: Account1 && Account4 + * Expecting incomplete as Bob needs 2 signatures (account1 && (account2 || account3)) but only got account1 + */ const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), @@ -130,6 +157,14 @@ describe('AggregatedTransactionService', () => { }); it('should return isComplete: false for aggregated complete transaction - 2 levels Multisig', () => { + /** + * MLMA + * Alice (account1): normal account + * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) + * Charles (multisig1) - Multisig 1-1 (account2 || account3) + * Given signatories: Account1 && Account4 + * Expecting incomplete as Bob needs 2 signatures (account1 && (account2 || account3)) but got account4 + */ const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), @@ -151,9 +186,18 @@ describe('AggregatedTransactionService', () => { }); it('should return correct isComplete status for aggregated complete transaction - 2 levels Multisig, multi inner transaction', () => { + /** + * MLMA - with multiple transaction + * Alice (account1): normal account + * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) + * Charles (multisig1) - Multisig 1-1 (account2 || account3) + * An extra inner transaction to account4 (just to increase the complexity) + * Given signatories: Account1 && Account4 + * Expecting incomplete as Bob needs 2 signatures (account1 && (account2 || account3)) + */ const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), - Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + account2.address, [], PlainMessage.create('test-message'), NetworkType.MIJIN_TEST, @@ -161,7 +205,7 @@ describe('AggregatedTransactionService', () => { const transferTransaction2 = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), - Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + account2.address, [], PlainMessage.create('test-message'), NetworkType.MIJIN_TEST, @@ -170,17 +214,60 @@ describe('AggregatedTransactionService', () => { const aggregateTransaction = AggregateTransaction.createComplete( Deadline.create(), [transferTransaction.toAggregate(multisig2.publicAccount), - transferTransaction.toAggregate(account4.publicAccount)], + transferTransaction2.toAggregate(account4.publicAccount)], NetworkType.MIJIN_TEST, []); + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); - const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account2]); + it('should return correct isComplete status for aggregated complete transaction - 2 levels Multisig, multi inner transaction', () => { + /** + * MLMA - with multiple transaction + * Alice (account1): normal account + * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) + * Charles (multisig1) - Multisig 1-1 (account2 || account3) + * An extra inner transaction to account4 (just to increase the complexity) + * Given signatories: Account1 && Account4 && Account2 + * Expecting complete + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account2.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const transferTransaction2 = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account2.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount), + transferTransaction2.toAggregate(account4.publicAccount)], + NetworkType.MIJIN_TEST, + []); + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4, account2]); aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); it('should use minRemoval for multisig account validation if inner transaction is modify multisig remove', () => { + /** + * If the inner transaction is issued to a multisig account + * and the inner transaction itself is a ModifyMultiSigAccountTransaction - Removal + * The validator should use minRemoval value rather than minApproval value + * to determine if the act is complete or not + */ const modifyMultisigTransaction = ModifyMultisigAccountTransaction.create( Deadline.create(1, ChronoUnit.HOURS), 1, @@ -197,14 +284,19 @@ describe('AggregatedTransactionService', () => { [modifyMultisigTransaction.toAggregate(multisig2.publicAccount)], NetworkType.MIJIN_TEST, []); - const signedTransaction = aggregateTransaction.signWith(account2); aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); - it('should return correct isComplete status for aggregated complete transaction - none multisig', () => { + it('should return correct isComplete status (false) for aggregated complete transaction - none multisig', () => { + /** + * If the inner transaction is issued to a multisig account + * and the inner transaction itself is a ModifyMultiSigAccountTransaction - Removal + * The validator should use minRemoval value rather than minApproval value + * to determine if the act is complete or not + */ const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), @@ -220,11 +312,176 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signWith(account1); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + + it('should return correct isComplete status (true) for aggregated complete transaction - none multisig', () => { + /** + * ACT + * Alice (account1): normal account + * Bob (account4) - normal account + * Alice initiate the transaction + * Bob sign + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(account4.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signWith(account4); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + it('should return correct isComplete status TRUE - multiple normal account', () => { + /** + * ACT + * Alice: account1 + * Bog: account4 + * An escrow contract is signed by all the participants (normal accounts) + * Given Alice defined the following escrow contract: + * | sender | recipient | type | data | + * | Alice | Bob | send-an-asset | 1 concert.ticket | + * | Bob | Alice | send-an-asset | 20 euros | + * And Bob signs the contract + * And Alice signs the contract + * Then the contract should appear as complete + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account1.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const transferTransaction2 = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account4.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(account4.publicAccount), + transferTransaction2.toAggregate(account1.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); + it('should return correct isComplete status FALSE - multiple normal account', () => { + /** + * ACT + * Alice: account1 + * Bog: account4 + * An escrow contract is signed by all the participants (normal accounts) + * Given Alice defined the following escrow contract: + * | sender | recipient | type | data | + * | Alice | Bob | send-an-asset | 1 concert.ticket | + * | Bob | Alice | send-an-asset | 20 euros | + * And Alice signs the contract + * Then the contract should appear as incomplete + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account1.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const transferTransaction2 = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account4.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(account4.publicAccount), + transferTransaction2.toAggregate(account1.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, []); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + + it('should return correct isComplete status TRUE - multisig Single Level', () => { + /** + * ACT - Multisig single level + * Alice (account1): initiate an transfer to Bob + * Bob (multisig3): is a 2/2 multisig account (account2 && account3) + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account4.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig3.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account2, [account3]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + it('should return correct isComplete status FALSE - multisig Single Level', () => { + /** + * ACT - Multisig single level + * Alice (account1): initiate an transfer to Bob + * Bob (multisig3): is a 2/2 multisig account (account2 && account3) + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account4.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig3.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account2, []); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + function givenMultisig2AccountInfo(): MultisigAccountInfo { return new MultisigAccountInfo(multisig2.publicAccount, 2, 1, @@ -233,6 +490,14 @@ describe('AggregatedTransactionService', () => { [], ); } + function givenMultisig3AccountInfo(): MultisigAccountInfo { + return new MultisigAccountInfo(multisig3.publicAccount, + 2, 2, + [account2.publicAccount, + account3.publicAccount], + [], + ); + } function givenAccount1Info(): MultisigAccountInfo { return new MultisigAccountInfo(account1.publicAccount, @@ -241,6 +506,22 @@ describe('AggregatedTransactionService', () => { [multisig2.publicAccount], ); } + function givenAccount2Info(): MultisigAccountInfo { + return new MultisigAccountInfo(account2.publicAccount, + 0, 0, + [], + [multisig2.publicAccount, + multisig3.publicAccount], + ); + } + function givenAccount3Info(): MultisigAccountInfo { + return new MultisigAccountInfo(account3.publicAccount, + 0, 0, + [], + [multisig2.publicAccount, + multisig3.publicAccount], + ); + } function givenAccount4Info(): MultisigAccountInfo { return new MultisigAccountInfo(account4.publicAccount, 0, 0, @@ -266,4 +547,16 @@ describe('AggregatedTransactionService', () => { return new MultisigAccountGraphInfo(map); } + function givenMultisig3AccountGraphInfo(): MultisigAccountGraphInfo { + const map = new Map(); + map.set(0, [new MultisigAccountInfo(multisig3.publicAccount, + 2, 2, + [account2.publicAccount, + account3.publicAccount], + [], + )]); + + return new MultisigAccountGraphInfo(map); + } + }); From 461f34b0f792393eff761daceeded468593ac15d Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Wed, 10 Apr 2019 15:13:01 +0100 Subject: [PATCH 42/83] renamed AggregatedTransactionService --- ...vice.ts => AggregateTransactionService.ts} | 2 +- src/service/service.ts | 2 +- ...ts => AggregateTransactionService.spec.ts} | 32 +++++++++---------- 3 files changed, 18 insertions(+), 18 deletions(-) rename src/service/{AggregatedTransactionService.ts => AggregateTransactionService.ts} (99%) rename test/service/{AggregatedTransactionService.spec.ts => AggregateTransactionService.spec.ts} (93%) diff --git a/src/service/AggregatedTransactionService.ts b/src/service/AggregateTransactionService.ts similarity index 99% rename from src/service/AggregatedTransactionService.ts rename to src/service/AggregateTransactionService.ts index 75a806bf96..800d1a1b89 100644 --- a/src/service/AggregatedTransactionService.ts +++ b/src/service/AggregateTransactionService.ts @@ -29,7 +29,7 @@ import { TransactionType } from '../model/transaction/TransactionType'; /** * Aggregated Transaction service */ -export class AggregatedTransactionService { +export class AggregateTransactionService { /** * Constructor diff --git a/src/service/service.ts b/src/service/service.ts index fcf4c84680..c988e13ffb 100644 --- a/src/service/service.ts +++ b/src/service/service.ts @@ -16,4 +16,4 @@ export * from './NamespaceService'; export * from './MosaicService'; -export * from './AggregatedTransactionService'; +export * from './AggregateTransactionService'; diff --git a/test/service/AggregatedTransactionService.spec.ts b/test/service/AggregateTransactionService.spec.ts similarity index 93% rename from test/service/AggregatedTransactionService.spec.ts rename to test/service/AggregateTransactionService.spec.ts index 23470cb2a1..ae2deab99b 100644 --- a/test/service/AggregatedTransactionService.spec.ts +++ b/test/service/AggregateTransactionService.spec.ts @@ -31,13 +31,13 @@ import { MultisigCosignatoryModification } from '../../src/model/transaction/Mul import { MultisigCosignatoryModificationType } from '../../src/model/transaction/MultisigCosignatoryModificationType'; import { PlainMessage } from '../../src/model/transaction/PlainMessage'; import { TransferTransaction } from '../../src/model/transaction/TransferTransaction'; -import { AggregatedTransactionService } from '../../src/service/AggregatedTransactionService'; +import { AggregateTransactionService } from '../../src/service/AggregateTransactionService'; /** * For multi level multisig scenario visit: https://github.com/nemtech/nem2-docs/issues/10 */ -describe('AggregatedTransactionService', () => { - let aggregatedTransactionService: AggregatedTransactionService; +describe('AggregateTransactionService', () => { + let aggregateTransactionService: AggregateTransactionService; /** * Multisig2 Account: SBROWP-7YMG2M-K45RO6-Q7ZPK7-G7GXWQ-JK5VNQ-OSUX @@ -95,7 +95,7 @@ describe('AggregatedTransactionService', () => { .thenReturn(observableOf(givenAccount3Info())); const accountHttp = instance(mockedAccountHttp); - aggregatedTransactionService = new AggregatedTransactionService(accountHttp); + aggregateTransactionService = new AggregateTransactionService(accountHttp); }); it('should return isComplete: true for aggregated complete transaction - 2 levels Multisig', () => { @@ -122,7 +122,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account2]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); @@ -151,7 +151,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, []); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.false; }); }); @@ -180,7 +180,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.false; }); }); @@ -218,7 +218,7 @@ describe('AggregatedTransactionService', () => { NetworkType.MIJIN_TEST, []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.false; }); }); @@ -256,7 +256,7 @@ describe('AggregatedTransactionService', () => { NetworkType.MIJIN_TEST, []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4, account2]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); @@ -285,7 +285,7 @@ describe('AggregatedTransactionService', () => { NetworkType.MIJIN_TEST, []); const signedTransaction = aggregateTransaction.signWith(account2); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); @@ -312,7 +312,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signWith(account1); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.false; }); }); @@ -340,7 +340,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signWith(account4); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); @@ -383,7 +383,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); @@ -425,7 +425,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, []); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.false; }); }); @@ -451,7 +451,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account2, [account3]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); @@ -477,7 +477,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account2, []); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.false; }); }); From f944d3beba246b063946e8104eb60288b4abd11e Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 1 Apr 2019 20:39:11 +0100 Subject: [PATCH 43/83] #4 1. Addes service for Aggregated Transaction - isComplete 2. Fixed couple of bugs in TransactionMappings --- .../CreateTransactionFromPayload.ts | 8 +- src/service/AggregatedTransactionService.ts | 127 +++++++++ src/service/service.ts | 1 + .../AggregatedTransactionService.spec.ts | 242 ++++++++++++++++++ 4 files changed, 374 insertions(+), 4 deletions(-) create mode 100644 src/service/AggregatedTransactionService.ts create mode 100644 test/service/AggregatedTransactionService.spec.ts diff --git a/src/infrastructure/transaction/CreateTransactionFromPayload.ts b/src/infrastructure/transaction/CreateTransactionFromPayload.ts index af8bd12125..494d4bc10e 100644 --- a/src/infrastructure/transaction/CreateTransactionFromPayload.ts +++ b/src/infrastructure/transaction/CreateTransactionFromPayload.ts @@ -377,7 +377,7 @@ const CreateTransaction = (type: number, transactionData: string, networkType: N networkType, consignatureArray ? consignatureArray.map((cosignature) => new AggregateTransactionCosignature( cosignature.substring(0, 64), - PublicAccount.createFromPublicKey(cosignature.substring(64, 192), networkType), + PublicAccount.createFromPublicKey(cosignature.substring(0, 64), networkType), )) : [], ); case TransactionType.AGGREGATE_BONDED: @@ -401,7 +401,7 @@ const CreateTransaction = (type: number, transactionData: string, networkType: N networkType, bondedConsignatureArray ? bondedConsignatureArray.map((cosignature) => new AggregateTransactionCosignature( cosignature.substring(0, 64), - PublicAccount.createFromPublicKey(cosignature.substring(64, 192), networkType), + PublicAccount.createFromPublicKey(cosignature.substring(0, 64), networkType), )) : [], ); default: @@ -457,9 +457,9 @@ const parseInnerTransactionFromBinary = (innerTransactionBinary: string): string while (innerBinary.length) { const payloadSize = parseInt(convert.uint8ToHex(convert.hexToUint8(innerTransactionBinary.substring(0, 8)).reverse()), 16) * 2; - const innerTransaction = innerTransactionBinary.substring(8, 8 + payloadSize); + const innerTransaction = innerTransactionBinary.substring(8, payloadSize); embeddedTransaction.push(innerTransaction); - innerBinary = innerTransactionBinary.substring(8 + payloadSize); + innerBinary = innerBinary.substring(payloadSize); } return embeddedTransaction; }; diff --git a/src/service/AggregatedTransactionService.ts b/src/service/AggregatedTransactionService.ts new file mode 100644 index 0000000000..ccdd72cd3b --- /dev/null +++ b/src/service/AggregatedTransactionService.ts @@ -0,0 +1,127 @@ +/* + * Copyright 2019 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {from as observableFrom , Observable, of as observableOf} from 'rxjs'; +import { map, mergeMap} from 'rxjs/operators'; +import { TransactionMapping } from '../core/utils/TransactionMapping'; +import { AccountHttp } from '../infrastructure/AccountHttp'; +import { MultisigAccountGraphInfo } from '../model/account/MultisigAccountGraphInfo'; +import { AggregateTransaction } from '../model/transaction/AggregateTransaction'; +import { SignedTransaction } from '../model/transaction/SignedTransaction'; + +/** + * Aggregated Transaction service + */ +export class AggregatedTransactionService { + + /** + * Constructor + * @param accountHttp + */ + constructor(private readonly accountHttp: AccountHttp) { + } + + /** + * Check if an aggregate complete transaction has all cosignatories attached + * @param signedTransaction - The signed aggregate transaction (complete) to be verified + * @returns {Observable} + */ + public isComplete(signedTransaction: SignedTransaction): Observable { + const aggregateTransaction = TransactionMapping.createFromPayload(signedTransaction.payload) as AggregateTransaction; + /** + * Include both initiator & cosigners + */ + const signers = (aggregateTransaction.cosignatures.map((cosigner) => cosigner.signer.publicKey)); + if (signedTransaction.signer) { + signers.push(signedTransaction.signer); + } + + return observableFrom(aggregateTransaction.innerTransactions).pipe( + mergeMap((innerTransaction) => this.accountHttp.getMultisigAccountInfo(innerTransaction.signer.address) + .pipe( + /** + * For multisig account, we need to get the graph info in case it has multiple levels + */ + mergeMap((_) => _.minApproval !== 0 && _.minRemoval !== 0 ? + this.accountHttp.getMultisigAccountGraphInfo(_.account.address) + .pipe( + map((graphInfo) => this.validateCosignatories(graphInfo, signers)), + ) : observableOf(true), + ), + ), + ), + ); + } + + /** + * Validate cosignatories against multisig Account(s) + * @param graphInfo - multisig account graph info + * @param cosignatories - array of cosignatories extracted from aggregated transaction + * @returns {boolean} + */ + private validateCosignatories(graphInfo: MultisigAccountGraphInfo, cosignatories: string[]): boolean { + /** + * Validate cosignatories from bottom level to top + */ + const sortedKeys = Array.from(graphInfo.multisigAccounts.keys()).sort((a, b) => b - a); + const cosignatoriesReceived = cosignatories; + let validationResult = true; + + sortedKeys.forEach((key) => { + const multisigInfo = graphInfo.multisigAccounts.get(key); + if (multisigInfo && validationResult) { + multisigInfo.forEach((multisig) => { + if (multisig.minApproval >= 1) { + const matchedCosignatories = this.compareArrays(cosignatoriesReceived, + multisig.cosignatories.map((cosig) => cosig.publicKey)); + + /** + * if minimal signature requirement met at current level, push the multisig account + * into the received signatories array for next level validation. + * Otherwise return validation failed. + */ + if (matchedCosignatories.length >= multisig.minApproval) { + if (cosignatoriesReceived.indexOf(multisig.account.publicKey) === -1) { + cosignatoriesReceived.push(multisig.account.publicKey); + } + } else { + validationResult = false; + } + } + }); + } + }); + + return validationResult; + } + + /** + * Compare two string arrays + * @param array1 - base array + * @param array2 - array to be matched + * @returns {string[]} - array of matched elements + */ + private compareArrays(array1: string[], array2: string[]): string[] { + const results: string[] = []; + array1.forEach((a1) => array2.forEach((a2) => { + if (a1 === a2) { + results.push(a1); + } + })); + + return results; + } +} diff --git a/src/service/service.ts b/src/service/service.ts index 6a9945c1cd..fcf4c84680 100644 --- a/src/service/service.ts +++ b/src/service/service.ts @@ -16,3 +16,4 @@ export * from './NamespaceService'; export * from './MosaicService'; +export * from './AggregatedTransactionService'; diff --git a/test/service/AggregatedTransactionService.spec.ts b/test/service/AggregatedTransactionService.spec.ts new file mode 100644 index 0000000000..0aff3d8f47 --- /dev/null +++ b/test/service/AggregatedTransactionService.spec.ts @@ -0,0 +1,242 @@ +/* + * Copyright 2018 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect } from 'chai'; +import { ChronoUnit } from 'js-joda'; +import {of as observableOf} from 'rxjs'; +import {deepEqual, instance, mock, when} from 'ts-mockito'; +import { AccountHttp } from '../../src/infrastructure/AccountHttp'; +import { Account } from '../../src/model/account/Account'; +import { Address } from '../../src/model/account/Address'; +import { MultisigAccountGraphInfo } from '../../src/model/account/MultisigAccountGraphInfo'; +import { MultisigAccountInfo } from '../../src/model/account/MultisigAccountInfo'; +import {NetworkType} from '../../src/model/blockchain/NetworkType'; +import { AggregateTransaction } from '../../src/model/transaction/AggregateTransaction'; +import { Deadline } from '../../src/model/transaction/Deadline'; +import { PlainMessage } from '../../src/model/transaction/PlainMessage'; +import { TransferTransaction } from '../../src/model/transaction/TransferTransaction'; +import { AggregatedTransactionService } from '../../src/service/AggregatedTransactionService'; + +/** + * For multi level multisig scenario visit: https://github.com/nemtech/nem2-docs/issues/10 + */ +describe('AggregatedTransactionService', () => { + let aggregatedTransactionService: AggregatedTransactionService; + + /** + * Multisig2 Account: SBROWP-7YMG2M-K45RO6-Q7ZPK7-G7GXWQ-JK5VNQ-OSUX + * Public Key: 5E628EA59818D97AA4118780D9A88C5512FCE7A21C195E1574727EFCE5DF7C0D + * Private Key: 22A1D67F8519D1A45BD7116600BB6E857786E816FE0B45E4C5B9FFF3D64BC177 + * + * + * Multisig1 Account: SAK32M-5JQ43R-WYHWEH-WRBCW4-RXERT2-DLASGL-EANS + * Public Key: BFDF2610C5666A626434FE12FB4A9D896D2B9B033F5F84CCEABE82E043A6307E + * Private Key: 8B0622C2CCFC5CCC5A74B500163E3C68F3AD3643DB12932FC931143EAC67280D + */ + + /** + * Test accounts: + * Multisig1 (1/1): Account2, Account3 + * Multisig2 (2/1): Account1, Multisig1 + * Stranger Account: Account4 + */ + + const account1 = Account.createFromPrivateKey('82DB2528834C9926F0FCCE042466B24A266F5B685CB66D2869AF6648C043E950', + NetworkType.MIJIN_TEST); + const multisig1 = Account.createFromPrivateKey('8B0622C2CCFC5CCC5A74B500163E3C68F3AD3643DB12932FC931143EAC67280D', + NetworkType.MIJIN_TEST); + const multisig2 = Account.createFromPrivateKey('22A1D67F8519D1A45BD7116600BB6E857786E816FE0B45E4C5B9FFF3D64BC177', + NetworkType.MIJIN_TEST); + + const account2 = Account.createFromPrivateKey('A4D410270E01CECDCDEADCDE32EC79C8D9CDEA4DCD426CB1EB666EFEF148FBCE', + NetworkType.MIJIN_TEST); + const account3 = Account.createFromPrivateKey('336AB45EE65A6AFFC0E7ADC5342F91E34BACA0B901A1D9C876FA25A1E590077E', + NetworkType.MIJIN_TEST); + + const account4 = Account.createFromPrivateKey('4D8B3756592532753344E11E2B7541317BCCFBBCF4444274CDBF359D2C4AE0F1', + NetworkType.MIJIN_TEST); + before(() => { + const mockedAccountHttp = mock(AccountHttp); + + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account1.address))) + .thenReturn(observableOf(givenAccount1Info())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account4.address))) + .thenReturn(observableOf(givenAccount4Info())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(multisig2.address))) + .thenReturn(observableOf(givenMultisig2AccountInfo())); + when(mockedAccountHttp.getMultisigAccountGraphInfo(deepEqual(multisig2.address))) + .thenReturn(observableOf(givenMultisig2AccountGraphInfo())); + + const accountHttp = instance(mockedAccountHttp); + aggregatedTransactionService = new AggregatedTransactionService(accountHttp); + }); + + it('should return isComplete: true for aggregated complete transaction - 2 levels Multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account2]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + it('should return isComplete: false for aggregated complete transaction - 2 levels Multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, []); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + + it('should return isComplete: false for aggregated complete transaction - 2 levels Multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + + it('should return correct isComplete status for aggregated complete transaction - 2 levels Multisig, multi inner transaction', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const transferTransaction2 = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount), + transferTransaction.toAggregate(account4.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account2]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + it('should return correct isComplete status for aggregated complete transaction - none multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(account4.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signWith(account1); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + function givenMultisig2AccountInfo(): MultisigAccountInfo { + return new MultisigAccountInfo(multisig2.publicAccount, + 2, 1, + [multisig1.publicAccount, + account1.publicAccount], + [], + ); + } + + function givenAccount1Info(): MultisigAccountInfo { + return new MultisigAccountInfo(account1.publicAccount, + 0, 0, + [], + [multisig2.publicAccount], + ); + } + function givenAccount4Info(): MultisigAccountInfo { + return new MultisigAccountInfo(account4.publicAccount, + 0, 0, + [], + [], + ); + } + + function givenMultisig2AccountGraphInfo(): MultisigAccountGraphInfo { + const map = new Map(); + map.set(0, [new MultisigAccountInfo(multisig2.publicAccount, + 2, 1, + [multisig1.publicAccount, + account1.publicAccount], + [], + )]) + .set(1, [new MultisigAccountInfo(multisig1.publicAccount, + 1, 1, + [account2.publicAccount, account3.publicAccount], + [multisig2.publicAccount], + )]); + + return new MultisigAccountGraphInfo(map); + } + +}); From 516b5e64604a8403060cf04ac7dde3c7628f3f81 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 1 Apr 2019 21:25:50 +0100 Subject: [PATCH 44/83] Updated license year --- test/service/AggregatedTransactionService.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/service/AggregatedTransactionService.spec.ts b/test/service/AggregatedTransactionService.spec.ts index 0aff3d8f47..bc0c11f535 100644 --- a/test/service/AggregatedTransactionService.spec.ts +++ b/test/service/AggregatedTransactionService.spec.ts @@ -1,5 +1,5 @@ /* - * Copyright 2018 NEM + * Copyright 2019 NEM * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From fb471643fec486e8313c6c722a6728b624348a6d Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Wed, 3 Apr 2019 16:12:08 +0100 Subject: [PATCH 45/83] Added check in validate cosignatories for modify multisig account (removal) --- src/service/AggregatedTransactionService.ts | 29 ++++++++++++++++--- .../AggregatedTransactionService.spec.ts | 27 +++++++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/service/AggregatedTransactionService.ts b/src/service/AggregatedTransactionService.ts index ccdd72cd3b..7803b48bae 100644 --- a/src/service/AggregatedTransactionService.ts +++ b/src/service/AggregatedTransactionService.ts @@ -20,7 +20,11 @@ import { TransactionMapping } from '../core/utils/TransactionMapping'; import { AccountHttp } from '../infrastructure/AccountHttp'; import { MultisigAccountGraphInfo } from '../model/account/MultisigAccountGraphInfo'; import { AggregateTransaction } from '../model/transaction/AggregateTransaction'; +import { InnerTransaction } from '../model/transaction/InnerTransaction'; +import { ModifyMultisigAccountTransaction } from '../model/transaction/ModifyMultisigAccountTransaction'; +import { MultisigCosignatoryModificationType } from '../model/transaction/MultisigCosignatoryModificationType'; import { SignedTransaction } from '../model/transaction/SignedTransaction'; +import { TransactionType } from '../model/transaction/TransactionType'; /** * Aggregated Transaction service @@ -58,7 +62,7 @@ export class AggregatedTransactionService { mergeMap((_) => _.minApproval !== 0 && _.minRemoval !== 0 ? this.accountHttp.getMultisigAccountGraphInfo(_.account.address) .pipe( - map((graphInfo) => this.validateCosignatories(graphInfo, signers)), + map((graphInfo) => this.validateCosignatories(graphInfo, signers, innerTransaction)), ) : observableOf(true), ), ), @@ -70,9 +74,12 @@ export class AggregatedTransactionService { * Validate cosignatories against multisig Account(s) * @param graphInfo - multisig account graph info * @param cosignatories - array of cosignatories extracted from aggregated transaction + * @param innerTransaction - the inner transaction of the aggregated transaction * @returns {boolean} */ - private validateCosignatories(graphInfo: MultisigAccountGraphInfo, cosignatories: string[]): boolean { + private validateCosignatories(graphInfo: MultisigAccountGraphInfo, + cosignatories: string[], + innerTransaction: InnerTransaction): boolean { /** * Validate cosignatories from bottom level to top */ @@ -80,11 +87,24 @@ export class AggregatedTransactionService { const cosignatoriesReceived = cosignatories; let validationResult = true; + let isMultisigRemoval = false; + + /** + * Check inner transaction. If remove cosigner from multisig account, + * use minRemoval instead of minApproval for cosignatories validation. + */ + if (innerTransaction.type === TransactionType.MODIFY_MULTISIG_ACCOUNT) { + if ((innerTransaction as ModifyMultisigAccountTransaction).modifications + .find((modification) => modification.type === MultisigCosignatoryModificationType.Remove) !== undefined) { + isMultisigRemoval = true; + } + } + sortedKeys.forEach((key) => { const multisigInfo = graphInfo.multisigAccounts.get(key); if (multisigInfo && validationResult) { multisigInfo.forEach((multisig) => { - if (multisig.minApproval >= 1) { + if (multisig.minApproval >= 1 && multisig.minRemoval) { // To make sure it is multisig account const matchedCosignatories = this.compareArrays(cosignatoriesReceived, multisig.cosignatories.map((cosig) => cosig.publicKey)); @@ -93,7 +113,8 @@ export class AggregatedTransactionService { * into the received signatories array for next level validation. * Otherwise return validation failed. */ - if (matchedCosignatories.length >= multisig.minApproval) { + if ((matchedCosignatories.length >= multisig.minApproval && !isMultisigRemoval) || + (matchedCosignatories.length >= multisig.minRemoval && isMultisigRemoval)) { if (cosignatoriesReceived.indexOf(multisig.account.publicKey) === -1) { cosignatoriesReceived.push(multisig.account.publicKey); } diff --git a/test/service/AggregatedTransactionService.spec.ts b/test/service/AggregatedTransactionService.spec.ts index bc0c11f535..8ea1432ca6 100644 --- a/test/service/AggregatedTransactionService.spec.ts +++ b/test/service/AggregatedTransactionService.spec.ts @@ -26,6 +26,9 @@ import { MultisigAccountInfo } from '../../src/model/account/MultisigAccountInfo import {NetworkType} from '../../src/model/blockchain/NetworkType'; import { AggregateTransaction } from '../../src/model/transaction/AggregateTransaction'; import { Deadline } from '../../src/model/transaction/Deadline'; +import { ModifyMultisigAccountTransaction } from '../../src/model/transaction/ModifyMultisigAccountTransaction'; +import { MultisigCosignatoryModification } from '../../src/model/transaction/MultisigCosignatoryModification'; +import { MultisigCosignatoryModificationType } from '../../src/model/transaction/MultisigCosignatoryModificationType'; import { PlainMessage } from '../../src/model/transaction/PlainMessage'; import { TransferTransaction } from '../../src/model/transaction/TransferTransaction'; import { AggregatedTransactionService } from '../../src/service/AggregatedTransactionService'; @@ -177,6 +180,30 @@ describe('AggregatedTransactionService', () => { }); }); + it('should use minRemoval for multisig account validation if inner transaction is modify multisig remove', () => { + const modifyMultisigTransaction = ModifyMultisigAccountTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + 1, + 1, + [new MultisigCosignatoryModification( + MultisigCosignatoryModificationType.Remove, + account1.publicAccount, + )], + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [modifyMultisigTransaction.toAggregate(multisig2.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signWith(account2); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + it('should return correct isComplete status for aggregated complete transaction - none multisig', () => { const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), From daeb1cb7ca972745084f626e32f55b9e5f6c4988 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Thu, 4 Apr 2019 15:03:56 +0100 Subject: [PATCH 46/83] Fixed bug in create transaction from payload --- .../transaction/CreateTransactionFromPayload.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/infrastructure/transaction/CreateTransactionFromPayload.ts b/src/infrastructure/transaction/CreateTransactionFromPayload.ts index 494d4bc10e..ce68c0f974 100644 --- a/src/infrastructure/transaction/CreateTransactionFromPayload.ts +++ b/src/infrastructure/transaction/CreateTransactionFromPayload.ts @@ -376,7 +376,7 @@ const CreateTransaction = (type: number, transactionData: string, networkType: N }), networkType, consignatureArray ? consignatureArray.map((cosignature) => new AggregateTransactionCosignature( - cosignature.substring(0, 64), + cosignature.substring(64, 192), PublicAccount.createFromPublicKey(cosignature.substring(0, 64), networkType), )) : [], ); @@ -400,7 +400,7 @@ const CreateTransaction = (type: number, transactionData: string, networkType: N }), networkType, bondedConsignatureArray ? bondedConsignatureArray.map((cosignature) => new AggregateTransactionCosignature( - cosignature.substring(0, 64), + cosignature.substring(64, 192), PublicAccount.createFromPublicKey(cosignature.substring(0, 64), networkType), )) : [], ); From 3da7cf7aa8ee3825e5d3bf773195f2ea48f6186f Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Fri, 5 Apr 2019 19:01:52 +0100 Subject: [PATCH 47/83] Added more test cases and fixed a few bugs --- .../CreateTransactionFromPayload.ts | 4 +- src/service/AggregatedTransactionService.ts | 12 +- .../AggregatedTransactionService.spec.ts | 305 +++++++++++++++++- 3 files changed, 309 insertions(+), 12 deletions(-) diff --git a/src/infrastructure/transaction/CreateTransactionFromPayload.ts b/src/infrastructure/transaction/CreateTransactionFromPayload.ts index ce68c0f974..f32089fb31 100644 --- a/src/infrastructure/transaction/CreateTransactionFromPayload.ts +++ b/src/infrastructure/transaction/CreateTransactionFromPayload.ts @@ -456,8 +456,8 @@ const parseInnerTransactionFromBinary = (innerTransactionBinary: string): string let innerBinary = innerTransactionBinary; while (innerBinary.length) { - const payloadSize = parseInt(convert.uint8ToHex(convert.hexToUint8(innerTransactionBinary.substring(0, 8)).reverse()), 16) * 2; - const innerTransaction = innerTransactionBinary.substring(8, payloadSize); + const payloadSize = parseInt(convert.uint8ToHex(convert.hexToUint8(innerBinary.substring(0, 8)).reverse()), 16) * 2; + const innerTransaction = innerBinary.substring(8, payloadSize); embeddedTransaction.push(innerTransaction); innerBinary = innerBinary.substring(payloadSize); } diff --git a/src/service/AggregatedTransactionService.ts b/src/service/AggregatedTransactionService.ts index 7803b48bae..75a806bf96 100644 --- a/src/service/AggregatedTransactionService.ts +++ b/src/service/AggregatedTransactionService.ts @@ -15,7 +15,7 @@ */ import {from as observableFrom , Observable, of as observableOf} from 'rxjs'; -import { map, mergeMap} from 'rxjs/operators'; +import { flatMap, map, mergeMap, toArray} from 'rxjs/operators'; import { TransactionMapping } from '../core/utils/TransactionMapping'; import { AccountHttp } from '../infrastructure/AccountHttp'; import { MultisigAccountGraphInfo } from '../model/account/MultisigAccountGraphInfo'; @@ -52,7 +52,6 @@ export class AggregatedTransactionService { if (signedTransaction.signer) { signers.push(signedTransaction.signer); } - return observableFrom(aggregateTransaction.innerTransactions).pipe( mergeMap((innerTransaction) => this.accountHttp.getMultisigAccountInfo(innerTransaction.signer.address) .pipe( @@ -63,11 +62,16 @@ export class AggregatedTransactionService { this.accountHttp.getMultisigAccountGraphInfo(_.account.address) .pipe( map((graphInfo) => this.validateCosignatories(graphInfo, signers, innerTransaction)), - ) : observableOf(true), + ) : observableOf(signers.find((s) => s === _.account.publicKey ) !== undefined), ), ), ), - ); + toArray(), + ).pipe( + flatMap((results) => { + return observableOf(results.every((isComplete) => isComplete)); + }), + ); } /** diff --git a/test/service/AggregatedTransactionService.spec.ts b/test/service/AggregatedTransactionService.spec.ts index 8ea1432ca6..23470cb2a1 100644 --- a/test/service/AggregatedTransactionService.spec.ts +++ b/test/service/AggregatedTransactionService.spec.ts @@ -54,6 +54,7 @@ describe('AggregatedTransactionService', () => { * Test accounts: * Multisig1 (1/1): Account2, Account3 * Multisig2 (2/1): Account1, Multisig1 + * Multisig3 (2/2): Account2, Account3 * Stranger Account: Account4 */ @@ -64,6 +65,8 @@ describe('AggregatedTransactionService', () => { const multisig2 = Account.createFromPrivateKey('22A1D67F8519D1A45BD7116600BB6E857786E816FE0B45E4C5B9FFF3D64BC177', NetworkType.MIJIN_TEST); + const multisig3 = Account.createFromPrivateKey('5E7812AB0E709ABC45466034E1A209099F6A12C4698748A63CDCAA9B0DDE1DBD', + NetworkType.MIJIN_TEST); const account2 = Account.createFromPrivateKey('A4D410270E01CECDCDEADCDE32EC79C8D9CDEA4DCD426CB1EB666EFEF148FBCE', NetworkType.MIJIN_TEST); const account3 = Account.createFromPrivateKey('336AB45EE65A6AFFC0E7ADC5342F91E34BACA0B901A1D9C876FA25A1E590077E', @@ -80,14 +83,30 @@ describe('AggregatedTransactionService', () => { .thenReturn(observableOf(givenAccount4Info())); when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(multisig2.address))) .thenReturn(observableOf(givenMultisig2AccountInfo())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(multisig3.address))) + .thenReturn(observableOf(givenMultisig3AccountInfo())); when(mockedAccountHttp.getMultisigAccountGraphInfo(deepEqual(multisig2.address))) .thenReturn(observableOf(givenMultisig2AccountGraphInfo())); + when(mockedAccountHttp.getMultisigAccountGraphInfo(deepEqual(multisig3.address))) + .thenReturn(observableOf(givenMultisig3AccountGraphInfo())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account2.address))) + .thenReturn(observableOf(givenAccount2Info())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account3.address))) + .thenReturn(observableOf(givenAccount3Info())); const accountHttp = instance(mockedAccountHttp); aggregatedTransactionService = new AggregatedTransactionService(accountHttp); }); it('should return isComplete: true for aggregated complete transaction - 2 levels Multisig', () => { + /** + * MLMA + * Alice (account1): normal account + * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) + * Charles (multisig1) - Multisig 1-1 (account2 || account3) + * Given signatories: Account1 && Account4 + * Expecting complete as Bob needs 2 signatures (account1 && (account2 || account3)) + */ const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), @@ -109,6 +128,14 @@ describe('AggregatedTransactionService', () => { }); it('should return isComplete: false for aggregated complete transaction - 2 levels Multisig', () => { + /** + * MLMA + * Alice (account1): normal account + * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) + * Charles (multisig1) - Multisig 1-1 (account2 || account3) + * Given signatories: Account1 && Account4 + * Expecting incomplete as Bob needs 2 signatures (account1 && (account2 || account3)) but only got account1 + */ const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), @@ -130,6 +157,14 @@ describe('AggregatedTransactionService', () => { }); it('should return isComplete: false for aggregated complete transaction - 2 levels Multisig', () => { + /** + * MLMA + * Alice (account1): normal account + * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) + * Charles (multisig1) - Multisig 1-1 (account2 || account3) + * Given signatories: Account1 && Account4 + * Expecting incomplete as Bob needs 2 signatures (account1 && (account2 || account3)) but got account4 + */ const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), @@ -151,9 +186,18 @@ describe('AggregatedTransactionService', () => { }); it('should return correct isComplete status for aggregated complete transaction - 2 levels Multisig, multi inner transaction', () => { + /** + * MLMA - with multiple transaction + * Alice (account1): normal account + * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) + * Charles (multisig1) - Multisig 1-1 (account2 || account3) + * An extra inner transaction to account4 (just to increase the complexity) + * Given signatories: Account1 && Account4 + * Expecting incomplete as Bob needs 2 signatures (account1 && (account2 || account3)) + */ const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), - Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + account2.address, [], PlainMessage.create('test-message'), NetworkType.MIJIN_TEST, @@ -161,7 +205,7 @@ describe('AggregatedTransactionService', () => { const transferTransaction2 = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), - Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + account2.address, [], PlainMessage.create('test-message'), NetworkType.MIJIN_TEST, @@ -170,17 +214,60 @@ describe('AggregatedTransactionService', () => { const aggregateTransaction = AggregateTransaction.createComplete( Deadline.create(), [transferTransaction.toAggregate(multisig2.publicAccount), - transferTransaction.toAggregate(account4.publicAccount)], + transferTransaction2.toAggregate(account4.publicAccount)], NetworkType.MIJIN_TEST, []); + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); - const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account2]); + it('should return correct isComplete status for aggregated complete transaction - 2 levels Multisig, multi inner transaction', () => { + /** + * MLMA - with multiple transaction + * Alice (account1): normal account + * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) + * Charles (multisig1) - Multisig 1-1 (account2 || account3) + * An extra inner transaction to account4 (just to increase the complexity) + * Given signatories: Account1 && Account4 && Account2 + * Expecting complete + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account2.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const transferTransaction2 = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account2.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount), + transferTransaction2.toAggregate(account4.publicAccount)], + NetworkType.MIJIN_TEST, + []); + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4, account2]); aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); it('should use minRemoval for multisig account validation if inner transaction is modify multisig remove', () => { + /** + * If the inner transaction is issued to a multisig account + * and the inner transaction itself is a ModifyMultiSigAccountTransaction - Removal + * The validator should use minRemoval value rather than minApproval value + * to determine if the act is complete or not + */ const modifyMultisigTransaction = ModifyMultisigAccountTransaction.create( Deadline.create(1, ChronoUnit.HOURS), 1, @@ -197,14 +284,19 @@ describe('AggregatedTransactionService', () => { [modifyMultisigTransaction.toAggregate(multisig2.publicAccount)], NetworkType.MIJIN_TEST, []); - const signedTransaction = aggregateTransaction.signWith(account2); aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); - it('should return correct isComplete status for aggregated complete transaction - none multisig', () => { + it('should return correct isComplete status (false) for aggregated complete transaction - none multisig', () => { + /** + * If the inner transaction is issued to a multisig account + * and the inner transaction itself is a ModifyMultiSigAccountTransaction - Removal + * The validator should use minRemoval value rather than minApproval value + * to determine if the act is complete or not + */ const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), @@ -220,11 +312,176 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signWith(account1); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + + it('should return correct isComplete status (true) for aggregated complete transaction - none multisig', () => { + /** + * ACT + * Alice (account1): normal account + * Bob (account4) - normal account + * Alice initiate the transaction + * Bob sign + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(account4.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signWith(account4); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + it('should return correct isComplete status TRUE - multiple normal account', () => { + /** + * ACT + * Alice: account1 + * Bog: account4 + * An escrow contract is signed by all the participants (normal accounts) + * Given Alice defined the following escrow contract: + * | sender | recipient | type | data | + * | Alice | Bob | send-an-asset | 1 concert.ticket | + * | Bob | Alice | send-an-asset | 20 euros | + * And Bob signs the contract + * And Alice signs the contract + * Then the contract should appear as complete + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account1.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const transferTransaction2 = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account4.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(account4.publicAccount), + transferTransaction2.toAggregate(account1.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); + it('should return correct isComplete status FALSE - multiple normal account', () => { + /** + * ACT + * Alice: account1 + * Bog: account4 + * An escrow contract is signed by all the participants (normal accounts) + * Given Alice defined the following escrow contract: + * | sender | recipient | type | data | + * | Alice | Bob | send-an-asset | 1 concert.ticket | + * | Bob | Alice | send-an-asset | 20 euros | + * And Alice signs the contract + * Then the contract should appear as incomplete + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account1.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const transferTransaction2 = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account4.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(account4.publicAccount), + transferTransaction2.toAggregate(account1.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, []); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + + it('should return correct isComplete status TRUE - multisig Single Level', () => { + /** + * ACT - Multisig single level + * Alice (account1): initiate an transfer to Bob + * Bob (multisig3): is a 2/2 multisig account (account2 && account3) + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account4.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig3.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account2, [account3]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + it('should return correct isComplete status FALSE - multisig Single Level', () => { + /** + * ACT - Multisig single level + * Alice (account1): initiate an transfer to Bob + * Bob (multisig3): is a 2/2 multisig account (account2 && account3) + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account4.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig3.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account2, []); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + function givenMultisig2AccountInfo(): MultisigAccountInfo { return new MultisigAccountInfo(multisig2.publicAccount, 2, 1, @@ -233,6 +490,14 @@ describe('AggregatedTransactionService', () => { [], ); } + function givenMultisig3AccountInfo(): MultisigAccountInfo { + return new MultisigAccountInfo(multisig3.publicAccount, + 2, 2, + [account2.publicAccount, + account3.publicAccount], + [], + ); + } function givenAccount1Info(): MultisigAccountInfo { return new MultisigAccountInfo(account1.publicAccount, @@ -241,6 +506,22 @@ describe('AggregatedTransactionService', () => { [multisig2.publicAccount], ); } + function givenAccount2Info(): MultisigAccountInfo { + return new MultisigAccountInfo(account2.publicAccount, + 0, 0, + [], + [multisig2.publicAccount, + multisig3.publicAccount], + ); + } + function givenAccount3Info(): MultisigAccountInfo { + return new MultisigAccountInfo(account3.publicAccount, + 0, 0, + [], + [multisig2.publicAccount, + multisig3.publicAccount], + ); + } function givenAccount4Info(): MultisigAccountInfo { return new MultisigAccountInfo(account4.publicAccount, 0, 0, @@ -266,4 +547,16 @@ describe('AggregatedTransactionService', () => { return new MultisigAccountGraphInfo(map); } + function givenMultisig3AccountGraphInfo(): MultisigAccountGraphInfo { + const map = new Map(); + map.set(0, [new MultisigAccountInfo(multisig3.publicAccount, + 2, 2, + [account2.publicAccount, + account3.publicAccount], + [], + )]); + + return new MultisigAccountGraphInfo(map); + } + }); From 1cef7f82ebe3afc76be4389cf4dd66f59adf425f Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Wed, 10 Apr 2019 15:13:01 +0100 Subject: [PATCH 48/83] renamed AggregatedTransactionService --- ...vice.ts => AggregateTransactionService.ts} | 2 +- src/service/service.ts | 2 +- ...ts => AggregateTransactionService.spec.ts} | 32 +++++++++---------- 3 files changed, 18 insertions(+), 18 deletions(-) rename src/service/{AggregatedTransactionService.ts => AggregateTransactionService.ts} (99%) rename test/service/{AggregatedTransactionService.spec.ts => AggregateTransactionService.spec.ts} (93%) diff --git a/src/service/AggregatedTransactionService.ts b/src/service/AggregateTransactionService.ts similarity index 99% rename from src/service/AggregatedTransactionService.ts rename to src/service/AggregateTransactionService.ts index 75a806bf96..800d1a1b89 100644 --- a/src/service/AggregatedTransactionService.ts +++ b/src/service/AggregateTransactionService.ts @@ -29,7 +29,7 @@ import { TransactionType } from '../model/transaction/TransactionType'; /** * Aggregated Transaction service */ -export class AggregatedTransactionService { +export class AggregateTransactionService { /** * Constructor diff --git a/src/service/service.ts b/src/service/service.ts index fcf4c84680..c988e13ffb 100644 --- a/src/service/service.ts +++ b/src/service/service.ts @@ -16,4 +16,4 @@ export * from './NamespaceService'; export * from './MosaicService'; -export * from './AggregatedTransactionService'; +export * from './AggregateTransactionService'; diff --git a/test/service/AggregatedTransactionService.spec.ts b/test/service/AggregateTransactionService.spec.ts similarity index 93% rename from test/service/AggregatedTransactionService.spec.ts rename to test/service/AggregateTransactionService.spec.ts index 23470cb2a1..ae2deab99b 100644 --- a/test/service/AggregatedTransactionService.spec.ts +++ b/test/service/AggregateTransactionService.spec.ts @@ -31,13 +31,13 @@ import { MultisigCosignatoryModification } from '../../src/model/transaction/Mul import { MultisigCosignatoryModificationType } from '../../src/model/transaction/MultisigCosignatoryModificationType'; import { PlainMessage } from '../../src/model/transaction/PlainMessage'; import { TransferTransaction } from '../../src/model/transaction/TransferTransaction'; -import { AggregatedTransactionService } from '../../src/service/AggregatedTransactionService'; +import { AggregateTransactionService } from '../../src/service/AggregateTransactionService'; /** * For multi level multisig scenario visit: https://github.com/nemtech/nem2-docs/issues/10 */ -describe('AggregatedTransactionService', () => { - let aggregatedTransactionService: AggregatedTransactionService; +describe('AggregateTransactionService', () => { + let aggregateTransactionService: AggregateTransactionService; /** * Multisig2 Account: SBROWP-7YMG2M-K45RO6-Q7ZPK7-G7GXWQ-JK5VNQ-OSUX @@ -95,7 +95,7 @@ describe('AggregatedTransactionService', () => { .thenReturn(observableOf(givenAccount3Info())); const accountHttp = instance(mockedAccountHttp); - aggregatedTransactionService = new AggregatedTransactionService(accountHttp); + aggregateTransactionService = new AggregateTransactionService(accountHttp); }); it('should return isComplete: true for aggregated complete transaction - 2 levels Multisig', () => { @@ -122,7 +122,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account2]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); @@ -151,7 +151,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, []); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.false; }); }); @@ -180,7 +180,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.false; }); }); @@ -218,7 +218,7 @@ describe('AggregatedTransactionService', () => { NetworkType.MIJIN_TEST, []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.false; }); }); @@ -256,7 +256,7 @@ describe('AggregatedTransactionService', () => { NetworkType.MIJIN_TEST, []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4, account2]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); @@ -285,7 +285,7 @@ describe('AggregatedTransactionService', () => { NetworkType.MIJIN_TEST, []); const signedTransaction = aggregateTransaction.signWith(account2); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); @@ -312,7 +312,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signWith(account1); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.false; }); }); @@ -340,7 +340,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signWith(account4); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); @@ -383,7 +383,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); @@ -425,7 +425,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, []); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.false; }); }); @@ -451,7 +451,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account2, [account3]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); @@ -477,7 +477,7 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account2, []); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + aggregateTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.false; }); }); From 18a5f2a5c1b41587417a6383181d9a11b4ca8993 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 1 Apr 2019 20:39:11 +0100 Subject: [PATCH 49/83] #4 1. Addes service for Aggregated Transaction - isComplete 2. Fixed couple of bugs in TransactionMappings --- src/service/AggregatedTransactionService.ts | 127 +++++++++ .../AggregatedTransactionService.spec.ts | 242 ++++++++++++++++++ 2 files changed, 369 insertions(+) create mode 100644 src/service/AggregatedTransactionService.ts create mode 100644 test/service/AggregatedTransactionService.spec.ts diff --git a/src/service/AggregatedTransactionService.ts b/src/service/AggregatedTransactionService.ts new file mode 100644 index 0000000000..ccdd72cd3b --- /dev/null +++ b/src/service/AggregatedTransactionService.ts @@ -0,0 +1,127 @@ +/* + * Copyright 2019 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {from as observableFrom , Observable, of as observableOf} from 'rxjs'; +import { map, mergeMap} from 'rxjs/operators'; +import { TransactionMapping } from '../core/utils/TransactionMapping'; +import { AccountHttp } from '../infrastructure/AccountHttp'; +import { MultisigAccountGraphInfo } from '../model/account/MultisigAccountGraphInfo'; +import { AggregateTransaction } from '../model/transaction/AggregateTransaction'; +import { SignedTransaction } from '../model/transaction/SignedTransaction'; + +/** + * Aggregated Transaction service + */ +export class AggregatedTransactionService { + + /** + * Constructor + * @param accountHttp + */ + constructor(private readonly accountHttp: AccountHttp) { + } + + /** + * Check if an aggregate complete transaction has all cosignatories attached + * @param signedTransaction - The signed aggregate transaction (complete) to be verified + * @returns {Observable} + */ + public isComplete(signedTransaction: SignedTransaction): Observable { + const aggregateTransaction = TransactionMapping.createFromPayload(signedTransaction.payload) as AggregateTransaction; + /** + * Include both initiator & cosigners + */ + const signers = (aggregateTransaction.cosignatures.map((cosigner) => cosigner.signer.publicKey)); + if (signedTransaction.signer) { + signers.push(signedTransaction.signer); + } + + return observableFrom(aggregateTransaction.innerTransactions).pipe( + mergeMap((innerTransaction) => this.accountHttp.getMultisigAccountInfo(innerTransaction.signer.address) + .pipe( + /** + * For multisig account, we need to get the graph info in case it has multiple levels + */ + mergeMap((_) => _.minApproval !== 0 && _.minRemoval !== 0 ? + this.accountHttp.getMultisigAccountGraphInfo(_.account.address) + .pipe( + map((graphInfo) => this.validateCosignatories(graphInfo, signers)), + ) : observableOf(true), + ), + ), + ), + ); + } + + /** + * Validate cosignatories against multisig Account(s) + * @param graphInfo - multisig account graph info + * @param cosignatories - array of cosignatories extracted from aggregated transaction + * @returns {boolean} + */ + private validateCosignatories(graphInfo: MultisigAccountGraphInfo, cosignatories: string[]): boolean { + /** + * Validate cosignatories from bottom level to top + */ + const sortedKeys = Array.from(graphInfo.multisigAccounts.keys()).sort((a, b) => b - a); + const cosignatoriesReceived = cosignatories; + let validationResult = true; + + sortedKeys.forEach((key) => { + const multisigInfo = graphInfo.multisigAccounts.get(key); + if (multisigInfo && validationResult) { + multisigInfo.forEach((multisig) => { + if (multisig.minApproval >= 1) { + const matchedCosignatories = this.compareArrays(cosignatoriesReceived, + multisig.cosignatories.map((cosig) => cosig.publicKey)); + + /** + * if minimal signature requirement met at current level, push the multisig account + * into the received signatories array for next level validation. + * Otherwise return validation failed. + */ + if (matchedCosignatories.length >= multisig.minApproval) { + if (cosignatoriesReceived.indexOf(multisig.account.publicKey) === -1) { + cosignatoriesReceived.push(multisig.account.publicKey); + } + } else { + validationResult = false; + } + } + }); + } + }); + + return validationResult; + } + + /** + * Compare two string arrays + * @param array1 - base array + * @param array2 - array to be matched + * @returns {string[]} - array of matched elements + */ + private compareArrays(array1: string[], array2: string[]): string[] { + const results: string[] = []; + array1.forEach((a1) => array2.forEach((a2) => { + if (a1 === a2) { + results.push(a1); + } + })); + + return results; + } +} diff --git a/test/service/AggregatedTransactionService.spec.ts b/test/service/AggregatedTransactionService.spec.ts new file mode 100644 index 0000000000..0aff3d8f47 --- /dev/null +++ b/test/service/AggregatedTransactionService.spec.ts @@ -0,0 +1,242 @@ +/* + * Copyright 2018 NEM + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect } from 'chai'; +import { ChronoUnit } from 'js-joda'; +import {of as observableOf} from 'rxjs'; +import {deepEqual, instance, mock, when} from 'ts-mockito'; +import { AccountHttp } from '../../src/infrastructure/AccountHttp'; +import { Account } from '../../src/model/account/Account'; +import { Address } from '../../src/model/account/Address'; +import { MultisigAccountGraphInfo } from '../../src/model/account/MultisigAccountGraphInfo'; +import { MultisigAccountInfo } from '../../src/model/account/MultisigAccountInfo'; +import {NetworkType} from '../../src/model/blockchain/NetworkType'; +import { AggregateTransaction } from '../../src/model/transaction/AggregateTransaction'; +import { Deadline } from '../../src/model/transaction/Deadline'; +import { PlainMessage } from '../../src/model/transaction/PlainMessage'; +import { TransferTransaction } from '../../src/model/transaction/TransferTransaction'; +import { AggregatedTransactionService } from '../../src/service/AggregatedTransactionService'; + +/** + * For multi level multisig scenario visit: https://github.com/nemtech/nem2-docs/issues/10 + */ +describe('AggregatedTransactionService', () => { + let aggregatedTransactionService: AggregatedTransactionService; + + /** + * Multisig2 Account: SBROWP-7YMG2M-K45RO6-Q7ZPK7-G7GXWQ-JK5VNQ-OSUX + * Public Key: 5E628EA59818D97AA4118780D9A88C5512FCE7A21C195E1574727EFCE5DF7C0D + * Private Key: 22A1D67F8519D1A45BD7116600BB6E857786E816FE0B45E4C5B9FFF3D64BC177 + * + * + * Multisig1 Account: SAK32M-5JQ43R-WYHWEH-WRBCW4-RXERT2-DLASGL-EANS + * Public Key: BFDF2610C5666A626434FE12FB4A9D896D2B9B033F5F84CCEABE82E043A6307E + * Private Key: 8B0622C2CCFC5CCC5A74B500163E3C68F3AD3643DB12932FC931143EAC67280D + */ + + /** + * Test accounts: + * Multisig1 (1/1): Account2, Account3 + * Multisig2 (2/1): Account1, Multisig1 + * Stranger Account: Account4 + */ + + const account1 = Account.createFromPrivateKey('82DB2528834C9926F0FCCE042466B24A266F5B685CB66D2869AF6648C043E950', + NetworkType.MIJIN_TEST); + const multisig1 = Account.createFromPrivateKey('8B0622C2CCFC5CCC5A74B500163E3C68F3AD3643DB12932FC931143EAC67280D', + NetworkType.MIJIN_TEST); + const multisig2 = Account.createFromPrivateKey('22A1D67F8519D1A45BD7116600BB6E857786E816FE0B45E4C5B9FFF3D64BC177', + NetworkType.MIJIN_TEST); + + const account2 = Account.createFromPrivateKey('A4D410270E01CECDCDEADCDE32EC79C8D9CDEA4DCD426CB1EB666EFEF148FBCE', + NetworkType.MIJIN_TEST); + const account3 = Account.createFromPrivateKey('336AB45EE65A6AFFC0E7ADC5342F91E34BACA0B901A1D9C876FA25A1E590077E', + NetworkType.MIJIN_TEST); + + const account4 = Account.createFromPrivateKey('4D8B3756592532753344E11E2B7541317BCCFBBCF4444274CDBF359D2C4AE0F1', + NetworkType.MIJIN_TEST); + before(() => { + const mockedAccountHttp = mock(AccountHttp); + + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account1.address))) + .thenReturn(observableOf(givenAccount1Info())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account4.address))) + .thenReturn(observableOf(givenAccount4Info())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(multisig2.address))) + .thenReturn(observableOf(givenMultisig2AccountInfo())); + when(mockedAccountHttp.getMultisigAccountGraphInfo(deepEqual(multisig2.address))) + .thenReturn(observableOf(givenMultisig2AccountGraphInfo())); + + const accountHttp = instance(mockedAccountHttp); + aggregatedTransactionService = new AggregatedTransactionService(accountHttp); + }); + + it('should return isComplete: true for aggregated complete transaction - 2 levels Multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account2]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + it('should return isComplete: false for aggregated complete transaction - 2 levels Multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, []); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + + it('should return isComplete: false for aggregated complete transaction - 2 levels Multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + + it('should return correct isComplete status for aggregated complete transaction - 2 levels Multisig, multi inner transaction', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const transferTransaction2 = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount), + transferTransaction.toAggregate(account4.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account2]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + it('should return correct isComplete status for aggregated complete transaction - none multisig', () => { + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(account4.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signWith(account1); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + function givenMultisig2AccountInfo(): MultisigAccountInfo { + return new MultisigAccountInfo(multisig2.publicAccount, + 2, 1, + [multisig1.publicAccount, + account1.publicAccount], + [], + ); + } + + function givenAccount1Info(): MultisigAccountInfo { + return new MultisigAccountInfo(account1.publicAccount, + 0, 0, + [], + [multisig2.publicAccount], + ); + } + function givenAccount4Info(): MultisigAccountInfo { + return new MultisigAccountInfo(account4.publicAccount, + 0, 0, + [], + [], + ); + } + + function givenMultisig2AccountGraphInfo(): MultisigAccountGraphInfo { + const map = new Map(); + map.set(0, [new MultisigAccountInfo(multisig2.publicAccount, + 2, 1, + [multisig1.publicAccount, + account1.publicAccount], + [], + )]) + .set(1, [new MultisigAccountInfo(multisig1.publicAccount, + 1, 1, + [account2.publicAccount, account3.publicAccount], + [multisig2.publicAccount], + )]); + + return new MultisigAccountGraphInfo(map); + } + +}); From df799f6d04c9be6b78162157ea712b106daca054 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 1 Apr 2019 21:25:50 +0100 Subject: [PATCH 50/83] Updated license year --- test/service/AggregatedTransactionService.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/service/AggregatedTransactionService.spec.ts b/test/service/AggregatedTransactionService.spec.ts index 0aff3d8f47..bc0c11f535 100644 --- a/test/service/AggregatedTransactionService.spec.ts +++ b/test/service/AggregatedTransactionService.spec.ts @@ -1,5 +1,5 @@ /* - * Copyright 2018 NEM + * Copyright 2019 NEM * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 6a01621614bea4a597f2ff969a38178d0b8351a4 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Wed, 3 Apr 2019 16:12:08 +0100 Subject: [PATCH 51/83] Added check in validate cosignatories for modify multisig account (removal) --- src/service/AggregatedTransactionService.ts | 29 ++++++++++++++++--- .../AggregatedTransactionService.spec.ts | 27 +++++++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/service/AggregatedTransactionService.ts b/src/service/AggregatedTransactionService.ts index ccdd72cd3b..7803b48bae 100644 --- a/src/service/AggregatedTransactionService.ts +++ b/src/service/AggregatedTransactionService.ts @@ -20,7 +20,11 @@ import { TransactionMapping } from '../core/utils/TransactionMapping'; import { AccountHttp } from '../infrastructure/AccountHttp'; import { MultisigAccountGraphInfo } from '../model/account/MultisigAccountGraphInfo'; import { AggregateTransaction } from '../model/transaction/AggregateTransaction'; +import { InnerTransaction } from '../model/transaction/InnerTransaction'; +import { ModifyMultisigAccountTransaction } from '../model/transaction/ModifyMultisigAccountTransaction'; +import { MultisigCosignatoryModificationType } from '../model/transaction/MultisigCosignatoryModificationType'; import { SignedTransaction } from '../model/transaction/SignedTransaction'; +import { TransactionType } from '../model/transaction/TransactionType'; /** * Aggregated Transaction service @@ -58,7 +62,7 @@ export class AggregatedTransactionService { mergeMap((_) => _.minApproval !== 0 && _.minRemoval !== 0 ? this.accountHttp.getMultisigAccountGraphInfo(_.account.address) .pipe( - map((graphInfo) => this.validateCosignatories(graphInfo, signers)), + map((graphInfo) => this.validateCosignatories(graphInfo, signers, innerTransaction)), ) : observableOf(true), ), ), @@ -70,9 +74,12 @@ export class AggregatedTransactionService { * Validate cosignatories against multisig Account(s) * @param graphInfo - multisig account graph info * @param cosignatories - array of cosignatories extracted from aggregated transaction + * @param innerTransaction - the inner transaction of the aggregated transaction * @returns {boolean} */ - private validateCosignatories(graphInfo: MultisigAccountGraphInfo, cosignatories: string[]): boolean { + private validateCosignatories(graphInfo: MultisigAccountGraphInfo, + cosignatories: string[], + innerTransaction: InnerTransaction): boolean { /** * Validate cosignatories from bottom level to top */ @@ -80,11 +87,24 @@ export class AggregatedTransactionService { const cosignatoriesReceived = cosignatories; let validationResult = true; + let isMultisigRemoval = false; + + /** + * Check inner transaction. If remove cosigner from multisig account, + * use minRemoval instead of minApproval for cosignatories validation. + */ + if (innerTransaction.type === TransactionType.MODIFY_MULTISIG_ACCOUNT) { + if ((innerTransaction as ModifyMultisigAccountTransaction).modifications + .find((modification) => modification.type === MultisigCosignatoryModificationType.Remove) !== undefined) { + isMultisigRemoval = true; + } + } + sortedKeys.forEach((key) => { const multisigInfo = graphInfo.multisigAccounts.get(key); if (multisigInfo && validationResult) { multisigInfo.forEach((multisig) => { - if (multisig.minApproval >= 1) { + if (multisig.minApproval >= 1 && multisig.minRemoval) { // To make sure it is multisig account const matchedCosignatories = this.compareArrays(cosignatoriesReceived, multisig.cosignatories.map((cosig) => cosig.publicKey)); @@ -93,7 +113,8 @@ export class AggregatedTransactionService { * into the received signatories array for next level validation. * Otherwise return validation failed. */ - if (matchedCosignatories.length >= multisig.minApproval) { + if ((matchedCosignatories.length >= multisig.minApproval && !isMultisigRemoval) || + (matchedCosignatories.length >= multisig.minRemoval && isMultisigRemoval)) { if (cosignatoriesReceived.indexOf(multisig.account.publicKey) === -1) { cosignatoriesReceived.push(multisig.account.publicKey); } diff --git a/test/service/AggregatedTransactionService.spec.ts b/test/service/AggregatedTransactionService.spec.ts index bc0c11f535..8ea1432ca6 100644 --- a/test/service/AggregatedTransactionService.spec.ts +++ b/test/service/AggregatedTransactionService.spec.ts @@ -26,6 +26,9 @@ import { MultisigAccountInfo } from '../../src/model/account/MultisigAccountInfo import {NetworkType} from '../../src/model/blockchain/NetworkType'; import { AggregateTransaction } from '../../src/model/transaction/AggregateTransaction'; import { Deadline } from '../../src/model/transaction/Deadline'; +import { ModifyMultisigAccountTransaction } from '../../src/model/transaction/ModifyMultisigAccountTransaction'; +import { MultisigCosignatoryModification } from '../../src/model/transaction/MultisigCosignatoryModification'; +import { MultisigCosignatoryModificationType } from '../../src/model/transaction/MultisigCosignatoryModificationType'; import { PlainMessage } from '../../src/model/transaction/PlainMessage'; import { TransferTransaction } from '../../src/model/transaction/TransferTransaction'; import { AggregatedTransactionService } from '../../src/service/AggregatedTransactionService'; @@ -177,6 +180,30 @@ describe('AggregatedTransactionService', () => { }); }); + it('should use minRemoval for multisig account validation if inner transaction is modify multisig remove', () => { + const modifyMultisigTransaction = ModifyMultisigAccountTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + 1, + 1, + [new MultisigCosignatoryModification( + MultisigCosignatoryModificationType.Remove, + account1.publicAccount, + )], + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [modifyMultisigTransaction.toAggregate(multisig2.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signWith(account2); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + it('should return correct isComplete status for aggregated complete transaction - none multisig', () => { const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), From e6136e08f8462d417d872426c377e921330a0abb Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Fri, 5 Apr 2019 19:01:52 +0100 Subject: [PATCH 52/83] Added more test cases and fixed a few bugs --- src/service/AggregatedTransactionService.ts | 12 +- .../AggregatedTransactionService.spec.ts | 305 +++++++++++++++++- 2 files changed, 307 insertions(+), 10 deletions(-) diff --git a/src/service/AggregatedTransactionService.ts b/src/service/AggregatedTransactionService.ts index 7803b48bae..75a806bf96 100644 --- a/src/service/AggregatedTransactionService.ts +++ b/src/service/AggregatedTransactionService.ts @@ -15,7 +15,7 @@ */ import {from as observableFrom , Observable, of as observableOf} from 'rxjs'; -import { map, mergeMap} from 'rxjs/operators'; +import { flatMap, map, mergeMap, toArray} from 'rxjs/operators'; import { TransactionMapping } from '../core/utils/TransactionMapping'; import { AccountHttp } from '../infrastructure/AccountHttp'; import { MultisigAccountGraphInfo } from '../model/account/MultisigAccountGraphInfo'; @@ -52,7 +52,6 @@ export class AggregatedTransactionService { if (signedTransaction.signer) { signers.push(signedTransaction.signer); } - return observableFrom(aggregateTransaction.innerTransactions).pipe( mergeMap((innerTransaction) => this.accountHttp.getMultisigAccountInfo(innerTransaction.signer.address) .pipe( @@ -63,11 +62,16 @@ export class AggregatedTransactionService { this.accountHttp.getMultisigAccountGraphInfo(_.account.address) .pipe( map((graphInfo) => this.validateCosignatories(graphInfo, signers, innerTransaction)), - ) : observableOf(true), + ) : observableOf(signers.find((s) => s === _.account.publicKey ) !== undefined), ), ), ), - ); + toArray(), + ).pipe( + flatMap((results) => { + return observableOf(results.every((isComplete) => isComplete)); + }), + ); } /** diff --git a/test/service/AggregatedTransactionService.spec.ts b/test/service/AggregatedTransactionService.spec.ts index 8ea1432ca6..23470cb2a1 100644 --- a/test/service/AggregatedTransactionService.spec.ts +++ b/test/service/AggregatedTransactionService.spec.ts @@ -54,6 +54,7 @@ describe('AggregatedTransactionService', () => { * Test accounts: * Multisig1 (1/1): Account2, Account3 * Multisig2 (2/1): Account1, Multisig1 + * Multisig3 (2/2): Account2, Account3 * Stranger Account: Account4 */ @@ -64,6 +65,8 @@ describe('AggregatedTransactionService', () => { const multisig2 = Account.createFromPrivateKey('22A1D67F8519D1A45BD7116600BB6E857786E816FE0B45E4C5B9FFF3D64BC177', NetworkType.MIJIN_TEST); + const multisig3 = Account.createFromPrivateKey('5E7812AB0E709ABC45466034E1A209099F6A12C4698748A63CDCAA9B0DDE1DBD', + NetworkType.MIJIN_TEST); const account2 = Account.createFromPrivateKey('A4D410270E01CECDCDEADCDE32EC79C8D9CDEA4DCD426CB1EB666EFEF148FBCE', NetworkType.MIJIN_TEST); const account3 = Account.createFromPrivateKey('336AB45EE65A6AFFC0E7ADC5342F91E34BACA0B901A1D9C876FA25A1E590077E', @@ -80,14 +83,30 @@ describe('AggregatedTransactionService', () => { .thenReturn(observableOf(givenAccount4Info())); when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(multisig2.address))) .thenReturn(observableOf(givenMultisig2AccountInfo())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(multisig3.address))) + .thenReturn(observableOf(givenMultisig3AccountInfo())); when(mockedAccountHttp.getMultisigAccountGraphInfo(deepEqual(multisig2.address))) .thenReturn(observableOf(givenMultisig2AccountGraphInfo())); + when(mockedAccountHttp.getMultisigAccountGraphInfo(deepEqual(multisig3.address))) + .thenReturn(observableOf(givenMultisig3AccountGraphInfo())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account2.address))) + .thenReturn(observableOf(givenAccount2Info())); + when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account3.address))) + .thenReturn(observableOf(givenAccount3Info())); const accountHttp = instance(mockedAccountHttp); aggregatedTransactionService = new AggregatedTransactionService(accountHttp); }); it('should return isComplete: true for aggregated complete transaction - 2 levels Multisig', () => { + /** + * MLMA + * Alice (account1): normal account + * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) + * Charles (multisig1) - Multisig 1-1 (account2 || account3) + * Given signatories: Account1 && Account4 + * Expecting complete as Bob needs 2 signatures (account1 && (account2 || account3)) + */ const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), @@ -109,6 +128,14 @@ describe('AggregatedTransactionService', () => { }); it('should return isComplete: false for aggregated complete transaction - 2 levels Multisig', () => { + /** + * MLMA + * Alice (account1): normal account + * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) + * Charles (multisig1) - Multisig 1-1 (account2 || account3) + * Given signatories: Account1 && Account4 + * Expecting incomplete as Bob needs 2 signatures (account1 && (account2 || account3)) but only got account1 + */ const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), @@ -130,6 +157,14 @@ describe('AggregatedTransactionService', () => { }); it('should return isComplete: false for aggregated complete transaction - 2 levels Multisig', () => { + /** + * MLMA + * Alice (account1): normal account + * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) + * Charles (multisig1) - Multisig 1-1 (account2 || account3) + * Given signatories: Account1 && Account4 + * Expecting incomplete as Bob needs 2 signatures (account1 && (account2 || account3)) but got account4 + */ const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), @@ -151,9 +186,18 @@ describe('AggregatedTransactionService', () => { }); it('should return correct isComplete status for aggregated complete transaction - 2 levels Multisig, multi inner transaction', () => { + /** + * MLMA - with multiple transaction + * Alice (account1): normal account + * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) + * Charles (multisig1) - Multisig 1-1 (account2 || account3) + * An extra inner transaction to account4 (just to increase the complexity) + * Given signatories: Account1 && Account4 + * Expecting incomplete as Bob needs 2 signatures (account1 && (account2 || account3)) + */ const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), - Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + account2.address, [], PlainMessage.create('test-message'), NetworkType.MIJIN_TEST, @@ -161,7 +205,7 @@ describe('AggregatedTransactionService', () => { const transferTransaction2 = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), - Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + account2.address, [], PlainMessage.create('test-message'), NetworkType.MIJIN_TEST, @@ -170,17 +214,60 @@ describe('AggregatedTransactionService', () => { const aggregateTransaction = AggregateTransaction.createComplete( Deadline.create(), [transferTransaction.toAggregate(multisig2.publicAccount), - transferTransaction.toAggregate(account4.publicAccount)], + transferTransaction2.toAggregate(account4.publicAccount)], NetworkType.MIJIN_TEST, []); + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); - const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account2]); + it('should return correct isComplete status for aggregated complete transaction - 2 levels Multisig, multi inner transaction', () => { + /** + * MLMA - with multiple transaction + * Alice (account1): normal account + * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) + * Charles (multisig1) - Multisig 1-1 (account2 || account3) + * An extra inner transaction to account4 (just to increase the complexity) + * Given signatories: Account1 && Account4 && Account2 + * Expecting complete + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account2.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const transferTransaction2 = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account2.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig2.publicAccount), + transferTransaction2.toAggregate(account4.publicAccount)], + NetworkType.MIJIN_TEST, + []); + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4, account2]); aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); it('should use minRemoval for multisig account validation if inner transaction is modify multisig remove', () => { + /** + * If the inner transaction is issued to a multisig account + * and the inner transaction itself is a ModifyMultiSigAccountTransaction - Removal + * The validator should use minRemoval value rather than minApproval value + * to determine if the act is complete or not + */ const modifyMultisigTransaction = ModifyMultisigAccountTransaction.create( Deadline.create(1, ChronoUnit.HOURS), 1, @@ -197,14 +284,19 @@ describe('AggregatedTransactionService', () => { [modifyMultisigTransaction.toAggregate(multisig2.publicAccount)], NetworkType.MIJIN_TEST, []); - const signedTransaction = aggregateTransaction.signWith(account2); aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); - it('should return correct isComplete status for aggregated complete transaction - none multisig', () => { + it('should return correct isComplete status (false) for aggregated complete transaction - none multisig', () => { + /** + * If the inner transaction is issued to a multisig account + * and the inner transaction itself is a ModifyMultiSigAccountTransaction - Removal + * The validator should use minRemoval value rather than minApproval value + * to determine if the act is complete or not + */ const transferTransaction = TransferTransaction.create( Deadline.create(1, ChronoUnit.HOURS), Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), @@ -220,11 +312,176 @@ describe('AggregatedTransactionService', () => { []); const signedTransaction = aggregateTransaction.signWith(account1); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + + it('should return correct isComplete status (true) for aggregated complete transaction - none multisig', () => { + /** + * ACT + * Alice (account1): normal account + * Bob (account4) - normal account + * Alice initiate the transaction + * Bob sign + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(account4.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signWith(account4); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + it('should return correct isComplete status TRUE - multiple normal account', () => { + /** + * ACT + * Alice: account1 + * Bog: account4 + * An escrow contract is signed by all the participants (normal accounts) + * Given Alice defined the following escrow contract: + * | sender | recipient | type | data | + * | Alice | Bob | send-an-asset | 1 concert.ticket | + * | Bob | Alice | send-an-asset | 20 euros | + * And Bob signs the contract + * And Alice signs the contract + * Then the contract should appear as complete + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account1.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const transferTransaction2 = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account4.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(account4.publicAccount), + transferTransaction2.toAggregate(account1.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { expect(isComplete).to.be.true; }); }); + it('should return correct isComplete status FALSE - multiple normal account', () => { + /** + * ACT + * Alice: account1 + * Bog: account4 + * An escrow contract is signed by all the participants (normal accounts) + * Given Alice defined the following escrow contract: + * | sender | recipient | type | data | + * | Alice | Bob | send-an-asset | 1 concert.ticket | + * | Bob | Alice | send-an-asset | 20 euros | + * And Alice signs the contract + * Then the contract should appear as incomplete + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account1.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const transferTransaction2 = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account4.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(account4.publicAccount), + transferTransaction2.toAggregate(account1.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, []); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + + it('should return correct isComplete status TRUE - multisig Single Level', () => { + /** + * ACT - Multisig single level + * Alice (account1): initiate an transfer to Bob + * Bob (multisig3): is a 2/2 multisig account (account2 && account3) + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account4.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig3.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account2, [account3]); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.true; + }); + }); + + it('should return correct isComplete status FALSE - multisig Single Level', () => { + /** + * ACT - Multisig single level + * Alice (account1): initiate an transfer to Bob + * Bob (multisig3): is a 2/2 multisig account (account2 && account3) + */ + const transferTransaction = TransferTransaction.create( + Deadline.create(1, ChronoUnit.HOURS), + account4.address, + [], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete( + Deadline.create(), + [transferTransaction.toAggregate(multisig3.publicAccount)], + NetworkType.MIJIN_TEST, + []); + + const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account2, []); + aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { + expect(isComplete).to.be.false; + }); + }); + function givenMultisig2AccountInfo(): MultisigAccountInfo { return new MultisigAccountInfo(multisig2.publicAccount, 2, 1, @@ -233,6 +490,14 @@ describe('AggregatedTransactionService', () => { [], ); } + function givenMultisig3AccountInfo(): MultisigAccountInfo { + return new MultisigAccountInfo(multisig3.publicAccount, + 2, 2, + [account2.publicAccount, + account3.publicAccount], + [], + ); + } function givenAccount1Info(): MultisigAccountInfo { return new MultisigAccountInfo(account1.publicAccount, @@ -241,6 +506,22 @@ describe('AggregatedTransactionService', () => { [multisig2.publicAccount], ); } + function givenAccount2Info(): MultisigAccountInfo { + return new MultisigAccountInfo(account2.publicAccount, + 0, 0, + [], + [multisig2.publicAccount, + multisig3.publicAccount], + ); + } + function givenAccount3Info(): MultisigAccountInfo { + return new MultisigAccountInfo(account3.publicAccount, + 0, 0, + [], + [multisig2.publicAccount, + multisig3.publicAccount], + ); + } function givenAccount4Info(): MultisigAccountInfo { return new MultisigAccountInfo(account4.publicAccount, 0, 0, @@ -266,4 +547,16 @@ describe('AggregatedTransactionService', () => { return new MultisigAccountGraphInfo(map); } + function givenMultisig3AccountGraphInfo(): MultisigAccountGraphInfo { + const map = new Map(); + map.set(0, [new MultisigAccountInfo(multisig3.publicAccount, + 2, 2, + [account2.publicAccount, + account3.publicAccount], + [], + )]); + + return new MultisigAccountGraphInfo(map); + } + }); From 9754295b2694308262f4365476c3d81faad3376e Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Wed, 10 Apr 2019 15:13:01 +0100 Subject: [PATCH 53/83] renamed AggregatedTransactionService --- src/service/AggregatedTransactionService.ts | 152 ----- .../AggregatedTransactionService.spec.ts | 562 ------------------ 2 files changed, 714 deletions(-) delete mode 100644 src/service/AggregatedTransactionService.ts delete mode 100644 test/service/AggregatedTransactionService.spec.ts diff --git a/src/service/AggregatedTransactionService.ts b/src/service/AggregatedTransactionService.ts deleted file mode 100644 index 75a806bf96..0000000000 --- a/src/service/AggregatedTransactionService.ts +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2019 NEM - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import {from as observableFrom , Observable, of as observableOf} from 'rxjs'; -import { flatMap, map, mergeMap, toArray} from 'rxjs/operators'; -import { TransactionMapping } from '../core/utils/TransactionMapping'; -import { AccountHttp } from '../infrastructure/AccountHttp'; -import { MultisigAccountGraphInfo } from '../model/account/MultisigAccountGraphInfo'; -import { AggregateTransaction } from '../model/transaction/AggregateTransaction'; -import { InnerTransaction } from '../model/transaction/InnerTransaction'; -import { ModifyMultisigAccountTransaction } from '../model/transaction/ModifyMultisigAccountTransaction'; -import { MultisigCosignatoryModificationType } from '../model/transaction/MultisigCosignatoryModificationType'; -import { SignedTransaction } from '../model/transaction/SignedTransaction'; -import { TransactionType } from '../model/transaction/TransactionType'; - -/** - * Aggregated Transaction service - */ -export class AggregatedTransactionService { - - /** - * Constructor - * @param accountHttp - */ - constructor(private readonly accountHttp: AccountHttp) { - } - - /** - * Check if an aggregate complete transaction has all cosignatories attached - * @param signedTransaction - The signed aggregate transaction (complete) to be verified - * @returns {Observable} - */ - public isComplete(signedTransaction: SignedTransaction): Observable { - const aggregateTransaction = TransactionMapping.createFromPayload(signedTransaction.payload) as AggregateTransaction; - /** - * Include both initiator & cosigners - */ - const signers = (aggregateTransaction.cosignatures.map((cosigner) => cosigner.signer.publicKey)); - if (signedTransaction.signer) { - signers.push(signedTransaction.signer); - } - return observableFrom(aggregateTransaction.innerTransactions).pipe( - mergeMap((innerTransaction) => this.accountHttp.getMultisigAccountInfo(innerTransaction.signer.address) - .pipe( - /** - * For multisig account, we need to get the graph info in case it has multiple levels - */ - mergeMap((_) => _.minApproval !== 0 && _.minRemoval !== 0 ? - this.accountHttp.getMultisigAccountGraphInfo(_.account.address) - .pipe( - map((graphInfo) => this.validateCosignatories(graphInfo, signers, innerTransaction)), - ) : observableOf(signers.find((s) => s === _.account.publicKey ) !== undefined), - ), - ), - ), - toArray(), - ).pipe( - flatMap((results) => { - return observableOf(results.every((isComplete) => isComplete)); - }), - ); - } - - /** - * Validate cosignatories against multisig Account(s) - * @param graphInfo - multisig account graph info - * @param cosignatories - array of cosignatories extracted from aggregated transaction - * @param innerTransaction - the inner transaction of the aggregated transaction - * @returns {boolean} - */ - private validateCosignatories(graphInfo: MultisigAccountGraphInfo, - cosignatories: string[], - innerTransaction: InnerTransaction): boolean { - /** - * Validate cosignatories from bottom level to top - */ - const sortedKeys = Array.from(graphInfo.multisigAccounts.keys()).sort((a, b) => b - a); - const cosignatoriesReceived = cosignatories; - let validationResult = true; - - let isMultisigRemoval = false; - - /** - * Check inner transaction. If remove cosigner from multisig account, - * use minRemoval instead of minApproval for cosignatories validation. - */ - if (innerTransaction.type === TransactionType.MODIFY_MULTISIG_ACCOUNT) { - if ((innerTransaction as ModifyMultisigAccountTransaction).modifications - .find((modification) => modification.type === MultisigCosignatoryModificationType.Remove) !== undefined) { - isMultisigRemoval = true; - } - } - - sortedKeys.forEach((key) => { - const multisigInfo = graphInfo.multisigAccounts.get(key); - if (multisigInfo && validationResult) { - multisigInfo.forEach((multisig) => { - if (multisig.minApproval >= 1 && multisig.minRemoval) { // To make sure it is multisig account - const matchedCosignatories = this.compareArrays(cosignatoriesReceived, - multisig.cosignatories.map((cosig) => cosig.publicKey)); - - /** - * if minimal signature requirement met at current level, push the multisig account - * into the received signatories array for next level validation. - * Otherwise return validation failed. - */ - if ((matchedCosignatories.length >= multisig.minApproval && !isMultisigRemoval) || - (matchedCosignatories.length >= multisig.minRemoval && isMultisigRemoval)) { - if (cosignatoriesReceived.indexOf(multisig.account.publicKey) === -1) { - cosignatoriesReceived.push(multisig.account.publicKey); - } - } else { - validationResult = false; - } - } - }); - } - }); - - return validationResult; - } - - /** - * Compare two string arrays - * @param array1 - base array - * @param array2 - array to be matched - * @returns {string[]} - array of matched elements - */ - private compareArrays(array1: string[], array2: string[]): string[] { - const results: string[] = []; - array1.forEach((a1) => array2.forEach((a2) => { - if (a1 === a2) { - results.push(a1); - } - })); - - return results; - } -} diff --git a/test/service/AggregatedTransactionService.spec.ts b/test/service/AggregatedTransactionService.spec.ts deleted file mode 100644 index 23470cb2a1..0000000000 --- a/test/service/AggregatedTransactionService.spec.ts +++ /dev/null @@ -1,562 +0,0 @@ -/* - * Copyright 2019 NEM - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { expect } from 'chai'; -import { ChronoUnit } from 'js-joda'; -import {of as observableOf} from 'rxjs'; -import {deepEqual, instance, mock, when} from 'ts-mockito'; -import { AccountHttp } from '../../src/infrastructure/AccountHttp'; -import { Account } from '../../src/model/account/Account'; -import { Address } from '../../src/model/account/Address'; -import { MultisigAccountGraphInfo } from '../../src/model/account/MultisigAccountGraphInfo'; -import { MultisigAccountInfo } from '../../src/model/account/MultisigAccountInfo'; -import {NetworkType} from '../../src/model/blockchain/NetworkType'; -import { AggregateTransaction } from '../../src/model/transaction/AggregateTransaction'; -import { Deadline } from '../../src/model/transaction/Deadline'; -import { ModifyMultisigAccountTransaction } from '../../src/model/transaction/ModifyMultisigAccountTransaction'; -import { MultisigCosignatoryModification } from '../../src/model/transaction/MultisigCosignatoryModification'; -import { MultisigCosignatoryModificationType } from '../../src/model/transaction/MultisigCosignatoryModificationType'; -import { PlainMessage } from '../../src/model/transaction/PlainMessage'; -import { TransferTransaction } from '../../src/model/transaction/TransferTransaction'; -import { AggregatedTransactionService } from '../../src/service/AggregatedTransactionService'; - -/** - * For multi level multisig scenario visit: https://github.com/nemtech/nem2-docs/issues/10 - */ -describe('AggregatedTransactionService', () => { - let aggregatedTransactionService: AggregatedTransactionService; - - /** - * Multisig2 Account: SBROWP-7YMG2M-K45RO6-Q7ZPK7-G7GXWQ-JK5VNQ-OSUX - * Public Key: 5E628EA59818D97AA4118780D9A88C5512FCE7A21C195E1574727EFCE5DF7C0D - * Private Key: 22A1D67F8519D1A45BD7116600BB6E857786E816FE0B45E4C5B9FFF3D64BC177 - * - * - * Multisig1 Account: SAK32M-5JQ43R-WYHWEH-WRBCW4-RXERT2-DLASGL-EANS - * Public Key: BFDF2610C5666A626434FE12FB4A9D896D2B9B033F5F84CCEABE82E043A6307E - * Private Key: 8B0622C2CCFC5CCC5A74B500163E3C68F3AD3643DB12932FC931143EAC67280D - */ - - /** - * Test accounts: - * Multisig1 (1/1): Account2, Account3 - * Multisig2 (2/1): Account1, Multisig1 - * Multisig3 (2/2): Account2, Account3 - * Stranger Account: Account4 - */ - - const account1 = Account.createFromPrivateKey('82DB2528834C9926F0FCCE042466B24A266F5B685CB66D2869AF6648C043E950', - NetworkType.MIJIN_TEST); - const multisig1 = Account.createFromPrivateKey('8B0622C2CCFC5CCC5A74B500163E3C68F3AD3643DB12932FC931143EAC67280D', - NetworkType.MIJIN_TEST); - const multisig2 = Account.createFromPrivateKey('22A1D67F8519D1A45BD7116600BB6E857786E816FE0B45E4C5B9FFF3D64BC177', - NetworkType.MIJIN_TEST); - - const multisig3 = Account.createFromPrivateKey('5E7812AB0E709ABC45466034E1A209099F6A12C4698748A63CDCAA9B0DDE1DBD', - NetworkType.MIJIN_TEST); - const account2 = Account.createFromPrivateKey('A4D410270E01CECDCDEADCDE32EC79C8D9CDEA4DCD426CB1EB666EFEF148FBCE', - NetworkType.MIJIN_TEST); - const account3 = Account.createFromPrivateKey('336AB45EE65A6AFFC0E7ADC5342F91E34BACA0B901A1D9C876FA25A1E590077E', - NetworkType.MIJIN_TEST); - - const account4 = Account.createFromPrivateKey('4D8B3756592532753344E11E2B7541317BCCFBBCF4444274CDBF359D2C4AE0F1', - NetworkType.MIJIN_TEST); - before(() => { - const mockedAccountHttp = mock(AccountHttp); - - when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account1.address))) - .thenReturn(observableOf(givenAccount1Info())); - when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account4.address))) - .thenReturn(observableOf(givenAccount4Info())); - when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(multisig2.address))) - .thenReturn(observableOf(givenMultisig2AccountInfo())); - when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(multisig3.address))) - .thenReturn(observableOf(givenMultisig3AccountInfo())); - when(mockedAccountHttp.getMultisigAccountGraphInfo(deepEqual(multisig2.address))) - .thenReturn(observableOf(givenMultisig2AccountGraphInfo())); - when(mockedAccountHttp.getMultisigAccountGraphInfo(deepEqual(multisig3.address))) - .thenReturn(observableOf(givenMultisig3AccountGraphInfo())); - when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account2.address))) - .thenReturn(observableOf(givenAccount2Info())); - when(mockedAccountHttp.getMultisigAccountInfo(deepEqual(account3.address))) - .thenReturn(observableOf(givenAccount3Info())); - - const accountHttp = instance(mockedAccountHttp); - aggregatedTransactionService = new AggregatedTransactionService(accountHttp); - }); - - it('should return isComplete: true for aggregated complete transaction - 2 levels Multisig', () => { - /** - * MLMA - * Alice (account1): normal account - * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) - * Charles (multisig1) - Multisig 1-1 (account2 || account3) - * Given signatories: Account1 && Account4 - * Expecting complete as Bob needs 2 signatures (account1 && (account2 || account3)) - */ - const transferTransaction = TransferTransaction.create( - Deadline.create(1, ChronoUnit.HOURS), - Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), - [], - PlainMessage.create('test-message'), - NetworkType.MIJIN_TEST, - ); - - const aggregateTransaction = AggregateTransaction.createComplete( - Deadline.create(), - [transferTransaction.toAggregate(multisig2.publicAccount)], - NetworkType.MIJIN_TEST, - []); - - const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account2]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { - expect(isComplete).to.be.true; - }); - }); - - it('should return isComplete: false for aggregated complete transaction - 2 levels Multisig', () => { - /** - * MLMA - * Alice (account1): normal account - * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) - * Charles (multisig1) - Multisig 1-1 (account2 || account3) - * Given signatories: Account1 && Account4 - * Expecting incomplete as Bob needs 2 signatures (account1 && (account2 || account3)) but only got account1 - */ - const transferTransaction = TransferTransaction.create( - Deadline.create(1, ChronoUnit.HOURS), - Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), - [], - PlainMessage.create('test-message'), - NetworkType.MIJIN_TEST, - ); - - const aggregateTransaction = AggregateTransaction.createComplete( - Deadline.create(), - [transferTransaction.toAggregate(multisig2.publicAccount)], - NetworkType.MIJIN_TEST, - []); - - const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, []); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { - expect(isComplete).to.be.false; - }); - }); - - it('should return isComplete: false for aggregated complete transaction - 2 levels Multisig', () => { - /** - * MLMA - * Alice (account1): normal account - * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) - * Charles (multisig1) - Multisig 1-1 (account2 || account3) - * Given signatories: Account1 && Account4 - * Expecting incomplete as Bob needs 2 signatures (account1 && (account2 || account3)) but got account4 - */ - const transferTransaction = TransferTransaction.create( - Deadline.create(1, ChronoUnit.HOURS), - Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), - [], - PlainMessage.create('test-message'), - NetworkType.MIJIN_TEST, - ); - - const aggregateTransaction = AggregateTransaction.createComplete( - Deadline.create(), - [transferTransaction.toAggregate(multisig2.publicAccount)], - NetworkType.MIJIN_TEST, - []); - - const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { - expect(isComplete).to.be.false; - }); - }); - - it('should return correct isComplete status for aggregated complete transaction - 2 levels Multisig, multi inner transaction', () => { - /** - * MLMA - with multiple transaction - * Alice (account1): normal account - * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) - * Charles (multisig1) - Multisig 1-1 (account2 || account3) - * An extra inner transaction to account4 (just to increase the complexity) - * Given signatories: Account1 && Account4 - * Expecting incomplete as Bob needs 2 signatures (account1 && (account2 || account3)) - */ - const transferTransaction = TransferTransaction.create( - Deadline.create(1, ChronoUnit.HOURS), - account2.address, - [], - PlainMessage.create('test-message'), - NetworkType.MIJIN_TEST, - ); - - const transferTransaction2 = TransferTransaction.create( - Deadline.create(1, ChronoUnit.HOURS), - account2.address, - [], - PlainMessage.create('test-message'), - NetworkType.MIJIN_TEST, - ); - - const aggregateTransaction = AggregateTransaction.createComplete( - Deadline.create(), - [transferTransaction.toAggregate(multisig2.publicAccount), - transferTransaction2.toAggregate(account4.publicAccount)], - NetworkType.MIJIN_TEST, - []); - const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { - expect(isComplete).to.be.false; - }); - }); - - it('should return correct isComplete status for aggregated complete transaction - 2 levels Multisig, multi inner transaction', () => { - /** - * MLMA - with multiple transaction - * Alice (account1): normal account - * Bob (multisig2) - Multisig 2-1 (account1 && multisig1) - * Charles (multisig1) - Multisig 1-1 (account2 || account3) - * An extra inner transaction to account4 (just to increase the complexity) - * Given signatories: Account1 && Account4 && Account2 - * Expecting complete - */ - const transferTransaction = TransferTransaction.create( - Deadline.create(1, ChronoUnit.HOURS), - account2.address, - [], - PlainMessage.create('test-message'), - NetworkType.MIJIN_TEST, - ); - - const transferTransaction2 = TransferTransaction.create( - Deadline.create(1, ChronoUnit.HOURS), - account2.address, - [], - PlainMessage.create('test-message'), - NetworkType.MIJIN_TEST, - ); - - const aggregateTransaction = AggregateTransaction.createComplete( - Deadline.create(), - [transferTransaction.toAggregate(multisig2.publicAccount), - transferTransaction2.toAggregate(account4.publicAccount)], - NetworkType.MIJIN_TEST, - []); - const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4, account2]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { - expect(isComplete).to.be.true; - }); - }); - - it('should use minRemoval for multisig account validation if inner transaction is modify multisig remove', () => { - /** - * If the inner transaction is issued to a multisig account - * and the inner transaction itself is a ModifyMultiSigAccountTransaction - Removal - * The validator should use minRemoval value rather than minApproval value - * to determine if the act is complete or not - */ - const modifyMultisigTransaction = ModifyMultisigAccountTransaction.create( - Deadline.create(1, ChronoUnit.HOURS), - 1, - 1, - [new MultisigCosignatoryModification( - MultisigCosignatoryModificationType.Remove, - account1.publicAccount, - )], - NetworkType.MIJIN_TEST, - ); - - const aggregateTransaction = AggregateTransaction.createComplete( - Deadline.create(), - [modifyMultisigTransaction.toAggregate(multisig2.publicAccount)], - NetworkType.MIJIN_TEST, - []); - const signedTransaction = aggregateTransaction.signWith(account2); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { - expect(isComplete).to.be.true; - }); - }); - - it('should return correct isComplete status (false) for aggregated complete transaction - none multisig', () => { - /** - * If the inner transaction is issued to a multisig account - * and the inner transaction itself is a ModifyMultiSigAccountTransaction - Removal - * The validator should use minRemoval value rather than minApproval value - * to determine if the act is complete or not - */ - const transferTransaction = TransferTransaction.create( - Deadline.create(1, ChronoUnit.HOURS), - Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), - [], - PlainMessage.create('test-message'), - NetworkType.MIJIN_TEST, - ); - - const aggregateTransaction = AggregateTransaction.createComplete( - Deadline.create(), - [transferTransaction.toAggregate(account4.publicAccount)], - NetworkType.MIJIN_TEST, - []); - - const signedTransaction = aggregateTransaction.signWith(account1); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { - expect(isComplete).to.be.false; - }); - }); - - it('should return correct isComplete status (true) for aggregated complete transaction - none multisig', () => { - /** - * ACT - * Alice (account1): normal account - * Bob (account4) - normal account - * Alice initiate the transaction - * Bob sign - */ - const transferTransaction = TransferTransaction.create( - Deadline.create(1, ChronoUnit.HOURS), - Address.createFromRawAddress('SBILTA367K2LX2FEXG5TFWAS7GEFYAGY7QLFBYKC'), - [], - PlainMessage.create('test-message'), - NetworkType.MIJIN_TEST, - ); - - const aggregateTransaction = AggregateTransaction.createComplete( - Deadline.create(), - [transferTransaction.toAggregate(account4.publicAccount)], - NetworkType.MIJIN_TEST, - []); - - const signedTransaction = aggregateTransaction.signWith(account4); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { - expect(isComplete).to.be.true; - }); - }); - - it('should return correct isComplete status TRUE - multiple normal account', () => { - /** - * ACT - * Alice: account1 - * Bog: account4 - * An escrow contract is signed by all the participants (normal accounts) - * Given Alice defined the following escrow contract: - * | sender | recipient | type | data | - * | Alice | Bob | send-an-asset | 1 concert.ticket | - * | Bob | Alice | send-an-asset | 20 euros | - * And Bob signs the contract - * And Alice signs the contract - * Then the contract should appear as complete - */ - const transferTransaction = TransferTransaction.create( - Deadline.create(1, ChronoUnit.HOURS), - account1.address, - [], - PlainMessage.create('test-message'), - NetworkType.MIJIN_TEST, - ); - - const transferTransaction2 = TransferTransaction.create( - Deadline.create(1, ChronoUnit.HOURS), - account4.address, - [], - PlainMessage.create('test-message'), - NetworkType.MIJIN_TEST, - ); - - const aggregateTransaction = AggregateTransaction.createComplete( - Deadline.create(), - [transferTransaction.toAggregate(account4.publicAccount), - transferTransaction2.toAggregate(account1.publicAccount)], - NetworkType.MIJIN_TEST, - []); - - const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, [account4]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { - expect(isComplete).to.be.true; - }); - }); - - it('should return correct isComplete status FALSE - multiple normal account', () => { - /** - * ACT - * Alice: account1 - * Bog: account4 - * An escrow contract is signed by all the participants (normal accounts) - * Given Alice defined the following escrow contract: - * | sender | recipient | type | data | - * | Alice | Bob | send-an-asset | 1 concert.ticket | - * | Bob | Alice | send-an-asset | 20 euros | - * And Alice signs the contract - * Then the contract should appear as incomplete - */ - const transferTransaction = TransferTransaction.create( - Deadline.create(1, ChronoUnit.HOURS), - account1.address, - [], - PlainMessage.create('test-message'), - NetworkType.MIJIN_TEST, - ); - - const transferTransaction2 = TransferTransaction.create( - Deadline.create(1, ChronoUnit.HOURS), - account4.address, - [], - PlainMessage.create('test-message'), - NetworkType.MIJIN_TEST, - ); - - const aggregateTransaction = AggregateTransaction.createComplete( - Deadline.create(), - [transferTransaction.toAggregate(account4.publicAccount), - transferTransaction2.toAggregate(account1.publicAccount)], - NetworkType.MIJIN_TEST, - []); - - const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account1, []); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { - expect(isComplete).to.be.false; - }); - }); - - it('should return correct isComplete status TRUE - multisig Single Level', () => { - /** - * ACT - Multisig single level - * Alice (account1): initiate an transfer to Bob - * Bob (multisig3): is a 2/2 multisig account (account2 && account3) - */ - const transferTransaction = TransferTransaction.create( - Deadline.create(1, ChronoUnit.HOURS), - account4.address, - [], - PlainMessage.create('test-message'), - NetworkType.MIJIN_TEST, - ); - - const aggregateTransaction = AggregateTransaction.createComplete( - Deadline.create(), - [transferTransaction.toAggregate(multisig3.publicAccount)], - NetworkType.MIJIN_TEST, - []); - - const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account2, [account3]); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { - expect(isComplete).to.be.true; - }); - }); - - it('should return correct isComplete status FALSE - multisig Single Level', () => { - /** - * ACT - Multisig single level - * Alice (account1): initiate an transfer to Bob - * Bob (multisig3): is a 2/2 multisig account (account2 && account3) - */ - const transferTransaction = TransferTransaction.create( - Deadline.create(1, ChronoUnit.HOURS), - account4.address, - [], - PlainMessage.create('test-message'), - NetworkType.MIJIN_TEST, - ); - - const aggregateTransaction = AggregateTransaction.createComplete( - Deadline.create(), - [transferTransaction.toAggregate(multisig3.publicAccount)], - NetworkType.MIJIN_TEST, - []); - - const signedTransaction = aggregateTransaction.signTransactionWithCosignatories(account2, []); - aggregatedTransactionService.isComplete(signedTransaction).toPromise().then((isComplete) => { - expect(isComplete).to.be.false; - }); - }); - - function givenMultisig2AccountInfo(): MultisigAccountInfo { - return new MultisigAccountInfo(multisig2.publicAccount, - 2, 1, - [multisig1.publicAccount, - account1.publicAccount], - [], - ); - } - function givenMultisig3AccountInfo(): MultisigAccountInfo { - return new MultisigAccountInfo(multisig3.publicAccount, - 2, 2, - [account2.publicAccount, - account3.publicAccount], - [], - ); - } - - function givenAccount1Info(): MultisigAccountInfo { - return new MultisigAccountInfo(account1.publicAccount, - 0, 0, - [], - [multisig2.publicAccount], - ); - } - function givenAccount2Info(): MultisigAccountInfo { - return new MultisigAccountInfo(account2.publicAccount, - 0, 0, - [], - [multisig2.publicAccount, - multisig3.publicAccount], - ); - } - function givenAccount3Info(): MultisigAccountInfo { - return new MultisigAccountInfo(account3.publicAccount, - 0, 0, - [], - [multisig2.publicAccount, - multisig3.publicAccount], - ); - } - function givenAccount4Info(): MultisigAccountInfo { - return new MultisigAccountInfo(account4.publicAccount, - 0, 0, - [], - [], - ); - } - - function givenMultisig2AccountGraphInfo(): MultisigAccountGraphInfo { - const map = new Map(); - map.set(0, [new MultisigAccountInfo(multisig2.publicAccount, - 2, 1, - [multisig1.publicAccount, - account1.publicAccount], - [], - )]) - .set(1, [new MultisigAccountInfo(multisig1.publicAccount, - 1, 1, - [account2.publicAccount, account3.publicAccount], - [multisig2.publicAccount], - )]); - - return new MultisigAccountGraphInfo(map); - } - - function givenMultisig3AccountGraphInfo(): MultisigAccountGraphInfo { - const map = new Map(); - map.set(0, [new MultisigAccountInfo(multisig3.publicAccount, - 2, 2, - [account2.publicAccount, - account3.publicAccount], - [], - )]); - - return new MultisigAccountGraphInfo(map); - } - -}); From 85439ba9142722814a04c7052bef93eaaf1a27d6 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Wed, 10 Apr 2019 15:35:36 +0100 Subject: [PATCH 54/83] Fixed issue on createTransactionFromJson Changed default validation result value to false; --- src/model/transaction/Transaction.ts | 2 +- src/service/AggregateTransactionService.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/model/transaction/Transaction.ts b/src/model/transaction/Transaction.ts index e4cb7f2cfc..ed197082ff 100644 --- a/src/model/transaction/Transaction.ts +++ b/src/model/transaction/Transaction.ts @@ -211,7 +211,7 @@ export abstract class Transaction { const commonTransactionObject = { type: this.type, networkType: this.networkType, - version: this.version, + version: this.versionToDTO(), maxFee: this.maxFee.toDTO(), deadline: this.deadline.toDTO(), signature: this.signature ? this.signature : '', diff --git a/src/service/AggregateTransactionService.ts b/src/service/AggregateTransactionService.ts index 800d1a1b89..cd7bb44381 100644 --- a/src/service/AggregateTransactionService.ts +++ b/src/service/AggregateTransactionService.ts @@ -89,7 +89,7 @@ export class AggregateTransactionService { */ const sortedKeys = Array.from(graphInfo.multisigAccounts.keys()).sort((a, b) => b - a); const cosignatoriesReceived = cosignatories; - let validationResult = true; + let validationResult = false; let isMultisigRemoval = false; @@ -106,7 +106,7 @@ export class AggregateTransactionService { sortedKeys.forEach((key) => { const multisigInfo = graphInfo.multisigAccounts.get(key); - if (multisigInfo && validationResult) { + if (multisigInfo && !validationResult) { multisigInfo.forEach((multisig) => { if (multisig.minApproval >= 1 && multisig.minRemoval) { // To make sure it is multisig account const matchedCosignatories = this.compareArrays(cosignatoriesReceived, @@ -122,6 +122,7 @@ export class AggregateTransactionService { if (cosignatoriesReceived.indexOf(multisig.account.publicKey) === -1) { cosignatoriesReceived.push(multisig.account.publicKey); } + validationResult = true; } else { validationResult = false; } From bca5ac97e2b86376aa7ee9e755357f76c521dad9 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 13:22:33 +0100 Subject: [PATCH 55/83] Added #109 Make duration optiuon for mosaic definition --- src/model/mosaic/MosaicInfo.ts | 2 +- src/model/mosaic/MosaicProperties.ts | 14 ++++++++++---- .../transaction/MosaicDefinitionTransaction.ts | 2 +- test/core/utils/TransactionMapping.spec.ts | 4 ++-- .../MosaicDefinitionTransaction.spec.ts | 8 ++++---- 5 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/model/mosaic/MosaicInfo.ts b/src/model/mosaic/MosaicInfo.ts index 162e6d7238..999d044e48 100644 --- a/src/model/mosaic/MosaicInfo.ts +++ b/src/model/mosaic/MosaicInfo.ts @@ -81,7 +81,7 @@ export class MosaicInfo { * Mosaic duration * @returns {UInt64} */ - public get duration(): UInt64 { + public get duration(): UInt64 | undefined { return this.properties.duration; } diff --git a/src/model/mosaic/MosaicProperties.ts b/src/model/mosaic/MosaicProperties.ts index dfb7ebdb22..7c8551be7a 100644 --- a/src/model/mosaic/MosaicProperties.ts +++ b/src/model/mosaic/MosaicProperties.ts @@ -59,8 +59,9 @@ export class MosaicProperties { /** * The duration in blocks a mosaic will be available. * After the duration finishes mosaic is inactive and can be renewed. + * Duration is optional when defining the mosaic */ - public readonly duration: UInt64) { + public readonly duration?: UInt64) { let binaryFlags = '00' + (flags.lower >>> 0).toString(2); binaryFlags = binaryFlags.substr(binaryFlags.length - 3, 3); this.supplyMutable = binaryFlags[2] === '1'; @@ -78,7 +79,7 @@ export class MosaicProperties { transferable: boolean, levyMutable: boolean, divisibility: number, - duration: UInt64, + duration?: UInt64, }) { const flags = (params.supplyMutable ? 1 : 0) + (params.transferable ? 2 : 0) + (params.levyMutable ? 4 : 0); return new MosaicProperties(UInt64.fromUint(flags), params.divisibility, params.duration); @@ -88,12 +89,17 @@ export class MosaicProperties { * Create DTO object */ toDTO() { - return [ + const dto = [ {id: 0, value: UInt64.fromUint((this.supplyMutable ? 1 : 0) + (this.transferable ? 2 : 0) + (this.levyMutable ? 4 : 0)).toDTO()}, {id: 1, value: UInt64.fromUint(this.divisibility).toDTO()}, - {id: 2, value: this.duration.toDTO()}, ]; + + if (this.duration !== undefined) { + dto.push({id: 2, value: this.duration.toDTO()}); + } + + return dto; } } diff --git a/src/model/transaction/MosaicDefinitionTransaction.ts b/src/model/transaction/MosaicDefinitionTransaction.ts index 9443d08c26..851306c3b4 100644 --- a/src/model/transaction/MosaicDefinitionTransaction.ts +++ b/src/model/transaction/MosaicDefinitionTransaction.ts @@ -128,7 +128,7 @@ export class MosaicDefinitionTransaction extends Transaction { .addFee(this.maxFee.toDTO()) .addVersion(this.versionToDTO()) .addDivisibility(this.mosaicProperties.divisibility) - .addDuration(this.mosaicProperties.duration.toDTO()) + .addDuration(this.mosaicProperties.duration ? this.mosaicProperties.duration.toDTO() : []) .addNonce(this.nonce.toDTO()) .addMosaicId(this.mosaicId.id.toDTO()); diff --git a/test/core/utils/TransactionMapping.spec.ts b/test/core/utils/TransactionMapping.spec.ts index 467f8d7626..4c9974c6e7 100644 --- a/test/core/utils/TransactionMapping.spec.ts +++ b/test/core/utils/TransactionMapping.spec.ts @@ -192,8 +192,8 @@ describe('TransactionMapping - createFromPayload', () => { const transaction = TransactionMapping.createFromPayload(signedTransaction.payload) as MosaicDefinitionTransaction; - expect(transaction.mosaicProperties.duration.lower).to.be.equal(1000); - expect(transaction.mosaicProperties.duration.higher).to.be.equal(0); + expect(transaction.mosaicProperties.duration!.lower).to.be.equal(1000); + expect(transaction.mosaicProperties.duration! .higher).to.be.equal(0); expect(transaction.mosaicProperties.divisibility).to.be.equal(3); expect(transaction.mosaicProperties.supplyMutable).to.be.equal(false); expect(transaction.mosaicProperties.transferable).to.be.equal(false); diff --git a/test/model/transaction/MosaicDefinitionTransaction.spec.ts b/test/model/transaction/MosaicDefinitionTransaction.spec.ts index 23ae1bc416..a70fecf0cb 100644 --- a/test/model/transaction/MosaicDefinitionTransaction.spec.ts +++ b/test/model/transaction/MosaicDefinitionTransaction.spec.ts @@ -87,8 +87,8 @@ describe('MosaicDefinitionTransaction', () => { NetworkType.MIJIN_TEST, ); - expect(mosaicDefinitionTransaction.mosaicProperties.duration.lower).to.be.equal(1000); - expect(mosaicDefinitionTransaction.mosaicProperties.duration.higher).to.be.equal(0); + expect(mosaicDefinitionTransaction.mosaicProperties.duration!.lower).to.be.equal(1000); + expect(mosaicDefinitionTransaction.mosaicProperties.duration!.higher).to.be.equal(0); expect(mosaicDefinitionTransaction.mosaicProperties.divisibility).to.be.equal(3); expect(mosaicDefinitionTransaction.mosaicProperties.supplyMutable).to.be.equal(true); expect(mosaicDefinitionTransaction.mosaicProperties.transferable).to.be.equal(true); @@ -119,8 +119,8 @@ describe('MosaicDefinitionTransaction', () => { NetworkType.MIJIN_TEST, ); - expect(mosaicDefinitionTransaction.mosaicProperties.duration.lower).to.be.equal(1000); - expect(mosaicDefinitionTransaction.mosaicProperties.duration.higher).to.be.equal(0); + expect(mosaicDefinitionTransaction.mosaicProperties.duration!.lower).to.be.equal(1000); + expect(mosaicDefinitionTransaction.mosaicProperties.duration!.higher).to.be.equal(0); expect(mosaicDefinitionTransaction.mosaicProperties.divisibility).to.be.equal(3); expect(mosaicDefinitionTransaction.mosaicProperties.supplyMutable).to.be.equal(false); expect(mosaicDefinitionTransaction.mosaicProperties.transferable).to.be.equal(false); From 86daf36db5b478aa5daa39120fc90ab9c8a355f3 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 16:21:51 +0100 Subject: [PATCH 56/83] made mosaic properties duration option to all references --- .../transaction/CreateTransactionFromDTO.ts | 2 +- .../CreateTransactionFromPayload.ts | 2 +- test/core/utils/TransactionMapping.spec.ts | 25 ++++++++++++++++ .../SerializeTransactionToJSON.spec.ts | 24 +++++++++++++++ test/model/mosaic/MosaicInfo.spec.ts | 27 +++++++++++++++++ test/model/mosaic/MosaicProperties.spec.ts | 16 ++++++++++ .../MosaicDefinitionTransaction.spec.ts | 30 ++++++++++++++++++- 7 files changed, 123 insertions(+), 3 deletions(-) diff --git a/src/infrastructure/transaction/CreateTransactionFromDTO.ts b/src/infrastructure/transaction/CreateTransactionFromDTO.ts index 04baf86f74..0c0543ad26 100644 --- a/src/infrastructure/transaction/CreateTransactionFromDTO.ts +++ b/src/infrastructure/transaction/CreateTransactionFromDTO.ts @@ -158,7 +158,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr new MosaicProperties( new UInt64(transactionDTO.properties[0].value), (new UInt64(transactionDTO.properties[1].value)).compact(), - new UInt64(transactionDTO.properties.length === 3 ? transactionDTO.properties[2].value : [0, 0]), + new UInt64(transactionDTO.properties.length === 3 ? transactionDTO.properties[2].value : undefined), ), transactionDTO.signature, transactionDTO.signer ? PublicAccount.createFromPublicKey(transactionDTO.signer, diff --git a/src/infrastructure/transaction/CreateTransactionFromPayload.ts b/src/infrastructure/transaction/CreateTransactionFromPayload.ts index af8bd12125..d35c12db63 100644 --- a/src/infrastructure/transaction/CreateTransactionFromPayload.ts +++ b/src/infrastructure/transaction/CreateTransactionFromPayload.ts @@ -224,7 +224,7 @@ const CreateTransaction = (type: number, transactionData: string, networkType: N transferable: (flags & 2) === 2, levyMutable: (flags & 4) === 4, divisibility: parseInt(convert.uint8ToHex(convert.hexToUint8(divisibility).reverse()), 16), - duration: UInt64.fromHex(reverse(duration)), + duration: duration ? UInt64.fromHex(reverse(duration)) : undefined, }), networkType, ); diff --git a/test/core/utils/TransactionMapping.spec.ts b/test/core/utils/TransactionMapping.spec.ts index 4c9974c6e7..6c34145b14 100644 --- a/test/core/utils/TransactionMapping.spec.ts +++ b/test/core/utils/TransactionMapping.spec.ts @@ -201,6 +201,31 @@ describe('TransactionMapping - createFromPayload', () => { }); + it('should create MosaicDefinitionTransaction - without duration', () => { + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce + new MosaicId(UInt64.fromUint(1).toDTO()), // ID + MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 3, + }), + NetworkType.MIJIN_TEST, + ); + + const signedTransaction = mosaicDefinitionTransaction.signWith(account); + + const transaction = TransactionMapping.createFromPayload(signedTransaction.payload) as MosaicDefinitionTransaction; + + expect(transaction.mosaicProperties.divisibility).to.be.equal(3); + expect(transaction.mosaicProperties.supplyMutable).to.be.equal(false); + expect(transaction.mosaicProperties.transferable).to.be.equal(false); + expect(transaction.mosaicProperties.levyMutable).to.be.equal(false); + + }); + it('should create MosaicSupplyChangeTransaction', () => { const mosaicId = new MosaicId([2262289484, 3405110546]); const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( diff --git a/test/infrastructure/SerializeTransactionToJSON.spec.ts b/test/infrastructure/SerializeTransactionToJSON.spec.ts index 34807cb745..2c3abf38e5 100644 --- a/test/infrastructure/SerializeTransactionToJSON.spec.ts +++ b/test/infrastructure/SerializeTransactionToJSON.spec.ts @@ -197,6 +197,30 @@ describe('SerializeTransactionToJSON', () => { }); + it('should create MosaicDefinitionTransaction without duration', () => { + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce + new MosaicId(UInt64.fromUint(1).toDTO()), // ID + MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 3, + }), + NetworkType.MIJIN_TEST, + ); + + const json = mosaicDefinitionTransaction.toJSON(); + + expect(json.type).to.be.equal(TransactionType.MOSAIC_DEFINITION); + expect(json.mosaicProperties.supplyMutable).to.be.equal(false); + expect(json.mosaicProperties.transferable).to.be.equal(false); + expect(json.mosaicProperties.levyMutable).to.be.equal(false); + expect(json.mosaicProperties.divisibility).to.be.equal(3); + + }); + it('should create MosaicSupplyChangeTransaction', () => { const mosaicId = new MosaicId([2262289484, 3405110546]); const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( diff --git a/test/model/mosaic/MosaicInfo.spec.ts b/test/model/mosaic/MosaicInfo.spec.ts index a46eb459ea..b60de0ca3b 100644 --- a/test/model/mosaic/MosaicInfo.spec.ts +++ b/test/model/mosaic/MosaicInfo.spec.ts @@ -78,6 +78,33 @@ describe('MosaicInfo', () => { }); + it('should createComplete an MosaicInfo object without duration', () => { + const mosaicInfo = new MosaicInfo( + mosaicInfoDTO.meta.id, + mosaicInfoDTO.mosaic.mosaicId, + mosaicInfoDTO.mosaic.supply, + mosaicInfoDTO.mosaic.height, + mosaicInfoDTO.mosaic.owner, + mosaicInfoDTO.mosaic.revision, + new MosaicProperties( + mosaicInfoDTO.mosaic.properties[0], + mosaicInfoDTO.mosaic.properties[1].compact(), + ), + mosaicInfoDTO.mosaic.levy, + ); + + expect(mosaicInfo.metaId).to.be.equal(mosaicInfoDTO.meta.id); + deepEqual(mosaicInfo.mosaicId, mosaicInfoDTO.mosaic.mosaicId); + deepEqual(mosaicInfo.supply, mosaicInfoDTO.mosaic.supply); + deepEqual(mosaicInfo.height, mosaicInfoDTO.mosaic.height); + expect(mosaicInfo.owner).to.be.equal(mosaicInfoDTO.mosaic.owner); + deepEqual(mosaicInfo.revision, mosaicInfoDTO.mosaic.revision); + + expect(mosaicInfo.divisibility).to.be.equal(mosaicInfoDTO.mosaic.properties[1].lower); + deepEqual(mosaicInfo.duration, undefined); + + }); + describe('isSupplyMutable', () => { it('should return true when it\'s mutable', () => { const mosaicInfo = new MosaicInfo( diff --git a/test/model/mosaic/MosaicProperties.spec.ts b/test/model/mosaic/MosaicProperties.spec.ts index 5f711f2da8..f2603b3fa8 100644 --- a/test/model/mosaic/MosaicProperties.spec.ts +++ b/test/model/mosaic/MosaicProperties.spec.ts @@ -69,4 +69,20 @@ describe('MosaicProperties', () => { expect(mosaicProperties.transferable).to.be.equal(false); expect(mosaicProperties.levyMutable).to.be.equal(false); }); + + it('should createComplete an MosaicProperties object without duration', () => { + const mosaicProperties = MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 10, + }); + + expect(mosaicProperties.divisibility).to.be.equal(10); + deepEqual(mosaicProperties.duration, undefined); + + expect(mosaicProperties.supplyMutable).to.be.equal(false); + expect(mosaicProperties.transferable).to.be.equal(false); + expect(mosaicProperties.levyMutable).to.be.equal(false); + }); }); diff --git a/test/model/transaction/MosaicDefinitionTransaction.spec.ts b/test/model/transaction/MosaicDefinitionTransaction.spec.ts index a70fecf0cb..4f4f3f61ff 100644 --- a/test/model/transaction/MosaicDefinitionTransaction.spec.ts +++ b/test/model/transaction/MosaicDefinitionTransaction.spec.ts @@ -24,7 +24,6 @@ import {Deadline} from '../../../src/model/transaction/Deadline'; import {MosaicDefinitionTransaction} from '../../../src/model/transaction/MosaicDefinitionTransaction'; import {UInt64} from '../../../src/model/UInt64'; import {TestingAccount} from '../../conf/conf.spec'; -import {convert, mosaicId, uint64 as uint64_t} from 'nem2-library'; describe('MosaicDefinitionTransaction', () => { let account: Account; @@ -154,4 +153,33 @@ describe('MosaicDefinitionTransaction', () => { expect(mosaicDefinitionTransaction.size).to.be.equal(144); }); }); + + it('should createComplete an MosaicDefinitionTransaction object and sign it without duration', () => { + + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce + new MosaicId(UInt64.fromUint(1).toDTO()), // ID + MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 3, + }), + NetworkType.MIJIN_TEST, + ); + + expect(mosaicDefinitionTransaction.mosaicProperties.divisibility).to.be.equal(3); + expect(mosaicDefinitionTransaction.mosaicProperties.supplyMutable).to.be.equal(false); + expect(mosaicDefinitionTransaction.mosaicProperties.transferable).to.be.equal(false); + expect(mosaicDefinitionTransaction.mosaicProperties.levyMutable).to.be.equal(false); + + const signedTransaction = mosaicDefinitionTransaction.signWith(account); + + expect(signedTransaction.payload.substring( + 240, + signedTransaction.payload.length, + )).to.be.equal('E6DE84B80100000000000000000003'); + + }); }); From ab82539fe9a938f51a3b82ad302a606a773cf3e3 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 18:02:11 +0100 Subject: [PATCH 57/83] Updared tslint to disable "no-non-null-assertion" --- tslint.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tslint.json b/tslint.json index aeca64ccdf..f47346aaf9 100644 --- a/tslint.json +++ b/tslint.json @@ -65,7 +65,7 @@ "ignore-params" ], "no-misused-new": true, - "no-non-null-assertion": true, + "no-non-null-assertion": false, "no-shadowed-variable": true, "no-string-literal": false, "no-string-throw": true, @@ -139,6 +139,6 @@ "use-pipe-transform-interface": true, "component-class-suffix": true, "directive-class-suffix": true, - "invoke-injectable": true + "invoke-injectable": true, } } From ed85c28d3b0d6fcc8532245e0954cea3d240a30e Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Tue, 9 Apr 2019 12:46:33 +0100 Subject: [PATCH 58/83] Fixed white space issue on TransactionMapping test Fixed trailing comma in tslint --- test/core/utils/TransactionMapping.spec.ts | 2 +- tslint.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/core/utils/TransactionMapping.spec.ts b/test/core/utils/TransactionMapping.spec.ts index 6c34145b14..e302aed6a4 100644 --- a/test/core/utils/TransactionMapping.spec.ts +++ b/test/core/utils/TransactionMapping.spec.ts @@ -193,7 +193,7 @@ describe('TransactionMapping - createFromPayload', () => { const transaction = TransactionMapping.createFromPayload(signedTransaction.payload) as MosaicDefinitionTransaction; expect(transaction.mosaicProperties.duration!.lower).to.be.equal(1000); - expect(transaction.mosaicProperties.duration! .higher).to.be.equal(0); + expect(transaction.mosaicProperties.duration!.higher).to.be.equal(0); expect(transaction.mosaicProperties.divisibility).to.be.equal(3); expect(transaction.mosaicProperties.supplyMutable).to.be.equal(false); expect(transaction.mosaicProperties.transferable).to.be.equal(false); diff --git a/tslint.json b/tslint.json index f47346aaf9..ded366576a 100644 --- a/tslint.json +++ b/tslint.json @@ -139,6 +139,6 @@ "use-pipe-transform-interface": true, "component-class-suffix": true, "directive-class-suffix": true, - "invoke-injectable": true, + "invoke-injectable": true } } From d7690d54059371abcf6bb4cc9c3e158e67c53210 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 13:22:33 +0100 Subject: [PATCH 59/83] Added #109 Make duration optiuon for mosaic definition --- test/core/utils/TransactionMapping.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/core/utils/TransactionMapping.spec.ts b/test/core/utils/TransactionMapping.spec.ts index e302aed6a4..8f9bb83071 100644 --- a/test/core/utils/TransactionMapping.spec.ts +++ b/test/core/utils/TransactionMapping.spec.ts @@ -219,6 +219,7 @@ describe('TransactionMapping - createFromPayload', () => { const transaction = TransactionMapping.createFromPayload(signedTransaction.payload) as MosaicDefinitionTransaction; + expect(transaction.mosaicProperties.duration! .higher).to.be.equal(0); expect(transaction.mosaicProperties.divisibility).to.be.equal(3); expect(transaction.mosaicProperties.supplyMutable).to.be.equal(false); expect(transaction.mosaicProperties.transferable).to.be.equal(false); From ea8df0f522d8c0fcf15ce121d29acfa67431b2df Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 16:21:51 +0100 Subject: [PATCH 60/83] made mosaic properties duration option to all references --- test/core/utils/TransactionMapping.spec.ts | 25 +++++++++++++++++++ .../SerializeTransactionToJSON.spec.ts | 24 ++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/test/core/utils/TransactionMapping.spec.ts b/test/core/utils/TransactionMapping.spec.ts index 8f9bb83071..818111cf85 100644 --- a/test/core/utils/TransactionMapping.spec.ts +++ b/test/core/utils/TransactionMapping.spec.ts @@ -227,6 +227,31 @@ describe('TransactionMapping - createFromPayload', () => { }); + it('should create MosaicDefinitionTransaction - without duration', () => { + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce + new MosaicId(UInt64.fromUint(1).toDTO()), // ID + MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 3, + }), + NetworkType.MIJIN_TEST, + ); + + const signedTransaction = mosaicDefinitionTransaction.signWith(account); + + const transaction = TransactionMapping.createFromPayload(signedTransaction.payload) as MosaicDefinitionTransaction; + + expect(transaction.mosaicProperties.divisibility).to.be.equal(3); + expect(transaction.mosaicProperties.supplyMutable).to.be.equal(false); + expect(transaction.mosaicProperties.transferable).to.be.equal(false); + expect(transaction.mosaicProperties.levyMutable).to.be.equal(false); + + }); + it('should create MosaicSupplyChangeTransaction', () => { const mosaicId = new MosaicId([2262289484, 3405110546]); const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( diff --git a/test/infrastructure/SerializeTransactionToJSON.spec.ts b/test/infrastructure/SerializeTransactionToJSON.spec.ts index 2c3abf38e5..7683fea603 100644 --- a/test/infrastructure/SerializeTransactionToJSON.spec.ts +++ b/test/infrastructure/SerializeTransactionToJSON.spec.ts @@ -221,6 +221,30 @@ describe('SerializeTransactionToJSON', () => { }); + it('should create MosaicDefinitionTransaction without duration', () => { + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce + new MosaicId(UInt64.fromUint(1).toDTO()), // ID + MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 3, + }), + NetworkType.MIJIN_TEST, + ); + + const json = mosaicDefinitionTransaction.toJSON(); + + expect(json.type).to.be.equal(TransactionType.MOSAIC_DEFINITION); + expect(json.mosaicProperties.supplyMutable).to.be.equal(false); + expect(json.mosaicProperties.transferable).to.be.equal(false); + expect(json.mosaicProperties.levyMutable).to.be.equal(false); + expect(json.mosaicProperties.divisibility).to.be.equal(3); + + }); + it('should create MosaicSupplyChangeTransaction', () => { const mosaicId = new MosaicId([2262289484, 3405110546]); const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( From dc40691b60477b12686de7b5c86641865ae74d42 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 18:02:11 +0100 Subject: [PATCH 61/83] Updared tslint to disable "no-non-null-assertion" --- tslint.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tslint.json b/tslint.json index ded366576a..f47346aaf9 100644 --- a/tslint.json +++ b/tslint.json @@ -139,6 +139,6 @@ "use-pipe-transform-interface": true, "component-class-suffix": true, "directive-class-suffix": true, - "invoke-injectable": true + "invoke-injectable": true, } } From be9d05e7b12e931f58a6e866e88150d9d47dbea9 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Tue, 9 Apr 2019 12:46:33 +0100 Subject: [PATCH 62/83] Fixed white space issue on TransactionMapping test Fixed trailing comma in tslint --- tslint.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tslint.json b/tslint.json index f47346aaf9..ded366576a 100644 --- a/tslint.json +++ b/tslint.json @@ -139,6 +139,6 @@ "use-pipe-transform-interface": true, "component-class-suffix": true, "directive-class-suffix": true, - "invoke-injectable": true, + "invoke-injectable": true } } From 057d05d2ed0bc891183dc578169647d52e306351 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Wed, 10 Apr 2019 16:15:14 +0100 Subject: [PATCH 63/83] Fixed a few merge issues --- src/model/transaction/Transaction.ts | 2 +- test/core/utils/TransactionMapping.spec.ts | 1 - .../SerializeTransactionToJSON.spec.ts | 55 +------------------ 3 files changed, 3 insertions(+), 55 deletions(-) diff --git a/src/model/transaction/Transaction.ts b/src/model/transaction/Transaction.ts index e4cb7f2cfc..ed197082ff 100644 --- a/src/model/transaction/Transaction.ts +++ b/src/model/transaction/Transaction.ts @@ -211,7 +211,7 @@ export abstract class Transaction { const commonTransactionObject = { type: this.type, networkType: this.networkType, - version: this.version, + version: this.versionToDTO(), maxFee: this.maxFee.toDTO(), deadline: this.deadline.toDTO(), signature: this.signature ? this.signature : '', diff --git a/test/core/utils/TransactionMapping.spec.ts b/test/core/utils/TransactionMapping.spec.ts index 8f9bb83071..e302aed6a4 100644 --- a/test/core/utils/TransactionMapping.spec.ts +++ b/test/core/utils/TransactionMapping.spec.ts @@ -219,7 +219,6 @@ describe('TransactionMapping - createFromPayload', () => { const transaction = TransactionMapping.createFromPayload(signedTransaction.payload) as MosaicDefinitionTransaction; - expect(transaction.mosaicProperties.duration! .higher).to.be.equal(0); expect(transaction.mosaicProperties.divisibility).to.be.equal(3); expect(transaction.mosaicProperties.supplyMutable).to.be.equal(false); expect(transaction.mosaicProperties.transferable).to.be.equal(false); diff --git a/test/infrastructure/SerializeTransactionToJSON.spec.ts b/test/infrastructure/SerializeTransactionToJSON.spec.ts index 456855d367..74909fd85f 100644 --- a/test/infrastructure/SerializeTransactionToJSON.spec.ts +++ b/test/infrastructure/SerializeTransactionToJSON.spec.ts @@ -213,59 +213,8 @@ describe('SerializeTransactionToJSON', () => { const json = mosaicDefinitionTransaction.toJSON(); - expect(json.type).to.be.equal(TransactionType.MOSAIC_DEFINITION); - expect(json.mosaicProperties.supplyMutable).to.be.equal(false); - expect(json.mosaicProperties.transferable).to.be.equal(false); - expect(json.mosaicProperties.levyMutable).to.be.equal(false); - expect(json.mosaicProperties.divisibility).to.be.equal(3); - - }); - - it('should create MosaicDefinitionTransaction without duration', () => { - const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( - Deadline.create(), - new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce - new MosaicId(UInt64.fromUint(1).toDTO()), // ID - MosaicProperties.create({ - supplyMutable: false, - transferable: false, - levyMutable: false, - divisibility: 3, - }), - NetworkType.MIJIN_TEST, - ); - - const json = mosaicDefinitionTransaction.toJSON(); - - expect(json.type).to.be.equal(TransactionType.MOSAIC_DEFINITION); - expect(json.mosaicProperties.supplyMutable).to.be.equal(false); - expect(json.mosaicProperties.transferable).to.be.equal(false); - expect(json.mosaicProperties.levyMutable).to.be.equal(false); - expect(json.mosaicProperties.divisibility).to.be.equal(3); - - }); - - it('should create MosaicDefinitionTransaction without duration', () => { - const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( - Deadline.create(), - new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce - new MosaicId(UInt64.fromUint(1).toDTO()), // ID - MosaicProperties.create({ - supplyMutable: false, - transferable: false, - levyMutable: false, - divisibility: 3, - }), - NetworkType.MIJIN_TEST, - ); - - const json = mosaicDefinitionTransaction.toJSON(); - - expect(json.type).to.be.equal(TransactionType.MOSAIC_DEFINITION); - expect(json.mosaicProperties.supplyMutable).to.be.equal(false); - expect(json.mosaicProperties.transferable).to.be.equal(false); - expect(json.mosaicProperties.levyMutable).to.be.equal(false); - expect(json.mosaicProperties.divisibility).to.be.equal(3); + expect(json.transaction.type).to.be.equal(TransactionType.MOSAIC_DEFINITION); + expect(json.transaction.properties.length).to.be.equal(2); }); From ecf1cdd19fbf728adae08f85726e9e85f5dea794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Garc=C3=ADa?= Date: Wed, 10 Apr 2019 16:35:33 +0100 Subject: [PATCH 64/83] Update CHANGELOG.md --- CHANGELOG.md | 70 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a323fedad..01516635a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,32 +1,54 @@ -# CHANGELOG +# Changelog +All notable changes to this project will be documented in this file. -# v0.11 +The changelog format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). -- Fixed NetworkCurrencyMosaic, NetworkHarvestMosaic -- Added exposed UInt64.fromHex and UInt64.toHex -- Added MosaicId.createFromNonce -- Added MosaicNonce, MosaicNonce.createRandom -- Fixed AliasDTO.mosaicId to be UInt64 -- Added nem2-library@v0.9.8 version update (cow compatibility) -- Added cow network update *base* compatibility -- Added AliasTransaction, AddressAliasTransaction, MosaicAliasTransaction -- Changed MosaicDefinition to hold MosaicNonce -- Changed SecretLock transaction to work with Sha3_256 instead of Sha3_512 -- Added delegated harvesting -- Fixed #38: error message for aggregate as inner tx -- Added TransferTransaction.recipient NamespaceId argument type +## [0.11.2] - 1-Apr-2019 +- Todo. -# v0.10.1-beta +## [0.11.1] - 18-Mar-2019 +- Todo. +## [0.11] - 14-Mar-2019 -- added replyGiven in Transaction model -- several linter fixes +## Added +- UInt64.fromHex and UInt64.toHex functions have been exposed. +- Error message when a developer tries to add an aggregate as an inner transaction. +- AccountLink Transaction to enable delegated harvesting. +- Support for AliasTransaction. +- TransferTransactions can be sent to an alias instead of an address. -# v0.10.0-beta +## Changed +- Mosaics were splited from namespaces. MosaicDefinition does not have a related namespaceId, but instead a MosaicNonce. +- SecretLockTransaction to work with Sha3_256 instead of Sha3_512 +- Network and nem2-library (0.9.8) required update to be compatible with catpault-server 0.3. +- XEM class splited into NetworkCurrencyMosaic and NetworkHarvestMosaic. -- update rxjs to v6 -- use observableFrom +## [0.10.1-beta] - 27-Jun-2018 -# v0.9.5 +## Added +- Transaction deadline has been exposed to the public. -- data signatures -- nodejs version 10 updates +## Changed +- Compatibility with rxjs v6. + +## Fixed +- Several linter errors. + +## [0.9.5] - 27-Jun-2018 + +## Added +- An account can sign strings with its private key, and verify the signature later. + +## Changed +- Compatibility with Node.js 10. + +## [0.9.0] - 30-Mar-2018 +### Added +- Initial code release. + +[0.11.2]: https://github.com/nemtech/nem2-sdk-typescript-javascript/compare/v0.11.1...v0.11.2 +[0.11.1]: https://github.com/nemtech/nem2-sdk-typescript-javascript/compare/v0.11.0...v0.11.1 +[0.11]: https://github.com/nemtech/nem2-sdk-typescript-javascript/compare/v0.10.1-beta...v0.11.0 +[0.10.1-beta]: https://github.com/nemtech/nem2-sdk-typescript-javascript/compare/v0.9.5...v0.10.1-beta +[0.9.5]: https://github.com/nemtech/nem2-sdk-typescript-javascript/compare/v0.9.0...v0.9.5 +[0.9.0]: https://github.com/nemtech/nem2-sdk-typescript-javascript/releases/tag/v0.9.0 From 9f47bd5a046948346914c69ece49a2aabda7796c Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Wed, 10 Apr 2019 16:48:47 +0100 Subject: [PATCH 65/83] Updated lib version to 0.9.14 Fixed a few lint issues on EncryptedMessage and test Fixed failed test on EncryptedMessage spec --- package-lock.json | 6 +-- package.json | 2 +- src/model/transaction/EncryptedMessage.ts | 4 +- .../transaction/EncryptedMessage.spec.ts | 39 ++++++++----------- 4 files changed, 22 insertions(+), 29 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0fc3fdf7b3..556da5d6a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1213,9 +1213,9 @@ "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" }, "nem2-library": { - "version": "0.9.13", - "resolved": "https://registry.npmjs.org/nem2-library/-/nem2-library-0.9.13.tgz", - "integrity": "sha512-KCvg+8UD5mXHAJIpkP85p/0PivIfhRL+IkIWKm2xxiYgETNbCu82qf4zDIpLGIqBgzpQ++j5i4a04c9mq39K5w==", + "version": "0.9.14", + "resolved": "https://registry.npmjs.org/nem2-library/-/nem2-library-0.9.14.tgz", + "integrity": "sha512-WTWycF01RwDj9UIn0B3Gb7BIhOQszYVKi7/IzfR4mGGrxsJcmMoJ71oB0OUtjHyHv47lM6g5sk2QGu58u376sw==", "requires": { "bufferutil": "^3.0.5", "crypto-browserify": "3.12.0", diff --git a/package.json b/package.json index dc62464f43..f03445c74a 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "@types/crypto-js": "^3.1.43", "crypto-js": "^3.1.9-1", "js-joda": "^1.6.2", - "nem2-library": "^0.9.13", + "nem2-library": "^0.9.14", "request": "^2.83.0", "request-promise-native": "^1.0.5", "rxjs": "^6.2.1", diff --git a/src/model/transaction/EncryptedMessage.ts b/src/model/transaction/EncryptedMessage.ts index 443af6c125..64d0b49451 100644 --- a/src/model/transaction/EncryptedMessage.ts +++ b/src/model/transaction/EncryptedMessage.ts @@ -28,8 +28,8 @@ export class EncryptedMessage extends Message { public readonly recipientPublicAccount?: PublicAccount; constructor(payload: string, - recipientPublicAccount?: PublicAccount){ - super(MessageType.EncryptedMessage,payload); + recipientPublicAccount?: PublicAccount) { + super(MessageType.EncryptedMessage, payload); this.recipientPublicAccount = recipientPublicAccount; } diff --git a/test/model/transaction/EncryptedMessage.spec.ts b/test/model/transaction/EncryptedMessage.spec.ts index 1eee4e9c9a..09355e0ada 100644 --- a/test/model/transaction/EncryptedMessage.spec.ts +++ b/test/model/transaction/EncryptedMessage.spec.ts @@ -20,39 +20,32 @@ import {Account} from '../../../src/model/account/Account'; import {PublicAccount} from '../../../src/model/account/PublicAccount'; import {NetworkType} from '../../../src/model/blockchain/NetworkType'; import {EncryptedMessage} from '../../../src/model/transaction/EncryptedMessage'; - +import { TestingAccount } from '../../conf/conf.spec'; describe('EncryptedMessage', () => { - const accountInformation = { - address: 'SCTVW23D2MN5VE4AQ4TZIDZENGNOZXPRPRLIKCF2', - privateKey: '26b64cb10f005e5988a36744ca19e20d835ccc7c105aaa5f3b212da593180930'.toUpperCase(), - publicKey: 'c2f93346e27ce6ad1a9f8f5e3066f8326593a406bdf357acb041e2f9ab402efe'.toUpperCase(), - }; - let recipientPublicAccount:PublicAccount; + let account: Account; before(() => { - recipientPublicAccount = PublicAccount.createFromPublicKey(accountInformation.publicKey,NetworkType.MIJIN_TEST); + account = TestingAccount; }); - it("should create a encrypted message from a DTO", () => { - const encryptedMessage = EncryptedMessage.createFromDTO("test transaction"); - expect(encryptedMessage.payload).to.be.equal("test transaction"); + it('should create a encrypted message from a DTO', () => { + const encryptedMessage = EncryptedMessage.createFromDTO('test transaction'); + expect(encryptedMessage.payload).to.be.equal('test transaction'); }); - it("should return encrypted message dto", () => { - const account = Account.createFromPrivateKey(accountInformation.privateKey,NetworkType.MIJIN_TEST); - const publicAccount = PublicAccount.createFromPublicKey(account.publicKey,NetworkType.MIJIN_TEST); - const encryptedMessage = account.encryptMessage("test transaction", publicAccount); - const plainMessage = account.decryptMessage(encryptedMessage, publicAccount); - expect(plainMessage.payload).to.be.equal("test transaction"); + it('should return encrypted message dto', () => {; + const encryptedMessage = account.encryptMessage('test transaction', account.publicAccount); + const plainMessage = account.decryptMessage(encryptedMessage, account.publicAccount); + expect(plainMessage.payload).to.be.equal('test transaction'); }); - it("should create an encrypted message from a DTO and decrypt it", () => { - const account = Account.createFromPrivateKey(accountInformation.privateKey,NetworkType.MIJIN_TEST); - const publicAccount = PublicAccount.createFromPublicKey("0414fe7647ec008e533aac98a4bf1c5fbf1d236c75b81fdadf1f5d1042fdd2ff",NetworkType.MIJIN_TEST); - const encryptMessage = EncryptedMessage.createFromDTO("02bb332c0fdd445455117882b2bec5e49f5713860d6b34650d0f769159d021a27518ea03539af8913231b9f80f600daae9291bb100a6d32e36b52a6c457fea287ca9942a32368618fe1fd0c185dbf834"); - const plainMessage = account.decryptMessage(encryptMessage, publicAccount); - expect(plainMessage.payload).to.be.equal("test transaction"); + it('should create an encrypted message from a DTO and decrypt it', () => { + const encryptMessage = EncryptedMessage + .createFromDTO('1E4DCC2C381A0346F72346F758B5D6C1CF236B96E2E68B9B40FB7EEF7FB035F6401A1993E6F5F0B1379' + + '7A6593358F06C90dee57f68880931f7062ecf9ec0c0837bb583732474442db72d71255250b021'); + const plainMessage = account.decryptMessage(encryptMessage, account.publicAccount); + expect(plainMessage.payload).to.be.equal('test transaction'); }); }); From 6ca8beeb4e602d3172649f1dd4ee0a86cc290b4a Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 13:22:33 +0100 Subject: [PATCH 66/83] Added #109 Make duration optiuon for mosaic definition --- src/model/mosaic/MosaicInfo.ts | 2 +- src/model/mosaic/MosaicProperties.ts | 18 ++++++++++++------ .../transaction/MosaicDefinitionTransaction.ts | 2 +- test/core/utils/TransactionMapping.spec.ts | 4 ++-- .../MosaicDefinitionTransaction.spec.ts | 8 ++++---- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/model/mosaic/MosaicInfo.ts b/src/model/mosaic/MosaicInfo.ts index 162e6d7238..999d044e48 100644 --- a/src/model/mosaic/MosaicInfo.ts +++ b/src/model/mosaic/MosaicInfo.ts @@ -81,7 +81,7 @@ export class MosaicInfo { * Mosaic duration * @returns {UInt64} */ - public get duration(): UInt64 { + public get duration(): UInt64 | undefined { return this.properties.duration; } diff --git a/src/model/mosaic/MosaicProperties.ts b/src/model/mosaic/MosaicProperties.ts index dfb7ebdb22..0de18a473e 100644 --- a/src/model/mosaic/MosaicProperties.ts +++ b/src/model/mosaic/MosaicProperties.ts @@ -59,8 +59,9 @@ export class MosaicProperties { /** * The duration in blocks a mosaic will be available. * After the duration finishes mosaic is inactive and can be renewed. + * Duration is optional when defining the mosaic */ - public readonly duration: UInt64) { + public readonly duration?: UInt64) { let binaryFlags = '00' + (flags.lower >>> 0).toString(2); binaryFlags = binaryFlags.substr(binaryFlags.length - 3, 3); this.supplyMutable = binaryFlags[2] === '1'; @@ -78,7 +79,7 @@ export class MosaicProperties { transferable: boolean, levyMutable: boolean, divisibility: number, - duration: UInt64, + duration?: UInt64, }) { const flags = (params.supplyMutable ? 1 : 0) + (params.transferable ? 2 : 0) + (params.levyMutable ? 4 : 0); return new MosaicProperties(UInt64.fromUint(flags), params.divisibility, params.duration); @@ -88,12 +89,17 @@ export class MosaicProperties { * Create DTO object */ toDTO() { - return [ + const dto = [ {id: 0, value: UInt64.fromUint((this.supplyMutable ? 1 : 0) + - (this.transferable ? 2 : 0) + - (this.levyMutable ? 4 : 0)).toDTO()}, + (this.transferable ? 2 : 0) + + (this.levyMutable ? 4 : 0)).toDTO()}, {id: 1, value: UInt64.fromUint(this.divisibility).toDTO()}, - {id: 2, value: this.duration.toDTO()}, ]; + + if (this.duration !== undefined) { + dto.push({id: 2, value: this.duration.toDTO()}); + } + + return dto; } } diff --git a/src/model/transaction/MosaicDefinitionTransaction.ts b/src/model/transaction/MosaicDefinitionTransaction.ts index 9443d08c26..851306c3b4 100644 --- a/src/model/transaction/MosaicDefinitionTransaction.ts +++ b/src/model/transaction/MosaicDefinitionTransaction.ts @@ -128,7 +128,7 @@ export class MosaicDefinitionTransaction extends Transaction { .addFee(this.maxFee.toDTO()) .addVersion(this.versionToDTO()) .addDivisibility(this.mosaicProperties.divisibility) - .addDuration(this.mosaicProperties.duration.toDTO()) + .addDuration(this.mosaicProperties.duration ? this.mosaicProperties.duration.toDTO() : []) .addNonce(this.nonce.toDTO()) .addMosaicId(this.mosaicId.id.toDTO()); diff --git a/test/core/utils/TransactionMapping.spec.ts b/test/core/utils/TransactionMapping.spec.ts index 467f8d7626..4c9974c6e7 100644 --- a/test/core/utils/TransactionMapping.spec.ts +++ b/test/core/utils/TransactionMapping.spec.ts @@ -192,8 +192,8 @@ describe('TransactionMapping - createFromPayload', () => { const transaction = TransactionMapping.createFromPayload(signedTransaction.payload) as MosaicDefinitionTransaction; - expect(transaction.mosaicProperties.duration.lower).to.be.equal(1000); - expect(transaction.mosaicProperties.duration.higher).to.be.equal(0); + expect(transaction.mosaicProperties.duration!.lower).to.be.equal(1000); + expect(transaction.mosaicProperties.duration! .higher).to.be.equal(0); expect(transaction.mosaicProperties.divisibility).to.be.equal(3); expect(transaction.mosaicProperties.supplyMutable).to.be.equal(false); expect(transaction.mosaicProperties.transferable).to.be.equal(false); diff --git a/test/model/transaction/MosaicDefinitionTransaction.spec.ts b/test/model/transaction/MosaicDefinitionTransaction.spec.ts index 23ae1bc416..a70fecf0cb 100644 --- a/test/model/transaction/MosaicDefinitionTransaction.spec.ts +++ b/test/model/transaction/MosaicDefinitionTransaction.spec.ts @@ -87,8 +87,8 @@ describe('MosaicDefinitionTransaction', () => { NetworkType.MIJIN_TEST, ); - expect(mosaicDefinitionTransaction.mosaicProperties.duration.lower).to.be.equal(1000); - expect(mosaicDefinitionTransaction.mosaicProperties.duration.higher).to.be.equal(0); + expect(mosaicDefinitionTransaction.mosaicProperties.duration!.lower).to.be.equal(1000); + expect(mosaicDefinitionTransaction.mosaicProperties.duration!.higher).to.be.equal(0); expect(mosaicDefinitionTransaction.mosaicProperties.divisibility).to.be.equal(3); expect(mosaicDefinitionTransaction.mosaicProperties.supplyMutable).to.be.equal(true); expect(mosaicDefinitionTransaction.mosaicProperties.transferable).to.be.equal(true); @@ -119,8 +119,8 @@ describe('MosaicDefinitionTransaction', () => { NetworkType.MIJIN_TEST, ); - expect(mosaicDefinitionTransaction.mosaicProperties.duration.lower).to.be.equal(1000); - expect(mosaicDefinitionTransaction.mosaicProperties.duration.higher).to.be.equal(0); + expect(mosaicDefinitionTransaction.mosaicProperties.duration!.lower).to.be.equal(1000); + expect(mosaicDefinitionTransaction.mosaicProperties.duration!.higher).to.be.equal(0); expect(mosaicDefinitionTransaction.mosaicProperties.divisibility).to.be.equal(3); expect(mosaicDefinitionTransaction.mosaicProperties.supplyMutable).to.be.equal(false); expect(mosaicDefinitionTransaction.mosaicProperties.transferable).to.be.equal(false); From 74bf17a522d78fc7d7409146e7f589e6fbf3b676 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 16:21:51 +0100 Subject: [PATCH 67/83] made mosaic properties duration option to all references --- .../transaction/CreateTransactionFromDTO.ts | 2 +- .../CreateTransactionFromPayload.ts | 2 +- test/core/utils/TransactionMapping.spec.ts | 25 +++++++++++++++ .../SerializeTransactionToJSON.spec.ts | 24 ++++++++++++++ test/model/mosaic/MosaicInfo.spec.ts | 27 ++++++++++++++++ test/model/mosaic/MosaicProperties.spec.ts | 16 ++++++++++ .../MosaicDefinitionTransaction.spec.ts | 31 +++++++++++++++++-- 7 files changed, 123 insertions(+), 4 deletions(-) diff --git a/src/infrastructure/transaction/CreateTransactionFromDTO.ts b/src/infrastructure/transaction/CreateTransactionFromDTO.ts index 04baf86f74..0c0543ad26 100644 --- a/src/infrastructure/transaction/CreateTransactionFromDTO.ts +++ b/src/infrastructure/transaction/CreateTransactionFromDTO.ts @@ -158,7 +158,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr new MosaicProperties( new UInt64(transactionDTO.properties[0].value), (new UInt64(transactionDTO.properties[1].value)).compact(), - new UInt64(transactionDTO.properties.length === 3 ? transactionDTO.properties[2].value : [0, 0]), + new UInt64(transactionDTO.properties.length === 3 ? transactionDTO.properties[2].value : undefined), ), transactionDTO.signature, transactionDTO.signer ? PublicAccount.createFromPublicKey(transactionDTO.signer, diff --git a/src/infrastructure/transaction/CreateTransactionFromPayload.ts b/src/infrastructure/transaction/CreateTransactionFromPayload.ts index f32089fb31..0144524f42 100644 --- a/src/infrastructure/transaction/CreateTransactionFromPayload.ts +++ b/src/infrastructure/transaction/CreateTransactionFromPayload.ts @@ -224,7 +224,7 @@ const CreateTransaction = (type: number, transactionData: string, networkType: N transferable: (flags & 2) === 2, levyMutable: (flags & 4) === 4, divisibility: parseInt(convert.uint8ToHex(convert.hexToUint8(divisibility).reverse()), 16), - duration: UInt64.fromHex(reverse(duration)), + duration: duration ? UInt64.fromHex(reverse(duration)) : undefined, }), networkType, ); diff --git a/test/core/utils/TransactionMapping.spec.ts b/test/core/utils/TransactionMapping.spec.ts index 4c9974c6e7..6c34145b14 100644 --- a/test/core/utils/TransactionMapping.spec.ts +++ b/test/core/utils/TransactionMapping.spec.ts @@ -201,6 +201,31 @@ describe('TransactionMapping - createFromPayload', () => { }); + it('should create MosaicDefinitionTransaction - without duration', () => { + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce + new MosaicId(UInt64.fromUint(1).toDTO()), // ID + MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 3, + }), + NetworkType.MIJIN_TEST, + ); + + const signedTransaction = mosaicDefinitionTransaction.signWith(account); + + const transaction = TransactionMapping.createFromPayload(signedTransaction.payload) as MosaicDefinitionTransaction; + + expect(transaction.mosaicProperties.divisibility).to.be.equal(3); + expect(transaction.mosaicProperties.supplyMutable).to.be.equal(false); + expect(transaction.mosaicProperties.transferable).to.be.equal(false); + expect(transaction.mosaicProperties.levyMutable).to.be.equal(false); + + }); + it('should create MosaicSupplyChangeTransaction', () => { const mosaicId = new MosaicId([2262289484, 3405110546]); const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( diff --git a/test/infrastructure/SerializeTransactionToJSON.spec.ts b/test/infrastructure/SerializeTransactionToJSON.spec.ts index 34807cb745..2c3abf38e5 100644 --- a/test/infrastructure/SerializeTransactionToJSON.spec.ts +++ b/test/infrastructure/SerializeTransactionToJSON.spec.ts @@ -197,6 +197,30 @@ describe('SerializeTransactionToJSON', () => { }); + it('should create MosaicDefinitionTransaction without duration', () => { + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce + new MosaicId(UInt64.fromUint(1).toDTO()), // ID + MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 3, + }), + NetworkType.MIJIN_TEST, + ); + + const json = mosaicDefinitionTransaction.toJSON(); + + expect(json.type).to.be.equal(TransactionType.MOSAIC_DEFINITION); + expect(json.mosaicProperties.supplyMutable).to.be.equal(false); + expect(json.mosaicProperties.transferable).to.be.equal(false); + expect(json.mosaicProperties.levyMutable).to.be.equal(false); + expect(json.mosaicProperties.divisibility).to.be.equal(3); + + }); + it('should create MosaicSupplyChangeTransaction', () => { const mosaicId = new MosaicId([2262289484, 3405110546]); const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( diff --git a/test/model/mosaic/MosaicInfo.spec.ts b/test/model/mosaic/MosaicInfo.spec.ts index a46eb459ea..b60de0ca3b 100644 --- a/test/model/mosaic/MosaicInfo.spec.ts +++ b/test/model/mosaic/MosaicInfo.spec.ts @@ -78,6 +78,33 @@ describe('MosaicInfo', () => { }); + it('should createComplete an MosaicInfo object without duration', () => { + const mosaicInfo = new MosaicInfo( + mosaicInfoDTO.meta.id, + mosaicInfoDTO.mosaic.mosaicId, + mosaicInfoDTO.mosaic.supply, + mosaicInfoDTO.mosaic.height, + mosaicInfoDTO.mosaic.owner, + mosaicInfoDTO.mosaic.revision, + new MosaicProperties( + mosaicInfoDTO.mosaic.properties[0], + mosaicInfoDTO.mosaic.properties[1].compact(), + ), + mosaicInfoDTO.mosaic.levy, + ); + + expect(mosaicInfo.metaId).to.be.equal(mosaicInfoDTO.meta.id); + deepEqual(mosaicInfo.mosaicId, mosaicInfoDTO.mosaic.mosaicId); + deepEqual(mosaicInfo.supply, mosaicInfoDTO.mosaic.supply); + deepEqual(mosaicInfo.height, mosaicInfoDTO.mosaic.height); + expect(mosaicInfo.owner).to.be.equal(mosaicInfoDTO.mosaic.owner); + deepEqual(mosaicInfo.revision, mosaicInfoDTO.mosaic.revision); + + expect(mosaicInfo.divisibility).to.be.equal(mosaicInfoDTO.mosaic.properties[1].lower); + deepEqual(mosaicInfo.duration, undefined); + + }); + describe('isSupplyMutable', () => { it('should return true when it\'s mutable', () => { const mosaicInfo = new MosaicInfo( diff --git a/test/model/mosaic/MosaicProperties.spec.ts b/test/model/mosaic/MosaicProperties.spec.ts index 5f711f2da8..f2603b3fa8 100644 --- a/test/model/mosaic/MosaicProperties.spec.ts +++ b/test/model/mosaic/MosaicProperties.spec.ts @@ -69,4 +69,20 @@ describe('MosaicProperties', () => { expect(mosaicProperties.transferable).to.be.equal(false); expect(mosaicProperties.levyMutable).to.be.equal(false); }); + + it('should createComplete an MosaicProperties object without duration', () => { + const mosaicProperties = MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 10, + }); + + expect(mosaicProperties.divisibility).to.be.equal(10); + deepEqual(mosaicProperties.duration, undefined); + + expect(mosaicProperties.supplyMutable).to.be.equal(false); + expect(mosaicProperties.transferable).to.be.equal(false); + expect(mosaicProperties.levyMutable).to.be.equal(false); + }); }); diff --git a/test/model/transaction/MosaicDefinitionTransaction.spec.ts b/test/model/transaction/MosaicDefinitionTransaction.spec.ts index a70fecf0cb..6710401c31 100644 --- a/test/model/transaction/MosaicDefinitionTransaction.spec.ts +++ b/test/model/transaction/MosaicDefinitionTransaction.spec.ts @@ -24,7 +24,6 @@ import {Deadline} from '../../../src/model/transaction/Deadline'; import {MosaicDefinitionTransaction} from '../../../src/model/transaction/MosaicDefinitionTransaction'; import {UInt64} from '../../../src/model/UInt64'; import {TestingAccount} from '../../conf/conf.spec'; -import {convert, mosaicId, uint64 as uint64_t} from 'nem2-library'; describe('MosaicDefinitionTransaction', () => { let account: Account; @@ -135,7 +134,6 @@ describe('MosaicDefinitionTransaction', () => { }); - describe('size', () => { it('should return 144 for MosaicDefinition transaction byte size', () => { const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( @@ -154,4 +152,33 @@ describe('MosaicDefinitionTransaction', () => { expect(mosaicDefinitionTransaction.size).to.be.equal(144); }); }); + + it('should createComplete an MosaicDefinitionTransaction object and sign it without duration', () => { + + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce + new MosaicId(UInt64.fromUint(1).toDTO()), // ID + MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 3, + }), + NetworkType.MIJIN_TEST, + ); + + expect(mosaicDefinitionTransaction.mosaicProperties.divisibility).to.be.equal(3); + expect(mosaicDefinitionTransaction.mosaicProperties.supplyMutable).to.be.equal(false); + expect(mosaicDefinitionTransaction.mosaicProperties.transferable).to.be.equal(false); + expect(mosaicDefinitionTransaction.mosaicProperties.levyMutable).to.be.equal(false); + + const signedTransaction = mosaicDefinitionTransaction.signWith(account); + + expect(signedTransaction.payload.substring( + 240, + signedTransaction.payload.length, + )).to.be.equal('E6DE84B80100000000000000000003'); + + }); }); From 10498966118031e7450466f71b776da454de2398 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 18:02:11 +0100 Subject: [PATCH 68/83] Updared tslint to disable "no-non-null-assertion" --- tslint.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tslint.json b/tslint.json index aeca64ccdf..f47346aaf9 100644 --- a/tslint.json +++ b/tslint.json @@ -65,7 +65,7 @@ "ignore-params" ], "no-misused-new": true, - "no-non-null-assertion": true, + "no-non-null-assertion": false, "no-shadowed-variable": true, "no-string-literal": false, "no-string-throw": true, @@ -139,6 +139,6 @@ "use-pipe-transform-interface": true, "component-class-suffix": true, "directive-class-suffix": true, - "invoke-injectable": true + "invoke-injectable": true, } } From 1b53a9a18c2796b6466bd4a69cc3d5f1089986e0 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Tue, 9 Apr 2019 12:46:33 +0100 Subject: [PATCH 69/83] Fixed white space issue on TransactionMapping test Fixed trailing comma in tslint --- test/core/utils/TransactionMapping.spec.ts | 2 +- tslint.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/core/utils/TransactionMapping.spec.ts b/test/core/utils/TransactionMapping.spec.ts index 6c34145b14..e302aed6a4 100644 --- a/test/core/utils/TransactionMapping.spec.ts +++ b/test/core/utils/TransactionMapping.spec.ts @@ -193,7 +193,7 @@ describe('TransactionMapping - createFromPayload', () => { const transaction = TransactionMapping.createFromPayload(signedTransaction.payload) as MosaicDefinitionTransaction; expect(transaction.mosaicProperties.duration!.lower).to.be.equal(1000); - expect(transaction.mosaicProperties.duration! .higher).to.be.equal(0); + expect(transaction.mosaicProperties.duration!.higher).to.be.equal(0); expect(transaction.mosaicProperties.divisibility).to.be.equal(3); expect(transaction.mosaicProperties.supplyMutable).to.be.equal(false); expect(transaction.mosaicProperties.transferable).to.be.equal(false); diff --git a/tslint.json b/tslint.json index f47346aaf9..ded366576a 100644 --- a/tslint.json +++ b/tslint.json @@ -139,6 +139,6 @@ "use-pipe-transform-interface": true, "component-class-suffix": true, "directive-class-suffix": true, - "invoke-injectable": true, + "invoke-injectable": true } } From 6f341ad4c3c73b522013c492e632c14713511933 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 16:21:51 +0100 Subject: [PATCH 70/83] made mosaic properties duration option to all references --- test/core/utils/TransactionMapping.spec.ts | 25 +++++++++++++++++++ .../SerializeTransactionToJSON.spec.ts | 24 ++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/test/core/utils/TransactionMapping.spec.ts b/test/core/utils/TransactionMapping.spec.ts index e302aed6a4..60a8dc0207 100644 --- a/test/core/utils/TransactionMapping.spec.ts +++ b/test/core/utils/TransactionMapping.spec.ts @@ -226,6 +226,31 @@ describe('TransactionMapping - createFromPayload', () => { }); + it('should create MosaicDefinitionTransaction - without duration', () => { + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce + new MosaicId(UInt64.fromUint(1).toDTO()), // ID + MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 3, + }), + NetworkType.MIJIN_TEST, + ); + + const signedTransaction = mosaicDefinitionTransaction.signWith(account); + + const transaction = TransactionMapping.createFromPayload(signedTransaction.payload) as MosaicDefinitionTransaction; + + expect(transaction.mosaicProperties.divisibility).to.be.equal(3); + expect(transaction.mosaicProperties.supplyMutable).to.be.equal(false); + expect(transaction.mosaicProperties.transferable).to.be.equal(false); + expect(transaction.mosaicProperties.levyMutable).to.be.equal(false); + + }); + it('should create MosaicSupplyChangeTransaction', () => { const mosaicId = new MosaicId([2262289484, 3405110546]); const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( diff --git a/test/infrastructure/SerializeTransactionToJSON.spec.ts b/test/infrastructure/SerializeTransactionToJSON.spec.ts index 2c3abf38e5..7683fea603 100644 --- a/test/infrastructure/SerializeTransactionToJSON.spec.ts +++ b/test/infrastructure/SerializeTransactionToJSON.spec.ts @@ -221,6 +221,30 @@ describe('SerializeTransactionToJSON', () => { }); + it('should create MosaicDefinitionTransaction without duration', () => { + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce + new MosaicId(UInt64.fromUint(1).toDTO()), // ID + MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 3, + }), + NetworkType.MIJIN_TEST, + ); + + const json = mosaicDefinitionTransaction.toJSON(); + + expect(json.type).to.be.equal(TransactionType.MOSAIC_DEFINITION); + expect(json.mosaicProperties.supplyMutable).to.be.equal(false); + expect(json.mosaicProperties.transferable).to.be.equal(false); + expect(json.mosaicProperties.levyMutable).to.be.equal(false); + expect(json.mosaicProperties.divisibility).to.be.equal(3); + + }); + it('should create MosaicSupplyChangeTransaction', () => { const mosaicId = new MosaicId([2262289484, 3405110546]); const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( From 67363f5083b61de8452aa9a6a0845a643bd5a399 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 18:02:11 +0100 Subject: [PATCH 71/83] Updared tslint to disable "no-non-null-assertion" --- tslint.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tslint.json b/tslint.json index ded366576a..f47346aaf9 100644 --- a/tslint.json +++ b/tslint.json @@ -139,6 +139,6 @@ "use-pipe-transform-interface": true, "component-class-suffix": true, "directive-class-suffix": true, - "invoke-injectable": true + "invoke-injectable": true, } } From f04bdee45b5be847effefad33062e7061b56f258 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Tue, 9 Apr 2019 12:46:33 +0100 Subject: [PATCH 72/83] Fixed white space issue on TransactionMapping test Fixed trailing comma in tslint --- tslint.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tslint.json b/tslint.json index f47346aaf9..ded366576a 100644 --- a/tslint.json +++ b/tslint.json @@ -139,6 +139,6 @@ "use-pipe-transform-interface": true, "component-class-suffix": true, "directive-class-suffix": true, - "invoke-injectable": true, + "invoke-injectable": true } } From 8ca5f944ce79f46a5854cda146adbba46f1d251c Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 16:21:51 +0100 Subject: [PATCH 73/83] made mosaic properties duration option to all references --- test/core/utils/TransactionMapping.spec.ts | 25 +++++++++++++++++++ .../SerializeTransactionToJSON.spec.ts | 24 ------------------ 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/test/core/utils/TransactionMapping.spec.ts b/test/core/utils/TransactionMapping.spec.ts index 60a8dc0207..69bc4bdd56 100644 --- a/test/core/utils/TransactionMapping.spec.ts +++ b/test/core/utils/TransactionMapping.spec.ts @@ -251,6 +251,31 @@ describe('TransactionMapping - createFromPayload', () => { }); + it('should create MosaicDefinitionTransaction - without duration', () => { + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce + new MosaicId(UInt64.fromUint(1).toDTO()), // ID + MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 3, + }), + NetworkType.MIJIN_TEST, + ); + + const signedTransaction = mosaicDefinitionTransaction.signWith(account); + + const transaction = TransactionMapping.createFromPayload(signedTransaction.payload) as MosaicDefinitionTransaction; + + expect(transaction.mosaicProperties.divisibility).to.be.equal(3); + expect(transaction.mosaicProperties.supplyMutable).to.be.equal(false); + expect(transaction.mosaicProperties.transferable).to.be.equal(false); + expect(transaction.mosaicProperties.levyMutable).to.be.equal(false); + + }); + it('should create MosaicSupplyChangeTransaction', () => { const mosaicId = new MosaicId([2262289484, 3405110546]); const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( diff --git a/test/infrastructure/SerializeTransactionToJSON.spec.ts b/test/infrastructure/SerializeTransactionToJSON.spec.ts index 7683fea603..2c3abf38e5 100644 --- a/test/infrastructure/SerializeTransactionToJSON.spec.ts +++ b/test/infrastructure/SerializeTransactionToJSON.spec.ts @@ -221,30 +221,6 @@ describe('SerializeTransactionToJSON', () => { }); - it('should create MosaicDefinitionTransaction without duration', () => { - const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( - Deadline.create(), - new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce - new MosaicId(UInt64.fromUint(1).toDTO()), // ID - MosaicProperties.create({ - supplyMutable: false, - transferable: false, - levyMutable: false, - divisibility: 3, - }), - NetworkType.MIJIN_TEST, - ); - - const json = mosaicDefinitionTransaction.toJSON(); - - expect(json.type).to.be.equal(TransactionType.MOSAIC_DEFINITION); - expect(json.mosaicProperties.supplyMutable).to.be.equal(false); - expect(json.mosaicProperties.transferable).to.be.equal(false); - expect(json.mosaicProperties.levyMutable).to.be.equal(false); - expect(json.mosaicProperties.divisibility).to.be.equal(3); - - }); - it('should create MosaicSupplyChangeTransaction', () => { const mosaicId = new MosaicId([2262289484, 3405110546]); const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( From 1225557b2cba581d8152414810326ef7435ae017 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 18:02:11 +0100 Subject: [PATCH 74/83] Updared tslint to disable "no-non-null-assertion" --- tslint.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tslint.json b/tslint.json index ded366576a..f47346aaf9 100644 --- a/tslint.json +++ b/tslint.json @@ -139,6 +139,6 @@ "use-pipe-transform-interface": true, "component-class-suffix": true, "directive-class-suffix": true, - "invoke-injectable": true + "invoke-injectable": true, } } From 3c097f4b04e3a0bc2fb2768007f34762cd295339 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Tue, 9 Apr 2019 12:46:33 +0100 Subject: [PATCH 75/83] Fixed white space issue on TransactionMapping test Fixed trailing comma in tslint --- tslint.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tslint.json b/tslint.json index f47346aaf9..ded366576a 100644 --- a/tslint.json +++ b/tslint.json @@ -139,6 +139,6 @@ "use-pipe-transform-interface": true, "component-class-suffix": true, "directive-class-suffix": true, - "invoke-injectable": true, + "invoke-injectable": true } } From 293f17f17db50a618f89066adfa932323e15a02a Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 13:22:33 +0100 Subject: [PATCH 76/83] Added #109 Make duration optiuon for mosaic definition --- test/core/utils/TransactionMapping.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/core/utils/TransactionMapping.spec.ts b/test/core/utils/TransactionMapping.spec.ts index 69bc4bdd56..63ff134445 100644 --- a/test/core/utils/TransactionMapping.spec.ts +++ b/test/core/utils/TransactionMapping.spec.ts @@ -219,6 +219,7 @@ describe('TransactionMapping - createFromPayload', () => { const transaction = TransactionMapping.createFromPayload(signedTransaction.payload) as MosaicDefinitionTransaction; + expect(transaction.mosaicProperties.duration! .higher).to.be.equal(0); expect(transaction.mosaicProperties.divisibility).to.be.equal(3); expect(transaction.mosaicProperties.supplyMutable).to.be.equal(false); expect(transaction.mosaicProperties.transferable).to.be.equal(false); From bba4ddfb1a1ce45e89833b529b96bd65662933d3 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 16:21:51 +0100 Subject: [PATCH 77/83] made mosaic properties duration option to all references --- test/core/utils/TransactionMapping.spec.ts | 25 +++++++++++++++++++ .../SerializeTransactionToJSON.spec.ts | 24 ++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/test/core/utils/TransactionMapping.spec.ts b/test/core/utils/TransactionMapping.spec.ts index 63ff134445..814686bafe 100644 --- a/test/core/utils/TransactionMapping.spec.ts +++ b/test/core/utils/TransactionMapping.spec.ts @@ -277,6 +277,31 @@ describe('TransactionMapping - createFromPayload', () => { }); + it('should create MosaicDefinitionTransaction - without duration', () => { + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce + new MosaicId(UInt64.fromUint(1).toDTO()), // ID + MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 3, + }), + NetworkType.MIJIN_TEST, + ); + + const signedTransaction = mosaicDefinitionTransaction.signWith(account); + + const transaction = TransactionMapping.createFromPayload(signedTransaction.payload) as MosaicDefinitionTransaction; + + expect(transaction.mosaicProperties.divisibility).to.be.equal(3); + expect(transaction.mosaicProperties.supplyMutable).to.be.equal(false); + expect(transaction.mosaicProperties.transferable).to.be.equal(false); + expect(transaction.mosaicProperties.levyMutable).to.be.equal(false); + + }); + it('should create MosaicSupplyChangeTransaction', () => { const mosaicId = new MosaicId([2262289484, 3405110546]); const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( diff --git a/test/infrastructure/SerializeTransactionToJSON.spec.ts b/test/infrastructure/SerializeTransactionToJSON.spec.ts index 2c3abf38e5..7683fea603 100644 --- a/test/infrastructure/SerializeTransactionToJSON.spec.ts +++ b/test/infrastructure/SerializeTransactionToJSON.spec.ts @@ -221,6 +221,30 @@ describe('SerializeTransactionToJSON', () => { }); + it('should create MosaicDefinitionTransaction without duration', () => { + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce + new MosaicId(UInt64.fromUint(1).toDTO()), // ID + MosaicProperties.create({ + supplyMutable: false, + transferable: false, + levyMutable: false, + divisibility: 3, + }), + NetworkType.MIJIN_TEST, + ); + + const json = mosaicDefinitionTransaction.toJSON(); + + expect(json.type).to.be.equal(TransactionType.MOSAIC_DEFINITION); + expect(json.mosaicProperties.supplyMutable).to.be.equal(false); + expect(json.mosaicProperties.transferable).to.be.equal(false); + expect(json.mosaicProperties.levyMutable).to.be.equal(false); + expect(json.mosaicProperties.divisibility).to.be.equal(3); + + }); + it('should create MosaicSupplyChangeTransaction', () => { const mosaicId = new MosaicId([2262289484, 3405110546]); const mosaicSupplyChangeTransaction = MosaicSupplyChangeTransaction.create( From 797b46941c05680107d90b145aefeb2306860452 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Mon, 8 Apr 2019 18:02:11 +0100 Subject: [PATCH 78/83] Updared tslint to disable "no-non-null-assertion" --- tslint.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tslint.json b/tslint.json index ded366576a..f47346aaf9 100644 --- a/tslint.json +++ b/tslint.json @@ -139,6 +139,6 @@ "use-pipe-transform-interface": true, "component-class-suffix": true, "directive-class-suffix": true, - "invoke-injectable": true + "invoke-injectable": true, } } From 5a03c058d8ff024766ba4de23e8e706cdeaf1133 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Tue, 9 Apr 2019 12:46:33 +0100 Subject: [PATCH 79/83] Fixed white space issue on TransactionMapping test Fixed trailing comma in tslint --- tslint.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tslint.json b/tslint.json index f47346aaf9..ded366576a 100644 --- a/tslint.json +++ b/tslint.json @@ -139,6 +139,6 @@ "use-pipe-transform-interface": true, "component-class-suffix": true, "directive-class-suffix": true, - "invoke-injectable": true, + "invoke-injectable": true } } From 2432e734f542067b5cf51119077310d4fb559282 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Wed, 10 Apr 2019 16:15:14 +0100 Subject: [PATCH 80/83] Fixed a few merge issues --- test/core/utils/TransactionMapping.spec.ts | 1 - .../SerializeTransactionToJSON.spec.ts | 31 ++----------------- 2 files changed, 2 insertions(+), 30 deletions(-) diff --git a/test/core/utils/TransactionMapping.spec.ts b/test/core/utils/TransactionMapping.spec.ts index 814686bafe..407384c60f 100644 --- a/test/core/utils/TransactionMapping.spec.ts +++ b/test/core/utils/TransactionMapping.spec.ts @@ -219,7 +219,6 @@ describe('TransactionMapping - createFromPayload', () => { const transaction = TransactionMapping.createFromPayload(signedTransaction.payload) as MosaicDefinitionTransaction; - expect(transaction.mosaicProperties.duration! .higher).to.be.equal(0); expect(transaction.mosaicProperties.divisibility).to.be.equal(3); expect(transaction.mosaicProperties.supplyMutable).to.be.equal(false); expect(transaction.mosaicProperties.transferable).to.be.equal(false); diff --git a/test/infrastructure/SerializeTransactionToJSON.spec.ts b/test/infrastructure/SerializeTransactionToJSON.spec.ts index 7683fea603..74909fd85f 100644 --- a/test/infrastructure/SerializeTransactionToJSON.spec.ts +++ b/test/infrastructure/SerializeTransactionToJSON.spec.ts @@ -213,35 +213,8 @@ describe('SerializeTransactionToJSON', () => { const json = mosaicDefinitionTransaction.toJSON(); - expect(json.type).to.be.equal(TransactionType.MOSAIC_DEFINITION); - expect(json.mosaicProperties.supplyMutable).to.be.equal(false); - expect(json.mosaicProperties.transferable).to.be.equal(false); - expect(json.mosaicProperties.levyMutable).to.be.equal(false); - expect(json.mosaicProperties.divisibility).to.be.equal(3); - - }); - - it('should create MosaicDefinitionTransaction without duration', () => { - const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( - Deadline.create(), - new MosaicNonce(new Uint8Array([0xE6, 0xDE, 0x84, 0xB8])), // nonce - new MosaicId(UInt64.fromUint(1).toDTO()), // ID - MosaicProperties.create({ - supplyMutable: false, - transferable: false, - levyMutable: false, - divisibility: 3, - }), - NetworkType.MIJIN_TEST, - ); - - const json = mosaicDefinitionTransaction.toJSON(); - - expect(json.type).to.be.equal(TransactionType.MOSAIC_DEFINITION); - expect(json.mosaicProperties.supplyMutable).to.be.equal(false); - expect(json.mosaicProperties.transferable).to.be.equal(false); - expect(json.mosaicProperties.levyMutable).to.be.equal(false); - expect(json.mosaicProperties.divisibility).to.be.equal(3); + expect(json.transaction.type).to.be.equal(TransactionType.MOSAIC_DEFINITION); + expect(json.transaction.properties.length).to.be.equal(2); }); From b0b4f23cde10884db41f0a4d7a957d5404287162 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Wed, 10 Apr 2019 16:48:47 +0100 Subject: [PATCH 81/83] Updated lib version to 0.9.14 Fixed a few lint issues on EncryptedMessage and test Fixed failed test on EncryptedMessage spec --- package-lock.json | 6 +-- package.json | 2 +- src/model/transaction/EncryptedMessage.ts | 4 +- .../transaction/EncryptedMessage.spec.ts | 39 ++++++++----------- 4 files changed, 22 insertions(+), 29 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0fc3fdf7b3..556da5d6a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1213,9 +1213,9 @@ "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" }, "nem2-library": { - "version": "0.9.13", - "resolved": "https://registry.npmjs.org/nem2-library/-/nem2-library-0.9.13.tgz", - "integrity": "sha512-KCvg+8UD5mXHAJIpkP85p/0PivIfhRL+IkIWKm2xxiYgETNbCu82qf4zDIpLGIqBgzpQ++j5i4a04c9mq39K5w==", + "version": "0.9.14", + "resolved": "https://registry.npmjs.org/nem2-library/-/nem2-library-0.9.14.tgz", + "integrity": "sha512-WTWycF01RwDj9UIn0B3Gb7BIhOQszYVKi7/IzfR4mGGrxsJcmMoJ71oB0OUtjHyHv47lM6g5sk2QGu58u376sw==", "requires": { "bufferutil": "^3.0.5", "crypto-browserify": "3.12.0", diff --git a/package.json b/package.json index dc62464f43..f03445c74a 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "@types/crypto-js": "^3.1.43", "crypto-js": "^3.1.9-1", "js-joda": "^1.6.2", - "nem2-library": "^0.9.13", + "nem2-library": "^0.9.14", "request": "^2.83.0", "request-promise-native": "^1.0.5", "rxjs": "^6.2.1", diff --git a/src/model/transaction/EncryptedMessage.ts b/src/model/transaction/EncryptedMessage.ts index 443af6c125..64d0b49451 100644 --- a/src/model/transaction/EncryptedMessage.ts +++ b/src/model/transaction/EncryptedMessage.ts @@ -28,8 +28,8 @@ export class EncryptedMessage extends Message { public readonly recipientPublicAccount?: PublicAccount; constructor(payload: string, - recipientPublicAccount?: PublicAccount){ - super(MessageType.EncryptedMessage,payload); + recipientPublicAccount?: PublicAccount) { + super(MessageType.EncryptedMessage, payload); this.recipientPublicAccount = recipientPublicAccount; } diff --git a/test/model/transaction/EncryptedMessage.spec.ts b/test/model/transaction/EncryptedMessage.spec.ts index 1eee4e9c9a..09355e0ada 100644 --- a/test/model/transaction/EncryptedMessage.spec.ts +++ b/test/model/transaction/EncryptedMessage.spec.ts @@ -20,39 +20,32 @@ import {Account} from '../../../src/model/account/Account'; import {PublicAccount} from '../../../src/model/account/PublicAccount'; import {NetworkType} from '../../../src/model/blockchain/NetworkType'; import {EncryptedMessage} from '../../../src/model/transaction/EncryptedMessage'; - +import { TestingAccount } from '../../conf/conf.spec'; describe('EncryptedMessage', () => { - const accountInformation = { - address: 'SCTVW23D2MN5VE4AQ4TZIDZENGNOZXPRPRLIKCF2', - privateKey: '26b64cb10f005e5988a36744ca19e20d835ccc7c105aaa5f3b212da593180930'.toUpperCase(), - publicKey: 'c2f93346e27ce6ad1a9f8f5e3066f8326593a406bdf357acb041e2f9ab402efe'.toUpperCase(), - }; - let recipientPublicAccount:PublicAccount; + let account: Account; before(() => { - recipientPublicAccount = PublicAccount.createFromPublicKey(accountInformation.publicKey,NetworkType.MIJIN_TEST); + account = TestingAccount; }); - it("should create a encrypted message from a DTO", () => { - const encryptedMessage = EncryptedMessage.createFromDTO("test transaction"); - expect(encryptedMessage.payload).to.be.equal("test transaction"); + it('should create a encrypted message from a DTO', () => { + const encryptedMessage = EncryptedMessage.createFromDTO('test transaction'); + expect(encryptedMessage.payload).to.be.equal('test transaction'); }); - it("should return encrypted message dto", () => { - const account = Account.createFromPrivateKey(accountInformation.privateKey,NetworkType.MIJIN_TEST); - const publicAccount = PublicAccount.createFromPublicKey(account.publicKey,NetworkType.MIJIN_TEST); - const encryptedMessage = account.encryptMessage("test transaction", publicAccount); - const plainMessage = account.decryptMessage(encryptedMessage, publicAccount); - expect(plainMessage.payload).to.be.equal("test transaction"); + it('should return encrypted message dto', () => {; + const encryptedMessage = account.encryptMessage('test transaction', account.publicAccount); + const plainMessage = account.decryptMessage(encryptedMessage, account.publicAccount); + expect(plainMessage.payload).to.be.equal('test transaction'); }); - it("should create an encrypted message from a DTO and decrypt it", () => { - const account = Account.createFromPrivateKey(accountInformation.privateKey,NetworkType.MIJIN_TEST); - const publicAccount = PublicAccount.createFromPublicKey("0414fe7647ec008e533aac98a4bf1c5fbf1d236c75b81fdadf1f5d1042fdd2ff",NetworkType.MIJIN_TEST); - const encryptMessage = EncryptedMessage.createFromDTO("02bb332c0fdd445455117882b2bec5e49f5713860d6b34650d0f769159d021a27518ea03539af8913231b9f80f600daae9291bb100a6d32e36b52a6c457fea287ca9942a32368618fe1fd0c185dbf834"); - const plainMessage = account.decryptMessage(encryptMessage, publicAccount); - expect(plainMessage.payload).to.be.equal("test transaction"); + it('should create an encrypted message from a DTO and decrypt it', () => { + const encryptMessage = EncryptedMessage + .createFromDTO('1E4DCC2C381A0346F72346F758B5D6C1CF236B96E2E68B9B40FB7EEF7FB035F6401A1993E6F5F0B1379' + + '7A6593358F06C90dee57f68880931f7062ecf9ec0c0837bb583732474442db72d71255250b021'); + const plainMessage = account.decryptMessage(encryptMessage, account.publicAccount); + expect(plainMessage.payload).to.be.equal('test transaction'); }); }); From b6a5566bc8c4fc627588d991f2c014e2f7463244 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Wed, 10 Apr 2019 17:16:37 +0100 Subject: [PATCH 82/83] Fixed encrypt message upper case issue --- src/model/transaction/EncryptedMessage.ts | 4 +++- test/model/transaction/EncryptedMessage.spec.ts | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/model/transaction/EncryptedMessage.ts b/src/model/transaction/EncryptedMessage.ts index 64d0b49451..2527f558d7 100644 --- a/src/model/transaction/EncryptedMessage.ts +++ b/src/model/transaction/EncryptedMessage.ts @@ -40,7 +40,9 @@ export class EncryptedMessage extends Message { * @param privateKey */ public static create(message: string, recipientPublicAccount: PublicAccount, privateKey) { - return new EncryptedMessage(crypto.encode(privateKey, recipientPublicAccount.publicKey, message), recipientPublicAccount); + return new EncryptedMessage( + crypto.encode(privateKey, recipientPublicAccount.publicKey, message).toUpperCase(), + recipientPublicAccount); } /** diff --git a/test/model/transaction/EncryptedMessage.spec.ts b/test/model/transaction/EncryptedMessage.spec.ts index 09355e0ada..85ad56a26e 100644 --- a/test/model/transaction/EncryptedMessage.spec.ts +++ b/test/model/transaction/EncryptedMessage.spec.ts @@ -43,8 +43,8 @@ describe('EncryptedMessage', () => { it('should create an encrypted message from a DTO and decrypt it', () => { const encryptMessage = EncryptedMessage - .createFromDTO('1E4DCC2C381A0346F72346F758B5D6C1CF236B96E2E68B9B40FB7EEF7FB035F6401A1993E6F5F0B1379' + - '7A6593358F06C90dee57f68880931f7062ecf9ec0c0837bb583732474442db72d71255250b021'); + .createFromDTO('7245170507448c53d808524221b5d157e19b06f574120a044e48f54dd8e0a4dedbf50ded7ae71' + + 'b90b59949bb6acde81d987ee6648aae9f093b94ac7cc3e8dba0bed8fa04ba286df6b32d2d6d21cbdc4e'); const plainMessage = account.decryptMessage(encryptMessage, account.publicAccount); expect(plainMessage.payload).to.be.equal('test transaction'); }); From 58a3f11c198a21843405659bd29e28e5c864c5c2 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Wed, 10 Apr 2019 17:17:09 +0100 Subject: [PATCH 83/83] removed unused import in EncryptedMessage test --- test/model/transaction/EncryptedMessage.spec.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/model/transaction/EncryptedMessage.spec.ts b/test/model/transaction/EncryptedMessage.spec.ts index 85ad56a26e..1aca799d39 100644 --- a/test/model/transaction/EncryptedMessage.spec.ts +++ b/test/model/transaction/EncryptedMessage.spec.ts @@ -16,9 +16,6 @@ import {expect} from 'chai'; import {Account} from '../../../src/model/account/Account'; - -import {PublicAccount} from '../../../src/model/account/PublicAccount'; -import {NetworkType} from '../../../src/model/blockchain/NetworkType'; import {EncryptedMessage} from '../../../src/model/transaction/EncryptedMessage'; import { TestingAccount } from '../../conf/conf.spec';