From f72c915ff150188575c0766ee4ab6dbc622bfed0 Mon Sep 17 00:00:00 2001 From: junderw Date: Mon, 1 Jul 2019 19:57:35 +0900 Subject: [PATCH] Start towards finalizing inputs --- package-lock.json | 6 ++--- package.json | 2 +- src/psbt.js | 60 ++++++++++++++++++++++++++++++++++++++++--- ts_src/psbt.ts | 65 ++++++++++++++++++++++++++++++++++++++++++++--- types/psbt.d.ts | 5 +++- 5 files changed, 125 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index d409322..4195bf2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -200,9 +200,9 @@ } }, "bip174": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.8.tgz", - "integrity": "sha512-xWPzmlCvLoOWTlXk1wG7+TyOfaN8xX07IieuG4ug5su3igC9s4Lsdq+IEEMo+YHDQ4hPPAX9LYio6aEIAA+Zrg==" + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.10.tgz", + "integrity": "sha512-gFtSEMayg7HPKGnIQcEx5CqD/qHWuMlxLJ/+VV4k4Q2mcA0rY040JbNpFuCGVI6rJYv211f0NA7nkU4xkPX4nQ==" }, "bip32": { "version": "2.0.3", diff --git a/package.json b/package.json index b964697..1346928 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "dependencies": { "@types/node": "10.12.18", "bech32": "^1.1.2", - "bip174": "0.0.8", + "bip174": "0.0.10", "bip32": "^2.0.3", "bip66": "^1.1.0", "bitcoin-ops": "^1.4.0", diff --git a/src/psbt.js b/src/psbt.js index 9a9ad7e..9d00e41 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -1,6 +1,8 @@ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); const bip174_1 = require('bip174'); +const utils_1 = require('bip174/src/lib/utils'); +const classify = require('./classify'); const payments = require('./payments'); const bscript = require('./script'); const transaction_1 = require('./transaction'); @@ -23,17 +25,67 @@ const checkWitnessScript = scriptCheckerFactory( payments.p2wsh, 'Witness script', ); -const isP2WPKH = script => { +const isPayment = (script, payment) => { try { - payments.p2wpkh({ output: script }); + payment({ output: script }); return true; } catch (err) { return false; } }; +function getScriptFromInput(inputIndex, input, _unsignedTx) { + let script; + if (input.nonWitnessUtxo) { + if (input.redeemScript) { + script = input.redeemScript; + } else { + const unsignedTx = transaction_1.Transaction.fromBuffer(_unsignedTx); + const nonWitnessUtxoTx = transaction_1.Transaction.fromBuffer( + input.nonWitnessUtxo, + ); + const prevoutIndex = unsignedTx.ins[inputIndex].index; + script = nonWitnessUtxoTx.outs[prevoutIndex].script; + } + } else if (input.witnessUtxo) { + if (input.witnessScript) { + script = input.witnessScript; + } else if (input.redeemScript) { + script = payments.p2pkh({ hash: input.redeemScript.slice(2) }).output; + } else { + script = payments.p2pkh({ hash: input.witnessUtxo.script.slice(2) }) + .output; + } + } else { + return; + } + return script; +} class Psbt extends bip174_1.Psbt { - constructor() { + constructor(network) { super(); + this.network = network; + } + canFinalize(inputIndex) { + const input = utils_1.checkForInput(this.inputs, inputIndex); + const script = getScriptFromInput( + inputIndex, + input, + this.globalMap.unsignedTx, + ); + if (!script) return false; + const scriptType = classify.output(script); + switch (scriptType) { + case 'pubkey': + return false; + case 'pubkeyhash': + return false; + case 'multisig': + return false; + case 'witnesspubkeyhash': + return false; + default: + return false; + } } signInput(inputIndex, keyPair) { // TODO: Implement BIP174 pre-sign checks: @@ -108,7 +160,7 @@ class Psbt extends bip174_1.Psbt { } else { script = input.witnessUtxo.script; } - if (isP2WPKH(script)) { + if (isPayment(script, payments.p2wpkh)) { // P2WPKH uses the P2PKH template for prevoutScript when signing const signingScript = payments.p2pkh({ hash: script.slice(2) }).output; hash = unsignedTx.hashForWitnessV0( diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 5ec9ef1..2f0b6ea 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -1,5 +1,9 @@ import { Psbt as PsbtBase } from 'bip174'; +import { PsbtInput } from 'bip174/src/lib/interfaces'; +import { checkForInput } from 'bip174/src/lib/utils'; +import * as classify from './classify'; import { Signer } from './ecpair'; +import { Network } from './networks'; import * as payments from './payments'; import * as bscript from './script'; import { Transaction } from './transaction'; @@ -31,20 +35,73 @@ const checkWitnessScript = scriptCheckerFactory( 'Witness script', ); -const isP2WPKH = (script: Buffer): boolean => { +const isPayment = (script: Buffer, payment: any): boolean => { try { - payments.p2wpkh({ output: script }); + payment({ output: script }); return true; } catch (err) { return false; } }; +function getScriptFromInput( + inputIndex: number, + input: PsbtInput, + _unsignedTx: Buffer, +): Buffer | undefined { + let script: Buffer; + if (input.nonWitnessUtxo) { + if (input.redeemScript) { + script = input.redeemScript; + } else { + const unsignedTx = Transaction.fromBuffer(_unsignedTx); + const nonWitnessUtxoTx = Transaction.fromBuffer(input.nonWitnessUtxo); + const prevoutIndex = unsignedTx.ins[inputIndex].index; + script = nonWitnessUtxoTx.outs[prevoutIndex].script; + } + } else if (input.witnessUtxo) { + if (input.witnessScript) { + script = input.witnessScript; + } else if (input.redeemScript) { + script = payments.p2pkh({ hash: input.redeemScript.slice(2) }).output!; + } else { + script = payments.p2pkh({ hash: input.witnessUtxo.script.slice(2) }) + .output!; + } + } else { + return; + } + return script; +} + export class Psbt extends PsbtBase { - constructor() { + constructor(public network?: Network) { super(); } + canFinalize(inputIndex: number): boolean { + const input = checkForInput(this.inputs, inputIndex); + const script = getScriptFromInput( + inputIndex, + input, + this.globalMap.unsignedTx!, + ); + if (!script) return false; + const scriptType = classify.output(script); + switch (scriptType) { + case 'pubkey': + return false; + case 'pubkeyhash': + return false; + case 'multisig': + return false; + case 'witnesspubkeyhash': + return false; + default: + return false; + } + } + signInput(inputIndex: number, keyPair: Signer): Psbt { // TODO: Implement BIP174 pre-sign checks: // https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#signer @@ -121,7 +178,7 @@ export class Psbt extends PsbtBase { } else { script = input.witnessUtxo.script; } - if (isP2WPKH(script)) { + if (isPayment(script, payments.p2wpkh)) { // P2WPKH uses the P2PKH template for prevoutScript when signing const signingScript = payments.p2pkh({ hash: script.slice(2) }).output!; hash = unsignedTx.hashForWitnessV0( diff --git a/types/psbt.d.ts b/types/psbt.d.ts index a58b982..fda7e6b 100644 --- a/types/psbt.d.ts +++ b/types/psbt.d.ts @@ -1,6 +1,9 @@ import { Psbt as PsbtBase } from 'bip174'; import { Signer } from './ecpair'; +import { Network } from './networks'; export declare class Psbt extends PsbtBase { - constructor(); + network?: Network | undefined; + constructor(network?: Network | undefined); + canFinalize(inputIndex: number): boolean; signInput(inputIndex: number, keyPair: Signer): Psbt; }