Skip to content

Commit

Permalink
fix: Decode transaction-request with offsets (#494)
Browse files Browse the repository at this point in the history
* fix: Decode transaction-request with offsets

* fix: pr comments

* fix: update tests

* fix: add witness data

Co-authored-by: Luiz Estácio | stacio.eth <luizstacio@gmail.com>
  • Loading branch information
QuinnLee and luizstacio authored Sep 11, 2022
1 parent 7d01caa commit 9d0ad53
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 25 deletions.
5 changes: 5 additions & 0 deletions .changeset/witty-clouds-buy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fuel-ts/transactions": minor
---

fix transaction decode offset
199 changes: 183 additions & 16 deletions packages/transactions/src/coders/transaction.test.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
import { arrayify, hexlify } from '@ethersproject/bytes';
import { bn } from '@fuel-ts/math';

import { InputType } from './input';
import { OutputType } from './output';
import type { Transaction } from './transaction';
import { TransactionCoder, TransactionType } from './transaction';

const B256 = '0xd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b';
const U32 = 1000;
const U16 = 32;
const U8 = 1;

describe('TransactionCoder', () => {
it('Can encode TransactionScript', () => {
it('Can encode TransactionScript without inputs, outputs and witnesses', () => {
const transaction: Transaction = {
type: TransactionType.Script,
gasPrice: bn(0),
gasLimit: bn(0),
maturity: 0,
scriptLength: 0,
scriptDataLength: 0,
gasPrice: bn(U32),
gasLimit: bn(U32),
maturity: U32,
scriptLength: U16,
scriptDataLength: U16,
inputsCount: 0,
outputsCount: 0,
witnessesCount: 0,
receiptsRoot: B256,
script: '0x',
scriptData: '0x',
script: B256,
scriptData: B256,
inputs: [],
outputs: [],
witnesses: [],
Expand All @@ -29,7 +34,7 @@ describe('TransactionCoder', () => {
const encoded = hexlify(new TransactionCoder().encode(transaction));

expect(encoded).toEqual(
'0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b'
'0x000000000000000000000000000003e800000000000003e800000000000003e800000000000000200000000000000020000000000000000000000000000000000000000000000000d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930bd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930bd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b'
);

const [decoded, offset] = new TransactionCoder().decode(arrayify(encoded), 0);
Expand All @@ -38,14 +43,85 @@ describe('TransactionCoder', () => {
expect(JSON.stringify(decoded)).toEqual(JSON.stringify(transaction));
});

it('Can encode TransactionCreate', () => {
it('Can encode TransactionScript with inputs, outputs and witnesses', () => {
const transaction: Transaction = {
type: TransactionType.Script,
gasPrice: bn(U32),
gasLimit: bn(U32),
maturity: U32,
scriptLength: U16,
scriptDataLength: U16,
inputsCount: 1,
outputsCount: 2,
witnessesCount: 3,
receiptsRoot: B256,
script: B256,
scriptData: B256,
inputs: [
{
type: InputType.Contract,
utxoID: { transactionId: B256, outputIndex: 0 },
balanceRoot: B256,
stateRoot: B256,
contractID: B256,
txPointer: {
blockHeight: 0,
txIndex: 0,
},
},
],
outputs: [
{
type: OutputType.Withdrawal,
to: B256,
amount: bn(1),
assetId: B256,
},
{
type: OutputType.Coin,
to: B256,
amount: bn(1),
assetId: B256,
},
],
witnesses: [
{
dataLength: 1,
data: '0x01',
},
{
dataLength: 2,
data: '0x0101',
},
{
dataLength: 3,
data: '0x010101',
},
],
};

const encoded = hexlify(new TransactionCoder().encode(transaction));

expect(encoded).toEqual(
'0x000000000000000000000000000003e800000000000003e800000000000003e800000000000000200000000000000020000000000000000100000000000000020000000000000003d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930bd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930bd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b0000000000000001d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b0000000000000000d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930bd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b00000000000000000000000000000000d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b0000000000000002d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b0000000000000001d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b0000000000000000d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b0000000000000001d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b000000000000000101000000000000000000000000000002010100000000000000000000000000030101010000000000'
);

const [decoded, offset] = new TransactionCoder().decode(arrayify(encoded), 0);

expect(offset).toEqual((encoded.length - 2) / 2);
expect(JSON.parse(JSON.stringify(decoded))).toMatchObject(
JSON.parse(JSON.stringify(transaction))
);
});

it('Can encode TransactionCreate without inputs, outputs and witnesses', () => {
const transaction: Transaction = {
type: TransactionType.Create,
gasPrice: bn(0),
gasLimit: bn(0),
maturity: 0,
bytecodeLength: 0,
bytecodeWitnessIndex: 0,
gasPrice: bn(U32),
gasLimit: bn(U32),
maturity: U32,
bytecodeLength: U16,
bytecodeWitnessIndex: U8,
storageSlotsCount: 0,
inputsCount: 0,
outputsCount: 0,
Expand All @@ -60,12 +136,103 @@ describe('TransactionCoder', () => {
const encoded = hexlify(new TransactionCoder().encode(transaction));

expect(encoded).toEqual(
'0x0000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b'
'0x000000000000000100000000000003e800000000000003e800000000000003e8000000000000002000000000000000010000000000000000000000000000000000000000000000000000000000000000d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b'
);

const [decoded, offset] = new TransactionCoder().decode(arrayify(encoded), 0);

expect(offset).toEqual((encoded.length - 2) / 2);
expect(JSON.stringify(decoded)).toEqual(JSON.stringify(transaction));
});

it('Can encode TransactionCreate with inputs, outputs and witnesses', () => {
const transaction: Transaction = {
type: TransactionType.Create,
gasPrice: bn(U32),
gasLimit: bn(U32),
maturity: U32,
bytecodeLength: U16,
bytecodeWitnessIndex: U8,
storageSlotsCount: 1,
inputsCount: 3,
outputsCount: 2,
witnessesCount: 1,
salt: B256,
storageSlots: [{ key: B256, value: B256 }],
inputs: [
{
type: InputType.Contract,
utxoID: { transactionId: B256, outputIndex: 0 },
balanceRoot: B256,
stateRoot: B256,
contractID: B256,
txPointer: {
blockHeight: 0,
txIndex: 0,
},
},
{
type: InputType.Contract,
utxoID: { transactionId: B256, outputIndex: 0 },
balanceRoot: B256,
stateRoot: B256,
contractID: B256,
txPointer: {
blockHeight: 0,
txIndex: 0,
},
},
{
type: InputType.Coin,
utxoID: { transactionId: B256, outputIndex: 0 },
owner: B256,
amount: bn(0),
assetId: B256,
txPointer: {
blockHeight: 0,
txIndex: 0,
},
witnessIndex: 0,
maturity: 0,
predicateLength: 0,
predicateDataLength: 0,
predicate: '0x',
predicateData: '0x',
},
],
outputs: [
{
type: OutputType.Coin,
to: B256,
amount: bn(1),
assetId: B256,
},
{
type: OutputType.Coin,
to: B256,
amount: bn(1),
assetId: B256,
},
],
witnesses: [
{
dataLength: 32,
data: B256,
},
],
};

const encoded = hexlify(new TransactionCoder().encode(transaction));

expect(encoded).toEqual(
'0x000000000000000100000000000003e800000000000003e800000000000003e8000000000000002000000000000000010000000000000001000000000000000300000000000000020000000000000001d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930bd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930bd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b0000000000000001d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b0000000000000000d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930bd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b00000000000000000000000000000000d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b0000000000000001d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b0000000000000000d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930bd5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b00000000000000000000000000000000d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b0000000000000000d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b0000000000000000d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b0000000000000000d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b0000000000000001d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b0000000000000000d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b0000000000000001d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b0000000000000020d5579c46dfcc7f18207013e65b44e4cb4e2c2298f4ac457ba8f82743f31e930b'
);

const [decoded, offset] = new TransactionCoder().decode(arrayify(encoded), 0);

expect(offset).toEqual((encoded.length - 2) / 2);
expect(JSON.parse(JSON.stringify(decoded))).toMatchObject(
JSON.parse(JSON.stringify(transaction))
);
});
});
18 changes: 9 additions & 9 deletions packages/transactions/src/coders/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,14 @@ export class TransactionScriptCoder extends Coder<TransactionScript, Transaction
[decoded, o] = new NumberCoder('u32').decode(data, o);
const maturity = decoded;
[decoded, o] = new NumberCoder('u16').decode(data, o);
[decoded, o] = new NumberCoder('u16').decode(data, o);
[decoded, o] = new NumberCoder('u8').decode(data, o);
[decoded, o] = new NumberCoder('u8').decode(data, o);
[decoded, o] = new NumberCoder('u8').decode(data, o);
const scriptLength = decoded;
[decoded, o] = new NumberCoder('u16').decode(data, o);
const scriptDataLength = decoded;
[decoded, o] = new NumberCoder('u8').decode(data, o);
const inputsCount = decoded;
[decoded, o] = new NumberCoder('u8').decode(data, o);
const outputsCount = decoded;
[decoded, o] = new NumberCoder('u8').decode(data, o);
const witnessesCount = decoded;
[decoded, o] = new B256Coder().decode(data, o);
const receiptsRoot = decoded;
Expand Down Expand Up @@ -238,16 +238,16 @@ export class TransactionCreateCoder extends Coder<TransactionCreate, Transaction
[decoded, o] = new NumberCoder('u32').decode(data, o);
const maturity = decoded;
[decoded, o] = new NumberCoder('u16').decode(data, o);
[decoded, o] = new NumberCoder('u8').decode(data, o);
[decoded, o] = new NumberCoder('u16').decode(data, o);
[decoded, o] = new NumberCoder('u8').decode(data, o);
[decoded, o] = new NumberCoder('u8').decode(data, o);
[decoded, o] = new NumberCoder('u8').decode(data, o);
const bytecodeLength = decoded;
[decoded, o] = new NumberCoder('u8').decode(data, o);
const bytecodeWitnessIndex = decoded;
[decoded, o] = new NumberCoder('u16').decode(data, o);
const storageSlotsCount = decoded;
[decoded, o] = new NumberCoder('u8').decode(data, o);
const inputsCount = decoded;
[decoded, o] = new NumberCoder('u8').decode(data, o);
const outputsCount = decoded;
[decoded, o] = new NumberCoder('u8').decode(data, o);
const witnessesCount = decoded;
[decoded, o] = new B256Coder().decode(data, o);
const salt = decoded;
Expand Down

1 comment on commit 9d0ad53

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage report

St.
Category Percentage Covered / Total
🟢 Statements 89.97% 3220/3579
🟡 Branches 70.3% 594/845
🟢 Functions 87.06% 646/742
🟢 Lines 89.82% 3087/3437

Test suite run success

489 tests passing in 44 suites.

Report generated by 🧪jest coverage report action from 9d0ad53

Please sign in to comment.