From f7034350e927743637658da303e835c21244df4f Mon Sep 17 00:00:00 2001 From: Luke Childs Date: Sun, 26 Apr 2020 15:22:38 +0700 Subject: [PATCH] Clone transaction data more efficiently --- src/bufferutils.js | 6 ++++++ src/psbt.js | 22 +++++++++++++++------- ts_src/bufferutils.ts | 6 ++++++ ts_src/psbt.ts | 31 +++++++++++++++++++------------ types/bufferutils.d.ts | 1 + types/psbt.d.ts | 6 +++--- 6 files changed, 50 insertions(+), 22 deletions(-) diff --git a/src/bufferutils.js b/src/bufferutils.js index 103ca5e..87645c6 100644 --- a/src/bufferutils.js +++ b/src/bufferutils.js @@ -41,6 +41,12 @@ function reverseBuffer(buffer) { return buffer; } exports.reverseBuffer = reverseBuffer; +function cloneBuffer(buffer) { + const clone = Buffer.alloc(buffer.length); + buffer.copy(clone); + return buffer; +} +exports.cloneBuffer = cloneBuffer; /** * Helper class for serialization of bitcoin data types into a pre-allocated buffer. */ diff --git a/src/psbt.js b/src/psbt.js index 95b344f..4519197 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -104,10 +104,23 @@ class Psbt { return this.__CACHE.__TX.locktime; } get txInputs() { - return deepClone(this.__CACHE.__TX.ins); + return this.__CACHE.__TX.ins.map(input => { + return { + hash: bufferutils_1.cloneBuffer(input.hash), + index: input.index, + script: bufferutils_1.cloneBuffer(input.script), + sequence: input.sequence, + witness: input.witness.map(buffer => bufferutils_1.cloneBuffer(buffer)), + }; + }); } get txOutputs() { - return deepClone(this.__CACHE.__TX.outs); + return this.__CACHE.__TX.outs.map(output => { + return { + script: bufferutils_1.cloneBuffer(output.script), + value: output.value, + }; + }); } combine(...those) { this.data.combine(...those.map(o => o.data)); @@ -591,11 +604,6 @@ class PsbtTransaction { return this.tx.toBuffer(); } } -function deepClone(obj) { - return JSON.parse(JSON.stringify(obj), (_, value) => - value.type === 'Buffer' ? Buffer.from(value.data) : value, - ); -} function canFinalize(input, script, scriptType) { switch (scriptType) { case 'pubkey': diff --git a/ts_src/bufferutils.ts b/ts_src/bufferutils.ts index 23e1c0d..087162f 100644 --- a/ts_src/bufferutils.ts +++ b/ts_src/bufferutils.ts @@ -48,6 +48,12 @@ export function reverseBuffer(buffer: Buffer): Buffer { return buffer; } +export function cloneBuffer(buffer: Buffer): Buffer { + const clone = Buffer.alloc(buffer.length); + buffer.copy(clone); + return buffer; +} + /** * Helper class for serialization of bitcoin data types into a pre-allocated buffer. */ diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index d86c714..f5f4e4d 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -14,7 +14,7 @@ import { } from 'bip174/src/lib/interfaces'; import { checkForInput } from 'bip174/src/lib/utils'; import { toOutputScript } from './address'; -import { reverseBuffer } from './bufferutils'; +import { cloneBuffer, reverseBuffer } from './bufferutils'; import { hash160 } from './crypto'; import { fromPublicKey as ecPairFromPublicKey, @@ -24,7 +24,7 @@ import { import { bitcoin as btcNetwork, Network } from './networks'; import * as payments from './payments'; import * as bscript from './script'; -import { Output, Transaction } from './transaction'; +import { Input, Output, Transaction } from './transaction'; /** * These are the default arguments for a Psbt instance. @@ -137,12 +137,25 @@ export class Psbt { return this.__CACHE.__TX.locktime; } - get txInputs(): TransactionInput[] { - return deepClone(this.__CACHE.__TX.ins); + get txInputs(): Input[] { + return this.__CACHE.__TX.ins.map(input => { + return { + hash: cloneBuffer(input.hash), + index: input.index, + script: cloneBuffer(input.script), + sequence: input.sequence, + witness: input.witness.map(buffer => cloneBuffer(buffer)), + }; + }); } - get txOutputs(): TransactionInput[] { - return deepClone(this.__CACHE.__TX.outs); + get txOutputs(): Output[] { + return this.__CACHE.__TX.outs.map(output => { + return { + script: cloneBuffer(output.script), + value: output.value, + }; + }); } combine(...those: Psbt[]): this { @@ -773,12 +786,6 @@ class PsbtTransaction implements ITransaction { } } -function deepClone(obj: any): any { - return JSON.parse(JSON.stringify(obj), (_, value) => - value.type === 'Buffer' ? Buffer.from(value.data) : value, - ); -} - function canFinalize( input: PsbtInput, script: Buffer, diff --git a/types/bufferutils.d.ts b/types/bufferutils.d.ts index 36e31fe..95a48ba 100644 --- a/types/bufferutils.d.ts +++ b/types/bufferutils.d.ts @@ -1,6 +1,7 @@ export declare function readUInt64LE(buffer: Buffer, offset: number): number; export declare function writeUInt64LE(buffer: Buffer, value: number, offset: number): number; export declare function reverseBuffer(buffer: Buffer): Buffer; +export declare function cloneBuffer(buffer: Buffer): Buffer; /** * Helper class for serialization of bitcoin data types into a pre-allocated buffer. */ diff --git a/types/psbt.d.ts b/types/psbt.d.ts index fd8f8dc..a2373c0 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -2,7 +2,7 @@ import { Psbt as PsbtBase } from 'bip174'; import { KeyValue, PsbtGlobalUpdate, PsbtInput, PsbtInputUpdate, PsbtOutput, PsbtOutputUpdate, TransactionInput } from 'bip174/src/lib/interfaces'; import { Signer, SignerAsync } from './ecpair'; import { Network } from './networks'; -import { Transaction } from './transaction'; +import { Input, Output, Transaction } from './transaction'; /** * Psbt class can parse and generate a PSBT binary based off of the BIP174. * There are 6 roles that this class fulfills. (Explained in BIP174) @@ -46,8 +46,8 @@ export declare class Psbt { readonly inputCount: number; readonly txVersion: number; readonly txLocktime: number; - readonly txInputs: TransactionInput[]; - readonly txOutputs: TransactionInput[]; + readonly txInputs: Input[]; + readonly txOutputs: Output[]; combine(...those: Psbt[]): this; clone(): Psbt; setMaximumFeeRate(satoshiPerByte: number): void;