diff --git a/src/psbt.js b/src/psbt.js index b9ed061..4a6306f 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -11,6 +11,39 @@ const bscript = require('./script'); const transaction_1 = require('./transaction'); const varuint = require('varuint-bitcoin'); class Psbt extends bip174_1.Psbt { + static fromTransaction(txBuf) { + const tx = transaction_1.Transaction.fromBuffer(txBuf); + const psbt = new this(); + psbt.__TX = tx; + let inputCount = tx.ins.length; + let outputCount = tx.outs.length; + while (inputCount > 0) { + psbt.inputs.push({ + keyVals: [], + }); + inputCount--; + } + while (outputCount > 0) { + psbt.outputs.push({ + keyVals: [], + }); + outputCount--; + } + return psbt; + } + static fromBuffer(buffer) { + let tx; + const txCountGetter = txBuf => { + tx = transaction_1.Transaction.fromBuffer(txBuf); + return { + inputCount: tx.ins.length, + outputCount: tx.outs.length, + }; + }; + const psbt = super.fromBuffer(buffer, txCountGetter); + psbt.__TX = tx; + return psbt; + } constructor(opts = {}) { super(); // set defaults @@ -40,7 +73,7 @@ class Psbt extends bip174_1.Psbt { enumerable, writable, }); - dpew(this, '__TX', false, false); + dpew(this, '__TX', false, true); dpew(this, '__TX_BUF_CACHE', false, true); dpew(this, 'opts', false, true); } @@ -112,7 +145,7 @@ class Psbt extends bip174_1.Psbt { } extractTransaction() { if (!this.inputs.every(isFinalized)) throw new Error('Not finalized'); - const tx = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx); + const tx = this.__TX.clone(); this.inputs.forEach((input, idx) => { if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig; if (input.finalScriptWitness) { @@ -138,7 +171,7 @@ class Psbt extends bip174_1.Psbt { const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( inputIndex, input, - this.globalMap.unsignedTx, + this.__TX, ); if (!script) return false; const scriptType = classifyScript(script); @@ -166,7 +199,7 @@ class Psbt extends bip174_1.Psbt { this.inputs, inputIndex, keyPair.publicKey, - this.globalMap.unsignedTx, + this.__TX, ); const partialSig = { pubkey: keyPair.publicKey, @@ -182,7 +215,7 @@ class Psbt extends bip174_1.Psbt { this.inputs, inputIndex, keyPair.publicKey, - this.globalMap.unsignedTx, + this.__TX, ); Promise.resolve(keyPair.sign(hash)).then(signature => { const partialSig = { @@ -202,9 +235,13 @@ const DEFAULT_OPTS = { function isFinalized(input) { return !!input.finalScriptSig || !!input.finalScriptWitness; } -function getHashAndSighashType(inputs, inputIndex, pubkey, txBuf) { +function getHashAndSighashType(inputs, inputIndex, pubkey, unsignedTx) { const input = utils_1.checkForInput(inputs, inputIndex); - const { hash, sighashType, script } = getHashForSig(inputIndex, input, txBuf); + const { hash, sighashType, script } = getHashForSig( + inputIndex, + input, + unsignedTx, + ); checkScriptForPubkey(pubkey, script); return { hash, @@ -322,8 +359,7 @@ function checkScriptForPubkey(pubkey, script) { ); } } -const getHashForSig = (inputIndex, input, txBuf) => { - const unsignedTx = transaction_1.Transaction.fromBuffer(txBuf); +const getHashForSig = (inputIndex, input, unsignedTx) => { const sighashType = input.sighashType || transaction_1.Transaction.SIGHASH_ALL; let hash; @@ -442,7 +478,7 @@ const classifyScript = script => { if (isP2PK(script)) return 'pubkey'; return 'nonstandard'; }; -function getScriptFromInput(inputIndex, input, _unsignedTx) { +function getScriptFromInput(inputIndex, input, unsignedTx) { const res = { script: null, isSegwit: false, @@ -454,7 +490,6 @@ function getScriptFromInput(inputIndex, input, _unsignedTx) { res.isP2SH = true; res.script = input.redeemScript; } else { - const unsignedTx = transaction_1.Transaction.fromBuffer(_unsignedTx); const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer( input.nonWitnessUtxo, ); diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 2c36130..0aee319 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -17,6 +17,50 @@ import { Transaction } from './transaction'; const varuint = require('varuint-bitcoin'); export class Psbt extends PsbtBase { + static fromTransaction( + this: T, + txBuf: Buffer, + ): InstanceType { + const tx = Transaction.fromBuffer(txBuf); + const psbt = new this() as Psbt; + psbt.__TX = tx; + let inputCount = tx.ins.length; + let outputCount = tx.outs.length; + while (inputCount > 0) { + psbt.inputs.push({ + keyVals: [], + }); + inputCount--; + } + while (outputCount > 0) { + psbt.outputs.push({ + keyVals: [], + }); + outputCount--; + } + return psbt as InstanceType; + } + static fromBuffer( + this: T, + buffer: Buffer, + ): InstanceType { + let tx: Transaction | undefined; + const txCountGetter = ( + txBuf: Buffer, + ): { + inputCount: number; + outputCount: number; + } => { + tx = Transaction.fromBuffer(txBuf); + return { + inputCount: tx.ins.length, + outputCount: tx.outs.length, + }; + }; + const psbt = super.fromBuffer(buffer, txCountGetter) as Psbt; + psbt.__TX = tx!; + return psbt as InstanceType; + } private __TX: Transaction; private __TX_BUF_CACHE?: Buffer; private opts: PsbtOpts; @@ -56,7 +100,7 @@ export class Psbt extends PsbtBase { enumerable, writable, }); - dpew(this, '__TX', false, false); + dpew(this, '__TX', false, true); dpew(this, '__TX_BUF_CACHE', false, true); dpew(this, 'opts', false, true); } @@ -138,7 +182,7 @@ export class Psbt extends PsbtBase { extractTransaction(): Transaction { if (!this.inputs.every(isFinalized)) throw new Error('Not finalized'); - const tx = Transaction.fromBuffer(this.globalMap.unsignedTx!); + const tx = this.__TX.clone(); this.inputs.forEach((input, idx) => { if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig; if (input.finalScriptWitness) { @@ -169,7 +213,7 @@ export class Psbt extends PsbtBase { const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( inputIndex, input, - this.globalMap.unsignedTx!, + this.__TX, ); if (!script) return false; @@ -195,14 +239,14 @@ export class Psbt extends PsbtBase { return true; } - signInput(inputIndex: number, keyPair: Signer): Psbt { + signInput(inputIndex: number, keyPair: Signer): this { if (!keyPair || !keyPair.publicKey) throw new Error('Need Signer to sign input'); const { hash, sighashType } = getHashAndSighashType( this.inputs, inputIndex, keyPair.publicKey, - this.globalMap.unsignedTx!, + this.__TX, ); const partialSig = { @@ -222,7 +266,7 @@ export class Psbt extends PsbtBase { this.inputs, inputIndex, keyPair.publicKey, - this.globalMap.unsignedTx!, + this.__TX, ); Promise.resolve(keyPair.sign(hash)).then(signature => { @@ -269,13 +313,17 @@ function getHashAndSighashType( inputs: PsbtInput[], inputIndex: number, pubkey: Buffer, - txBuf: Buffer, + unsignedTx: Transaction, ): { hash: Buffer; sighashType: number; } { const input = checkForInput(inputs, inputIndex); - const { hash, sighashType, script } = getHashForSig(inputIndex, input, txBuf); + const { hash, sighashType, script } = getHashForSig( + inputIndex, + input, + unsignedTx, + ); checkScriptForPubkey(pubkey, script); return { hash, @@ -424,9 +472,8 @@ interface HashForSigData { const getHashForSig = ( inputIndex: number, input: PsbtInput, - txBuf: Buffer, + unsignedTx: Transaction, ): HashForSigData => { - const unsignedTx = Transaction.fromBuffer(txBuf); const sighashType = input.sighashType || Transaction.SIGHASH_ALL; let hash: Buffer; let script: Buffer; @@ -571,7 +618,7 @@ interface GetScriptReturn { function getScriptFromInput( inputIndex: number, input: PsbtInput, - _unsignedTx: Buffer, + unsignedTx: Transaction, ): GetScriptReturn { const res: GetScriptReturn = { script: null, @@ -584,7 +631,6 @@ function getScriptFromInput( res.isP2SH = true; res.script = input.redeemScript; } else { - const unsignedTx = Transaction.fromBuffer(_unsignedTx); const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo); const prevoutIndex = unsignedTx.ins[inputIndex].index; res.script = nonWitnessUtxoTx.outs[prevoutIndex].script; diff --git a/types/psbt.d.ts b/types/psbt.d.ts index 213c31b..a203854 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -1,9 +1,12 @@ +/// import { Psbt as PsbtBase } from 'bip174'; import { TransactionInput, TransactionOutput } from 'bip174/src/lib/interfaces'; import { Signer, SignerAsync } from './ecpair'; import { Network } from './networks'; import { Transaction } from './transaction'; export declare class Psbt extends PsbtBase { + static fromTransaction(this: T, txBuf: Buffer): InstanceType; + static fromBuffer(this: T, buffer: Buffer): InstanceType; private __TX; private __TX_BUF_CACHE?; private opts; @@ -18,7 +21,7 @@ export declare class Psbt extends PsbtBase { inputResults: boolean[]; }; finalizeInput(inputIndex: number): boolean; - signInput(inputIndex: number, keyPair: Signer): Psbt; + signInput(inputIndex: number, keyPair: Signer): this; signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise; } interface PsbtOptsOptional {