From 48fc75c4f016f199c734e7433a9622027e3f81ad Mon Sep 17 00:00:00 2001 From: junderw Date: Wed, 3 Jul 2019 18:42:31 +0900 Subject: [PATCH] Fix p2sh and p2wsh not working --- src/psbt.js | 60 ++++++++++++++++++++++++++++++++++++++------ ts_src/psbt.ts | 67 ++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 111 insertions(+), 16 deletions(-) diff --git a/src/psbt.js b/src/psbt.js index 1ed7354..3cbd899 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -30,8 +30,9 @@ class Psbt extends bip174_1.Psbt { this.inputs.forEach((input, idx) => { if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig; if (input.finalScriptWitness) { - const decompiled = bscript.decompile(input.finalScriptWitness); - if (decompiled) tx.ins[idx].witness = bscript.toStack(decompiled); + tx.ins[idx].witness = scriptWitnessToWitnessStack( + input.finalScriptWitness, + ); } }); return tx; @@ -148,23 +149,44 @@ function getFinalScripts( finalScriptWitness = witnessStackToScriptWitness(payment.witness); } if (p2sh) { - finalScriptSig = bscript.compile([p2sh.redeem.output]); + finalScriptSig = p2sh.input; } } else { - finalScriptSig = payment.input; + if (p2sh) { + finalScriptSig = p2sh.input; + } else { + finalScriptSig = payment.input; + } } return { finalScriptSig, finalScriptWitness, }; } +function getSortedSigs(script, partialSig) { + const p2ms = payments.p2ms({ output: script }); + // for each pubkey in order of p2ms script + return p2ms.pubkeys + .map(pk => { + // filter partialSig array by pubkey being equal + return ( + partialSig.filter(ps => { + return ps.pubkey.equals(pk); + })[0] || {} + ).signature; + // Any pubkey without a match will return undefined + // this last filter removes all the undefined items in the array. + }) + .filter(v => !!v); +} function getPayment(script, scriptType, partialSig) { let payment; switch (scriptType) { case 'multisig': + const sigs = getSortedSigs(script, partialSig); payment = payments.p2ms({ output: script, - signatures: partialSig.map(ps => ps.signature), + signatures: sigs, }); break; case 'pubkey': @@ -283,7 +305,7 @@ const getHashForSig = (inputIndex, input, txBuf) => { checkWitnessScript(inputIndex, _script, input.witnessScript); hash = unsignedTx.hashForWitnessV0( inputIndex, - _script, + input.witnessScript, input.witnessUtxo.value, sighashType, ); @@ -363,11 +385,11 @@ function getScriptFromInput(inputIndex, input, _unsignedTx) { if (input.witnessScript) { res.script = input.witnessScript; } else if (input.redeemScript) { - res.script = payments.p2pkh({ + res.script = payments.p2wpkh({ hash: input.redeemScript.slice(2), }).output; } else { - res.script = payments.p2pkh({ + res.script = payments.p2wpkh({ hash: input.witnessUtxo.script.slice(2), }).output; } @@ -401,4 +423,26 @@ function witnessStackToScriptWitness(witness) { writeVector(witness); return buffer; } +function scriptWitnessToWitnessStack(buffer) { + let offset = 0; + function readSlice(n) { + offset += n; + return buffer.slice(offset - n, offset); + } + function readVarInt() { + const vi = varuint.decode(buffer, offset); + offset += varuint.decode.bytes; + return vi; + } + function readVarSlice() { + return readSlice(readVarInt()); + } + function readVector() { + const count = readVarInt(); + const vector = []; + for (let i = 0; i < count; i++) vector.push(readVarSlice()); + return vector; + } + return readVector(); +} const range = n => [...Array(n).keys()]; diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index 680ea92..7c60a1d 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -32,8 +32,9 @@ export class Psbt extends PsbtBase { this.inputs.forEach((input, idx) => { if (input.finalScriptSig) tx.ins[idx].script = input.finalScriptSig; if (input.finalScriptWitness) { - const decompiled = bscript.decompile(input.finalScriptWitness); - if (decompiled) tx.ins[idx].witness = bscript.toStack(decompiled); + tx.ins[idx].witness = scriptWitnessToWitnessStack( + input.finalScriptWitness, + ); } }); return tx; @@ -181,10 +182,14 @@ function getFinalScripts( finalScriptWitness = witnessStackToScriptWitness(payment.witness!); } if (p2sh) { - finalScriptSig = bscript.compile([p2sh.redeem!.output!]); + finalScriptSig = p2sh.input; } } else { - finalScriptSig = payment.input; + if (p2sh) { + finalScriptSig = p2sh.input; + } else { + finalScriptSig = payment.input; + } } return { finalScriptSig, @@ -192,6 +197,23 @@ function getFinalScripts( }; } +function getSortedSigs(script: Buffer, partialSig: PartialSig[]): Buffer[] { + const p2ms = payments.p2ms({ output: script }); + // for each pubkey in order of p2ms script + return p2ms + .pubkeys!.map(pk => { + // filter partialSig array by pubkey being equal + return ( + partialSig.filter(ps => { + return ps.pubkey.equals(pk); + })[0] || {} + ).signature; + // Any pubkey without a match will return undefined + // this last filter removes all the undefined items in the array. + }) + .filter(v => !!v); +} + function getPayment( script: Buffer, scriptType: string, @@ -200,9 +222,10 @@ function getPayment( let payment: payments.Payment; switch (scriptType) { case 'multisig': + const sigs = getSortedSigs(script, partialSig); payment = payments.p2ms({ output: script, - signatures: partialSig.map(ps => ps.signature), + signatures: sigs, }); break; case 'pubkey': @@ -343,7 +366,7 @@ const getHashForSig = ( checkWitnessScript(inputIndex, _script, input.witnessScript); hash = unsignedTx.hashForWitnessV0( inputIndex, - _script, + input.witnessScript, input.witnessUtxo.value, sighashType, ); @@ -446,11 +469,11 @@ function getScriptFromInput( if (input.witnessScript) { res.script = input.witnessScript; } else if (input.redeemScript) { - res.script = payments.p2pkh({ + res.script = payments.p2wpkh({ hash: input.redeemScript.slice(2), }).output!; } else { - res.script = payments.p2pkh({ + res.script = payments.p2wpkh({ hash: input.witnessUtxo.script.slice(2), }).output!; } @@ -494,4 +517,32 @@ function witnessStackToScriptWitness(witness: Buffer[]): Buffer { return buffer; } +function scriptWitnessToWitnessStack(buffer: Buffer): Buffer[] { + let offset = 0; + + function readSlice(n: number): Buffer { + offset += n; + return buffer.slice(offset - n, offset); + } + + function readVarInt(): number { + const vi = varuint.decode(buffer, offset); + offset += varuint.decode.bytes; + return vi; + } + + function readVarSlice(): Buffer { + return readSlice(readVarInt()); + } + + function readVector(): Buffer[] { + const count = readVarInt(); + const vector: Buffer[] = []; + for (let i = 0; i < count; i++) vector.push(readVarSlice()); + return vector; + } + + return readVector(); +} + const range = (n: number): number[] => [...Array(n).keys()];