Skip to content

Commit 6c8151e

Browse files
committed
feat(sdk-coin-icp): improve transaction signature validation
TICKET: WIN-4908
1 parent 91de8da commit 6c8151e

File tree

5 files changed

+47
-6
lines changed

5 files changed

+47
-6
lines changed

modules/sdk-coin-icp/src/lib/signedTransactionBuilder.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ export class SignedTransactionBuilder {
5151
const readState = utils.makeReadStateFromUpdate(update);
5252
const transactionSignature = utils.getTransactionSignature(signatureMap, update);
5353
if (!transactionSignature) {
54-
throw new Error('Transaction signature is undefined');
54+
throw new Error('Transaction signature is invalid');
5555
}
5656

5757
const readStateSignature = utils.getReadStateSignature(signatureMap, readState);
5858
if (!readStateSignature) {
59-
throw new Error('read state signature is undefined');
59+
throw new Error('read state signature is invalid');
6060
}
6161

6262
const pk_der = utils.getPublicKeyInDERFormat(transactionSignature.public_key.hex_bytes);

modules/sdk-coin-icp/src/lib/transaction.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export class Transaction extends BaseTransaction {
3434
protected _payloadsData: PayloadsData;
3535
protected _signedTransaction: string;
3636
protected _signaturePayload: Signatures[];
37+
protected _createdTimestamp: number | bigint | undefined;
3738
protected _utils: Utils;
3839

3940
constructor(_coinConfig: Readonly<CoinConfig>, utils: Utils) {
@@ -81,6 +82,14 @@ export class Transaction extends BaseTransaction {
8182
return this._payloadsData;
8283
}
8384

85+
set createdTimestamp(createdTimestamp: number) {
86+
this._createdTimestamp = createdTimestamp;
87+
}
88+
89+
get createdTimestamp(): number | bigint | undefined {
90+
return this._createdTimestamp;
91+
}
92+
8493
async fromRawTransaction(rawTransaction: string): Promise<void> {
8594
try {
8695
const jsonRawTransaction: RawTransaction = JSON.parse(rawTransaction);
@@ -273,6 +282,7 @@ export class Transaction extends BaseTransaction {
273282
},
274283
account_identifier_signers: accountIdentifierSigners,
275284
};
285+
this.createdTimestamp = args.createdAtTime.timestampNanos;
276286
return parsedTxn;
277287
}
278288

modules/sdk-coin-icp/src/lib/transferBuilder.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export class TransferBuilder extends TransactionBuilder {
4848
assert(this._publicKey, new BuildTransactionError('sender public key is required before building'));
4949
assert(this._amount, new BuildTransactionError('amount is required before building'));
5050
assert(this._receiverId, new BuildTransactionError('receiver is required before building'));
51-
assert(this._memo, new BuildTransactionError('memo is required before building'));
51+
this._utils.validateMemo(this._memo);
5252
const publicKey: IcpPublicKey = {
5353
hex_bytes: this._publicKey,
5454
curve_type: CurveType.SECP256K1,
@@ -90,8 +90,9 @@ export class TransferBuilder extends TransactionBuilder {
9090
},
9191
};
9292

93+
const createdTimestamp = this._transaction.createdTimestamp;
9394
const { metaData, ingressEndTime }: { metaData: IcpMetadata; ingressEndTime: number | BigInt } =
94-
this._utils.getMetaData(this._memo);
95+
this._utils.getMetaData(this._memo, createdTimestamp);
9596

9697
const icpTransaction: IcpTransaction = {
9798
public_keys: [publicKey],

modules/sdk-coin-icp/src/lib/utils.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -608,8 +608,14 @@ export class Utils implements BaseUtils {
608608
);
609609
}
610610

611-
getMetaData(memo: number | BigInt): { metaData: IcpMetadata; ingressEndTime: number | BigInt } {
612-
const currentTime = Date.now() * 1000000;
611+
getMetaData(
612+
memo: number | BigInt,
613+
timestamp: number | bigint | undefined
614+
): { metaData: IcpMetadata; ingressEndTime: number | BigInt } {
615+
let currentTime = Date.now() * 1000000;
616+
if (timestamp) {
617+
currentTime = Number(timestamp);
618+
}
613619
const ingressStartTime = currentTime;
614620
const ingressEndTime = ingressStartTime + 5 * 60 * 1000000000; // 5 mins in nanoseconds
615621
const metaData: IcpMetadata = {

modules/sdk-coin-icp/test/unit/transactionBuilder/transactionBuilder.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,28 @@ describe('ICP Transaction Builder', async () => {
101101
should.equal(broadcastTxnObj.signed_transaction, signedTxn);
102102
should.equal(broadcastTxnObj.network_identifier.network, '00000000000000020101');
103103
});
104+
105+
it('should build a txn then parse it and then again build', async () => {
106+
sinon.restore(); // do not stub getMetaData
107+
txBuilder = factory.getTransferBuilder();
108+
txBuilder.sender(testData.accounts.account1.address, testData.accounts.account1.publicKey);
109+
txBuilder.receiverId(testData.accounts.account2.address);
110+
txBuilder.amount('10');
111+
txBuilder.memo(testData.metaData.memo);
112+
113+
await txBuilder.build();
114+
txn = txBuilder.transaction;
115+
const unsignedTxn = txBuilder.transaction.unsignedTransaction;
116+
unsignedTxn.should.be.a.String();
117+
const rawTransaction = {
118+
serializedTxHex: unsignedTxn,
119+
publicKey: testData.accounts.account1.publicKey,
120+
};
121+
await txn.fromRawTransaction(JSON.stringify(rawTransaction));
122+
const baseKey: BaseKey = { key: testData.accounts.account1.secretKey };
123+
txBuilder.sign(baseKey);
124+
txBuilder.combine();
125+
const signedTxn = txBuilder.transaction.signedTransaction;
126+
signedTxn.should.be.a.String();
127+
});
104128
});

0 commit comments

Comments
 (0)