diff --git a/src/transaction_signer.js b/src/transaction_signer.js index 476fd30..f06e099 100644 --- a/src/transaction_signer.js +++ b/src/transaction_signer.js @@ -185,12 +185,19 @@ InSigner.prototype.extractStandard = function (solution, chunks, sigVersion) { if (solution.type === bscript.types.P2PK) { if (bscript.pubKey.input.check(chunks)) { decoded = bscript.pubKey.input.decode(chunks) + if (!decoded.pubKey.equals(solution.solvedBy)) { + throw new Error('Key in scriptSig does not match output script key') + } + signatures[0] = decoded.signature publicKeys[0] = solution.solvedBy } } else if (solution.type === bscript.types.P2PKH) { if (bscript.pubKeyHash.input.check(chunks)) { decoded = bscript.pubKeyHash.input.decode(chunks) + if (!bcrypto.hash160(decoded.pubKey).equals(solution.solvedBy)) { + throw new Error('Key in scriptSig does not match output script key-hash') + } signatures[0] = decoded.signature publicKeys[0] = decoded.pubKey } @@ -216,67 +223,75 @@ InSigner.prototype.extractStandard = function (solution, chunks, sigVersion) { } InSigner.prototype.solve = function (opts) { + // Determine scriptPubKey is solvable var scriptPubKey = solveScript(opts.scriptPubKey) if (scriptPubKey.type !== bscript.types.P2SH && ALLOWED_P2SH_SCRIPTS.indexOf(scriptPubKey.type) === -1) { throw new Error('txOut script is non-standard') } + var solution = scriptPubKey var redeemScript var witnessScript - var value = 0 var sigVersion = Transaction.SIG_V0 - var solution = scriptPubKey + var value = 0 + if (solution.type === bscript.types.P2SH) { var scriptHash = solution.solvedBy + // Redeem script must be provided by opts if (!(opts.redeemScript instanceof Buffer)) { throw new Error('Redeem script required to solve utxo') } - + // Check redeemScript against script-hash if (!scriptHash.equals(bcrypto.hash160(opts.redeemScript))) { throw new Error('Redeem script does not match txOut script hash') } - + // Determine redeemScript is solvable solution = solveScript(opts.redeemScript) - if (ALLOWED_P2SH_SCRIPTS.indexOf(solution.type)) { - redeemScript = solution - } else { + if (!ALLOWED_P2SH_SCRIPTS.indexOf(solution.type)) { throw new Error('Unsupported P2SH script') } + redeemScript = solution } if (solution.type === bscript.types.P2WPKH) { + // txOutValue is required here if (!types.Satoshi(opts.value)) { throw new Error('Value is required for witness-key-hash') } - + // We set solution to this to avoid work later, since P2WPKH is a special case of P2PKH solution = solveScript(bscript.pubKeyHash.output.encode(solution.solvedBy)) - value = opts.value sigVersion = Transaction.SIG_V1 + value = opts.value } else if (solution.type === bscript.types.P2WSH) { var witnessScriptHash = solution.solvedBy + // Witness script must be provided by opts if (!(opts.witnessScript instanceof Buffer)) { throw new Error('P2WSH script required to solve utxo') } + // txOutValue is required here if (!types.Satoshi(opts.value)) { throw new Error('Value is required for witness-script-hash') } - if (!bufferEquals(bcrypto.sha256(opts.witnessScript), witnessScriptHash)) { + // Check witnessScript against script hash + if (!bcrypto.sha256(opts.witnessScript).equals(witnessScriptHash)) { throw new Error('Witness script does not match txOut script hash') } - solution = witnessScript = solveScript(opts.witnessScript) - value = opts.value - sigVersion = Transaction.SIG_V1 - if (SIGNABLE_SCRIPTS.indexOf(witnessScript.type) === -1) { + solution = solveScript(opts.witnessScript) + // Determine if witnessScript is solvable + if (SIGNABLE_SCRIPTS.indexOf(solution.type) === -1) { throw new Error('witness script is not supported') } + witnessScript = solution + sigVersion = Transaction.SIG_V1 + value = opts.value } this.signScript = solution this.scriptPubKey = scriptPubKey - this.value = value - this.sigVersion = sigVersion this.redeemScript = redeemScript this.witnessScript = witnessScript + this.sigVersion = sigVersion + this.value = value } InSigner.prototype.extractSig = function () { @@ -284,20 +299,20 @@ InSigner.prototype.extractSig = function () { var input = this.tx.ins[this.nIn] var solution = this.scriptPubKey var extractChunks = [] - var sigVersion = 0 if (solution.canSign) { extractChunks = evalPushOnly(input.script) } if (solution.type === bscript.types.P2SH) { if (bscript.scriptHash.input.check(input.script)) { + // If we go to extract a P2SH scriptSig, verify the provided redeemScript var p2sh = bscript.scriptHash.input.decode(input.script) if (!p2sh.redeemScript.equals(this.redeemScript.script)) { throw new Error('Redeem script from scriptSig does not match') } - solution = this.redeemScript if (solution.canSign) { + // We only bother if canSign, otherwise we waste time extractChunks = evalPushOnly(p2sh.redeemScriptSig) } } @@ -310,7 +325,6 @@ InSigner.prototype.extractSig = function () { throw new Error('Public key does not match key-hash') } - sigVersion = 1 extractChunks = input.witness } } else if (solution.type === bscript.types.P2WSH) { @@ -318,14 +332,13 @@ InSigner.prototype.extractSig = function () { if (!this.witnessScript.equals(input.witness[ input.witness.length - 1 ])) { throw new Error('Witness script does not match') } - sigVersion = 1 solution = input.witnessScript extractChunks = input.witness.slice(0, -1) } } if (extractChunks.length > 0) { - [this.signatures, this.publicKeys] = this.extractStandard(solution, extractChunks, sigVersion) + [this.signatures, this.publicKeys] = this.extractStandard(solution, extractChunks, this.sigVersion) } }