Remove unnecessary extra Transaction Buffer parsing

This commit is contained in:
junderw 2019-07-04 14:33:36 +09:00
parent b98761a283
commit 5b5daf84dd
No known key found for this signature in database
GPG key ID: B256185D3A971908
3 changed files with 108 additions and 24 deletions

View file

@ -11,6 +11,39 @@ const bscript = require('./script');
const transaction_1 = require('./transaction'); const transaction_1 = require('./transaction');
const varuint = require('varuint-bitcoin'); const varuint = require('varuint-bitcoin');
class Psbt extends bip174_1.Psbt { 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 = {}) { constructor(opts = {}) {
super(); super();
// set defaults // set defaults
@ -40,7 +73,7 @@ class Psbt extends bip174_1.Psbt {
enumerable, enumerable,
writable, writable,
}); });
dpew(this, '__TX', false, false); dpew(this, '__TX', false, true);
dpew(this, '__TX_BUF_CACHE', false, true); dpew(this, '__TX_BUF_CACHE', false, true);
dpew(this, 'opts', false, true); dpew(this, 'opts', false, true);
} }
@ -112,7 +145,7 @@ class Psbt extends bip174_1.Psbt {
} }
extractTransaction() { extractTransaction() {
if (!this.inputs.every(isFinalized)) throw new Error('Not finalized'); 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) => { this.inputs.forEach((input, idx) => {
if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig; if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig;
if (input.finalScriptWitness) { if (input.finalScriptWitness) {
@ -138,7 +171,7 @@ class Psbt extends bip174_1.Psbt {
const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
inputIndex, inputIndex,
input, input,
this.globalMap.unsignedTx, this.__TX,
); );
if (!script) return false; if (!script) return false;
const scriptType = classifyScript(script); const scriptType = classifyScript(script);
@ -166,7 +199,7 @@ class Psbt extends bip174_1.Psbt {
this.inputs, this.inputs,
inputIndex, inputIndex,
keyPair.publicKey, keyPair.publicKey,
this.globalMap.unsignedTx, this.__TX,
); );
const partialSig = { const partialSig = {
pubkey: keyPair.publicKey, pubkey: keyPair.publicKey,
@ -182,7 +215,7 @@ class Psbt extends bip174_1.Psbt {
this.inputs, this.inputs,
inputIndex, inputIndex,
keyPair.publicKey, keyPair.publicKey,
this.globalMap.unsignedTx, this.__TX,
); );
Promise.resolve(keyPair.sign(hash)).then(signature => { Promise.resolve(keyPair.sign(hash)).then(signature => {
const partialSig = { const partialSig = {
@ -202,9 +235,13 @@ const DEFAULT_OPTS = {
function isFinalized(input) { function isFinalized(input) {
return !!input.finalScriptSig || !!input.finalScriptWitness; 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 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); checkScriptForPubkey(pubkey, script);
return { return {
hash, hash,
@ -322,8 +359,7 @@ function checkScriptForPubkey(pubkey, script) {
); );
} }
} }
const getHashForSig = (inputIndex, input, txBuf) => { const getHashForSig = (inputIndex, input, unsignedTx) => {
const unsignedTx = transaction_1.Transaction.fromBuffer(txBuf);
const sighashType = const sighashType =
input.sighashType || transaction_1.Transaction.SIGHASH_ALL; input.sighashType || transaction_1.Transaction.SIGHASH_ALL;
let hash; let hash;
@ -442,7 +478,7 @@ const classifyScript = script => {
if (isP2PK(script)) return 'pubkey'; if (isP2PK(script)) return 'pubkey';
return 'nonstandard'; return 'nonstandard';
}; };
function getScriptFromInput(inputIndex, input, _unsignedTx) { function getScriptFromInput(inputIndex, input, unsignedTx) {
const res = { const res = {
script: null, script: null,
isSegwit: false, isSegwit: false,
@ -454,7 +490,6 @@ function getScriptFromInput(inputIndex, input, _unsignedTx) {
res.isP2SH = true; res.isP2SH = true;
res.script = input.redeemScript; res.script = input.redeemScript;
} else { } else {
const unsignedTx = transaction_1.Transaction.fromBuffer(_unsignedTx);
const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer( const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer(
input.nonWitnessUtxo, input.nonWitnessUtxo,
); );

View file

@ -17,6 +17,50 @@ import { Transaction } from './transaction';
const varuint = require('varuint-bitcoin'); const varuint = require('varuint-bitcoin');
export class Psbt extends PsbtBase { export class Psbt extends PsbtBase {
static fromTransaction<T extends typeof PsbtBase>(
this: T,
txBuf: Buffer,
): InstanceType<T> {
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<T>;
}
static fromBuffer<T extends typeof PsbtBase>(
this: T,
buffer: Buffer,
): InstanceType<T> {
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<T>;
}
private __TX: Transaction; private __TX: Transaction;
private __TX_BUF_CACHE?: Buffer; private __TX_BUF_CACHE?: Buffer;
private opts: PsbtOpts; private opts: PsbtOpts;
@ -56,7 +100,7 @@ export class Psbt extends PsbtBase {
enumerable, enumerable,
writable, writable,
}); });
dpew(this, '__TX', false, false); dpew(this, '__TX', false, true);
dpew(this, '__TX_BUF_CACHE', false, true); dpew(this, '__TX_BUF_CACHE', false, true);
dpew(this, 'opts', false, true); dpew(this, 'opts', false, true);
} }
@ -138,7 +182,7 @@ export class Psbt extends PsbtBase {
extractTransaction(): Transaction { extractTransaction(): Transaction {
if (!this.inputs.every(isFinalized)) throw new Error('Not finalized'); 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) => { this.inputs.forEach((input, idx) => {
if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig; if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig;
if (input.finalScriptWitness) { if (input.finalScriptWitness) {
@ -169,7 +213,7 @@ export class Psbt extends PsbtBase {
const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
inputIndex, inputIndex,
input, input,
this.globalMap.unsignedTx!, this.__TX,
); );
if (!script) return false; if (!script) return false;
@ -195,14 +239,14 @@ export class Psbt extends PsbtBase {
return true; return true;
} }
signInput(inputIndex: number, keyPair: Signer): Psbt { signInput(inputIndex: number, keyPair: Signer): this {
if (!keyPair || !keyPair.publicKey) if (!keyPair || !keyPair.publicKey)
throw new Error('Need Signer to sign input'); throw new Error('Need Signer to sign input');
const { hash, sighashType } = getHashAndSighashType( const { hash, sighashType } = getHashAndSighashType(
this.inputs, this.inputs,
inputIndex, inputIndex,
keyPair.publicKey, keyPair.publicKey,
this.globalMap.unsignedTx!, this.__TX,
); );
const partialSig = { const partialSig = {
@ -222,7 +266,7 @@ export class Psbt extends PsbtBase {
this.inputs, this.inputs,
inputIndex, inputIndex,
keyPair.publicKey, keyPair.publicKey,
this.globalMap.unsignedTx!, this.__TX,
); );
Promise.resolve(keyPair.sign(hash)).then(signature => { Promise.resolve(keyPair.sign(hash)).then(signature => {
@ -269,13 +313,17 @@ function getHashAndSighashType(
inputs: PsbtInput[], inputs: PsbtInput[],
inputIndex: number, inputIndex: number,
pubkey: Buffer, pubkey: Buffer,
txBuf: Buffer, unsignedTx: Transaction,
): { ): {
hash: Buffer; hash: Buffer;
sighashType: number; sighashType: number;
} { } {
const input = checkForInput(inputs, inputIndex); const input = checkForInput(inputs, inputIndex);
const { hash, sighashType, script } = getHashForSig(inputIndex, input, txBuf); const { hash, sighashType, script } = getHashForSig(
inputIndex,
input,
unsignedTx,
);
checkScriptForPubkey(pubkey, script); checkScriptForPubkey(pubkey, script);
return { return {
hash, hash,
@ -424,9 +472,8 @@ interface HashForSigData {
const getHashForSig = ( const getHashForSig = (
inputIndex: number, inputIndex: number,
input: PsbtInput, input: PsbtInput,
txBuf: Buffer, unsignedTx: Transaction,
): HashForSigData => { ): HashForSigData => {
const unsignedTx = Transaction.fromBuffer(txBuf);
const sighashType = input.sighashType || Transaction.SIGHASH_ALL; const sighashType = input.sighashType || Transaction.SIGHASH_ALL;
let hash: Buffer; let hash: Buffer;
let script: Buffer; let script: Buffer;
@ -571,7 +618,7 @@ interface GetScriptReturn {
function getScriptFromInput( function getScriptFromInput(
inputIndex: number, inputIndex: number,
input: PsbtInput, input: PsbtInput,
_unsignedTx: Buffer, unsignedTx: Transaction,
): GetScriptReturn { ): GetScriptReturn {
const res: GetScriptReturn = { const res: GetScriptReturn = {
script: null, script: null,
@ -584,7 +631,6 @@ function getScriptFromInput(
res.isP2SH = true; res.isP2SH = true;
res.script = input.redeemScript; res.script = input.redeemScript;
} else { } else {
const unsignedTx = Transaction.fromBuffer(_unsignedTx);
const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo); const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo);
const prevoutIndex = unsignedTx.ins[inputIndex].index; const prevoutIndex = unsignedTx.ins[inputIndex].index;
res.script = nonWitnessUtxoTx.outs[prevoutIndex].script; res.script = nonWitnessUtxoTx.outs[prevoutIndex].script;

5
types/psbt.d.ts vendored
View file

@ -1,9 +1,12 @@
/// <reference types="node" />
import { Psbt as PsbtBase } from 'bip174'; import { Psbt as PsbtBase } from 'bip174';
import { TransactionInput, TransactionOutput } from 'bip174/src/lib/interfaces'; import { TransactionInput, TransactionOutput } from 'bip174/src/lib/interfaces';
import { Signer, SignerAsync } from './ecpair'; import { Signer, SignerAsync } from './ecpair';
import { Network } from './networks'; import { Network } from './networks';
import { Transaction } from './transaction'; import { Transaction } from './transaction';
export declare class Psbt extends PsbtBase { export declare class Psbt extends PsbtBase {
static fromTransaction<T extends typeof PsbtBase>(this: T, txBuf: Buffer): InstanceType<T>;
static fromBuffer<T extends typeof PsbtBase>(this: T, buffer: Buffer): InstanceType<T>;
private __TX; private __TX;
private __TX_BUF_CACHE?; private __TX_BUF_CACHE?;
private opts; private opts;
@ -18,7 +21,7 @@ export declare class Psbt extends PsbtBase {
inputResults: boolean[]; inputResults: boolean[];
}; };
finalizeInput(inputIndex: number): boolean; finalizeInput(inputIndex: number): boolean;
signInput(inputIndex: number, keyPair: Signer): Psbt; signInput(inputIndex: number, keyPair: Signer): this;
signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void>; signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void>;
} }
interface PsbtOptsOptional { interface PsbtOptsOptional {