Skip to content

Commit f90260c

Browse files
committed
fix: allowing plain base32 addresses in rest payloads
1 parent 2630c1f commit f90260c

18 files changed

+134
-60
lines changed

src/core/utils/DtoMapping.ts

+19-2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import { MerkleTreeBranch } from '../../model/state/MerkleTreeBranch';
3434
import { MerkleTreeBranchLink } from '../../model/state/MerkleTreeBranchLink';
3535
import { MerkleTreeLeaf } from '../../model/state/MerkleTreeLeaf';
3636
import { MerkleTreeNodeType } from '../../model/state/MerkleTreeNodeType';
37+
import { Convert } from '../format';
3738

3839
export class DtoMapping {
3940
/**
@@ -45,7 +46,7 @@ export class DtoMapping {
4546
return new AccountRestrictions(
4647
accountRestrictions.accountRestrictions.version || 1,
4748
accountRestrictions['id'],
48-
Address.createFromEncoded(accountRestrictions.accountRestrictions.address),
49+
DtoMapping.toAddress(accountRestrictions.accountRestrictions.address),
4950
accountRestrictions.accountRestrictions.restrictions.map((prop) => {
5051
const restrictionFlags = prop.restrictionFlags as number;
5152
switch (restrictionFlags) {
@@ -55,7 +56,7 @@ export class DtoMapping {
5556
case AddressRestrictionFlag.BlockOutgoingAddress:
5657
return new AccountRestriction(
5758
restrictionFlags,
58-
prop.values.map((value) => Address.createFromEncoded(value as string)),
59+
prop.values.map((value) => DtoMapping.toAddress(value as string)),
5960
);
6061
case MosaicRestrictionFlag.AllowMosaic:
6162
case MosaicRestrictionFlag.BlockMosaic:
@@ -76,6 +77,22 @@ export class DtoMapping {
7677
);
7778
}
7879

80+
/**
81+
* This method knows how to convert an address payload sent by Rest to Address.
82+
*
83+
* Currently rest sends hex encoded addresses, it is desired to use base32 encoded addresses.
84+
*
85+
* This method handles both format, encoded (deprecated) and base32 encoded addresses.
86+
*
87+
* Clients using this SDK will be able to process both payloads.
88+
*
89+
* @param value the address in encoded (6823BB7C3C089D996585466380EDBDC19D4959184893E38C) format or decoded/plain format (SB3KUBHATFCPV7UZQLWAQ2EUR6SIHBSBEOEDDDF3)
90+
* @return the Address object.
91+
*/
92+
public static toAddress(value: string): Address {
93+
return Convert.isHexString(value, 48) ? Address.createFromEncoded(value) : Address.createFromRawAddress(value);
94+
}
95+
7996
/**
8097
* Creates a copy of the first object adding the attributes of the second object.
8198
* @param object the object to be cloned

src/core/utils/UnresolvedMapping.ts

+32-17
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { NamespaceId } from '../../model/namespace/NamespaceId';
2121
import { NetworkType } from '../../model/network/NetworkType';
2222
import { Convert } from '../format/Convert';
2323
import { RawAddress } from '../format/RawAddress';
24+
import { DtoMapping } from './DtoMapping';
2425

2526
/**
2627
* @internal
@@ -48,26 +49,40 @@ export class UnresolvedMapping {
4849
}
4950

5051
/**
51-
* Map unresolved address string to Address or NamespaceId
52-
* @param {string} address The unresolved address in hex
52+
* Map unresolved address string to Address or NamespaceId.
53+
*
54+
* Input examples:
55+
* - 6826D27E1D0A26CA4E316F901E23E55C8711DB20DF45C536 Hex address (old rest)
56+
* - NATNE7Q5BITMUTRRN6IB4I7FLSDRDWZA35C4KNQ Base32 address (new reset)
57+
* - 99C2860B73398FD8D3000000000000000000000000000000 Hex namespace id (old rest)
58+
* - THBIMC3THGH5RUYAAAAAAAAAAAAAAAAAAAAAAAA Base32 namespace id (new rest)
59+
*
60+
* @param {string} unresolvedAddressString The unresolved address in hex (old rest) or base32 address (new rest)
5361
* @returns {UnresolvedAddress}
5462
*/
55-
public static toUnresolvedAddress(address: string): UnresolvedAddress {
56-
if (!Convert.isHexString(address)) {
57-
throw new Error('Input string is not in valid hexadecimal notation.');
58-
}
59-
// If bit 0 of byte 0 is not set (like in 0x90), then it is a regular address.
60-
// Else (e.g. 0x91) it represents a namespace id which starts at byte 1.
61-
const bit0 = Convert.hexToUint8(address.substr(1, 2))[0];
62-
if ((bit0 & 16) === 16) {
63-
// namespaceId encoded hexadecimal notation provided
64-
// only 8 bytes are relevant to resolve the NamespaceId
65-
const relevantPart = address.substr(2, 16);
66-
return NamespaceId.createFromEncoded(Convert.uint8ToHex(Convert.hexToUint8Reverse(relevantPart)));
67-
}
63+
public static toUnresolvedAddress(unresolvedAddressString: string): UnresolvedAddress {
64+
const fromHexToUnresolvedAddress = (unresolvedAddressStringHex: string) => {
65+
// If bit 0 of byte 0 is not set (like in 0x90), then it is a regular address.
66+
// Else (e.g. 0x91) it represents a namespace id which starts at byte 1.
67+
const bit0 = Convert.hexToUint8(unresolvedAddressStringHex.substr(1, 2))[0];
68+
if ((bit0 & 16) === 16) {
69+
// namespaceId encoded hexadecimal notation provided
70+
// only 8 bytes are relevant to resolve the NamespaceId
71+
const relevantPart = unresolvedAddressStringHex.substr(2, 16);
72+
return NamespaceId.createFromEncoded(Convert.uint8ToHex(Convert.hexToUint8Reverse(relevantPart)));
73+
}
6874

69-
// read address from encoded hexadecimal notation
70-
return Address.createFromEncoded(address);
75+
// read address from encoded hexadecimal notation
76+
return Address.createFromEncoded(unresolvedAddressStringHex);
77+
};
78+
79+
if (Convert.isHexString(unresolvedAddressString, 48)) {
80+
// Old Rest
81+
return fromHexToUnresolvedAddress(unresolvedAddressString);
82+
} else {
83+
// New rest
84+
return fromHexToUnresolvedAddress(DtoMapping.toAddress(unresolvedAddressString).encoded());
85+
}
7186
}
7287

7388
/**

src/infrastructure/AccountHttp.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ export class AccountHttp extends Http implements AccountRepository {
113113
return new AccountInfo(
114114
dto.account.version || 1,
115115
dto.id,
116-
Address.createFromEncoded(dto.account.address),
116+
DtoMapping.toAddress(dto.account.address),
117117
UInt64.fromNumericString(dto.account.addressHeight),
118118
dto.account.publicKey,
119119
UInt64.fromNumericString(dto.account.publicKeyHeight),

src/infrastructure/BlockHttp.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import { Observable } from 'rxjs';
1818
import { BlockInfoDTO, BlockRoutesApi, ImportanceBlockDTO } from 'symbol-openapi-typescript-fetch-client';
1919
import { DtoMapping } from '../core/utils/DtoMapping';
20-
import { Address } from '../model/account/Address';
2120
import { PublicAccount } from '../model/account/PublicAccount';
2221
import { BlockInfo } from '../model/blockchain/BlockInfo';
2322
import { BlockType } from '../model/blockchain/BlockType';
@@ -121,7 +120,7 @@ export class BlockHttp extends Http implements BlockRepository {
121120
dto.block.proofGamma,
122121
dto.block.proofScalar,
123122
dto.block.proofVerificationHash,
124-
Address.createFromEncoded(dto.block.beneficiaryAddress),
123+
DtoMapping.toAddress(dto.block.beneficiaryAddress),
125124
dto.meta.transactionsCount,
126125
dto.meta.statementsCount,
127126
);

src/infrastructure/HashLockHttp.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import { Observable } from 'rxjs';
1818
import { HashLockInfoDTO, HashLockRoutesApi } from 'symbol-openapi-typescript-fetch-client';
1919
import { DtoMapping } from '../core/utils/DtoMapping';
20-
import { Address } from '../model/account/Address';
2120
import { MerkleStateInfo } from '../model/blockchain/MerkleStateInfo';
2221
import { HashLockInfo } from '../model/lock/HashLockInfo';
2322
import { MosaicId } from '../model/mosaic/MosaicId';
@@ -100,7 +99,7 @@ export class HashLockHttp extends Http implements HashLockRepository {
10099
return new HashLockInfo(
101100
dto.lock.version || 1,
102101
dto.id,
103-
Address.createFromEncoded(dto.lock.ownerAddress),
102+
DtoMapping.toAddress(dto.lock.ownerAddress),
104103
new MosaicId(dto.lock.mosaicId),
105104
UInt64.fromNumericString(dto.lock.amount),
106105
UInt64.fromNumericString(dto.lock.endHeight),

src/infrastructure/Listener.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { catchError, distinctUntilChanged, filter, map, mergeMap, share, switchM
1919
import { BlockInfoDTO } from 'symbol-openapi-typescript-fetch-client';
2020
import * as WebSocket from 'ws';
2121
import { parseObjectProperties } from '../core/format/Utilities';
22-
import { MultisigChildrenTreeObject, MultisigGraphUtils } from '../core/utils';
22+
import { DtoMapping, MultisigChildrenTreeObject, MultisigGraphUtils } from '../core/utils';
2323
import { MultisigAccountInfo, UnresolvedAddress } from '../model';
2424
import { Address } from '../model/account/Address';
2525
import { PublicAccount } from '../model/account/PublicAccount';
@@ -36,6 +36,7 @@ import { MultisigHttp } from './MultisigHttp';
3636
import { MultisigRepository } from './MultisigRepository';
3737
import { NamespaceRepository } from './NamespaceRepository';
3838
import { CreateTransactionFromDTO } from './transaction/CreateTransactionFromDTO';
39+
3940
export enum ListenerChannelName {
4041
block = 'block',
4142
confirmedAdded = 'confirmedAdded',
@@ -579,7 +580,7 @@ export class Listener implements IListener {
579580
dto.block.proofGamma,
580581
dto.block.proofScalar,
581582
dto.block.proofVerificationHash,
582-
dto.block.beneficiaryAddress ? Address.createFromEncoded(dto.block.beneficiaryAddress) : undefined,
583+
dto.block.beneficiaryAddress ? DtoMapping.toAddress(dto.block.beneficiaryAddress) : undefined,
583584
);
584585
}
585586

src/infrastructure/MetadataHttp.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import { Observable } from 'rxjs';
1818
import { MetadataInfoDTO, MetadataRoutesApi } from 'symbol-openapi-typescript-fetch-client';
1919
import { Convert } from '../core/format/Convert';
2020
import { DtoMapping } from '../core/utils/DtoMapping';
21-
import { Address } from '../model/account/Address';
2221
import { MerkleStateInfo } from '../model/blockchain';
2322
import { Metadata } from '../model/metadata/Metadata';
2423
import { MetadataEntry } from '../model/metadata/MetadataEntry';
@@ -122,8 +121,8 @@ export class MetadataHttp extends Http implements MetadataRepository {
122121
new MetadataEntry(
123122
metadataEntry.version || 1,
124123
metadataEntry.compositeHash,
125-
Address.createFromEncoded(metadataEntry.sourceAddress),
126-
Address.createFromEncoded(metadataEntry.targetAddress),
124+
DtoMapping.toAddress(metadataEntry.sourceAddress),
125+
DtoMapping.toAddress(metadataEntry.targetAddress),
127126
UInt64.fromHex(metadataEntry.scopedMetadataKey),
128127
metadataEntry.metadataType.valueOf(),
129128
Convert.decodeHex(metadataEntry.value),

src/infrastructure/MosaicHttp.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import { Observable } from 'rxjs';
1818
import { mergeMap } from 'rxjs/operators';
1919
import { MosaicInfoDTO, MosaicRoutesApi } from 'symbol-openapi-typescript-fetch-client';
2020
import { DtoMapping } from '../core/utils/DtoMapping';
21-
import { Address } from '../model/account/Address';
2221
import { MerkleStateInfo } from '../model/blockchain';
2322
import { MosaicFlags } from '../model/mosaic/MosaicFlags';
2423
import { MosaicId } from '../model/mosaic/MosaicId';
@@ -133,7 +132,7 @@ export class MosaicHttp extends Http implements MosaicRepository {
133132
new MosaicId(mosaicInfo.mosaic.id),
134133
UInt64.fromNumericString(mosaicInfo.mosaic.supply),
135134
UInt64.fromNumericString(mosaicInfo.mosaic.startHeight),
136-
Address.createFromEncoded(mosaicInfo.mosaic.ownerAddress),
135+
DtoMapping.toAddress(mosaicInfo.mosaic.ownerAddress),
137136
mosaicInfo.mosaic.revision,
138137
new MosaicFlags(mosaicInfo.mosaic.flags),
139138
mosaicInfo.mosaic.divisibility,

src/infrastructure/MultisigHttp.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,11 @@ export class MultisigHttp extends Http implements MultisigRepository {
9292
private toMultisigAccountInfo(dto: MultisigAccountInfoDTO): MultisigAccountInfo {
9393
return new MultisigAccountInfo(
9494
dto.multisig.version || 1,
95-
Address.createFromEncoded(dto.multisig.accountAddress),
95+
DtoMapping.toAddress(dto.multisig.accountAddress),
9696
dto.multisig.minApproval,
9797
dto.multisig.minRemoval,
98-
dto.multisig.cosignatoryAddresses.map((cosigner) => Address.createFromEncoded(cosigner)),
99-
dto.multisig.multisigAddresses.map((multisig) => Address.createFromEncoded(multisig)),
98+
dto.multisig.cosignatoryAddresses.map((cosigner) => DtoMapping.toAddress(cosigner)),
99+
dto.multisig.multisigAddresses.map((multisig) => DtoMapping.toAddress(multisig)),
100100
);
101101
}
102102
}

src/infrastructure/NamespaceHttp.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export class NamespaceHttp extends Http implements NamespaceRepository {
8282
body.accountNames.map(
8383
(accountName) =>
8484
new AccountNames(
85-
Address.createFromEncoded(accountName.address),
85+
DtoMapping.toAddress(accountName.address),
8686
accountName.names.map((name) => {
8787
return new NamespaceName(new NamespaceId(name), name);
8888
}),
@@ -255,7 +255,7 @@ export class NamespaceHttp extends Http implements NamespaceRepository {
255255
dto.namespace.depth,
256256
NamespaceHttp.extractLevels(dto.namespace),
257257
NamespaceId.createFromEncoded(dto.namespace.parentId),
258-
Address.createFromEncoded(dto.namespace.ownerAddress),
258+
DtoMapping.toAddress(dto.namespace.ownerAddress),
259259
UInt64.fromNumericString(dto.namespace.startHeight),
260260
UInt64.fromNumericString(dto.namespace.endHeight),
261261
NamespaceHttp.extractAlias(dto.namespace),
@@ -292,7 +292,7 @@ export class NamespaceHttp extends Http implements NamespaceRepository {
292292
if (namespace.alias && namespace.alias.type.valueOf() === AliasType.Mosaic) {
293293
return new MosaicAlias(new MosaicId(namespace.alias.mosaicId!));
294294
} else if (namespace.alias && namespace.alias.type.valueOf() === AliasType.Address) {
295-
return new AddressAlias(Address.createFromEncoded(namespace.alias.address!));
295+
return new AddressAlias(DtoMapping.toAddress(namespace.alias.address!));
296296
}
297297
return new EmptyAlias();
298298
}

src/infrastructure/RestrictionMosaicHttp.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import {
2424
} from 'symbol-openapi-typescript-fetch-client';
2525
import { DtoMapping } from '../core/utils';
2626
import { MerkleStateInfo, UInt64 } from '../model';
27-
import { Address } from '../model/account';
2827
import { MosaicId } from '../model/mosaic';
2928
import {
3029
MosaicAddressRestriction,
@@ -100,7 +99,7 @@ export class RestrictionMosaicHttp extends Http implements RestrictionMosaicRepo
10099
dto.mosaicRestrictionEntry.compositeHash,
101100
dto.mosaicRestrictionEntry.entryType.valueOf(),
102101
new MosaicId(dto.mosaicRestrictionEntry.mosaicId),
103-
Address.createFromEncoded(addressRestrictionDto.mosaicRestrictionEntry.targetAddress),
102+
DtoMapping.toAddress(addressRestrictionDto.mosaicRestrictionEntry.targetAddress),
104103
addressRestrictionDto.mosaicRestrictionEntry.restrictions.map(RestrictionMosaicHttp.toMosaicAddressRestrictionItem),
105104
);
106105
}

src/infrastructure/SecretLockHttp.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import { Observable } from 'rxjs';
1818
import { SecretLockInfoDTO, SecretLockRoutesApi } from 'symbol-openapi-typescript-fetch-client';
1919
import { DtoMapping } from '../core/utils';
2020
import { UInt64 } from '../model';
21-
import { Address } from '../model/account';
2221
import { MerkleStateInfo } from '../model/blockchain';
2322
import { SecretLockInfo } from '../model/lock';
2423
import { MosaicId } from '../model/mosaic';
@@ -92,14 +91,14 @@ export class SecretLockHttp extends Http implements SecretLockRepository {
9291
return new SecretLockInfo(
9392
dto.lock.version || 1,
9493
dto.id,
95-
Address.createFromEncoded(dto.lock.ownerAddress),
94+
DtoMapping.toAddress(dto.lock.ownerAddress),
9695
new MosaicId(dto.lock.mosaicId),
9796
UInt64.fromNumericString(dto.lock.amount),
9897
UInt64.fromNumericString(dto.lock.endHeight),
9998
dto.lock.status.valueOf(),
10099
dto.lock.hashAlgorithm.valueOf(),
101100
dto.lock.secret,
102-
Address.createFromEncoded(dto.lock.recipientAddress),
101+
DtoMapping.toAddress(dto.lock.recipientAddress),
103102
dto.lock.compositeHash,
104103
);
105104
}

src/infrastructure/receipt/CreateReceiptFromDTO.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
import { ResolutionStatementInfoDTO, TransactionStatementInfoDTO } from 'symbol-openapi-typescript-fetch-client';
18+
import { DtoMapping } from '../../core/utils';
1819
import { UnresolvedMapping } from '../../core/utils/UnresolvedMapping';
1920
import { Address } from '../../model/account/Address';
2021
import { UnresolvedAddress } from '../../model/account/UnresolvedAddress';
@@ -85,7 +86,7 @@ export const createAddressResolutionStatement = (statementInfoDTO: ResolutionSta
8586
extractUnresolvedAddress(statementDTO.unresolved),
8687
statementDTO.resolutionEntries.map((entry) => {
8788
return new ResolutionEntry(
88-
Address.createFromEncoded(entry.resolved),
89+
DtoMapping.toAddress(entry.resolved),
8990
new ReceiptSource(entry.source.primaryId, entry.source.secondaryId),
9091
);
9192
}),
@@ -100,7 +101,7 @@ export const createAddressResolutionStatement = (statementInfoDTO: ResolutionSta
100101
*/
101102
const createBalanceChangeReceipt = (receiptDTO): Receipt => {
102103
return new BalanceChangeReceipt(
103-
Address.createFromEncoded(receiptDTO.targetAddress),
104+
DtoMapping.toAddress(receiptDTO.targetAddress),
104105
new MosaicId(receiptDTO.mosaicId),
105106
UInt64.fromNumericString(receiptDTO.amount),
106107
receiptDTO.version,
@@ -116,8 +117,8 @@ const createBalanceChangeReceipt = (receiptDTO): Receipt => {
116117
*/
117118
const createBalanceTransferReceipt = (receiptDTO): Receipt => {
118119
return new BalanceTransferReceipt(
119-
Address.createFromEncoded(receiptDTO.senderAddress),
120-
Address.createFromEncoded(receiptDTO.recipientAddress),
120+
DtoMapping.toAddress(receiptDTO.senderAddress),
121+
DtoMapping.toAddress(receiptDTO.recipientAddress),
121122
new MosaicId(receiptDTO.mosaicId),
122123
UInt64.fromNumericString(receiptDTO.amount),
123124
receiptDTO.version,

src/model/transaction/AddressAliasTransaction.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
TransactionBuilder,
2424
} from 'catbuffer-typescript';
2525
import { Convert } from '../../core/format';
26+
import { DtoMapping } from '../../core/utils';
2627
import { Address } from '../account/Address';
2728
import { PublicAccount } from '../account/PublicAccount';
2829
import { AliasAction } from '../namespace/AliasAction';
@@ -131,7 +132,7 @@ export class AddressAliasTransaction extends Transaction {
131132
: Deadline.createFromDTO((builder as AddressAliasTransactionBuilder).getDeadline().timestamp),
132133
builder.getAliasAction().valueOf(),
133134
new NamespaceId(builder.getNamespaceId().namespaceId),
134-
Address.createFromEncoded(Convert.uint8ToHex(builder.getAddress().address)),
135+
DtoMapping.toAddress(Convert.uint8ToHex(builder.getAddress().address)),
135136
networkType,
136137
isEmbedded ? new UInt64([0, 0]) : new UInt64((builder as AddressAliasTransactionBuilder).fee.amount),
137138
signature,

0 commit comments

Comments
 (0)