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 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/e2e/infrastructure/TransactionUtils.ts b/e2e/infrastructure/TransactionUtils.ts index bf0bbf23cb..48ce119e93 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 { 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/infrastructure/BlockchainHttp.ts b/src/infrastructure/BlockchainHttp.ts index 0b1b931798..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. @@ -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, + extractBeneficiary(blockDTO, 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, + extractBeneficiary(blockDTO, networkType), ); }); })); diff --git a/src/infrastructure/Listener.ts b/src/infrastructure/Listener.ts index f11c60b61c..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', @@ -135,8 +136,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, + extractBeneficiary(message, networkType), // passing `message` as `blockDTO` ), }); } else if (message.status) { diff --git a/src/infrastructure/TransactionHttp.ts b/src/infrastructure/TransactionHttp.ts index 9351063fbc..226781080b 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'; @@ -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'; @@ -44,6 +45,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 +58,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 +198,29 @@ 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) => { + // 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) => { + + // @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/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/infrastructure/transaction/CreateTransactionFromDTO.ts b/src/infrastructure/transaction/CreateTransactionFromDTO.ts index 5bd0a420d1..0c0543ad26 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'; @@ -68,7 +67,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 +77,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) => { @@ -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); } }; @@ -122,13 +122,13 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), extractRecipient(transactionDTO.recipient), extractMosaics(transactionDTO.mosaics), - transactionDTO.message !== undefined ? - PlainMessage.createFromDTO(transactionDTO.message.payload) : EmptyMessage, + extractMessage(transactionDTO.message.payload), 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) { @@ -136,14 +136,15 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), transactionDTO.namespaceType, transactionDTO.name, new NamespaceId(transactionDTO.namespaceId), 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) { @@ -151,16 +152,17 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [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]), + new UInt64(transactionDTO.properties.length === 3 ? transactionDTO.properties[2].value : 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_SUPPLY_CHANGE) { @@ -168,12 +170,13 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), new MosaicId(transactionDTO.mosaicId), 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) { @@ -181,7 +184,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), transactionDTO.minApprovalDelta, transactionDTO.minRemovalDelta, transactionDTO.modifications ? transactionDTO.modifications.map((modificationDTO) => new MultisigCosignatoryModification( @@ -189,7 +192,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) { @@ -198,27 +202,30 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr networkType, extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [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), 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), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), new Mosaic(new MosaicId(transactionDTO.mosaicId), new UInt64(transactionDTO.amount)), 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) { @@ -226,12 +233,13 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), transactionDTO.hashAlgorithm, 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) { @@ -239,12 +247,13 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), transactionDTO.aliasAction, 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) { @@ -252,12 +261,13 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), transactionDTO.aliasAction, 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) { @@ -265,14 +275,15 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), transactionDTO.propertyType, transactionDTO.modifications ? transactionDTO.modifications.map((modificationDTO) => new AccountPropertyModification( modificationDTO.modificationType, 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) { @@ -280,14 +291,15 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), transactionDTO.propertyType, transactionDTO.modifications ? transactionDTO.modifications.map((modificationDTO) => new AccountPropertyModification( modificationDTO.modificationType, 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) { @@ -295,14 +307,15 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), transactionDTO.propertyType, transactionDTO.modifications ? transactionDTO.modifications.map((modificationDTO) => new AccountPropertyModification( modificationDTO.modificationType, 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) { @@ -310,19 +323,19 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractNetworkType(transactionDTO.version), extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), - new UInt64(transactionDTO.fee || [0, 0]), + UInt64.fromUint(transactionDTO.maxFee || 0), 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); }; -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 +349,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,20 +362,28 @@ 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]; +export 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); + } 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); + } } - - // read address from encoded hexadecimal notation - return Address.createFromEncoded(recipient); + throw new Error(`Recipient: ${recipient} type is not recognised`); }; /** @@ -374,7 +395,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 +417,61 @@ 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.createFromPayload(message); + } else { + plainMessage = PlainMessage.create(message); + } + + return plainMessage; +}; + +/** + * 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/infrastructure/transaction/CreateTransactionFromPayload.ts b/src/infrastructure/transaction/CreateTransactionFromPayload.ts index e8b66453ff..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, ); @@ -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: @@ -376,8 +376,8 @@ 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), + cosignature.substring(64, 192), + PublicAccount.createFromPublicKey(cosignature.substring(0, 64), networkType), )) : [], ); case TransactionType.AGGREGATE_BONDED: @@ -400,8 +400,8 @@ 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), + cosignature.substring(64, 192), + PublicAccount.createFromPublicKey(cosignature.substring(0, 64), networkType), )) : [], ); default: @@ -456,10 +456,10 @@ 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, 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 = innerTransactionBinary.substring(8 + payloadSize); + innerBinary = innerBinary.substring(payloadSize); } return embeddedTransaction; }; diff --git a/src/infrastructure/transaction/SerializeTransactionToJSON.ts b/src/infrastructure/transaction/SerializeTransactionToJSON.ts index fcd384ff3f..dac42aa70f 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,15 +101,15 @@ 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(), }; 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(), + 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/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/blockchain/BlockInfo.ts b/src/model/blockchain/BlockInfo.ts index dd8eca9f27..0a98a54dfd 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 | undefined) { } } 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..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 { - supplyMutable: this.supplyMutable, - transferable: this.transferable, - levyMutable: this.levyMutable, - divisibility: this.divisibility, - duration: this.duration.toDTO(), - }; + 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()}, + ]; + + if (this.duration !== undefined) { + dto.push({id: 2, value: this.duration.toDTO()}); + } + + return dto; } } diff --git a/src/model/transaction/AccountLinkTransaction.ts b/src/model/transaction/AccountLinkTransaction.ts index 71d935f26d..a837f135de 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,23 @@ 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); + } + + /** + * @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; } /** @@ -85,7 +103,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..91be92fba8 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 48683ef893..a02881a8ae 100644 --- a/src/model/transaction/AddressAliasTransaction.ts +++ b/src/model/transaction/AddressAliasTransaction.ts @@ -40,17 +40,19 @@ export class AddressAliasTransaction extends Transaction { * @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, 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, - 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. */ @@ -88,7 +90,24 @@ 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); + } + + /** + * @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; } /** @@ -98,7 +117,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..3b7bb1567e 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,7 +61,7 @@ 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); } /** @@ -70,17 +70,19 @@ export class AggregateTransaction extends Transaction { * @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, 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, deadline, - new UInt64([0, 0]), + maxFee, innerTransactions, cosignatures, ); @@ -92,17 +94,19 @@ export class AggregateTransaction extends Transaction { * @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, 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, 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(); @@ -147,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/AliasTransaction.ts b/src/model/transaction/AliasTransaction.ts index acf60748a3..579f9d4969 100644 --- a/src/model/transaction/AliasTransaction.ts +++ b/src/model/transaction/AliasTransaction.ts @@ -39,14 +39,23 @@ export abstract class AliasTransaction extends Transaction { * @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, aliasAction: AliasActionType, namespaceId: NamespaceId, address: Address, - networkType: NetworkType): AliasTransaction { - return AddressAliasTransaction.create(deadline, aliasAction, namespaceId, address, networkType); + networkType: NetworkType, + maxFee: UInt64 = new UInt64([0, 0])): AliasTransaction { + return AddressAliasTransaction.create( + deadline, + aliasAction, + namespaceId, + address, + networkType, + maxFee, + ); } /** @@ -56,14 +65,23 @@ export abstract class AliasTransaction extends Transaction { * @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, aliasAction: AliasActionType, namespaceId: NamespaceId, mosaicId: MosaicId, - networkType: NetworkType): AliasTransaction { - return MosaicAliasTransaction.create(deadline, 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/EncryptedMessage.ts b/src/model/transaction/EncryptedMessage.ts new file mode 100644 index 0000000000..2527f558d7 --- /dev/null +++ b/src/model/transaction/EncryptedMessage.ts @@ -0,0 +1,65 @@ +/* + * 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 {MessageType} from './MessageType'; +import {PlainMessage} from './PlainMessage'; + +/** + * Encrypted Message model + */ +export class EncryptedMessage extends Message { + + public readonly recipientPublicAccount?: PublicAccount; + + constructor(payload: string, + recipientPublicAccount?: PublicAccount) { + super(MessageType.EncryptedMessage, 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).toUpperCase(), + 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/src/model/transaction/LockFundsTransaction.ts b/src/model/transaction/LockFundsTransaction.ts index 27f2059219..aeafd6328a 100644 --- a/src/model/transaction/LockFundsTransaction.ts +++ b/src/model/transaction/LockFundsTransaction.ts @@ -46,17 +46,20 @@ export class LockFundsTransaction extends Transaction { * @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, mosaic: Mosaic, duration: UInt64, signedTransaction: SignedTransaction, - networkType: NetworkType): LockFundsTransaction { + networkType: NetworkType, + maxFee: UInt64 = new UInt64([0, 0])): LockFundsTransaction { return new LockFundsTransaction( networkType, TransactionVersion.LOCK, deadline, - new UInt64([0, 0]), + maxFee, mosaic, duration, signedTransaction, @@ -67,7 +70,7 @@ export class LockFundsTransaction extends Transaction { * @param networkType * @param version * @param deadline - * @param fee + * @param maxFee * @param mosaic * @param duration * @param signedTransaction @@ -78,7 +81,7 @@ export class LockFundsTransaction extends Transaction { constructor(networkType: NetworkType, version: number, deadline: Deadline, - fee: UInt64, + maxFee: UInt64, /** * The locked mosaic. */ @@ -91,13 +94,31 @@ 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'); } } + /** + * @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} @@ -106,7 +127,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/MessageType.ts b/src/model/transaction/MessageType.ts new file mode 100644 index 0000000000..a43da5d6b1 --- /dev/null +++ b/src/model/transaction/MessageType.ts @@ -0,0 +1,25 @@ +/* + * 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. + */ + +/** + * The Message type. Supported supply types are: + * 0: PlainMessage + * 1: EncryptedMessage. + */ +export enum MessageType { + PlainMessage = 0, + EncryptedMessage = 1, +} diff --git a/src/model/transaction/ModifyAccountPropertyAddressTransaction.ts b/src/model/transaction/ModifyAccountPropertyAddressTransaction.ts index b275879711..2c7874d5d7 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,34 @@ 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); + } + + /** + * @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; } /** @@ -79,7 +102,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..8fae5b10b4 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,34 @@ 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); + } + + /** + * @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; } /** @@ -80,7 +103,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..d8bd9e3dfb 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,34 @@ 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); + } + + /** + * @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; } /** @@ -79,7 +102,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 5f489a83c1..2510e8770a 100644 --- a/src/model/transaction/ModifyMultisigAccountTransaction.ts +++ b/src/model/transaction/ModifyMultisigAccountTransaction.ts @@ -40,17 +40,19 @@ export class ModifyMultisigAccountTransaction extends Transaction { * @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, 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, - 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,29 @@ 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); + } + + /** + * @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; } /** @@ -99,7 +123,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..b80f047548 100644 --- a/src/model/transaction/MosaicAliasTransaction.ts +++ b/src/model/transaction/MosaicAliasTransaction.ts @@ -40,17 +40,19 @@ export class MosaicAliasTransaction extends Transaction { * @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, 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, - 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,24 @@ 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); + } + + /** + * @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; } /** @@ -98,7 +117,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..851306c3b4 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'; @@ -45,17 +44,19 @@ export class MosaicDefinitionTransaction extends Transaction { * @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, 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, - 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,28 @@ 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); + } + + /** + * @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; } /** @@ -103,10 +125,10 @@ 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()) + .addDuration(this.mosaicProperties.duration ? this.mosaicProperties.duration.toDTO() : []) .addNonce(this.nonce.toDTO()) .addMosaicId(this.mosaicId.id.toDTO()); diff --git a/src/model/transaction/MosaicSupplyChangeTransaction.ts b/src/model/transaction/MosaicSupplyChangeTransaction.ts index b5db90e8e5..4518f21edd 100644 --- a/src/model/transaction/MosaicSupplyChangeTransaction.ts +++ b/src/model/transaction/MosaicSupplyChangeTransaction.ts @@ -39,17 +39,19 @@ export class MosaicSupplyChangeTransaction extends Transaction { * @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, 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, - 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,24 @@ 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); + } + + /** + * @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; } /** @@ -97,7 +116,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/PlainMessage.ts b/src/model/transaction/PlainMessage.ts index 2f611ca473..2be59deace 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. @@ -31,7 +32,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)); } @@ -40,7 +41,7 @@ export class PlainMessage extends Message { * @param payload */ constructor(payload: string) { - super(0, payload); + super(MessageType.PlainMessage, payload); } } diff --git a/src/model/transaction/RegisterNamespaceTransaction.ts b/src/model/transaction/RegisterNamespaceTransaction.ts index d69070b226..5cbabf32e3 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, namespaceId } from 'nem2-library'; +import { convert, NamespaceCreationTransaction as RegisterNamespaceTransactionLibrary, subnamespaceNamespaceId, subnamespaceParentId, namespaceId, VerifiableTransaction } from 'nem2-library'; import { PublicAccount } from '../account/PublicAccount'; import { NetworkType } from '../blockchain/NetworkType'; import { NamespaceId } from '../namespace/NamespaceId'; @@ -38,16 +38,18 @@ export class RegisterNamespaceTransaction extends Transaction { * @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, namespaceName: string, duration: UInt64, - networkType: NetworkType): RegisterNamespaceTransaction { + networkType: NetworkType, + maxFee: UInt64 = new UInt64([0, 0])): RegisterNamespaceTransaction { return new RegisterNamespaceTransaction(networkType, TransactionVersion.REGISTER_NAMESPACE, deadline, - new UInt64([0, 0]), + maxFee, NamespaceType.RootNamespace, namespaceName, new NamespaceId(namespaceName), @@ -61,12 +63,14 @@ export class RegisterNamespaceTransaction extends Transaction { * @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, 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)); @@ -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, typeof parentNamespace === 'string' ? @@ -91,7 +95,7 @@ export class RegisterNamespaceTransaction extends Transaction { * @param networkType * @param version * @param deadline - * @param fee + * @param maxFee * @param namespaceType * @param namespaceName * @param namespaceId @@ -104,7 +108,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 */ @@ -129,7 +133,28 @@ 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); + } + + /** + * @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; } /** @@ -139,7 +164,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 9fab5e34e5..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'; @@ -38,6 +38,7 @@ export class SecretLockTransaction extends Transaction { * @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 */ @@ -47,12 +48,13 @@ export class SecretLockTransaction extends Transaction { 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, deadline, - new UInt64([0, 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,12 +104,34 @@ 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'); } } + /** + * @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} @@ -116,7 +140,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 ab5df9cbf0..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'; @@ -34,6 +34,7 @@ export class SecretProofTransaction extends Transaction { * @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 */ @@ -41,12 +42,13 @@ export class SecretProofTransaction extends Transaction { 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, deadline, - new UInt64([0, 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,19 +70,39 @@ 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'); } } + /** + * @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} @@ -89,7 +111,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/Transaction.ts b/src/model/transaction/Transaction.ts index 28421aec1c..ed197082ff 100644 --- a/src/model/transaction/Transaction.ts +++ b/src/model/transaction/Transaction.ts @@ -38,7 +38,7 @@ export abstract class Transaction { * @param networkType * @param version * @param deadline - * @param fee + * @param maxFee * @param signature * @param signer * @param transactionInfo @@ -60,10 +60,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). */ @@ -175,6 +175,23 @@ 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; + } + /** * @description Serialize a transaction object * @returns {string} @@ -194,18 +211,17 @@ export abstract class Transaction { const commonTransactionObject = { type: this.type, networkType: this.networkType, - version: this.version, - fee: this.fee.toDTO(), + version: this.versionToDTO(), + maxFee: this.maxFee.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/src/model/transaction/TransferTransaction.ts b/src/model/transaction/TransferTransaction.ts index cac454588a..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'; @@ -39,17 +39,19 @@ export class TransferTransaction extends 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, 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, - 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); } /** @@ -105,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} @@ -112,7 +136,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/src/service/AggregateTransactionService.ts b/src/service/AggregateTransactionService.ts new file mode 100644 index 0000000000..cd7bb44381 --- /dev/null +++ b/src/service/AggregateTransactionService.ts @@ -0,0 +1,153 @@ +/* + * 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 AggregateTransactionService { + + /** + * 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 = false; + + 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); + } + validationResult = true; + } 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..c988e13ffb 100644 --- a/src/service/service.ts +++ b/src/service/service.ts @@ -16,3 +16,4 @@ export * from './NamespaceService'; export * from './MosaicService'; +export * from './AggregateTransactionService'; diff --git a/test/core/utils/TransactionMapping.spec.ts b/test/core/utils/TransactionMapping.spec.ts index 6091705a33..407384c60f 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(() => { @@ -191,8 +192,108 @@ 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); + expect(transaction.mosaicProperties.levyMutable).to.be.equal(false); + + }); + + 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 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 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 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); @@ -430,3 +531,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..74909fd85f 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,32 @@ 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); + + }); + + 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.transaction.type).to.be.equal(TransactionType.MOSAIC_DEFINITION); + expect(json.transaction.properties.length).to.be.equal(2); }); @@ -209,8 +230,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 +248,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 +269,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 +286,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 +308,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 +330,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 +351,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 +371,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 +385,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 +399,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); }); }); diff --git a/test/infrastructure/TransactionHttp.spec.ts b/test/infrastructure/TransactionHttp.spec.ts index 09c72ad659..e7dcbc6d6c 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', () => { diff --git a/test/model/blockchain/BlockInfo.spec.ts b/test/model/blockchain/BlockInfo.spec.ts index 1af2bd4eb5..ba2e9034b9 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 as PublicAccount).publicKey).to.be.equal(blockDTO.block.beneficiaryPublicKey); }); }); 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/AccountLinkTransaction.spec.ts b/test/model/transaction/AccountLinkTransaction.spec.ts index 632ae9ba8d..513452e816 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(), @@ -66,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 f6a5bb473e..71cf21c198 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,90 @@ 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( + 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 68de07ec25..88f2291f27 100644 --- a/test/model/transaction/AddressAliasTransaction.spec.ts +++ b/test/model/transaction/AddressAliasTransaction.spec.ts @@ -33,6 +33,37 @@ 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'); @@ -57,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 4ecb1f129e..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'; @@ -45,6 +46,47 @@ 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), @@ -242,7 +284,7 @@ describe('AggregateTransaction', () => { 3266625578, 11, ], - fee: [ + maxFee: [ 0, 0, ], @@ -339,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/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/EncryptedMessage.spec.ts b/test/model/transaction/EncryptedMessage.spec.ts new file mode 100644 index 0000000000..1aca799d39 --- /dev/null +++ b/test/model/transaction/EncryptedMessage.spec.ts @@ -0,0 +1,48 @@ +/* + * 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 {EncryptedMessage} from '../../../src/model/transaction/EncryptedMessage'; +import { TestingAccount } from '../../conf/conf.spec'; + +describe('EncryptedMessage', () => { + + let account: Account; + + before(() => { + 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 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 encryptMessage = EncryptedMessage + .createFromDTO('7245170507448c53d808524221b5d157e19b06f574120a044e48f54dd8e0a4dedbf50ded7ae71' + + 'b90b59949bb6acde81d987ee6648aae9f093b94ac7cc3e8dba0bed8fa04ba286df6b32d2d6d21cbdc4e'); + const plainMessage = account.decryptMessage(encryptMessage, account.publicAccount); + expect(plainMessage.payload).to.be.equal('test transaction'); + }); +}); diff --git a/test/model/transaction/LockFundsTransaction.spec.ts b/test/model/transaction/LockFundsTransaction.spec.ts index bf475519e9..6adeb0cff4 100644 --- a/test/model/transaction/LockFundsTransaction.spec.ts +++ b/test/model/transaction/LockFundsTransaction.spec.ts @@ -25,6 +25,46 @@ 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(), @@ -59,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 6a0e7a8589..559969c4e1 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', () => { @@ -31,6 +32,51 @@ 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(), @@ -73,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 e72c5256d2..2c77986550 100644 --- a/test/model/transaction/MosaicAliasTransaction.spec.ts +++ b/test/model/transaction/MosaicAliasTransaction.spec.ts @@ -32,6 +32,37 @@ 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]); @@ -57,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 41799026bc..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; @@ -33,6 +32,45 @@ 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(), @@ -48,8 +86,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 +118,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); @@ -95,4 +133,52 @@ 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); + }); + }); + + 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'); + + }); }); diff --git a/test/model/transaction/MosaicSupplyChangeTransaction.spec.ts b/test/model/transaction/MosaicSupplyChangeTransaction.spec.ts index ae6e4c99ef..99f7438f1e 100644 --- a/test/model/transaction/MosaicSupplyChangeTransaction.spec.ts +++ b/test/model/transaction/MosaicSupplyChangeTransaction.spec.ts @@ -31,6 +31,35 @@ 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( @@ -55,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/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'); }); diff --git a/test/model/transaction/RegisterNamespaceTransaction.spec.ts b/test/model/transaction/RegisterNamespaceTransaction.spec.ts index bcdf3b5eef..f45a85276b 100644 --- a/test/model/transaction/RegisterNamespaceTransaction.spec.ts +++ b/test/model/transaction/RegisterNamespaceTransaction.spec.ts @@ -30,6 +30,31 @@ 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(), @@ -66,7 +91,7 @@ describe('RegisterNamespaceTransaction', () => { )).to.be.equal('014DF55E7F6D8FB7FF924207DF2CA1BBF313726F6F742D746573742D6E616D657370616365'); }); - + it('should createComplete an sub RegisterNamespaceTransaction object and sign it - ParentId', () => { const registerNamespaceTransaction = RegisterNamespaceTransaction.createSubNamespace( Deadline.create(), @@ -81,6 +106,17 @@ describe('RegisterNamespaceTransaction', () => { 240, signedTransaction.payload.length, )).to.be.equal('014BFA5F372D55B384CFCBE72D994BE69B13726F6F742D746573742D6E616D657370616365'); - + }); + + 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 7166ab790e..9795d7f5ae 100644 --- a/test/model/transaction/SecretLockTransaction.spec.ts +++ b/test/model/transaction/SecretLockTransaction.spec.ts @@ -28,6 +28,41 @@ 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'); @@ -165,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 1434017965..1a4258c299 100644 --- a/test/model/transaction/SecretProofTransaction.spec.ts +++ b/test/model/transaction/SecretProofTransaction.spec.ts @@ -21,9 +21,39 @@ 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', () => { + 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( @@ -129,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 d63980ec56..a095bb0fd8 100644 --- a/test/model/transaction/Transaction.spec.ts +++ b/test/model/transaction/Transaction.spec.ts @@ -198,6 +198,21 @@ describe('Transaction', () => { )).to.be.equal('9050B9837EFAB4BBE8A4B9BB32D812F9885C00D8FC1650E1420D000000746573742D6D657373616765'); }); }); + + 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 2fabc317b3..211c7677fb 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', () => { @@ -32,6 +33,33 @@ 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(), @@ -150,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); + }); + }); }); diff --git a/test/service/AggregateTransactionService.spec.ts b/test/service/AggregateTransactionService.spec.ts new file mode 100644 index 0000000000..ae2deab99b --- /dev/null +++ b/test/service/AggregateTransactionService.spec.ts @@ -0,0 +1,562 @@ +/* + * 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 { AggregateTransactionService } from '../../src/service/AggregateTransactionService'; + +/** + * For multi level multisig scenario visit: https://github.com/nemtech/nem2-docs/issues/10 + */ +describe('AggregateTransactionService', () => { + let aggregateTransactionService: AggregateTransactionService; + + /** + * 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); + aggregateTransactionService = new AggregateTransactionService(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]); + aggregateTransactionService.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, []); + aggregateTransactionService.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]); + aggregateTransactionService.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]); + aggregateTransactionService.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]); + aggregateTransactionService.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); + aggregateTransactionService.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); + aggregateTransactionService.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); + aggregateTransactionService.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]); + aggregateTransactionService.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, []); + aggregateTransactionService.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]); + aggregateTransactionService.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, []); + aggregateTransactionService.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); + } + +}); diff --git a/tslint.json b/tslint.json index aeca64ccdf..ded366576a 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,