Skip to content

Commit 41fd197

Browse files
fix(sdk-coin-iota): handle serialised tx as hex in iota controller
Ticket: WIN-8054
1 parent 895fd55 commit 41fd197

File tree

7 files changed

+98
-41
lines changed

7 files changed

+98
-41
lines changed

modules/sdk-coin-iota/src/iota.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,9 @@ export class Iota extends BaseCoin {
9191
* @inheritDoc
9292
*/
9393
async explainTransaction(params: ExplainTransactionOptions): Promise<TransactionExplanation> {
94-
const rawTx = params.txBase64;
94+
const rawTx = params.txHex;
9595
if (!rawTx) {
96-
throw new Error('missing required tx prebuild property txBase64');
96+
throw new Error('missing required tx prebuild property txHex');
9797
}
9898
const transaction = await this.rebuildTransaction(rawTx);
9999
if (!transaction) {
@@ -108,9 +108,9 @@ export class Iota extends BaseCoin {
108108
*/
109109
async verifyTransaction(params: VerifyTransactionOptions): Promise<boolean> {
110110
const { txPrebuild: txPrebuild, txParams: txParams } = params;
111-
const rawTx = txPrebuild.txBase64;
111+
const rawTx = txPrebuild.txHex;
112112
if (!rawTx) {
113-
throw new Error('missing required tx prebuild property txBase64');
113+
throw new Error('missing required tx prebuild property txHex');
114114
}
115115
const transaction = await this.rebuildTransaction(rawTx);
116116
if (!transaction) {
@@ -145,7 +145,7 @@ export class Iota extends BaseCoin {
145145
* @param params
146146
*/
147147
async parseTransaction(params: IotaParseTransactionOptions): Promise<ParsedTransaction> {
148-
const transactionExplanation = await this.explainTransaction({ txBase64: params.txBase64 });
148+
const transactionExplanation = await this.explainTransaction({ txHex: params.txHex });
149149

150150
if (!transactionExplanation) {
151151
throw new Error('Invalid transaction');
@@ -262,10 +262,9 @@ export class Iota extends BaseCoin {
262262
const txBuilderFactory = this.getTxBuilderFactory();
263263
try {
264264
const txBuilder = txBuilderFactory.from(txHex);
265-
txBuilder.transaction.isSimulateTx = false;
266265
return (await txBuilder.build()) as Transaction;
267-
} catch {
268-
throw new Error('Failed to rebuild transaction');
266+
} catch (err) {
267+
throw new Error(`Failed to rebuild transaction ${err.toString()}`);
269268
}
270269
}
271270
}

modules/sdk-coin-iota/src/lib/iface.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ export interface TransferTxData extends TxData {
4343
}
4444

4545
export interface ExplainTransactionOptions {
46-
txBase64: string;
46+
txHex: string;
4747
}
4848

4949
export interface IotaParseTransactionOptions extends BaseParseTransactionOptions {
50-
txBase64: string;
50+
txHex: string;
5151
}

modules/sdk-coin-iota/src/lib/transactionBuilderFactory.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { TransactionBuilder } from './transactionBuilder';
33
import { TransferBuilder } from './transferBuilder';
44
import { Transaction } from './transaction';
55
import { TRANSFER_TRANSACTION_COMMANDS } from './constants';
6+
import utils from './utils';
67
import { Transaction as IotaTransaction } from '@iota/iota-sdk/transactions';
78
import { BaseCoin as CoinConfig } from '@bitgo/statics';
89

@@ -22,11 +23,12 @@ export class TransactionBuilderFactory extends BaseTransactionBuilderFactory {
2223

2324
from(rawTx: string | Uint8Array): TransactionBuilder {
2425
let builder: TransactionBuilder;
25-
const txType: TransactionType = this.identifyTxTypeFromRawTx(rawTx);
26+
const rawTxBase64 = utils.getBase64String(rawTx);
27+
const txType: TransactionType = this.identifyTxTypeFromRawTx(rawTxBase64);
2628
switch (txType) {
2729
case TransactionType.Send:
2830
builder = new TransferBuilder(this._coinConfig);
29-
builder.fromImplementation(rawTx);
31+
builder.fromImplementation(rawTxBase64);
3032
return builder;
3133
}
3234
throw new InvalidTransactionError('Unsupported transaction');

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
} from './constants';
88
import { Ed25519PublicKey } from '@iota/iota-sdk/keypairs/ed25519';
99
import { Transaction as IotaTransaction } from '@iota/iota-sdk/transactions';
10-
import { fromBase64 } from '@iota/bcs';
10+
import { toBase64, fromBase64 } from '@iota/iota-sdk/utils';
1111

1212
export class Utils implements BaseUtils {
1313
/** @inheritdoc */
@@ -49,6 +49,14 @@ export class Utils implements BaseUtils {
4949
return regex.test(value);
5050
}
5151

52+
getBase64String(value: string | Uint8Array): string {
53+
if (value instanceof Uint8Array) {
54+
return toBase64(value);
55+
} else {
56+
return toBase64(Buffer.from(value, 'hex'));
57+
}
58+
}
59+
5260
getAddressFromPublicKey(publicKey: string): string {
5361
const iotaPublicKey = new Ed25519PublicKey(Buffer.from(publicKey, 'hex'));
5462
return iotaPublicKey.toIotaAddress();

modules/sdk-coin-iota/test/unit/iota.ts

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -136,16 +136,16 @@ describe('IOTA:', function () {
136136
});
137137

138138
describe('explainTransaction', () => {
139-
it('should throw error for missing txBase64', async function () {
139+
it('should throw error for missing txHex', async function () {
140140
await assert.rejects(
141-
async () => await basecoin.explainTransaction({ txBase64: '' }),
142-
/missing required tx prebuild property txBase64/
141+
async () => await basecoin.explainTransaction({ txHex: '' }),
142+
/missing required tx prebuild property txHex/
143143
);
144144
});
145145

146146
it('should throw error for invalid transaction', async function () {
147147
await assert.rejects(
148-
async () => await basecoin.explainTransaction({ txBase64: 'invalidTxBase64' }),
148+
async () => await basecoin.explainTransaction({ txHex: 'invalidTxHex' }),
149149
/Failed to rebuild transaction/
150150
);
151151
});
@@ -171,14 +171,14 @@ describe('IOTA:', function () {
171171
});
172172

173173
describe('verifyTransaction', () => {
174-
it('should throw error for missing txBase64', async function () {
174+
it('should throw error for missing txHex', async function () {
175175
await assert.rejects(
176176
async () =>
177177
await basecoin.verifyTransaction({
178178
txPrebuild: {},
179179
txParams: { recipients: testData.recipients },
180180
}),
181-
/missing required tx prebuild property txBase64/
181+
/missing required tx prebuild property txHex/
182182
);
183183
});
184184

@@ -191,16 +191,14 @@ describe('IOTA:', function () {
191191
txBuilder.gasData(testData.gasData);
192192

193193
const tx = (await txBuilder.build()) as TransferTransaction;
194-
const txJson = tx.toJson();
195-
196-
// Rebuild from JSON to simulate what would happen in verification
197-
const rebuiltTxBuilder = factory.getTransferBuilder();
198-
(rebuiltTxBuilder.transaction as TransferTransaction).parseFromJSON(txJson);
199-
const rebuiltTx = (await rebuiltTxBuilder.build()) as TransferTransaction;
200-
201-
// Verify the rebuilt transaction matches
202-
should.equal(rebuiltTx.sender, testData.sender.address);
203-
should.deepEqual(rebuiltTx.recipients, testData.recipients);
194+
const txHex = Buffer.from(await tx.toBroadcastFormat(), 'base64').toString('hex');
195+
should.equal(
196+
await basecoin.verifyTransaction({
197+
txPrebuild: { txHex: txHex },
198+
txParams: { recipients: testData.recipients },
199+
}),
200+
true
201+
);
204202
});
205203

206204
it('should detect mismatched recipients', async function () {
@@ -211,12 +209,15 @@ describe('IOTA:', function () {
211209
txBuilder.gasData(testData.gasData);
212210

213211
const tx = (await txBuilder.build()) as TransferTransaction;
214-
const txJson = tx.toJson();
215-
216-
const differentRecipients = [{ address: testData.addresses.validAddresses[0], amount: '9999' }];
217-
218-
// Recipients don't match
219-
should.notDeepEqual(txJson.recipients, differentRecipients);
212+
const txHex = Buffer.from(await tx.toBroadcastFormat(), 'base64').toString('hex');
213+
assert.rejects(
214+
async () =>
215+
await basecoin.verifyTransaction({
216+
txPrebuild: { txHex: txHex },
217+
txParams: { recipients: testData.recipients },
218+
}),
219+
/Tx recipients does not match with expected txParams recipients/
220+
);
220221
});
221222

222223
it('should verify transaction without recipients parameter', async function () {
@@ -227,17 +228,22 @@ describe('IOTA:', function () {
227228
txBuilder.gasData(testData.gasData);
228229

229230
const tx = (await txBuilder.build()) as TransferTransaction;
230-
231-
// Verification should still work even if no recipients are provided for comparison
232-
should.exist(tx);
233-
should.equal(tx.type, TransactionType.Send);
231+
const txHex = Buffer.from(await tx.toBroadcastFormat(), 'base64').toString('hex');
232+
233+
should.equal(
234+
await basecoin.verifyTransaction({
235+
txPrebuild: { txHex: txHex },
236+
txParams: {},
237+
}),
238+
true
239+
);
234240
});
235241
});
236242

237243
describe('parseTransaction', () => {
238244
it('should throw error for invalid transaction', async function () {
239245
await assert.rejects(
240-
async () => await basecoin.parseTransaction({ txBase64: 'invalidTxBase64' }),
246+
async () => await basecoin.parseTransaction({ txHex: 'invalidTxHex' }),
241247
/Failed to rebuild transaction/
242248
);
243249
});

modules/sdk-coin-iota/test/unit/transactionBuilder/transactionBuilderFactory.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ describe('Iota Transaction Builder Factory', () => {
4242

4343
const tx = (await txBuilder.build()) as TransferTransaction;
4444
const rawTx = await tx.toBroadcastFormat();
45+
const txHex = Buffer.from(rawTx, 'base64').toString('hex');
4546

46-
const rebuiltBuilder = factory.from(rawTx);
47+
const rebuiltBuilder = factory.from(txHex);
4748
should.exist(rebuiltBuilder);
4849
should(rebuiltBuilder instanceof TransferBuilder).be.true();
4950
const rebuiltTx = (await rebuiltBuilder.build()) as TransferTransaction;

modules/sdk-coin-iota/test/unit/utils.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,47 @@ describe('Iota util library', function () {
205205
});
206206
});
207207

208+
describe('getBase64String', function () {
209+
it('should convert Uint8Array to base64', function () {
210+
const uint8Array = new Uint8Array([72, 101, 108, 108, 111]); // "Hello"
211+
const result = utils.getBase64String(uint8Array);
212+
should.exist(result);
213+
should.equal(typeof result, 'string');
214+
should.equal(result.length > 0, true);
215+
});
216+
217+
it('should convert hex string to base64', function () {
218+
const hexString = '48656c6c6f'; // "Hello" in hex
219+
const result = utils.getBase64String(hexString);
220+
should.exist(result);
221+
should.equal(typeof result, 'string');
222+
should.equal(result.length > 0, true);
223+
});
224+
225+
it('should handle empty Uint8Array', function () {
226+
const emptyArray = new Uint8Array([]);
227+
const result = utils.getBase64String(emptyArray);
228+
should.exist(result);
229+
should.equal(typeof result, 'string');
230+
});
231+
232+
it('should handle Buffer conversion to base64', function () {
233+
const buffer = Buffer.from('Hello World');
234+
const uint8Array = new Uint8Array(buffer);
235+
const result = utils.getBase64String(uint8Array);
236+
should.exist(result);
237+
should.equal(typeof result, 'string');
238+
should.equal(result.length > 0, true);
239+
});
240+
241+
it('should consistently convert same input to same output', function () {
242+
const uint8Array = new Uint8Array([1, 2, 3, 4, 5]);
243+
const result1 = utils.getBase64String(uint8Array);
244+
const result2 = utils.getBase64String(new Uint8Array([1, 2, 3, 4, 5]));
245+
should.equal(result1, result2);
246+
});
247+
});
248+
208249
describe('isValidRawTransaction', function () {
209250
it('should validate proper raw transactions', async function () {
210251
const factory = new TransactionBuilderFactory(coins.get('tiota'));

0 commit comments

Comments
 (0)