From fddf2e54e99ad13e6a7293ba937f0e98cbaeb2ac Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Fri, 30 Aug 2019 17:22:11 -0600 Subject: [PATCH] fix: enable serialization of unsigned transactions --- web3.js/src/transaction.js | 37 +++-- web3.js/test/transaction.test.js | 234 +++++++++++++++++++++++++++++++ 2 files changed, 256 insertions(+), 15 deletions(-) diff --git a/web3.js/src/transaction.js b/web3.js/src/transaction.js index f618d66fb9..510814a5dc 100644 --- a/web3.js/src/transaction.js +++ b/web3.js/src/transaction.js @@ -16,6 +16,13 @@ import type {Blockhash} from './blockhash'; */ export type TransactionSignature = string; +/** + * Default (empty) signature + * + * Signatures are 64 bytes in length + */ +const DEFAULT_SIGNATURE = Array(64).fill(0); + /** * Maximum over-the-wire size of a Transaction * @@ -164,7 +171,6 @@ export class Transaction { } const keys = this.signatures.map(({publicKey}) => publicKey.toString()); - let numRequiredSignatures = 0; let numCreditOnlySignedAccounts = 0; let numCreditOnlyUnsignedAccounts = 0; @@ -175,7 +181,10 @@ export class Transaction { const keyStr = keySignerPair.pubkey.toString(); if (!keys.includes(keyStr)) { if (keySignerPair.isSigner) { - numRequiredSignatures += 1; + this.signatures.push({ + signature: null, + publicKey: keySignerPair.pubkey, + }); if (!keySignerPair.isDebitable) { numCreditOnlySignedAccounts += 1; } @@ -201,12 +210,6 @@ export class Transaction { } }); - if (numRequiredSignatures > this.signatures.length) { - throw new Error( - `Insufficent signatures: expected ${numRequiredSignatures} but got ${this.signatures.length}`, - ); - } - let keyCount = []; shortvec.encodeLength(keyCount, keys.length); @@ -391,12 +394,13 @@ export class Transaction { invariant(signatures.length < 256); Buffer.from(signatureCount).copy(wireTransaction, 0); signatures.forEach(({signature}, index) => { - invariant(signature !== null, `null signature`); - invariant(signature.length === 64, `signature has invalid length`); - Buffer.from(signature).copy( - wireTransaction, - signatureCount.length + index * 64, - ); + if (signature !== null) { + invariant(signature.length === 64, `signature has invalid length`); + Buffer.from(signature).copy( + wireTransaction, + signatureCount.length + index * 64, + ); + } }); signData.copy( wireTransaction, @@ -506,7 +510,10 @@ export class Transaction { transaction.recentBlockhash = new PublicKey(recentBlockhash).toBase58(); for (let i = 0; i < signatureCount; i++) { const sigPubkeyPair = { - signature: Buffer.from(signatures[i]), + signature: + signatures[i].toString() == DEFAULT_SIGNATURE.toString() + ? null + : Buffer.from(signatures[i]), publicKey: new PublicKey(accounts[i]), }; transaction.signatures.push(sigPubkeyPair); diff --git a/web3.js/test/transaction.test.js b/web3.js/test/transaction.test.js index f63210960d..9856f6fa75 100644 --- a/web3.js/test/transaction.test.js +++ b/web3.js/test/transaction.test.js @@ -313,3 +313,237 @@ test('parse wire format and serialize', () => { expect(tx).toEqual(expectedTransaction); expect(wireTransaction).toEqual(expectedTransaction.serialize()); }); + +test('serialize unsigned transaction', () => { + const keypair = nacl.sign.keyPair.fromSeed( + Uint8Array.from(Array(32).fill(8)), + ); + const sender = new Account(Buffer.from(keypair.secretKey)); // Arbitrary known account + const recentBlockhash = 'EETubP5AKHgjPAhzPAFcb8BAY1hMH639CWCFTqi3hq1k'; // Arbitrary known recentBlockhash + const recipient = new PublicKey( + 'J3dxNj7nDRRqRRXuEMynDG57DkZK4jYRuv3Garmb1i99', + ); // Arbitrary known public key + const transfer = SystemProgram.transfer(sender.publicKey, recipient, 49); + const expectedTransaction = new Transaction({recentBlockhash}).add(transfer); + const wireTransaction = Buffer.from([ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 2, + 3, + 19, + 152, + 246, + 44, + 109, + 26, + 69, + 124, + 81, + 186, + 106, + 75, + 95, + 61, + 189, + 47, + 105, + 252, + 169, + 50, + 22, + 33, + 141, + 200, + 153, + 126, + 65, + 107, + 209, + 125, + 147, + 202, + 253, + 67, + 159, + 204, + 182, + 103, + 39, + 242, + 137, + 197, + 198, + 222, + 59, + 196, + 168, + 254, + 93, + 213, + 215, + 119, + 112, + 188, + 143, + 241, + 92, + 62, + 238, + 220, + 177, + 74, + 243, + 252, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 196, + 154, + 231, + 118, + 3, + 120, + 32, + 84, + 241, + 122, + 157, + 236, + 234, + 67, + 180, + 68, + 235, + 160, + 237, + 177, + 44, + 111, + 29, + 49, + 198, + 224, + 228, + 168, + 75, + 240, + 82, + 235, + 1, + 2, + 2, + 0, + 1, + 12, + 2, + 0, + 0, + 0, + 49, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]); + expect(wireTransaction).toEqual(expectedTransaction.serialize()); + + const tx = Transaction.from(wireTransaction); + expect(tx).toEqual(expectedTransaction); +});