Skip to content

Commit a1bc66b

Browse files
committed
JAV-64 [Github #295] Unresolved Mosaic and Address in transactions
1 parent 61b3837 commit a1bc66b

15 files changed

+923
-87
lines changed

e2e/infrastructure/UnresolvedMapping.spec.ts

Lines changed: 494 additions & 0 deletions
Large diffs are not rendered by default.

e2e/service/MosaicRestrictionTransactionService.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ describe('MosaicRestrictionTransactionService', () => {
196196
expect(transaction.type).to.be.equal(TransactionType.MOSAIC_ADDRESS_RESTRICTION);
197197
expect(transaction.previousRestrictionValue.toString()).to.be.equal('2');
198198
expect(transaction.newRestrictionValue.toString()).to.be.equal('3');
199-
expect(transaction.targetAddress.plain()).to.be.equal(targetAccount.address.plain());
199+
expect(transaction.targetAddressToString()).to.be.equal(targetAccount.address.plain());
200200
expect(transaction.restrictionKey.toHex()).to.be.equal(key.toHex());
201201
done();
202202
});
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright 2019 NEM
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
import { Address } from '../../model/account/Address';
17+
import { MosaicId } from '../../model/mosaic/MosaicId';
18+
import { NamespaceId } from '../../model/namespace/NamespaceId';
19+
import { Convert } from '../format/Convert';
20+
import { RawAddress } from '../format/RawAddress';
21+
22+
/**
23+
* @internal
24+
*/
25+
export class UnresolvedMapping {
26+
27+
/**
28+
* @internal
29+
* Map unresolved mosaic string to MosaicId or NamespaceId
30+
* @param {string} mosaicId The unresolvedMosaic id in hex.
31+
* @returns {MosaicId | NamespaceId}
32+
*/
33+
public static toUnresolvedMosaic(mosaicId: string): MosaicId | NamespaceId {
34+
if (!Convert.isHexString(mosaicId)) {
35+
throw new Error('Input string is not in valid hexadecimal notation.');
36+
}
37+
const bytes = Convert.hexToUint8(mosaicId);
38+
const byte0 = bytes[0];
39+
40+
// if most significant bit of byte 0 is set, then we have a namespaceId
41+
if ((byte0 & 128) === 128) {
42+
return NamespaceId.createFromEncoded(mosaicId);
43+
}
44+
// most significant bit of byte 0 is not set => mosaicId
45+
return new MosaicId(mosaicId);
46+
}
47+
48+
/**
49+
* Map unresolved address string to Address or NamespaceId
50+
* @param {string} address The unresolved address in hex
51+
* @returns {Address | NamespaceId}
52+
*/
53+
public static toUnresolvedAddress(address: string): Address | NamespaceId {
54+
if (!Convert.isHexString(address)) {
55+
throw new Error('Input string is not in valid hexadecimal notation.');
56+
}
57+
// If bit 0 of byte 0 is not set (like in 0x90), then it is a regular address.
58+
// Else (e.g. 0x91) it represents a namespace id which starts at byte 1.
59+
const bit0 = Convert.hexToUint8(address.substr(1, 2))[0];
60+
if ((bit0 & 16) === 16) {
61+
// namespaceId encoded hexadecimal notation provided
62+
// only 8 bytes are relevant to resolve the NamespaceId
63+
const relevantPart = address.substr(2, 16);
64+
return NamespaceId.createFromEncoded(relevantPart);
65+
}
66+
67+
// read address from encoded hexadecimal notation
68+
return Address.createFromEncoded(address);
69+
}
70+
71+
/**
72+
* Return unresolved address bytes of the unresolved address
73+
* @internal
74+
* @param {Address | NamespaceId} unresolvedAddress The unresolved address
75+
* @return {Uint8Array}
76+
*/
77+
public static toUnresolvedAddressBytes(unresolvedAddress: Address | NamespaceId): Uint8Array {
78+
if (unresolvedAddress instanceof NamespaceId) {
79+
// received hexadecimal notation of namespaceId (alias)
80+
return RawAddress.aliasToRecipient(Convert.hexToUint8((unresolvedAddress as NamespaceId).toHex()));
81+
} else {
82+
// received recipient address
83+
return RawAddress.stringToAddress((unresolvedAddress as Address).plain());
84+
}
85+
}
86+
}

src/infrastructure/transaction/CreateTransactionFromDTO.ts

Lines changed: 10 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* limitations under the License.
1515
*/
1616
import {Convert as convert} from '../../core/format';
17+
import { UnresolvedMapping } from '../../core/utils/UnresolvedMapping';
1718
import {Address} from '../../model/account/Address';
1819
import {PublicAccount} from '../../model/account/PublicAccount';
1920
import {NetworkType} from '../../model/blockchain/NetworkType';
@@ -345,8 +346,8 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr
345346
extractTransactionVersion(transactionDTO.version),
346347
Deadline.createFromDTO(transactionDTO.deadline),
347348
UInt64.fromNumericString(transactionDTO.maxFee || '0'),
348-
new MosaicId(transactionDTO.mosaicId),
349-
new MosaicId(transactionDTO.referenceMosaicId),
349+
UnresolvedMapping.toUnresolvedMosaic(transactionDTO.mosaicId),
350+
UnresolvedMapping.toUnresolvedMosaic(transactionDTO.referenceMosaicId),
350351
UInt64.fromHex(transactionDTO.restrictionKey),
351352
UInt64.fromNumericString(transactionDTO.previousRestrictionValue),
352353
transactionDTO.previousRestrictionType,
@@ -364,11 +365,10 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr
364365
extractTransactionVersion(transactionDTO.version),
365366
Deadline.createFromDTO(transactionDTO.deadline),
366367
UInt64.fromNumericString(transactionDTO.maxFee || '0'),
367-
new MosaicId(transactionDTO.mosaicId),
368+
UnresolvedMapping.toUnresolvedMosaic(transactionDTO.mosaicId),
368369
UInt64.fromHex(transactionDTO.restrictionKey),
369-
typeof targetAddress === 'object' && targetAddress.hasOwnProperty('address') ?
370-
Address.createFromRawAddress(targetAddress.address) : Address.createFromEncoded(targetAddress),
371-
UInt64.fromNumericString(transactionDTO.previousRestrictionValue),
370+
extractRecipient(transactionDTO.targetAddress),
371+
UInt64.fromNumericString(transactionDTO.previousRestrictionValue),
372372
UInt64.fromNumericString(transactionDTO.newRestrictionValue),
373373
transactionDTO.signature,
374374
transactionDTO.signerPublicKey ? PublicAccount.createFromPublicKey(transactionDTO.signerPublicKey,
@@ -398,7 +398,7 @@ const CreateStandaloneTransactionFromDTO = (transactionDTO, transactionInfo): Tr
398398
UInt64.fromNumericString(transactionDTO.maxFee || '0'),
399399
transactionDTO.targetPublicKey,
400400
UInt64.fromHex(transactionDTO.scopedMetadataKey),
401-
new MosaicId(transactionDTO.targetMosaicId),
401+
UnresolvedMapping.toUnresolvedMosaic(transactionDTO.targetMosaicId),
402402
transactionDTO.valueSizeDelta,
403403
convert.decodeHex(transactionDTO.value),
404404
transactionDTO.signature,
@@ -455,18 +455,7 @@ export const extractTransactionVersion = (version: number): number => {
455455
*/
456456
export const extractRecipient = (recipientAddress: any): Address | NamespaceId => {
457457
if (typeof recipientAddress === 'string') {
458-
// If bit 0 of byte 0 is not set (like in 0x90), then it is a regular address.
459-
// Else (e.g. 0x91) it represents a namespace id which starts at byte 1.
460-
const bit0 = convert.hexToUint8(recipientAddress.substr(1, 2))[0];
461-
if ((bit0 & 16) === 16) {
462-
// namespaceId encoded hexadecimal notation provided
463-
// only 8 bytes are relevant to resolve the NamespaceId
464-
const relevantPart = recipientAddress.substr(2, 16);
465-
return NamespaceId.createFromEncoded(relevantPart);
466-
}
467-
468-
// read address from encoded hexadecimal notation
469-
return Address.createFromEncoded(recipientAddress);
458+
return UnresolvedMapping.toUnresolvedAddress(recipientAddress);
470459
} else if (typeof recipientAddress === 'object') { // Is JSON object
471460
if (recipientAddress.hasOwnProperty('address')) {
472461
return Address.createFromRawAddress(recipientAddress.address);
@@ -487,25 +476,12 @@ export const extractRecipient = (recipientAddress: any): Address | NamespaceId
487476
* @return {Mosaic[]}
488477
*/
489478
export const extractMosaics = (mosaics: any): Mosaic[] => {
490-
491479
if (mosaics === undefined) {
492480
return [];
493481
}
494-
495482
return mosaics.map((mosaicDTO) => {
496-
497-
// convert ID to UInt8 bytes array and get first byte (most significant byte)
498-
const bytes = convert.hexToUint8(mosaicDTO.id);
499-
const byte0 = bytes[0];
500-
501-
// if most significant bit of byte 0 is set, then we have a namespaceId
502-
if ((byte0 & 128) === 128) {
503-
const namespaceId = NamespaceId.createFromEncoded(mosaicDTO.id);
504-
return new Mosaic(namespaceId, UInt64.fromNumericString(mosaicDTO.amount));
505-
}
506-
507-
// most significant bit of byte 0 is not set => mosaicId
508-
return new Mosaic(new MosaicId(mosaicDTO.id), UInt64.fromNumericString(mosaicDTO.amount));
483+
const id = UnresolvedMapping.toUnresolvedMosaic(mosaicDTO.id);
484+
return new Mosaic(id, UInt64.fromNumericString(mosaicDTO.amount));
509485
});
510486
};
511487

src/model/transaction/MosaicAddressRestrictionTransaction.ts

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
import { Convert, RawAddress } from '../../core/format';
18+
import { UnresolvedMapping } from '../../core/utils/UnresolvedMapping';
1819
import { AmountDto } from '../../infrastructure/catbuffer/AmountDto';
1920
import {
2021
EmbeddedMosaicAddressRestrictionTransactionBuilder,
@@ -29,6 +30,7 @@ import { Address } from '../account/Address';
2930
import { PublicAccount } from '../account/PublicAccount';
3031
import { NetworkType } from '../blockchain/NetworkType';
3132
import { MosaicId } from '../mosaic/MosaicId';
33+
import { NamespaceId } from '../namespace/NamespaceId';
3234
import { UInt64 } from '../UInt64';
3335
import { Deadline } from './Deadline';
3436
import { InnerTransaction } from './InnerTransaction';
@@ -51,7 +53,7 @@ export class MosaicAddressRestrictionTransaction extends Transaction {
5153
* **MosaicAddressRestrictionTransaction can only be announced in with Aggregate Transaction
5254
*
5355
* @param deadline - The deadline to include the transaction.
54-
* @param mosaicId - The mosaic id ex: new MosaicId([481110499, 231112638]).
56+
* @param mosaicId - The unresolved mosaic identifier.
5557
* @param restrictionKey - The restriction key.
5658
* @param targetAddress - The affected unresolved address.
5759
* @param newRestrictionValue - The new restriction value.
@@ -61,9 +63,9 @@ export class MosaicAddressRestrictionTransaction extends Transaction {
6163
* @returns {MosaicAddressRestrictionTransaction}
6264
*/
6365
public static create(deadline: Deadline,
64-
mosaicId: MosaicId,
66+
mosaicId: MosaicId | NamespaceId,
6567
restrictionKey: UInt64,
66-
targetAddress: Address,
68+
targetAddress: Address | NamespaceId,
6769
newRestrictionValue: UInt64,
6870
networkType: NetworkType,
6971
previousRestrictionValue: UInt64 = UInt64.fromHex('FFFFFFFFFFFFFFFF'),
@@ -102,15 +104,15 @@ export class MosaicAddressRestrictionTransaction extends Transaction {
102104
/**
103105
* The mosaic id.
104106
*/
105-
public readonly mosaicId: MosaicId,
107+
public readonly mosaicId: MosaicId | NamespaceId,
106108
/**
107109
* The restriction key.
108110
*/
109111
public readonly restrictionKey: UInt64,
110112
/**
111113
* The affected unresolved address.
112114
*/
113-
public readonly targetAddress: Address,
115+
public readonly targetAddress: Address | NamespaceId,
114116
/**
115117
* The previous restriction value.
116118
*/
@@ -140,9 +142,9 @@ export class MosaicAddressRestrictionTransaction extends Transaction {
140142
const transaction = MosaicAddressRestrictionTransaction.create(
141143
isEmbedded ? Deadline.create() : Deadline.createFromDTO(
142144
(builder as MosaicAddressRestrictionTransactionBuilder).getDeadline().timestamp),
143-
new MosaicId(builder.getMosaicId().unresolvedMosaicId),
145+
UnresolvedMapping.toUnresolvedMosaic(new UInt64(builder.getMosaicId().unresolvedMosaicId).toHex()),
144146
new UInt64(builder.getRestrictionKey()),
145-
Address.createFromEncoded(Convert.uint8ToHex(builder.getTargetAddress().unresolvedAddress)),
147+
UnresolvedMapping.toUnresolvedAddress(Convert.uint8ToHex(builder.getTargetAddress().unresolvedAddress)),
146148
new UInt64(builder.getNewRestrictionValue()),
147149
networkType,
148150
new UInt64(builder.getPreviousRestrictionValue()),
@@ -173,6 +175,22 @@ export class MosaicAddressRestrictionTransaction extends Transaction {
173175
byteTargetAddress + bytePreviousRestrictionValue + byteNewRestrictionValue;
174176
}
175177

178+
/**
179+
* Return the string notation for the set recipient
180+
* @internal
181+
* @returns {string}
182+
*/
183+
public targetAddressToString(): string {
184+
185+
if (this.targetAddress instanceof NamespaceId) {
186+
// namespaceId recipient, return hexadecimal notation
187+
return (this.targetAddress as NamespaceId).toHex();
188+
}
189+
190+
// address recipient
191+
return (this.targetAddress as Address).plain();
192+
}
193+
176194
/**
177195
* @internal
178196
* @returns {Uint8Array}
@@ -190,7 +208,7 @@ export class MosaicAddressRestrictionTransaction extends Transaction {
190208
new TimestampDto(this.deadline.toDTO()),
191209
new UnresolvedMosaicIdDto(this.mosaicId.id.toDTO()),
192210
this.restrictionKey.toDTO(),
193-
new UnresolvedAddressDto(RawAddress.stringToAddress(this.targetAddress.plain())),
211+
new UnresolvedAddressDto(UnresolvedMapping.toUnresolvedAddressBytes(this.targetAddress)),
194212
this.previousRestrictionValue.toDTO(),
195213
this.newRestrictionValue.toDTO(),
196214
);
@@ -208,7 +226,7 @@ export class MosaicAddressRestrictionTransaction extends Transaction {
208226
TransactionType.MOSAIC_ADDRESS_RESTRICTION.valueOf(),
209227
new UnresolvedMosaicIdDto(this.mosaicId.id.toDTO()),
210228
this.restrictionKey.toDTO(),
211-
new UnresolvedAddressDto(RawAddress.stringToAddress(this.targetAddress.plain())),
229+
new UnresolvedAddressDto(UnresolvedMapping.toUnresolvedAddressBytes(this.targetAddress)),
212230
this.previousRestrictionValue.toDTO(),
213231
this.newRestrictionValue.toDTO(),
214232
);

src/model/transaction/MosaicGlobalRestrictionTransaction.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
import { Convert } from '../../core/format';
18+
import { UnresolvedMapping } from '../../core/utils/UnresolvedMapping';
1819
import { AmountDto } from '../../infrastructure/catbuffer/AmountDto';
1920
import {
2021
EmbeddedMosaicGlobalRestrictionTransactionBuilder,
@@ -27,6 +28,7 @@ import { UnresolvedMosaicIdDto } from '../../infrastructure/catbuffer/Unresolved
2728
import { PublicAccount } from '../account/PublicAccount';
2829
import { NetworkType } from '../blockchain/NetworkType';
2930
import { MosaicId } from '../mosaic/MosaicId';
31+
import { NamespaceId } from '../namespace/NamespaceId';
3032
import { MosaicRestrictionType } from '../restriction/MosaicRestrictionType';
3133
import { UInt64 } from '../UInt64';
3234
import { Deadline } from './Deadline';
@@ -60,19 +62,19 @@ export class MosaicGlobalRestrictionTransaction extends Transaction {
6062
* @param newRestrictionValue - The new restriction value.
6163
* @param newRestrictionType - The new restriction tpye.
6264
* @param networkType - The network type.
63-
* @param referenceMosaicId - (Optional) The mosaic id providing the restriction key.
65+
* @param referenceMosaicId - (Optional) The unresolved mosaic identifier providing the restriction key.
6466
* @param maxFee - (Optional) Max fee defined by the sender
6567
* @returns {MosaicGlobalRestrictionTransaction}
6668
*/
6769
public static create(deadline: Deadline,
68-
mosaicId: MosaicId,
70+
mosaicId: MosaicId | NamespaceId,
6971
restrictionKey: UInt64,
7072
previousRestrictionValue: UInt64,
7173
previousRestrictionType: MosaicRestrictionType,
7274
newRestrictionValue: UInt64,
7375
newRestrictionType: MosaicRestrictionType,
7476
networkType: NetworkType,
75-
referenceMosaicId: MosaicId = new MosaicId(UInt64.fromUint(0).toDTO()),
77+
referenceMosaicId: MosaicId | NamespaceId = UnresolvedMapping.toUnresolvedMosaic(UInt64.fromUint(0).toHex()),
7678
maxFee: UInt64 = new UInt64([0, 0])): MosaicGlobalRestrictionTransaction {
7779
return new MosaicGlobalRestrictionTransaction(networkType,
7880
TransactionVersion.MOSAIC_GLOBAL_RESTRICTION,
@@ -93,7 +95,7 @@ export class MosaicGlobalRestrictionTransaction extends Transaction {
9395
* @param version - The transaction version
9496
* @param deadline - The deadline to include the transaction.
9597
* @param maxFee - (Optional) Max fee defined by the sender
96-
* @param mosaicId - The mosaic id ex: new MosaicId([481110499, 231112638]).
98+
* @param mosaicId - The unresolved mosaic identifier.
9799
* @param referenceMosaicId - The mosaic id providing the restriction key.
98100
* @param restrictionKey - The restriction key.
99101
* @param previousRestrictionValue - The previous restriction value.
@@ -111,11 +113,11 @@ export class MosaicGlobalRestrictionTransaction extends Transaction {
111113
/**
112114
* The mosaic id.
113115
*/
114-
public readonly mosaicId: MosaicId,
116+
public readonly mosaicId: MosaicId | NamespaceId,
115117
/**
116118
* The refrence mosaic id.
117119
*/
118-
public readonly referenceMosaicId: MosaicId,
120+
public readonly referenceMosaicId: MosaicId | NamespaceId,
119121
/**
120122
* The restriction key.
121123
*/
@@ -157,14 +159,14 @@ export class MosaicGlobalRestrictionTransaction extends Transaction {
157159
const transaction = MosaicGlobalRestrictionTransaction.create(
158160
isEmbedded ? Deadline.create() : Deadline.createFromDTO(
159161
(builder as MosaicGlobalRestrictionTransactionBuilder).getDeadline().timestamp),
160-
new MosaicId(builder.getMosaicId().unresolvedMosaicId),
162+
UnresolvedMapping.toUnresolvedMosaic(new UInt64(builder.getMosaicId().unresolvedMosaicId).toHex()),
161163
new UInt64(builder.getRestrictionKey()),
162164
new UInt64(builder.getPreviousRestrictionValue()),
163165
builder.getPreviousRestrictionType().valueOf(),
164166
new UInt64(builder.getNewRestrictionValue()),
165167
builder.getNewRestrictionType().valueOf(),
166168
networkType,
167-
new MosaicId(builder.getReferenceMosaicId().unresolvedMosaicId),
169+
UnresolvedMapping.toUnresolvedMosaic(new UInt64(builder.getReferenceMosaicId().unresolvedMosaicId).toHex()),
168170
isEmbedded ? new UInt64([0, 0]) : new UInt64((builder as MosaicGlobalRestrictionTransactionBuilder).fee.amount),
169171
);
170172
return isEmbedded ?

0 commit comments

Comments
 (0)