From a1bc66b85c3d13d1ef1ffda891e239c3b90faa84 Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Fri, 11 Oct 2019 16:07:34 +0100 Subject: [PATCH 1/2] JAV-64 [Github #295] Unresolved Mosaic and Address in transactions --- e2e/infrastructure/UnresolvedMapping.spec.ts | 494 ++++++++++++++++++ ...osaicRestrictionTransactionService.spec.ts | 2 +- src/core/utils/UnresolvedMapping.ts | 86 +++ .../transaction/CreateTransactionFromDTO.ts | 44 +- .../MosaicAddressRestrictionTransaction.ts | 36 +- .../MosaicGlobalRestrictionTransaction.ts | 18 +- .../transaction/MosaicMetadataTransaction.ts | 10 +- src/model/transaction/TransactionVersion.ts | 2 - src/model/transaction/TransferTransaction.ts | 28 +- test/core/utils/TransactionMapping.spec.ts | 34 +- test/core/utils/UnresolvedMapping.spec.ts | 89 ++++ ...osaicAddressRestrictionTransaction.spec.ts | 77 ++- ...MosaicGlobalRestrictionTransaction.spec.ts | 66 +++ .../MosaicMetadataTransaction.spec.ts | 22 + ...osaicRestrictionTransactionservice.spec.ts | 2 +- 15 files changed, 923 insertions(+), 87 deletions(-) create mode 100644 e2e/infrastructure/UnresolvedMapping.spec.ts create mode 100644 src/core/utils/UnresolvedMapping.ts create mode 100644 test/core/utils/UnresolvedMapping.spec.ts diff --git a/e2e/infrastructure/UnresolvedMapping.spec.ts b/e2e/infrastructure/UnresolvedMapping.spec.ts new file mode 100644 index 0000000000..fe5bddabf2 --- /dev/null +++ b/e2e/infrastructure/UnresolvedMapping.spec.ts @@ -0,0 +1,494 @@ +/* + * 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 {assert, expect} from 'chai'; +import { Convert } from '../../src/core/format'; +import {AccountHttp} from '../../src/infrastructure/AccountHttp'; +import { NamespaceHttp } from '../../src/infrastructure/infrastructure'; +import {Listener} from '../../src/infrastructure/Listener'; +import {TransactionHttp} from '../../src/infrastructure/TransactionHttp'; +import {Account} from '../../src/model/account/Account'; +import {NetworkType} from '../../src/model/blockchain/NetworkType'; +import { PlainMessage } from '../../src/model/message/PlainMessage'; +import {MosaicFlags} from '../../src/model/mosaic/MosaicFlags'; +import {MosaicId} from '../../src/model/mosaic/MosaicId'; +import {MosaicNonce} from '../../src/model/mosaic/MosaicNonce'; +import {NetworkCurrencyMosaic} from '../../src/model/mosaic/NetworkCurrencyMosaic'; +import { AliasAction } from '../../src/model/namespace/AliasAction'; +import { NamespaceId } from '../../src/model/namespace/NamespaceId'; +import { MosaicRestrictionType } from '../../src/model/restriction/MosaicRestrictionType'; +import { AddressAliasTransaction } from '../../src/model/transaction/AddressAliasTransaction'; +import {AggregateTransaction} from '../../src/model/transaction/AggregateTransaction'; +import {Deadline} from '../../src/model/transaction/Deadline'; +import { MosaicAddressRestrictionTransaction } from '../../src/model/transaction/MosaicAddressRestrictionTransaction'; +import { MosaicAliasTransaction } from '../../src/model/transaction/MosaicAliasTransaction'; +import {MosaicDefinitionTransaction} from '../../src/model/transaction/MosaicDefinitionTransaction'; +import { MosaicGlobalRestrictionTransaction } from '../../src/model/transaction/MosaicGlobalRestrictionTransaction'; +import { MosaicMetadataTransaction } from '../../src/model/transaction/MosaicMetadataTransaction'; +import {NamespaceRegistrationTransaction} from '../../src/model/transaction/NamespaceRegistrationTransaction'; +import {Transaction} from '../../src/model/transaction/Transaction'; +import {TransferTransaction} from '../../src/model/transaction/TransferTransaction'; +import {UInt64} from '../../src/model/UInt64'; +describe('TransactionHttp', () => { + let account: Account; + let account2: Account; + let account3: Account; + let testAccountNoBalance: Account; + let harvestingAccount: Account; + let transactionHttp: TransactionHttp; + let multisigAccount: Account; + let cosignAccount1: Account; + let cosignAccount2: Account; + let cosignAccount3: Account; + let mosaicId: MosaicId; + let namespaceIdAddress: NamespaceId; + let namespaceIdMosaic: NamespaceId; + let networkCurrencyMosaicId: MosaicId; + let accountHttp: AccountHttp; + let namespaceHttp: NamespaceHttp; + let config; + let generationHash: string; + + before((done) => { + const path = require('path'); + require('fs').readFile(path.resolve(__dirname, '../conf/network.conf'), (err, data) => { + if (err) { + throw err; + } + const json = JSON.parse(data); + config = json; + account = Account.createFromPrivateKey(json.testAccount.privateKey, NetworkType.MIJIN_TEST); + account2 = Account.createFromPrivateKey(json.testAccount2.privateKey, NetworkType.MIJIN_TEST); + account3 = Account.createFromPrivateKey(json.testAccount3.privateKey, NetworkType.MIJIN_TEST); + testAccountNoBalance = Account.createFromPrivateKey(json.testAccountNoBalance.privateKey, NetworkType.MIJIN_TEST); + harvestingAccount = Account.createFromPrivateKey(json.harvestingAccount.privateKey, NetworkType.MIJIN_TEST); + multisigAccount = Account.createFromPrivateKey(json.multisigAccount.privateKey, NetworkType.MIJIN_TEST); + cosignAccount1 = Account.createFromPrivateKey(json.cosignatoryAccount.privateKey, NetworkType.MIJIN_TEST); + cosignAccount2 = Account.createFromPrivateKey(json.cosignatory2Account.privateKey, NetworkType.MIJIN_TEST); + cosignAccount3 = Account.createFromPrivateKey(json.cosignatory3Account.privateKey, NetworkType.MIJIN_TEST); + accountHttp = new AccountHttp(json.apiUrl); + transactionHttp = new TransactionHttp(json.apiUrl); + namespaceHttp = new NamespaceHttp(json.apiUrl); + generationHash = json.generationHash; + done(); + }); + }); + + /** + * ========================= + * Setup test data + * ========================= + */ + describe('Get network currency mosaic id', () => { + it('get mosaicId', (done) => { + namespaceHttp.getLinkedMosaicId(new NamespaceId('cat.currency')).subscribe((networkMosaicId) => { + networkCurrencyMosaicId = networkMosaicId; + done(); + }); + }); + }); + + describe('MosaicDefinitionTransaction', () => { + let listener: Listener; + before (() => { + listener = new Listener(config.apiUrl); + return listener.open(); + }); + after(() => { + return listener.close(); + }); + it('standalone', (done) => { + const nonce = MosaicNonce.createRandom(); + mosaicId = MosaicId.createFromNonce(nonce, account.publicAccount); + const mosaicDefinitionTransaction = MosaicDefinitionTransaction.create( + Deadline.create(), + nonce, + mosaicId, + MosaicFlags.create( true, true, true), + 3, + UInt64.fromUint(1000), + NetworkType.MIJIN_TEST, + ); + const signedTransaction = mosaicDefinitionTransaction.signWith(account, generationHash); + listener.confirmed(account.address).subscribe(() => { + done(); + }); + listener.status(account.address).subscribe((error) => { + console.log('Error:', error); + assert(false); + done(); + }); + transactionHttp.announce(signedTransaction); + }); + }); + + describe('NamespaceRegistrationTransaction', () => { + let listener: Listener; + before (() => { + listener = new Listener(config.apiUrl); + return listener.open(); + }); + after(() => { + return listener.close(); + }); + it('standalone', (done) => { + const namespaceName = 'root-test-namespace-' + Math.floor(Math.random() * 10000); + const registerNamespaceTransaction = NamespaceRegistrationTransaction.createRootNamespace( + Deadline.create(), + namespaceName, + UInt64.fromUint(50), + NetworkType.MIJIN_TEST, + ); + namespaceIdMosaic = new NamespaceId(namespaceName); + const signedTransaction = registerNamespaceTransaction.signWith(account, generationHash); + listener.confirmed(account.address).subscribe(() => { + done(); + }); + listener.status(account.address).subscribe((error) => { + console.log('Error:', error); + assert(false); + done(); + }); + transactionHttp.announce(signedTransaction); + }); + }); + + describe('NamespaceRegistrationTransaction', () => { + let listener: Listener; + before (() => { + listener = new Listener(config.apiUrl); + return listener.open(); + }); + after(() => { + return listener.close(); + }); + it('standalone', (done) => { + const namespaceName = 'root-test-namespace-' + Math.floor(Math.random() * 10000); + const registerNamespaceTransaction = NamespaceRegistrationTransaction.createRootNamespace( + Deadline.create(), + namespaceName, + UInt64.fromUint(50), + NetworkType.MIJIN_TEST, + ); + namespaceIdAddress = new NamespaceId(namespaceName); + const signedTransaction = registerNamespaceTransaction.signWith(account, generationHash); + listener.confirmed(account.address).subscribe(() => { + done(); + }); + listener.status(account.address).subscribe((error) => { + console.log('Error:', error); + assert(false); + done(); + }); + transactionHttp.announce(signedTransaction); + }); + }); + + describe('AddressAliasTransaction', () => { + let listener: Listener; + before (() => { + listener = new Listener(config.apiUrl); + return listener.open(); + }); + after(() => { + return listener.close(); + }); + + it('standalone', (done) => { + const addressAliasTransaction = AddressAliasTransaction.create( + Deadline.create(), + AliasAction.Link, + namespaceIdAddress, + account.address, + NetworkType.MIJIN_TEST, + ); + const signedTransaction = addressAliasTransaction.signWith(account, generationHash); + + listener.confirmed(account.address).subscribe(() => { + done(); + }); + listener.status(account.address).subscribe((error) => { + console.log('Error:', error); + assert(false); + done(); + }); + transactionHttp.announce(signedTransaction); + }); + }); + + describe('MosaicAliasTransaction', () => { + let listener: Listener; + before (() => { + listener = new Listener(config.apiUrl); + return listener.open(); + }); + after(() => { + return listener.close(); + }); + + it('standalone', (done) => { + const mosaicAliasTransaction = MosaicAliasTransaction.create( + Deadline.create(), + AliasAction.Link, + namespaceIdMosaic, + mosaicId, + NetworkType.MIJIN_TEST, + ); + const signedTransaction = mosaicAliasTransaction.signWith(account, generationHash); + + listener.confirmed(account.address).subscribe(() => { + done(); + }); + listener.status(account.address).subscribe((error) => { + console.log('Error:', error); + assert(false); + done(); + }); + transactionHttp.announce(signedTransaction); + }); + }); + + /** + * ========================= + * Test unresolved inputs + * ========================= + */ + + describe('MosaicMetadataTransaction', () => { + let listener: Listener; + before (() => { + listener = new Listener(config.apiUrl); + return listener.open(); + }); + after(() => { + return listener.close(); + }); + it('aggregate', (done) => { + const mosaicMetadataTransaction = MosaicMetadataTransaction.create( + Deadline.create(), + account.publicKey, + UInt64.fromUint(5), + namespaceIdMosaic, + 10, + Convert.uint8ToUtf8(new Uint8Array(10)), + NetworkType.MIJIN_TEST, + ); + + const aggregateTransaction = AggregateTransaction.createComplete(Deadline.create(), + [mosaicMetadataTransaction.toAggregate(account.publicAccount)], + NetworkType.MIJIN_TEST, + [], + ); + const signedTransaction = aggregateTransaction.signWith(account, generationHash); + listener.confirmed(account.address).subscribe((transaction: AggregateTransaction) => { + transaction.innerTransactions.forEach((innerTx) => { + expect((innerTx as MosaicMetadataTransaction).targetMosaicId instanceof NamespaceId).to.be.true; + }); + done(); + }); + listener.status(account.address).subscribe((error) => { + console.log('Error:', error); + assert(false); + done(); + }); + transactionHttp.announce(signedTransaction); + }); + }); + + describe('MosaicGlobalRestrictionTransaction', () => { + let listener: Listener; + before (() => { + listener = new Listener(config.apiUrl); + return listener.open(); + }); + after(() => { + return listener.close(); + }); + + it('standalone', (done) => { + const mosaicGlobalRestrictionTransaction = MosaicGlobalRestrictionTransaction.create( + Deadline.create(), + namespaceIdMosaic, + UInt64.fromUint(60641), + UInt64.fromUint(0), + MosaicRestrictionType.NONE, + UInt64.fromUint(0), + MosaicRestrictionType.GE, + NetworkType.MIJIN_TEST, + ); + const signedTransaction = mosaicGlobalRestrictionTransaction.signWith(account, generationHash); + + listener.confirmed(account.address).subscribe((transaction: Transaction) => { + expect((transaction as MosaicGlobalRestrictionTransaction).mosaicId instanceof NamespaceId).to.be.true; + done(); + }); + listener.status(account.address).subscribe((error) => { + console.log('Error:', error); + assert(false); + done(); + }); + transactionHttp.announce(signedTransaction); + }); + }); + + describe('MosaicAddressRestrictionTransaction', () => { + let listener: Listener; + before (() => { + listener = new Listener(config.apiUrl); + return listener.open(); + }); + after(() => { + return listener.close(); + }); + it('aggregate', (done) => { + const mosaicAddressRestrictionTransaction = MosaicAddressRestrictionTransaction.create( + Deadline.create(), + namespaceIdMosaic, + UInt64.fromUint(60641), + namespaceIdAddress, + UInt64.fromUint(2), + NetworkType.MIJIN_TEST, + ); + const aggregateTransaction = AggregateTransaction.createComplete(Deadline.create(), + [mosaicAddressRestrictionTransaction.toAggregate(account.publicAccount)], + NetworkType.MIJIN_TEST, + [], + ); + const signedTransaction = aggregateTransaction.signWith(account, generationHash); + listener.confirmed(account.address).subscribe((transaction: AggregateTransaction) => { + transaction.innerTransactions.forEach((innerTx) => { + expect((innerTx as MosaicAddressRestrictionTransaction).mosaicId instanceof NamespaceId).to.be.true; + expect((innerTx as MosaicAddressRestrictionTransaction).targetAddress instanceof NamespaceId).to.be.true; + }); + done(); + }); + listener.status(account.address).subscribe((error) => { + console.log('Error:', error); + assert(false); + done(); + }); + transactionHttp.announce(signedTransaction); + }); + }); + + describe('TransferTransaction', () => { + let listener: Listener; + before (() => { + listener = new Listener(config.apiUrl); + return listener.open(); + }); + after(() => { + return listener.close(); + }); + + it('standalone', (done) => { + const transferTransaction = TransferTransaction.create( + Deadline.create(), + account2.address, + [NetworkCurrencyMosaic.createAbsolute(1)], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + const signedTransaction = transferTransaction.signWith(account, generationHash); + + listener.confirmed(account.address).subscribe((transaction: TransferTransaction) => { + expect(transaction.mosaics[0].id instanceof NamespaceId).to.be.true; + done(); + }); + listener.status(account.address).subscribe((error) => { + console.log('Error:', error); + assert(false); + done(); + }); + transactionHttp.announce(signedTransaction); + }); + }); + + /** + * ========================= + * House Keeping + * ========================= + */ + + describe('AddressAliasTransaction', () => { + let listener: Listener; + before (() => { + listener = new Listener(config.apiUrl); + return listener.open(); + }); + after(() => { + return listener.close(); + }); + + it('standalone', (done) => { + const addressAliasTransaction = AddressAliasTransaction.create( + Deadline.create(), + AliasAction.Unlink, + namespaceIdAddress, + account.address, + NetworkType.MIJIN_TEST, + ); + const signedTransaction = addressAliasTransaction.signWith(account, generationHash); + + listener.confirmed(account.address).subscribe((transaction: AddressAliasTransaction) => { + expect(transaction.namespaceId, 'NamespaceId').not.to.be.undefined; + expect(transaction.aliasAction, 'AliasAction').not.to.be.undefined; + expect(transaction.address, 'Address').not.to.be.undefined; + done(); + }); + listener.status(account.address).subscribe((error) => { + console.log('Error:', error); + assert(false); + done(); + }); + transactionHttp.announce(signedTransaction); + }); + }); + + describe('MosaicAliasTransaction', () => { + let listener: Listener; + before (() => { + listener = new Listener(config.apiUrl); + return listener.open(); + }); + after(() => { + return listener.close(); + }); + + it('standalone', (done) => { + const mosaicAliasTransaction = MosaicAliasTransaction.create( + Deadline.create(), + AliasAction.Unlink, + namespaceIdMosaic, + mosaicId, + NetworkType.MIJIN_TEST, + ); + const signedTransaction = mosaicAliasTransaction.signWith(account, generationHash); + + listener.confirmed(account.address).subscribe((transaction: MosaicAliasTransaction) => { + expect(transaction.namespaceId, 'NamespaceId').not.to.be.undefined; + expect(transaction.aliasAction, 'AliasAction').not.to.be.undefined; + expect(transaction.mosaicId, 'MosaicId').not.to.be.undefined; + done(); + }); + listener.status(account.address).subscribe((error) => { + console.log('Error:', error); + assert(false); + done(); + }); + transactionHttp.announce(signedTransaction); + }); + }); +}); diff --git a/e2e/service/MosaicRestrictionTransactionService.spec.ts b/e2e/service/MosaicRestrictionTransactionService.spec.ts index b13a685687..fbf830a3f2 100644 --- a/e2e/service/MosaicRestrictionTransactionService.spec.ts +++ b/e2e/service/MosaicRestrictionTransactionService.spec.ts @@ -196,7 +196,7 @@ describe('MosaicRestrictionTransactionService', () => { expect(transaction.type).to.be.equal(TransactionType.MOSAIC_ADDRESS_RESTRICTION); expect(transaction.previousRestrictionValue.toString()).to.be.equal('2'); expect(transaction.newRestrictionValue.toString()).to.be.equal('3'); - expect(transaction.targetAddress.plain()).to.be.equal(targetAccount.address.plain()); + expect(transaction.targetAddressToString()).to.be.equal(targetAccount.address.plain()); expect(transaction.restrictionKey.toHex()).to.be.equal(key.toHex()); done(); }); diff --git a/src/core/utils/UnresolvedMapping.ts b/src/core/utils/UnresolvedMapping.ts new file mode 100644 index 0000000000..35d9cc2887 --- /dev/null +++ b/src/core/utils/UnresolvedMapping.ts @@ -0,0 +1,86 @@ +/* + * 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 { Address } from '../../model/account/Address'; +import { MosaicId } from '../../model/mosaic/MosaicId'; +import { NamespaceId } from '../../model/namespace/NamespaceId'; +import { Convert } from '../format/Convert'; +import { RawAddress } from '../format/RawAddress'; + +/** + * @internal + */ +export class UnresolvedMapping { + + /** + * @internal + * Map unresolved mosaic string to MosaicId or NamespaceId + * @param {string} mosaicId The unresolvedMosaic id in hex. + * @returns {MosaicId | NamespaceId} + */ + public static toUnresolvedMosaic(mosaicId: string): MosaicId | NamespaceId { + if (!Convert.isHexString(mosaicId)) { + throw new Error('Input string is not in valid hexadecimal notation.'); + } + const bytes = Convert.hexToUint8(mosaicId); + const byte0 = bytes[0]; + + // if most significant bit of byte 0 is set, then we have a namespaceId + if ((byte0 & 128) === 128) { + return NamespaceId.createFromEncoded(mosaicId); + } + // most significant bit of byte 0 is not set => mosaicId + return new MosaicId(mosaicId); + } + + /** + * Map unresolved address string to Address or NamespaceId + * @param {string} address The unresolved address in hex + * @returns {Address | NamespaceId} + */ + public static toUnresolvedAddress(address: string): Address | NamespaceId { + if (!Convert.isHexString(address)) { + throw new Error('Input string is not in valid hexadecimal notation.'); + } + // 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(address.substr(1, 2))[0]; + if ((bit0 & 16) === 16) { + // namespaceId encoded hexadecimal notation provided + // only 8 bytes are relevant to resolve the NamespaceId + const relevantPart = address.substr(2, 16); + return NamespaceId.createFromEncoded(relevantPart); + } + + // read address from encoded hexadecimal notation + return Address.createFromEncoded(address); + } + + /** + * Return unresolved address bytes of the unresolved address + * @internal + * @param {Address | NamespaceId} unresolvedAddress The unresolved address + * @return {Uint8Array} + */ + public static toUnresolvedAddressBytes(unresolvedAddress: Address | NamespaceId): Uint8Array { + if (unresolvedAddress instanceof NamespaceId) { + // received hexadecimal notation of namespaceId (alias) + return RawAddress.aliasToRecipient(Convert.hexToUint8((unresolvedAddress as NamespaceId).toHex())); + } else { + // received recipient address + return RawAddress.stringToAddress((unresolvedAddress as Address).plain()); + } + } +} diff --git a/src/infrastructure/transaction/CreateTransactionFromDTO.ts b/src/infrastructure/transaction/CreateTransactionFromDTO.ts index a2cfd921cc..402146294a 100644 --- a/src/infrastructure/transaction/CreateTransactionFromDTO.ts +++ b/src/infrastructure/transaction/CreateTransactionFromDTO.ts @@ -14,6 +14,7 @@ * limitations under the License. */ import {Convert as convert} from '../../core/format'; +import { UnresolvedMapping } from '../../core/utils/UnresolvedMapping'; import {Address} from '../../model/account/Address'; import {PublicAccount} from '../../model/account/PublicAccount'; import {NetworkType} from '../../model/blockchain/NetworkType'; @@ -345,8 +346,8 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), UInt64.fromNumericString(transactionDTO.maxFee || '0'), - new MosaicId(transactionDTO.mosaicId), - new MosaicId(transactionDTO.referenceMosaicId), + UnresolvedMapping.toUnresolvedMosaic(transactionDTO.mosaicId), + UnresolvedMapping.toUnresolvedMosaic(transactionDTO.referenceMosaicId), UInt64.fromHex(transactionDTO.restrictionKey), UInt64.fromNumericString(transactionDTO.previousRestrictionValue), transactionDTO.previousRestrictionType, @@ -364,11 +365,10 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr extractTransactionVersion(transactionDTO.version), Deadline.createFromDTO(transactionDTO.deadline), UInt64.fromNumericString(transactionDTO.maxFee || '0'), - new MosaicId(transactionDTO.mosaicId), + UnresolvedMapping.toUnresolvedMosaic(transactionDTO.mosaicId), UInt64.fromHex(transactionDTO.restrictionKey), - typeof targetAddress === 'object' && targetAddress.hasOwnProperty('address') ? - Address.createFromRawAddress(targetAddress.address) : Address.createFromEncoded(targetAddress), - UInt64.fromNumericString(transactionDTO.previousRestrictionValue), + extractRecipient(transactionDTO.targetAddress), + UInt64.fromNumericString(transactionDTO.previousRestrictionValue), UInt64.fromNumericString(transactionDTO.newRestrictionValue), transactionDTO.signature, transactionDTO.signerPublicKey ? PublicAccount.createFromPublicKey(transactionDTO.signerPublicKey, @@ -398,7 +398,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr UInt64.fromNumericString(transactionDTO.maxFee || '0'), transactionDTO.targetPublicKey, UInt64.fromHex(transactionDTO.scopedMetadataKey), - new MosaicId(transactionDTO.targetMosaicId), + UnresolvedMapping.toUnresolvedMosaic(transactionDTO.targetMosaicId), transactionDTO.valueSizeDelta, convert.decodeHex(transactionDTO.value), transactionDTO.signature, @@ -455,18 +455,7 @@ export const extractTransactionVersion = (version: number): number => { */ export const extractRecipient = (recipientAddress: any): Address | NamespaceId => { if (typeof recipientAddress === '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(recipientAddress.substr(1, 2))[0]; - if ((bit0 & 16) === 16) { - // namespaceId encoded hexadecimal notation provided - // only 8 bytes are relevant to resolve the NamespaceId - const relevantPart = recipientAddress.substr(2, 16); - return NamespaceId.createFromEncoded(relevantPart); - } - - // read address from encoded hexadecimal notation - return Address.createFromEncoded(recipientAddress); + return UnresolvedMapping.toUnresolvedAddress(recipientAddress); } else if (typeof recipientAddress === 'object') { // Is JSON object if (recipientAddress.hasOwnProperty('address')) { return Address.createFromRawAddress(recipientAddress.address); @@ -487,25 +476,12 @@ export const extractRecipient = (recipientAddress: any): Address | NamespaceId * @return {Mosaic[]} */ export const extractMosaics = (mosaics: any): Mosaic[] => { - if (mosaics === undefined) { return []; } - return mosaics.map((mosaicDTO) => { - - // convert ID to UInt8 bytes array and get first byte (most significant byte) - const bytes = convert.hexToUint8(mosaicDTO.id); - const byte0 = bytes[0]; - - // if most significant bit of byte 0 is set, then we have a namespaceId - if ((byte0 & 128) === 128) { - const namespaceId = NamespaceId.createFromEncoded(mosaicDTO.id); - return new Mosaic(namespaceId, UInt64.fromNumericString(mosaicDTO.amount)); - } - - // most significant bit of byte 0 is not set => mosaicId - return new Mosaic(new MosaicId(mosaicDTO.id), UInt64.fromNumericString(mosaicDTO.amount)); + const id = UnresolvedMapping.toUnresolvedMosaic(mosaicDTO.id); + return new Mosaic(id, UInt64.fromNumericString(mosaicDTO.amount)); }); }; diff --git a/src/model/transaction/MosaicAddressRestrictionTransaction.ts b/src/model/transaction/MosaicAddressRestrictionTransaction.ts index 229eaa0681..644508c7a6 100644 --- a/src/model/transaction/MosaicAddressRestrictionTransaction.ts +++ b/src/model/transaction/MosaicAddressRestrictionTransaction.ts @@ -15,6 +15,7 @@ */ import { Convert, RawAddress } from '../../core/format'; +import { UnresolvedMapping } from '../../core/utils/UnresolvedMapping'; import { AmountDto } from '../../infrastructure/catbuffer/AmountDto'; import { EmbeddedMosaicAddressRestrictionTransactionBuilder, @@ -29,6 +30,7 @@ import { Address } from '../account/Address'; import { PublicAccount } from '../account/PublicAccount'; import { NetworkType } from '../blockchain/NetworkType'; import { MosaicId } from '../mosaic/MosaicId'; +import { NamespaceId } from '../namespace/NamespaceId'; import { UInt64 } from '../UInt64'; import { Deadline } from './Deadline'; import { InnerTransaction } from './InnerTransaction'; @@ -51,7 +53,7 @@ export class MosaicAddressRestrictionTransaction extends Transaction { * **MosaicAddressRestrictionTransaction can only be announced in with Aggregate Transaction * * @param deadline - The deadline to include the transaction. - * @param mosaicId - The mosaic id ex: new MosaicId([481110499, 231112638]). + * @param mosaicId - The unresolved mosaic identifier. * @param restrictionKey - The restriction key. * @param targetAddress - The affected unresolved address. * @param newRestrictionValue - The new restriction value. @@ -61,9 +63,9 @@ export class MosaicAddressRestrictionTransaction extends Transaction { * @returns {MosaicAddressRestrictionTransaction} */ public static create(deadline: Deadline, - mosaicId: MosaicId, + mosaicId: MosaicId | NamespaceId, restrictionKey: UInt64, - targetAddress: Address, + targetAddress: Address | NamespaceId, newRestrictionValue: UInt64, networkType: NetworkType, previousRestrictionValue: UInt64 = UInt64.fromHex('FFFFFFFFFFFFFFFF'), @@ -102,7 +104,7 @@ export class MosaicAddressRestrictionTransaction extends Transaction { /** * The mosaic id. */ - public readonly mosaicId: MosaicId, + public readonly mosaicId: MosaicId | NamespaceId, /** * The restriction key. */ @@ -110,7 +112,7 @@ export class MosaicAddressRestrictionTransaction extends Transaction { /** * The affected unresolved address. */ - public readonly targetAddress: Address, + public readonly targetAddress: Address | NamespaceId, /** * The previous restriction value. */ @@ -140,9 +142,9 @@ export class MosaicAddressRestrictionTransaction extends Transaction { const transaction = MosaicAddressRestrictionTransaction.create( isEmbedded ? Deadline.create() : Deadline.createFromDTO( (builder as MosaicAddressRestrictionTransactionBuilder).getDeadline().timestamp), - new MosaicId(builder.getMosaicId().unresolvedMosaicId), + UnresolvedMapping.toUnresolvedMosaic(new UInt64(builder.getMosaicId().unresolvedMosaicId).toHex()), new UInt64(builder.getRestrictionKey()), - Address.createFromEncoded(Convert.uint8ToHex(builder.getTargetAddress().unresolvedAddress)), + UnresolvedMapping.toUnresolvedAddress(Convert.uint8ToHex(builder.getTargetAddress().unresolvedAddress)), new UInt64(builder.getNewRestrictionValue()), networkType, new UInt64(builder.getPreviousRestrictionValue()), @@ -173,6 +175,22 @@ export class MosaicAddressRestrictionTransaction extends Transaction { byteTargetAddress + bytePreviousRestrictionValue + byteNewRestrictionValue; } + /** + * Return the string notation for the set recipient + * @internal + * @returns {string} + */ + public targetAddressToString(): string { + + if (this.targetAddress instanceof NamespaceId) { + // namespaceId recipient, return hexadecimal notation + return (this.targetAddress as NamespaceId).toHex(); + } + + // address recipient + return (this.targetAddress as Address).plain(); + } + /** * @internal * @returns {Uint8Array} @@ -190,7 +208,7 @@ export class MosaicAddressRestrictionTransaction extends Transaction { new TimestampDto(this.deadline.toDTO()), new UnresolvedMosaicIdDto(this.mosaicId.id.toDTO()), this.restrictionKey.toDTO(), - new UnresolvedAddressDto(RawAddress.stringToAddress(this.targetAddress.plain())), + new UnresolvedAddressDto(UnresolvedMapping.toUnresolvedAddressBytes(this.targetAddress)), this.previousRestrictionValue.toDTO(), this.newRestrictionValue.toDTO(), ); @@ -208,7 +226,7 @@ export class MosaicAddressRestrictionTransaction extends Transaction { TransactionType.MOSAIC_ADDRESS_RESTRICTION.valueOf(), new UnresolvedMosaicIdDto(this.mosaicId.id.toDTO()), this.restrictionKey.toDTO(), - new UnresolvedAddressDto(RawAddress.stringToAddress(this.targetAddress.plain())), + new UnresolvedAddressDto(UnresolvedMapping.toUnresolvedAddressBytes(this.targetAddress)), this.previousRestrictionValue.toDTO(), this.newRestrictionValue.toDTO(), ); diff --git a/src/model/transaction/MosaicGlobalRestrictionTransaction.ts b/src/model/transaction/MosaicGlobalRestrictionTransaction.ts index 282009a232..b6079fe85b 100644 --- a/src/model/transaction/MosaicGlobalRestrictionTransaction.ts +++ b/src/model/transaction/MosaicGlobalRestrictionTransaction.ts @@ -15,6 +15,7 @@ */ import { Convert } from '../../core/format'; +import { UnresolvedMapping } from '../../core/utils/UnresolvedMapping'; import { AmountDto } from '../../infrastructure/catbuffer/AmountDto'; import { EmbeddedMosaicGlobalRestrictionTransactionBuilder, @@ -27,6 +28,7 @@ import { UnresolvedMosaicIdDto } from '../../infrastructure/catbuffer/Unresolved import { PublicAccount } from '../account/PublicAccount'; import { NetworkType } from '../blockchain/NetworkType'; import { MosaicId } from '../mosaic/MosaicId'; +import { NamespaceId } from '../namespace/NamespaceId'; import { MosaicRestrictionType } from '../restriction/MosaicRestrictionType'; import { UInt64 } from '../UInt64'; import { Deadline } from './Deadline'; @@ -60,19 +62,19 @@ export class MosaicGlobalRestrictionTransaction extends Transaction { * @param newRestrictionValue - The new restriction value. * @param newRestrictionType - The new restriction tpye. * @param networkType - The network type. - * @param referenceMosaicId - (Optional) The mosaic id providing the restriction key. + * @param referenceMosaicId - (Optional) The unresolved mosaic identifier providing the restriction key. * @param maxFee - (Optional) Max fee defined by the sender * @returns {MosaicGlobalRestrictionTransaction} */ public static create(deadline: Deadline, - mosaicId: MosaicId, + mosaicId: MosaicId | NamespaceId, restrictionKey: UInt64, previousRestrictionValue: UInt64, previousRestrictionType: MosaicRestrictionType, newRestrictionValue: UInt64, newRestrictionType: MosaicRestrictionType, networkType: NetworkType, - referenceMosaicId: MosaicId = new MosaicId(UInt64.fromUint(0).toDTO()), + referenceMosaicId: MosaicId | NamespaceId = UnresolvedMapping.toUnresolvedMosaic(UInt64.fromUint(0).toHex()), maxFee: UInt64 = new UInt64([0, 0])): MosaicGlobalRestrictionTransaction { return new MosaicGlobalRestrictionTransaction(networkType, TransactionVersion.MOSAIC_GLOBAL_RESTRICTION, @@ -93,7 +95,7 @@ export class MosaicGlobalRestrictionTransaction extends Transaction { * @param version - The transaction version * @param deadline - The deadline to include the transaction. * @param maxFee - (Optional) Max fee defined by the sender - * @param mosaicId - The mosaic id ex: new MosaicId([481110499, 231112638]). + * @param mosaicId - The unresolved mosaic identifier. * @param referenceMosaicId - The mosaic id providing the restriction key. * @param restrictionKey - The restriction key. * @param previousRestrictionValue - The previous restriction value. @@ -111,11 +113,11 @@ export class MosaicGlobalRestrictionTransaction extends Transaction { /** * The mosaic id. */ - public readonly mosaicId: MosaicId, + public readonly mosaicId: MosaicId | NamespaceId, /** * The refrence mosaic id. */ - public readonly referenceMosaicId: MosaicId, + public readonly referenceMosaicId: MosaicId | NamespaceId, /** * The restriction key. */ @@ -157,14 +159,14 @@ export class MosaicGlobalRestrictionTransaction extends Transaction { const transaction = MosaicGlobalRestrictionTransaction.create( isEmbedded ? Deadline.create() : Deadline.createFromDTO( (builder as MosaicGlobalRestrictionTransactionBuilder).getDeadline().timestamp), - new MosaicId(builder.getMosaicId().unresolvedMosaicId), + UnresolvedMapping.toUnresolvedMosaic(new UInt64(builder.getMosaicId().unresolvedMosaicId).toHex()), new UInt64(builder.getRestrictionKey()), new UInt64(builder.getPreviousRestrictionValue()), builder.getPreviousRestrictionType().valueOf(), new UInt64(builder.getNewRestrictionValue()), builder.getNewRestrictionType().valueOf(), networkType, - new MosaicId(builder.getReferenceMosaicId().unresolvedMosaicId), + UnresolvedMapping.toUnresolvedMosaic(new UInt64(builder.getReferenceMosaicId().unresolvedMosaicId).toHex()), isEmbedded ? new UInt64([0, 0]) : new UInt64((builder as MosaicGlobalRestrictionTransactionBuilder).fee.amount), ); return isEmbedded ? diff --git a/src/model/transaction/MosaicMetadataTransaction.ts b/src/model/transaction/MosaicMetadataTransaction.ts index 020888a222..967df9c332 100644 --- a/src/model/transaction/MosaicMetadataTransaction.ts +++ b/src/model/transaction/MosaicMetadataTransaction.ts @@ -15,6 +15,7 @@ */ import { Convert } from '../../core/format'; +import { UnresolvedMapping } from '../../core/utils/UnresolvedMapping'; import { AmountDto } from '../../infrastructure/catbuffer/AmountDto'; import { EmbeddedMosaicMetadataTransactionBuilder } from '../../infrastructure/catbuffer/EmbeddedMosaicMetadataTransactionBuilder'; import { KeyDto } from '../../infrastructure/catbuffer/KeyDto'; @@ -25,6 +26,7 @@ import { UnresolvedMosaicIdDto } from '../../infrastructure/catbuffer/Unresolved import { PublicAccount } from '../account/PublicAccount'; import { NetworkType } from '../blockchain/NetworkType'; import { MosaicId } from '../mosaic/MosaicId'; +import { NamespaceId } from '../namespace/NamespaceId'; import { UInt64 } from '../UInt64'; import { Deadline } from './Deadline'; import { InnerTransaction } from './InnerTransaction'; @@ -42,7 +44,7 @@ export class MosaicMetadataTransaction extends Transaction { * @param deadline - transaction deadline * @param targetPublicKey - Public key of the target account. * @param scopedMetadataKey - Metadata key scoped to source, target and type. - * @param targetMosaicId - Target mosaic identifier. + * @param targetMosaicId - Target unresolved mosaic identifier. * @param valueSizeDelta - Change in value size in bytes. * @param value - String value with UTF-8 encoding * Difference between the previous value and new value. @@ -54,7 +56,7 @@ export class MosaicMetadataTransaction extends Transaction { public static create(deadline: Deadline, targetPublicKey: string, scopedMetadataKey: UInt64, - targetMosaicId: MosaicId, + targetMosaicId: MosaicId | NamespaceId, valueSizeDelta: number, value: string, networkType: NetworkType, @@ -99,7 +101,7 @@ export class MosaicMetadataTransaction extends Transaction { /** * Target mosaic identifier. */ - public readonly targetMosaicId: MosaicId, + public readonly targetMosaicId: MosaicId | NamespaceId, /** * Change in value size in bytes. */ @@ -134,7 +136,7 @@ export class MosaicMetadataTransaction extends Transaction { isEmbedded ? Deadline.create() : Deadline.createFromDTO((builder as MosaicMetadataTransactionBuilder).getDeadline().timestamp), Convert.uint8ToHex(builder.getTargetPublicKey().key), new UInt64(builder.getScopedMetadataKey()), - new MosaicId(builder.getTargetMosaicId().unresolvedMosaicId), + UnresolvedMapping.toUnresolvedMosaic(new UInt64(builder.getTargetMosaicId().unresolvedMosaicId).toHex()), builder.getValueSizeDelta(), Convert.uint8ToUtf8(builder.getValue()), networkType, diff --git a/src/model/transaction/TransactionVersion.ts b/src/model/transaction/TransactionVersion.ts index 5a09c39a20..ab60fef656 100644 --- a/src/model/transaction/TransactionVersion.ts +++ b/src/model/transaction/TransactionVersion.ts @@ -1,5 +1,3 @@ -import { NetworkType } from "../blockchain/NetworkType"; - /* * Copyright 2019 NEM * diff --git a/src/model/transaction/TransferTransaction.ts b/src/model/transaction/TransferTransaction.ts index dba889c578..160212b3f1 100644 --- a/src/model/transaction/TransferTransaction.ts +++ b/src/model/transaction/TransferTransaction.ts @@ -15,7 +15,7 @@ */ import { Convert, Convert as convert } from '../../core/format'; -import { RawAddress } from '../../core/format/RawAddress'; +import { UnresolvedMapping } from '../../core/utils/UnresolvedMapping'; import { AmountDto } from '../../infrastructure/catbuffer/AmountDto'; import { EmbeddedTransferTransactionBuilder } from '../../infrastructure/catbuffer/EmbeddedTransferTransactionBuilder'; import { GeneratorUtils } from '../../infrastructure/catbuffer/GeneratorUtils'; @@ -34,7 +34,6 @@ import { Message } from '../message/Message'; import { MessageType } from '../message/MessageType'; import { PlainMessage } from '../message/PlainMessage'; import { Mosaic } from '../mosaic/Mosaic'; -import { MosaicId } from '../mosaic/MosaicId'; import { NamespaceId } from '../namespace/NamespaceId'; import { UInt64 } from '../UInt64'; import { Deadline } from './Deadline'; @@ -128,10 +127,11 @@ export class TransferTransaction extends Transaction { const transaction = TransferTransaction.create( isEmbedded ? Deadline.create() : Deadline.createFromDTO( (builder as TransferTransactionBuilder).getDeadline().timestamp), - Address.createFromEncoded(Convert.uint8ToHex(builder.getRecipientAddress().unresolvedAddress)), + UnresolvedMapping.toUnresolvedAddress(Convert.uint8ToHex(builder.getRecipientAddress().unresolvedAddress)), builder.getMosaics().map((mosaic) => { + const id = new UInt64(mosaic.mosaicId.unresolvedMosaicId).toHex(); return new Mosaic( - new MosaicId(mosaic.mosaicId.unresolvedMosaicId), + UnresolvedMapping.toUnresolvedMosaic(id), new UInt64(mosaic.amount.amount)); }), messageType === MessageType.PlainMessage ? @@ -201,22 +201,6 @@ export class TransferTransaction extends Transaction { payloadBuffer : GeneratorUtils.concatTypedArrays(typeBuffer, payloadBuffer); } - /** - * Return unresolved address bytes of the recipient - * @internal - * @returns {string} - */ - public getRecipientBytes(): Uint8Array { - const recipient = this.recipientToString(); - if (/^[0-9a-fA-F]{16}$/.test(recipient)) { - // received hexadecimal notation of namespaceId (alias) - return RawAddress.aliasToRecipient(convert.hexToUint8(recipient)); - } else { - // received recipient address - return RawAddress.stringToAddress(recipient); - } - } - /** * @override Transaction.size() * @description get the byte size of a TransferTransaction @@ -254,7 +238,7 @@ export class TransferTransaction extends Transaction { TransactionType.TRANSFER.valueOf(), new AmountDto(this.maxFee.toDTO()), new TimestampDto(this.deadline.toDTO()), - new UnresolvedAddressDto(this.getRecipientBytes()), + new UnresolvedAddressDto(UnresolvedMapping.toUnresolvedAddressBytes(this.recipientAddress)), this.getMessageBuffer(), this.sortMosaics().map((mosaic) => { return new UnresolvedMosaicBuilder(new UnresolvedMosaicIdDto(mosaic.id.id.toDTO()), @@ -273,7 +257,7 @@ export class TransferTransaction extends Transaction { new KeyDto(Convert.hexToUint8(this.signer!.publicKey)), this.versionToDTO(), TransactionType.TRANSFER.valueOf(), - new UnresolvedAddressDto(RawAddress.stringToAddress(this.recipientToString())), + new UnresolvedAddressDto(UnresolvedMapping.toUnresolvedAddressBytes(this.recipientAddress)), this.getMessageBuffer(), this.sortMosaics().map((mosaic) => { return new UnresolvedMosaicBuilder(new UnresolvedMosaicIdDto(mosaic.id.id.toDTO()), diff --git a/test/core/utils/TransactionMapping.spec.ts b/test/core/utils/TransactionMapping.spec.ts index e406f57801..aea687e2f5 100644 --- a/test/core/utils/TransactionMapping.spec.ts +++ b/test/core/utils/TransactionMapping.spec.ts @@ -20,8 +20,6 @@ import { sha3_256 } from 'js-sha3'; import { Convert } from '../../../src/core/format'; import { TransactionMapping } from '../../../src/core/utils/TransactionMapping'; import { Account } from '../../../src/model/account/Account'; -import { AccountRestrictionModificationAction } from '../../../src/model/restriction/AccountRestrictionModificationAction'; -import { AccountRestrictionType } from '../../../src/model/restriction/AccountRestrictionType'; import { Address } from '../../../src/model/account/Address'; import { PublicAccount } from '../../../src/model/account/PublicAccount'; import { NetworkType } from '../../../src/model/blockchain/NetworkType'; @@ -31,12 +29,14 @@ import { PlainMessage } from '../../../src/model/message/PlainMessage'; import { MosaicFlags } from '../../../src/model/mosaic/MosaicFlags'; import { MosaicId } from '../../../src/model/mosaic/MosaicId'; import { MosaicNonce } from '../../../src/model/mosaic/MosaicNonce'; -import { MosaicRestrictionType } from '../../../src/model/restriction/MosaicRestrictionType'; import { MosaicSupplyChangeAction } from '../../../src/model/mosaic/MosaicSupplyChangeAction'; import { NetworkCurrencyMosaic } from '../../../src/model/mosaic/NetworkCurrencyMosaic'; import { AliasAction } from '../../../src/model/namespace/AliasAction'; import { NamespaceId } from '../../../src/model/namespace/NamespaceId'; import { NamespaceRegistrationType } from '../../../src/model/namespace/NamespaceRegistrationType'; +import { AccountRestrictionModificationAction } from '../../../src/model/restriction/AccountRestrictionModificationAction'; +import { AccountRestrictionType } from '../../../src/model/restriction/AccountRestrictionType'; +import { MosaicRestrictionType } from '../../../src/model/restriction/MosaicRestrictionType'; import { AccountAddressRestrictionTransaction } from '../../../src/model/transaction/AccountAddressRestrictionTransaction'; import { AccountLinkTransaction } from '../../../src/model/transaction/AccountLinkTransaction'; import { AccountMetadataTransaction } from '../../../src/model/transaction/AccountMetadataTransaction'; @@ -574,7 +574,31 @@ describe('TransactionMapping - createFromPayload', () => { expect(transaction.type).to.be.equal(TransactionType.MOSAIC_ADDRESS_RESTRICTION); expect(transaction.mosaicId.toHex()).to.be.equal(new MosaicId(UInt64.fromUint(1).toDTO()).toHex()); expect(transaction.restrictionKey.toHex()).to.be.equal(UInt64.fromUint(4444).toHex()); - expect(transaction.targetAddress.plain()).to.be.equal(account.address.plain()); + expect(transaction.targetAddressToString()).to.be.equal(account.address.plain()); + expect(transaction.previousRestrictionValue.toHex()).to.be.equal(UInt64.fromUint(0).toHex()); + expect(transaction.newRestrictionValue.toHex()).to.be.equal(UInt64.fromUint(0).toHex()); + }); + + it('should create MosaicAddressRestrictionTransaction - MosaicAlias', () => { + const mosaicAddressRestrictionTransaction = MosaicAddressRestrictionTransaction.create( + Deadline.create(), + new NamespaceId('test'), + UInt64.fromUint(4444), + account.address, + UInt64.fromUint(0), + NetworkType.MIJIN_TEST, + UInt64.fromUint(0), + ); + + const signedTx = mosaicAddressRestrictionTransaction.signWith(account, generationHash); + + const transaction = TransactionMapping.createFromPayload(signedTx.payload) as MosaicAddressRestrictionTransaction; + + expect(transaction.type).to.be.equal(TransactionType.MOSAIC_ADDRESS_RESTRICTION); + expect(transaction.mosaicId.toHex()).to.be.equal(new NamespaceId('test').toHex()); + expect(transaction.mosaicId instanceof NamespaceId).to.be.true; + expect(transaction.restrictionKey.toHex()).to.be.equal(UInt64.fromUint(4444).toHex()); + expect(transaction.targetAddressToString()).to.be.equal(account.address.plain()); expect(transaction.previousRestrictionValue.toHex()).to.be.equal(UInt64.fromUint(0).toHex()); expect(transaction.newRestrictionValue.toHex()).to.be.equal(UInt64.fromUint(0).toHex()); }); @@ -1056,7 +1080,7 @@ describe('TransactionMapping - createFromDTO (Transaction.toJSON() feed)', () => expect(transaction.type).to.be.equal(TransactionType.MOSAIC_ADDRESS_RESTRICTION); expect(transaction.mosaicId.id.compact()).to.be.equal(1); expect(transaction.restrictionKey.toHex()).to.be.equal(UInt64.fromUint(4444).toHex()); - expect(transaction.targetAddress.plain()).to.be.equal(account.address.plain()); + expect(transaction.targetAddressToString()).to.be.equal(account.address.plain()); expect(transaction.previousRestrictionValue.toHex()).to.be.equal(UInt64.fromUint(0).toHex()); expect(transaction.newRestrictionValue.toHex()).to.be.equal(UInt64.fromUint(0).toHex()); }); diff --git a/test/core/utils/UnresolvedMapping.spec.ts b/test/core/utils/UnresolvedMapping.spec.ts new file mode 100644 index 0000000000..6544c642e5 --- /dev/null +++ b/test/core/utils/UnresolvedMapping.spec.ts @@ -0,0 +1,89 @@ +/* + * 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 { Convert, RawAddress } from '../../../src/core/format'; +import { UnresolvedMapping } from '../../../src/core/utils/UnresolvedMapping'; +import { Address } from '../../../src/model/account/Address'; +import { MosaicId } from '../../../src/model/mosaic/MosaicId'; +import { NamespaceId } from '../../../src/model/namespace/NamespaceId'; + +describe('UnresolvedMapping', () => { + let mosaicId: MosaicId; + let namespacId: NamespaceId; + let address: Address; + + before(() => { + mosaicId = new MosaicId('11F4B1B3AC033DB5'); + namespacId = NamespaceId.createFromEncoded('9550CA3FC9B41FC5'); + address = Address.createFromRawAddress('MCTVW23D2MN5VE4AQ4TZIDZENGNOZXPRPR72DYSX'); + }); + + describe('toUnresolvedMosaic', () => { + it('can map hex string to MosaicId', () => { + const unresolved = UnresolvedMapping.toUnresolvedMosaic(mosaicId.toHex()); + expect(unresolved instanceof MosaicId).to.be.true; + expect(unresolved instanceof NamespaceId).to.be.false; + }); + + it('can map hex string to NamespaceId', () => { + const unresolved = UnresolvedMapping.toUnresolvedMosaic(namespacId.toHex()); + expect(unresolved instanceof MosaicId).to.be.false; + expect(unresolved instanceof NamespaceId).to.be.true; + }); + + it('should throw error if id not in hex', () => { + expect(() => { + UnresolvedMapping.toUnresolvedMosaic('test'); + }).to.throw(Error, 'Input string is not in valid hexadecimal notation.'); + }); + }); + + describe('toUnresolvedAddress', () => { + it('can map hex string to Address', () => { + const unresolved = UnresolvedMapping.toUnresolvedAddress( + Convert.uint8ToHex(RawAddress.stringToAddress(address.plain()))); + expect(unresolved instanceof Address).to.be.true; + expect(unresolved instanceof NamespaceId).to.be.false; + }); + + it('can map hex string to NamespaceId', () => { + const unresolved = UnresolvedMapping.toUnresolvedMosaic(namespacId.toHex()); + expect(unresolved instanceof Address).to.be.false; + expect(unresolved instanceof NamespaceId).to.be.true; + }); + + it('should throw error if id not in hex', () => { + expect(() => { + UnresolvedMapping.toUnresolvedAddress('test'); + }).to.throw(Error, 'Input string is not in valid hexadecimal notation.'); + }); + }); + + describe('toUnresolvedAddressBytes', () => { + it('can map Address to buffer', () => { + const buffer = UnresolvedMapping.toUnresolvedAddressBytes(address); + expect(buffer instanceof Uint8Array).to.be.true; + expect(Convert.uint8ToHex(buffer)).to.be.equal(Convert.uint8ToHex(RawAddress.stringToAddress(address.plain()))); + }); + + it('can map hex string to NamespaceId', () => { + const buffer = UnresolvedMapping.toUnresolvedAddressBytes(namespacId); + expect(buffer instanceof Uint8Array).to.be.true; + expect(buffer[0]).to.be.equal(145); + expect(Convert.uint8ToHex(buffer)).to.be.equal('91C51FB4C93FCA509500000000000000000000000000000000'); + }); + }); +}); diff --git a/test/model/transaction/MosaicAddressRestrictionTransaction.spec.ts b/test/model/transaction/MosaicAddressRestrictionTransaction.spec.ts index b38520aa9f..6afcb4bdf1 100644 --- a/test/model/transaction/MosaicAddressRestrictionTransaction.spec.ts +++ b/test/model/transaction/MosaicAddressRestrictionTransaction.spec.ts @@ -18,6 +18,7 @@ import {expect} from 'chai'; import {Account} from '../../../src/model/account/Account'; import {NetworkType} from '../../../src/model/blockchain/NetworkType'; import {MosaicId} from '../../../src/model/mosaic/MosaicId'; +import { NamespaceId } from '../../../src/model/namespace/NamespaceId'; import {Deadline} from '../../../src/model/transaction/Deadline'; import {MosaicAddressRestrictionTransaction} from '../../../src/model/transaction/MosaicAddressRestrictionTransaction'; import {UInt64} from '../../../src/model/UInt64'; @@ -45,7 +46,7 @@ describe('MosaicAddressRestrictionTransaction', () => { expect(mosaicAddressRestrictionTransaction.restrictionKey.toHex()).to.be.equal(UInt64.fromUint(1).toHex()); expect(mosaicAddressRestrictionTransaction.previousRestrictionValue.toHex()).to.be.equal(UInt64.fromUint(9).toHex()); expect(mosaicAddressRestrictionTransaction.newRestrictionValue.toHex()).to.be.equal(UInt64.fromUint(8).toHex()); - expect(mosaicAddressRestrictionTransaction.targetAddress.plain()).to.be.equal(account.address.plain()); + expect(mosaicAddressRestrictionTransaction.targetAddressToString()).to.be.equal(account.address.plain()); const signedTransaction = mosaicAddressRestrictionTransaction.signWith(account, generationHash); @@ -54,6 +55,80 @@ describe('MosaicAddressRestrictionTransaction', () => { signedTransaction.payload.length, )).to.be.equal('0100000000000000010000000000000090A75B6B63D31BDA93808727940F24699AE' + 'CDDF17C568508BA09000000000000000800000000000000'); + }); + + it('should createComplete an MosaicAddressRestrictionTransaction use mosaic alias', () => { + const namespacId = NamespaceId.createFromEncoded('9550CA3FC9B41FC5'); + const mosaicAddressRestrictionTransaction = MosaicAddressRestrictionTransaction.create( + Deadline.create(), + namespacId, + UInt64.fromUint(1), + account.address, + UInt64.fromUint(8), + NetworkType.MIJIN_TEST, + UInt64.fromUint(9), + ); + expect(mosaicAddressRestrictionTransaction.mosaicId.toHex()).to.be.equal(namespacId.toHex()); + expect(mosaicAddressRestrictionTransaction.restrictionKey.toHex()).to.be.equal(UInt64.fromUint(1).toHex()); + expect(mosaicAddressRestrictionTransaction.previousRestrictionValue.toHex()).to.be.equal(UInt64.fromUint(9).toHex()); + expect(mosaicAddressRestrictionTransaction.newRestrictionValue.toHex()).to.be.equal(UInt64.fromUint(8).toHex()); + expect(mosaicAddressRestrictionTransaction.targetAddressToString()).to.be.equal(account.address.plain()); + const signedTransaction = mosaicAddressRestrictionTransaction.signWith(account, generationHash); + + expect(signedTransaction.payload.substring( + 240, + signedTransaction.payload.length, + )).to.be.equal('C51FB4C93FCA5095010000000000000090A75B6B63D31BDA93808727940F24699AE' + + 'CDDF17C568508BA09000000000000000800000000000000'); + }); + + it('should createComplete an MosaicAddressRestrictionTransaction use address alias', () => { + const mosaicId = new MosaicId(UInt64.fromUint(1).toDTO()); + const namespacId = NamespaceId.createFromEncoded('9550CA3FC9B41FC5'); + const mosaicAddressRestrictionTransaction = MosaicAddressRestrictionTransaction.create( + Deadline.create(), + mosaicId, + UInt64.fromUint(1), + namespacId, + UInt64.fromUint(8), + NetworkType.MIJIN_TEST, + UInt64.fromUint(9), + ); + expect(mosaicAddressRestrictionTransaction.mosaicId.toHex()).to.be.equal(mosaicId.toHex()); + expect(mosaicAddressRestrictionTransaction.restrictionKey.toHex()).to.be.equal(UInt64.fromUint(1).toHex()); + expect(mosaicAddressRestrictionTransaction.previousRestrictionValue.toHex()).to.be.equal(UInt64.fromUint(9).toHex()); + expect(mosaicAddressRestrictionTransaction.newRestrictionValue.toHex()).to.be.equal(UInt64.fromUint(8).toHex()); + expect(mosaicAddressRestrictionTransaction.targetAddressToString()).to.be.equal(namespacId.toHex()); + + const signedTransaction = mosaicAddressRestrictionTransaction.signWith(account, generationHash); + + expect(signedTransaction.payload.substring( + 240, + signedTransaction.payload.length, + )).to.be.equal('0100000000000000010000000000000091C51FB4C93FCA509500000000000000000' + + '00000000000000009000000000000000800000000000000'); + }); + + it('should format targetAddress payload with 8 bytes binary namespaceId - targetAddressToString', () => { + const transaction = MosaicAddressRestrictionTransaction.create( + Deadline.create(), + new MosaicId(UInt64.fromUint(1).toDTO()), + UInt64.fromUint(1), + new NamespaceId('nem.owner'), + UInt64.fromUint(8), + NetworkType.MIJIN_TEST, + UInt64.fromUint(9), + ); + + // test targetAddressToString with NamespaceId recipient + expect(transaction.targetAddressToString()).to.be.equal('D85742D268617751'); + + const signedTransaction = transaction.signWith(account, generationHash); + + expect(signedTransaction.payload.substring( + 240, + 288, + )).to.be.equal('010000000000000001000000000000009151776168D24257'); }); }); diff --git a/test/model/transaction/MosaicGlobalRestrictionTransaction.spec.ts b/test/model/transaction/MosaicGlobalRestrictionTransaction.spec.ts index 20307fde5d..991e7ef55b 100644 --- a/test/model/transaction/MosaicGlobalRestrictionTransaction.spec.ts +++ b/test/model/transaction/MosaicGlobalRestrictionTransaction.spec.ts @@ -18,6 +18,7 @@ import {expect} from 'chai'; import {Account} from '../../../src/model/account/Account'; import {NetworkType} from '../../../src/model/blockchain/NetworkType'; import {MosaicId} from '../../../src/model/mosaic/MosaicId'; +import { NamespaceId } from '../../../src/model/namespace/NamespaceId'; import { MosaicRestrictionType } from '../../../src/model/restriction/MosaicRestrictionType'; import {Deadline} from '../../../src/model/transaction/Deadline'; import {MosaicGlobalRestrictionTransaction} from '../../../src/model/transaction/MosaicGlobalRestrictionTransaction'; @@ -62,4 +63,69 @@ describe('MosaicGlobalRestrictionTransaction', () => { )).to.be.equal('010000000000000002000000000000000100000000000000090000000000000001080000000000000006'); }); + + it('should createComplete an MosaicGlobalRestrictionTransaction use mosaic alias', () => { + const namespacId = NamespaceId.createFromEncoded('9550CA3FC9B41FC5'); + console.log(namespacId.toHex()); + const referenceMosaicId = new MosaicId(UInt64.fromUint(2).toDTO()); + const mosaicGlobalRestrictionTransaction = MosaicGlobalRestrictionTransaction.create( + Deadline.create(), + namespacId, + UInt64.fromUint(1), + UInt64.fromUint(9), + MosaicRestrictionType.EQ, + UInt64.fromUint(8), + MosaicRestrictionType.GE, + NetworkType.MIJIN_TEST, + referenceMosaicId, + ); + + expect(mosaicGlobalRestrictionTransaction.mosaicId.toHex()).to.be.equal(namespacId.toHex()); + expect(mosaicGlobalRestrictionTransaction.referenceMosaicId.toHex()).to.be.equal(referenceMosaicId.toHex()); + expect(mosaicGlobalRestrictionTransaction.restrictionKey.toHex()).to.be.equal(UInt64.fromUint(1).toHex()); + expect(mosaicGlobalRestrictionTransaction.previousRestrictionValue.toHex()).to.be.equal(UInt64.fromUint(9).toHex()); + expect(mosaicGlobalRestrictionTransaction.newRestrictionValue.toHex()).to.be.equal(UInt64.fromUint(8).toHex()); + expect(mosaicGlobalRestrictionTransaction.previousRestrictionType).to.be.equal(MosaicRestrictionType.EQ); + expect(mosaicGlobalRestrictionTransaction.newRestrictionType).to.be.equal(MosaicRestrictionType.GE); + + const signedTransaction = mosaicGlobalRestrictionTransaction.signWith(account, generationHash); + + expect(signedTransaction.payload.substring( + 240, + signedTransaction.payload.length, + )).to.be.equal('C51FB4C93FCA509502000000000000000100000000000000090000000000000001080000000000000006'); + + }); + + it('should createComplete an MosaicGlobalRestrictionTransaction use mosaic alias reference', () => { + const namespacId = NamespaceId.createFromEncoded('9550CA3FC9B41FC5'); + const mosaicId = new MosaicId(UInt64.fromUint(1).toDTO()); + const mosaicGlobalRestrictionTransaction = MosaicGlobalRestrictionTransaction.create( + Deadline.create(), + mosaicId, + UInt64.fromUint(1), + UInt64.fromUint(9), + MosaicRestrictionType.EQ, + UInt64.fromUint(8), + MosaicRestrictionType.GE, + NetworkType.MIJIN_TEST, + namespacId, + ); + + expect(mosaicGlobalRestrictionTransaction.mosaicId.toHex()).to.be.equal(mosaicId.toHex()); + expect(mosaicGlobalRestrictionTransaction.referenceMosaicId.toHex()).to.be.equal(namespacId.toHex()); + expect(mosaicGlobalRestrictionTransaction.restrictionKey.toHex()).to.be.equal(UInt64.fromUint(1).toHex()); + expect(mosaicGlobalRestrictionTransaction.previousRestrictionValue.toHex()).to.be.equal(UInt64.fromUint(9).toHex()); + expect(mosaicGlobalRestrictionTransaction.newRestrictionValue.toHex()).to.be.equal(UInt64.fromUint(8).toHex()); + expect(mosaicGlobalRestrictionTransaction.previousRestrictionType).to.be.equal(MosaicRestrictionType.EQ); + expect(mosaicGlobalRestrictionTransaction.newRestrictionType).to.be.equal(MosaicRestrictionType.GE); + + const signedTransaction = mosaicGlobalRestrictionTransaction.signWith(account, generationHash); + + expect(signedTransaction.payload.substring( + 240, + signedTransaction.payload.length, + )).to.be.equal('0100000000000000C51FB4C93FCA50950100000000000000090000000000000001080000000000000006'); + + }); }); diff --git a/test/model/transaction/MosaicMetadataTransaction.spec.ts b/test/model/transaction/MosaicMetadataTransaction.spec.ts index 3da1118486..70197feb9e 100644 --- a/test/model/transaction/MosaicMetadataTransaction.spec.ts +++ b/test/model/transaction/MosaicMetadataTransaction.spec.ts @@ -19,6 +19,7 @@ import { Convert } from '../../../src/core/format/Convert'; import { Account } from '../../../src/model/account/Account'; import { NetworkType } from '../../../src/model/blockchain/NetworkType'; import { MosaicId } from '../../../src/model/mosaic/MosaicId'; +import { NamespaceId } from '../../../src/model/namespace/NamespaceId'; import { Deadline } from '../../../src/model/transaction/Deadline'; import { MosaicMetadataTransaction } from '../../../src/model/transaction/MosaicMetadataTransaction'; import { UInt64 } from '../../../src/model/UInt64'; @@ -96,6 +97,27 @@ describe('MosaicMetadataTransaction', () => { }).to.throw(Error, 'The maximum value size is 1024'); }); + it('should create and sign an MosaicMetadataTransaction object using alias', () => { + const namespacId = NamespaceId.createFromEncoded('9550CA3FC9B41FC5'); + const mosaicMetadataTransaction = MosaicMetadataTransaction.create( + Deadline.create(), + account.publicKey, + UInt64.fromUint(1000), + namespacId, + 1, + Convert.uint8ToUtf8(new Uint8Array(10)), + NetworkType.MIJIN_TEST, + ); + + const signedTransaction = mosaicMetadataTransaction.signWith(account, generationHash); + + expect(signedTransaction.payload.substring( + 240, + signedTransaction.payload.length, + )).to.be.equal('C2F93346E27CE6AD1A9F8F5E3066F8326593A406BDF357ACB041E2F9AB402EFEE80' + + '3000000000000C51FB4C93FCA509501000A0000000000000000000000'); + }); + describe('size', () => { it('should return 153 for MosaicMetadataTransaction byte size', () => { const mosaicMetadataTransaction = MosaicMetadataTransaction.create( diff --git a/test/service/MosaicRestrictionTransactionservice.spec.ts b/test/service/MosaicRestrictionTransactionservice.spec.ts index 9aa4779ba5..8a08455ef4 100644 --- a/test/service/MosaicRestrictionTransactionservice.spec.ts +++ b/test/service/MosaicRestrictionTransactionservice.spec.ts @@ -115,7 +115,7 @@ describe('MosaicRestrictionTransactionService', () => { .subscribe((transaction: MosaicAddressRestrictionTransaction) => { expect(transaction.type).to.be.equal(TransactionType.MOSAIC_ADDRESS_RESTRICTION); expect(transaction.restrictionKey.toHex()).to.be.equal(key.toHex()); - expect(transaction.targetAddress.plain()).to.be.equal(account.address.plain()); + expect(transaction.targetAddressToString()).to.be.equal(account.address.plain()); expect(transaction.previousRestrictionValue.toString()).to.be.equal(addressRestrictionValue); done(); }); From 830c753f70ce6f647ddaa5f623d321e587c8154e Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Sun, 13 Oct 2019 20:31:24 +0100 Subject: [PATCH 2/2] Added integration test for transfer using address alias --- e2e/infrastructure/TransactionHttp.spec.ts | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/e2e/infrastructure/TransactionHttp.spec.ts b/e2e/infrastructure/TransactionHttp.spec.ts index e2607747e6..7712b91a63 100644 --- a/e2e/infrastructure/TransactionHttp.spec.ts +++ b/e2e/infrastructure/TransactionHttp.spec.ts @@ -1059,6 +1059,40 @@ describe('TransactionHttp', () => { transactionHttp.announce(signedTransaction); }); }); + + describe('Transfer Transaction using address alias', () => { + let listener: Listener; + before (() => { + listener = new Listener(config.apiUrl); + return listener.open(); + }); + after(() => { + return listener.close(); + }); + + it('Announce TransferTransaction', (done) => { + const transferTransaction = TransferTransaction.create( + Deadline.create(), + namespaceId, + [NetworkCurrencyMosaic.createAbsolute(1)], + PlainMessage.create('test-message'), + NetworkType.MIJIN_TEST, + ); + const signedTransaction = transferTransaction.signWith(account, generationHash); + + listener.confirmed(account.address).subscribe((transaction: TransferTransaction) => { + expect(transaction.recipientAddress, 'AecipientAddress').not.to.be.undefined; + done(); + }); + listener.status(account.address).subscribe((error) => { + console.log('Error:', error); + assert(false); + done(); + }); + transactionHttp.announce(signedTransaction); + }); + }); + describe('AddressAliasTransaction', () => { let listener: Listener; before (() => {