From 491d4f33316793455db237a305c82b4cbbebaabb Mon Sep 17 00:00:00 2001 From: Steven Luscher Date: Wed, 30 Nov 2022 09:57:54 -0800 Subject: [PATCH] fix: TransactionMessage.decompile() now counts the correct number of 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 --- web3.js/src/transaction/message.ts | 4 +- .../test/transaction-tests/message.test.ts | 75 ++++++++++++++++++- 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/web3.js/src/transaction/message.ts b/web3.js/src/transaction/message.ts index c854c95572..4f7c738d62 100644 --- a/web3.js/src/transaction/message.ts +++ b/web3.js/src/transaction/message.ts @@ -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); diff --git a/web3.js/test/transaction-tests/message.test.ts b/web3.js/test/transaction-tests/message.test.ts index 261d424d72..ce4937c043 100644 --- a/web3.js/test/transaction-tests/message.test.ts +++ b/web3.js/test/transaction-tests/message.test.ts @@ -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 { return new Array(count).fill(0).map(() => PublicKey.unique()); @@ -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];