From ad0e71d357c5ff0870bfd8b4c042f31c7d4a5f16 Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Thu, 11 Jun 2020 13:15:14 +0800 Subject: [PATCH] feat: make Transaction.populate method public and tweak MessageArgs --- web3.js/module.d.ts | 3 +- web3.js/module.flow.js | 3 +- web3.js/src/connection.js | 27 +++++++------- web3.js/src/message.js | 6 ++-- web3.js/src/transaction.js | 60 ++++++++------------------------ web3.js/test/transaction.test.js | 56 ++++++++++++++--------------- 6 files changed, 64 insertions(+), 91 deletions(-) diff --git a/web3.js/module.d.ts b/web3.js/module.d.ts index 75de22b5eb..1ea36ca15d 100644 --- a/web3.js/module.d.ts +++ b/web3.js/module.d.ts @@ -423,7 +423,7 @@ declare module '@solana/web3.js' { export type MessageArgs = { header: MessageHeader; - accountKeys: PublicKey[]; + accountKeys: string[]; recentBlockhash: Blockhash; instructions: CompiledInstruction[]; }; @@ -487,6 +487,7 @@ declare module '@solana/web3.js' { constructor(opts?: TransactionCtorFields); static from(buffer: Buffer | Uint8Array | Array): Transaction; + static populate(message: Message, signatures: Array): Transaction; add( ...items: Array< Transaction | TransactionInstruction | TransactionInstructionCtorFields diff --git a/web3.js/module.flow.js b/web3.js/module.flow.js index 9352bacb6a..f7f751eea7 100644 --- a/web3.js/module.flow.js +++ b/web3.js/module.flow.js @@ -433,7 +433,7 @@ declare module '@solana/web3.js' { declare export type MessageArgs = { header: MessageHeader, - accountKeys: PublicKey[], + accountKeys: string[], recentBlockhash: Blockhash, instructions: CompiledInstruction[], }; @@ -499,6 +499,7 @@ declare module '@solana/web3.js' { constructor(opts?: TransactionCtorFields): Transaction; static from(buffer: Buffer | Uint8Array | Array): Transaction; + static populate(message: Message, signatures: Array): Transaction; add( ...items: Array< Transaction | TransactionInstruction | TransactionInstructionCtorFields, diff --git a/web3.js/src/connection.js b/web3.js/src/connection.js index 3788e2ce0b..474aa43a2c 100644 --- a/web3.js/src/connection.js +++ b/web3.js/src/connection.js @@ -12,6 +12,7 @@ import {NonceAccount} from './nonce-account'; import {PublicKey} from './publickey'; import {DEFAULT_TICKS_PER_SLOT, NUM_TICKS_PER_SECOND} from './timing'; import {Transaction} from './transaction'; +import {Message} from './message'; import {sleep} from './util/sleep'; import {toBuffer} from './util/to-buffer'; import type {Blockhash} from './blockhash'; @@ -1580,27 +1581,26 @@ export class Connection { */ async getConfirmedBlock(slot: number): Promise { const unsafeRes = await this._rpcRequest('getConfirmedBlock', [slot]); - const result = GetConfirmedBlockRpcResult(unsafeRes); - if (result.error) { + const {result, error} = GetConfirmedBlockRpcResult(unsafeRes); + if (error) { throw new Error('failed to get confirmed block: ' + result.error.message); } - assert(typeof result.result !== 'undefined'); - if (!result.result) { + assert(typeof result !== 'undefined'); + if (!result) { throw new Error('Confirmed block ' + slot + ' not found'); } return { - blockhash: new PublicKey(result.result.blockhash).toString(), - previousBlockhash: new PublicKey( - result.result.previousBlockhash, - ).toString(), - parentSlot: result.result.parentSlot, - transactions: result.result.transactions.map(result => { + blockhash: new PublicKey(result.blockhash).toString(), + previousBlockhash: new PublicKey(result.previousBlockhash).toString(), + parentSlot: result.parentSlot, + transactions: result.transactions.map(result => { + const {message, signatures} = result.transaction; return { - transaction: Transaction.fromRpcResult(result.transaction), + transaction: Transaction.populate(new Message(message), signatures), meta: result.meta, }; }), - rewards: result.result.rewards || [], + rewards: result.rewards || [], }; } @@ -1622,9 +1622,10 @@ export class Connection { return result; } + const {message, signatures} = result.transaction; return { slot: result.slot, - transaction: Transaction.fromRpcResult(result.transaction), + transaction: Transaction.populate(new Message(message), signatures), meta: result.meta, }; } diff --git a/web3.js/src/message.js b/web3.js/src/message.js index 8c0fe7d7d7..40b9a521a9 100644 --- a/web3.js/src/message.js +++ b/web3.js/src/message.js @@ -42,13 +42,13 @@ export type CompiledInstruction = { * * @typedef {Object} MessageArgs * @property {MessageHeader} header The message header, identifying signed and read-only `accountKeys` - * @property {PublicKey[]} accounts All the account keys used by this transaction + * @property {string[]} accounts All the account keys used by this transaction * @property {Blockhash} recentBlockhash The hash of a recent ledger block * @property {CompiledInstruction[]} instructions Instructions that will be executed in sequence and committed in one atomic transaction if all succeed. */ type MessageArgs = { header: MessageHeader, - accountKeys: PublicKey[], + accountKeys: string[], recentBlockhash: Blockhash, instructions: CompiledInstruction[], }; @@ -64,7 +64,7 @@ export class Message { constructor(args: MessageArgs) { this.header = args.header; - this.accountKeys = args.accountKeys; + this.accountKeys = args.accountKeys.map(account => new PublicKey(account)); this.recentBlockhash = args.recentBlockhash; this.instructions = args.instructions; } diff --git a/web3.js/src/transaction.js b/web3.js/src/transaction.js index 08086e042a..6b4ec95d4d 100644 --- a/web3.js/src/transaction.js +++ b/web3.js/src/transaction.js @@ -210,7 +210,7 @@ export class Transaction { let numReadonlySignedAccounts = 0; let numReadonlyUnsignedAccounts = 0; - const keys = this.signatures.map(({publicKey}) => publicKey.toString()); + const accountKeys = this.signatures.map(({publicKey}) => publicKey.toString()); const programIds: string[] = []; const accountMetas: AccountMeta[] = []; this.instructions.forEach(instruction => { @@ -233,8 +233,8 @@ export class Transaction { accountMetas.forEach(({pubkey, isSigner, isWritable}) => { const keyStr = pubkey.toString(); - if (!keys.includes(keyStr)) { - keys.push(keyStr); + if (!accountKeys.includes(keyStr)) { + accountKeys.push(keyStr); if (isSigner) { this.signatures.push({ signature: null, @@ -250,8 +250,8 @@ export class Transaction { }); programIds.forEach(programId => { - if (!keys.includes(programId)) { - keys.push(programId); + if (!accountKeys.includes(programId)) { + accountKeys.push(programId); numReadonlyUnsignedAccounts += 1; } }); @@ -260,9 +260,9 @@ export class Transaction { instruction => { const {data, programId} = instruction; return { - programIdIndex: keys.indexOf(programId.toString()), + programIdIndex: accountKeys.indexOf(programId.toString()), accounts: instruction.keys.map(keyObj => - keys.indexOf(keyObj.pubkey.toString()), + accountKeys.indexOf(keyObj.pubkey.toString()), ), data: bs58.encode(data), }; @@ -280,7 +280,7 @@ export class Transaction { numReadonlySignedAccounts, numReadonlyUnsignedAccounts, }, - accountKeys: keys.map(k => new PublicKey(k)), + accountKeys, recentBlockhash, instructions, }); @@ -477,11 +477,11 @@ export class Transaction { const numReadonlyUnsignedAccounts = byteArray.shift(); const accountCount = shortvec.decodeLength(byteArray); - let accounts = []; + let accountKeys = []; for (let i = 0; i < accountCount; i++) { const account = byteArray.slice(0, PUBKEY_LENGTH); byteArray = byteArray.slice(PUBKEY_LENGTH); - accounts.push(bs58.encode(Buffer.from(account))); + accountKeys.push(bs58.encode(Buffer.from(account))); } const recentBlockhash = byteArray.slice(0, PUBKEY_LENGTH); @@ -502,54 +502,24 @@ export class Transaction { instructions.push(instruction); } - const message = { + const messageArgs = { header: { numRequiredSignatures, numReadonlySignedAccounts, numReadonlyUnsignedAccounts, }, recentBlockhash: bs58.encode(Buffer.from(recentBlockhash)), - accountKeys: accounts.map(account => new PublicKey(account)), + accountKeys, instructions, }; - return Transaction._populate(signatures, new Message(message)); + return Transaction.populate(new Message(messageArgs), signatures); } /** - * Parse an RPC result into a Transaction object. + * Populate Transaction object from message and signatures */ - static fromRpcResult(rpcResult: any): Transaction { - const signatures = rpcResult.signatures; - const accounts = rpcResult.message.accountKeys; - const instructions = rpcResult.message.instructions; - const recentBlockhash = rpcResult.message.recentBlockhash; - const numRequiredSignatures = - rpcResult.message.header.numRequiredSignatures; - const numReadonlySignedAccounts = - rpcResult.message.header.numReadonlySignedAccounts; - const numReadonlyUnsignedAccounts = - rpcResult.message.header.numReadonlyUnsignedAccounts; - - const message = { - header: { - numRequiredSignatures, - numReadonlySignedAccounts, - numReadonlyUnsignedAccounts, - }, - recentBlockhash, - accountKeys: accounts.map(account => new PublicKey(account)), - instructions, - }; - - return Transaction._populate(signatures, new Message(message)); - } - - /** - * Populate Transaction object - * @private - */ - static _populate(signatures: Array, message: Message): Transaction { + static populate(message: Message, signatures: Array): Transaction { const transaction = new Transaction(); transaction.recentBlockhash = message.recentBlockhash; signatures.forEach((signature, index) => { diff --git a/web3.js/test/transaction.test.js b/web3.js/test/transaction.test.js index 5abab196f3..630b07296a 100644 --- a/web3.js/test/transaction.test.js +++ b/web3.js/test/transaction.test.js @@ -7,6 +7,7 @@ import {PublicKey} from '../src/publickey'; import {Transaction} from '../src/transaction'; import {StakeProgram} from '../src/stake-program'; import {SystemProgram} from '../src/system-program'; +import {Message} from '../src/message'; test('signPartial', () => { const account1 = new Account(); @@ -159,38 +160,37 @@ test('parse wire format and serialize', () => { expect(wireTransaction).toEqual(expectedTransaction.serialize()); }); -test('transaction from rpc result', () => { +test('populate transaction', () => { const recentBlockhash = new PublicKey(1).toString(); - const rpcResult = { - message: { - accountKeys: [ - new PublicKey(1).toString(), - new PublicKey(2).toString(), - new PublicKey(3).toString(), - new PublicKey(4).toString(), - new PublicKey(5).toString(), - ], - header: { - num_ReadonlySignedAccounts: 0, - numReadonlyUnsignedAccounts: 3, - numRequiredSignatures: 2, - }, - instructions: [ - { - accounts: [1, 2, 3], - data: bs58.encode(Buffer.alloc(5).fill(9)), - programIdIndex: 4, - }, - ], - recentBlockhash, - }, - signatures: [ - bs58.encode(Buffer.alloc(64).fill(1)), - bs58.encode(Buffer.alloc(64).fill(2)), + const message = { + accountKeys: [ + new PublicKey(1).toString(), + new PublicKey(2).toString(), + new PublicKey(3).toString(), + new PublicKey(4).toString(), + new PublicKey(5).toString(), ], + header: { + numReadonlySignedAccounts: 0, + numReadonlyUnsignedAccounts: 3, + numRequiredSignatures: 2, + }, + instructions: [ + { + accounts: [1, 2, 3], + data: bs58.encode(Buffer.alloc(5).fill(9)), + programIdIndex: 4, + }, + ], + recentBlockhash, }; - const transaction = Transaction.fromRpcResult(rpcResult); + const signatures = [ + bs58.encode(Buffer.alloc(64).fill(1)), + bs58.encode(Buffer.alloc(64).fill(2)), + ]; + + const transaction = Transaction.populate(new Message(message), signatures); expect(transaction.instructions.length).toEqual(1); expect(transaction.signatures.length).toEqual(2); expect(transaction.recentBlockhash).toEqual(recentBlockhash);