From 81a10e649f6f4ca3d80bd5beba3e02a4dab4b62f Mon Sep 17 00:00:00 2001 From: Noah Prince <83885631+ChewingGlass@users.noreply.github.com> Date: Fri, 7 Jan 2022 23:38:58 -0600 Subject: [PATCH] fix: non-deterministic writeable account order (#21724) --- web3.js/src/transaction.ts | 5 +- web3.js/test/transaction.test.ts | 84 +++++++++++++++++++++++++++++++- 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/web3.js/src/transaction.ts b/web3.js/src/transaction.ts index 5466464f5a..87ee92508b 100644 --- a/web3.js/src/transaction.ts +++ b/web3.js/src/transaction.ts @@ -259,9 +259,12 @@ export class Transaction { // Sort. Prioritizing first by signer, then by writable accountMetas.sort(function (x, y) { + const pubkeySorting = x.pubkey + .toBase58() + .localeCompare(y.pubkey.toBase58()); const checkSigner = x.isSigner === y.isSigner ? 0 : x.isSigner ? -1 : 1; const checkWritable = - x.isWritable === y.isWritable ? 0 : x.isWritable ? -1 : 1; + x.isWritable === y.isWritable ? pubkeySorting : x.isWritable ? -1 : 1; return checkSigner || checkWritable; }); diff --git a/web3.js/test/transaction.test.ts b/web3.js/test/transaction.test.ts index 77d78805ba..93ff213fa4 100644 --- a/web3.js/test/transaction.test.ts +++ b/web3.js/test/transaction.test.ts @@ -5,7 +5,7 @@ import {expect} from 'chai'; import {Keypair} from '../src/keypair'; import {PublicKey} from '../src/publickey'; -import {Transaction} from '../src/transaction'; +import {Transaction, TransactionInstruction} from '../src/transaction'; import {StakeProgram} from '../src/stake-program'; import {SystemProgram} from '../src/system-program'; import {Message} from '../src/message'; @@ -505,4 +505,86 @@ describe('Transaction', () => { tx.addSignature(from.publicKey, toBuffer(signature)); expect(tx.verifySignatures()).to.be.true; }); + + it('can serialize, deserialize, and reserialize with a partial signer', () => { + const signer = Keypair.generate(); + const acc0Writable = Keypair.generate(); + const acc1Writable = Keypair.generate(); + const acc2Writable = Keypair.generate(); + const t0 = new Transaction({ + recentBlockhash: 'HZaTsZuhN1aaz9WuuimCFMyH7wJ5xiyMUHFCnZSMyguH', + feePayer: signer.publicKey, + }); + t0.add( + new TransactionInstruction({ + keys: [ + { + pubkey: signer.publicKey, + isWritable: true, + isSigner: true, + }, + { + pubkey: acc0Writable.publicKey, + isWritable: true, + isSigner: false, + }, + ], + programId: Keypair.generate().publicKey, + }), + ); + t0.add( + new TransactionInstruction({ + keys: [ + { + pubkey: acc1Writable.publicKey, + isWritable: false, + isSigner: false, + }, + ], + programId: Keypair.generate().publicKey, + }), + ); + t0.add( + new TransactionInstruction({ + keys: [ + { + pubkey: acc2Writable.publicKey, + isWritable: true, + isSigner: false, + }, + ], + programId: Keypair.generate().publicKey, + }), + ); + t0.add( + new TransactionInstruction({ + keys: [ + { + pubkey: signer.publicKey, + isWritable: true, + isSigner: true, + }, + { + pubkey: acc0Writable.publicKey, + isWritable: false, + isSigner: false, + }, + { + pubkey: acc2Writable.publicKey, + isWritable: false, + isSigner: false, + }, + { + pubkey: acc1Writable.publicKey, + isWritable: true, + isSigner: false, + }, + ], + programId: Keypair.generate().publicKey, + }), + ); + t0.partialSign(signer); + const t1 = Transaction.from(t0.serialize()); + t1.serialize(); + }); });