Skip to content

Commit

Permalink
fix: TransactionMessage.decompile() now counts the correct number of …
Browse files Browse the repository at this point in the history
…unsigned, writable accounts (#28990)

* Test that `TransactionMessage.decompile()` can decompile a legacy `Message`

* `TransactionMessage.decompile()` now correctly accounts for the number of writable unsigned accounts
  • Loading branch information
steveluscher committed Nov 30, 2022
1 parent c0a35b6 commit 4724539
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 3 deletions.
4 changes: 3 additions & 1 deletion src/transaction/message.ts
Expand Up @@ -49,7 +49,9 @@ export class TransactionMessage {
assert(numWritableSignedAccounts > 0, 'Message header is invalid');

const numWritableUnsignedAccounts =
message.staticAccountKeys.length - numReadonlyUnsignedAccounts;
message.staticAccountKeys.length -
numRequiredSignatures -
numReadonlyUnsignedAccounts;
assert(numWritableUnsignedAccounts >= 0, 'Message header is invalid');

const accountKeys = message.getAccountKeys(args);
Expand Down
75 changes: 73 additions & 2 deletions test/transaction-tests/message.test.ts
Expand Up @@ -3,12 +3,13 @@ import {expect} from 'chai';
import {sha256} from '@noble/hashes/sha256';

import {
Transaction,
TransactionInstruction,
TransactionMessage,
} from '../../src/transaction';
import {PublicKey} from '../../src/publickey';
import {AddressLookupTableAccount} from '../../src/programs';
import {MessageV0} from '../../src/message';
import {Message, MessageV0} from '../../src/message';

function createTestKeys(count: number): Array<PublicKey> {
return new Array(count).fill(0).map(() => PublicKey.unique());
Expand All @@ -31,7 +32,77 @@ function createTestLookupTable(
}

describe('TransactionMessage', () => {
it('decompile', () => {
it('decompiles a legacy message', () => {
const keys = createTestKeys(7);
const recentBlockhash = bs58.encode(sha256('test'));
const payerKey = keys[0];
const instructions = [
new TransactionInstruction({
programId: keys[5],
keys: [
{pubkey: keys[0], isSigner: true, isWritable: true},
{pubkey: keys[6], isSigner: false, isWritable: false},
{pubkey: keys[1], isSigner: false, isWritable: true},
{pubkey: keys[3], isSigner: false, isWritable: false},
{pubkey: keys[4], isSigner: false, isWritable: false},
{pubkey: keys[2], isSigner: false, isWritable: false},
],
data: Buffer.alloc(1),
}),
];

const message = Message.compile({
instructions,
payerKey,
recentBlockhash,
});

expect(() => TransactionMessage.decompile(message)).not.to.throw(
'Failed to get account keys because address table lookups were not resolved',
);

const decompiledMessage = TransactionMessage.decompile(message);

expect(decompiledMessage.payerKey).to.eql(payerKey);
expect(decompiledMessage.recentBlockhash).to.eq(recentBlockhash);
expect(decompiledMessage.instructions).to.eql(instructions);
});

// Regression test for https://github.com/solana-labs/solana/issues/28900
it('decompiles a legacy message the same way as the old API', () => {
const accountKeys = createTestKeys(7);
const legacyMessage = new Message({
header: {
numRequiredSignatures: 1,
numReadonlySignedAccounts: 0,
numReadonlyUnsignedAccounts: 5,
},
recentBlockhash: bs58.encode(sha256('test')),
accountKeys,
instructions: [
{
accounts: [0, 6, 1, 3, 4, 2],
data: '',
programIdIndex: 5,
},
],
});

const transactionFromLegacyAPI = Transaction.populate(legacyMessage);
const transactionMessage = TransactionMessage.decompile(legacyMessage);

expect(transactionMessage.payerKey).to.eql(
transactionFromLegacyAPI.feePayer,
);
expect(transactionMessage.instructions).to.eql(
transactionFromLegacyAPI.instructions,
);
expect(transactionMessage.recentBlockhash).to.eql(
transactionFromLegacyAPI.recentBlockhash,
);
});

it('decompiles a V0 message', () => {
const keys = createTestKeys(7);
const recentBlockhash = bs58.encode(sha256('test'));
const payerKey = keys[0];
Expand Down

0 comments on commit 4724539

Please sign in to comment.