From 7de019bc619b0b2433bfb553feba5f6dc58c8db8 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Fri, 12 Feb 2021 14:18:50 -0500 Subject: Introduce DeferringSignatureChecker and inherit with SignatureExtractor Introduces a DeferringSignatureChecker which simply takes a BaseSignatureChecker and passes through everything. SignatureExtractorChecker now subclasses DeferringSignatureChecker. This allows for all BaseSignatureChecker functions to be implemented for SignatureExtractorChecker, while allowing for future signature checkers which opreate similarly to SignatureExtractorChecker. Github-Pull: #21166 Rebased-From: 6965456c10c9c4025c71c5e24fa5b27b15e5933a --- src/script/interpreter.h | 28 ++++++++++++++++++++++++++++ src/script/sign.cpp | 8 ++++---- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/script/interpreter.h b/src/script/interpreter.h index c0c2b012c6..70789869a0 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -272,6 +272,34 @@ public: using TransactionSignatureChecker = GenericTransactionSignatureChecker; using MutableTransactionSignatureChecker = GenericTransactionSignatureChecker; +class DeferringSignatureChecker : public BaseSignatureChecker +{ +protected: + BaseSignatureChecker& m_checker; + +public: + DeferringSignatureChecker(BaseSignatureChecker& checker) : m_checker(checker) {} + + bool CheckECDSASignature(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override + { + return m_checker.CheckECDSASignature(scriptSig, vchPubKey, scriptCode, sigversion); + } + + bool CheckSchnorrSignature(Span sig, Span pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override + { + return m_checker.CheckSchnorrSignature(sig, pubkey, sigversion, execdata, serror); + } + + bool CheckLockTime(const CScriptNum& nLockTime) const override + { + return m_checker.CheckLockTime(nLockTime); + } + bool CheckSequence(const CScriptNum& nSequence) const override + { + return m_checker.CheckSequence(nSequence); + } +}; + bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptExecutionData& execdata, ScriptError* error = nullptr); bool EvalScript(std::vector >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = nullptr); bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = nullptr); diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 0e6864d547..903c95a5c9 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -253,17 +253,17 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato } namespace { -class SignatureExtractorChecker final : public BaseSignatureChecker +class SignatureExtractorChecker final : public DeferringSignatureChecker { private: SignatureData& sigdata; - BaseSignatureChecker& checker; public: - SignatureExtractorChecker(SignatureData& sigdata, BaseSignatureChecker& checker) : sigdata(sigdata), checker(checker) {} + SignatureExtractorChecker(SignatureData& sigdata, BaseSignatureChecker& checker) : DeferringSignatureChecker(checker), sigdata(sigdata) {} + bool CheckECDSASignature(const std::vector& scriptSig, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override { - if (checker.CheckECDSASignature(scriptSig, vchPubKey, scriptCode, sigversion)) { + if (m_checker.CheckECDSASignature(scriptSig, vchPubKey, scriptCode, sigversion)) { CPubKey pubkey(vchPubKey); sigdata.signatures.emplace(pubkey.GetID(), SigPair(pubkey, scriptSig)); return true; -- cgit v1.2.3 From f79189ca54524881d52b91679eb9035d6718ce01 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Fri, 12 Feb 2021 15:38:46 -0500 Subject: Test that signrawtx works when a signed CSV and CLTV inputs are present Github-Pull: #21166 Rebased-From: a97a9298cea085858e1a65a5e9b20d7a9e0f7303 --- test/functional/rpc_signrawtransaction.py | 83 +++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 4 deletions(-) diff --git a/test/functional/rpc_signrawtransaction.py b/test/functional/rpc_signrawtransaction.py index 2fbbdbbdf0..60b4d1c744 100755 --- a/test/functional/rpc_signrawtransaction.py +++ b/test/functional/rpc_signrawtransaction.py @@ -4,16 +4,17 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test transaction signing using the signrawtransaction* RPCs.""" -from test_framework.address import check_script, script_to_p2sh +from test_framework.address import check_script, script_to_p2sh, script_to_p2wsh from test_framework.key import ECKey from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error, find_vout_for_address, hex_str_to_bytes -from test_framework.messages import sha256 -from test_framework.script import CScript, OP_0, OP_CHECKSIG +from test_framework.messages import sha256, CTransaction, CTxInWitness +from test_framework.script import CScript, OP_0, OP_CHECKSIG, OP_CHECKSEQUENCEVERIFY, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_TRUE from test_framework.script_util import key_to_p2pkh_script, script_to_p2sh_p2wsh_script, script_to_p2wsh_script from test_framework.wallet_util import bytes_to_wif -from decimal import Decimal +from decimal import Decimal, getcontext +from io import BytesIO class SignRawTransactionsTest(BitcoinTestFramework): def set_test_params(self): @@ -238,6 +239,78 @@ class SignRawTransactionsTest(BitcoinTestFramework): txn = self.nodes[0].signrawtransactionwithwallet(hex_str, prev_txs) assert txn["complete"] + def test_signing_with_csv(self): + self.log.info("Test signing a transaction containing a fully signed CSV input") + self.nodes[0].walletpassphrase("password", 9999) + getcontext().prec = 8 + + # Make sure CSV is active + self.nodes[0].generate(500) + + # Create a P2WSH script with CSV + script = CScript([1, OP_CHECKSEQUENCEVERIFY, OP_DROP]) + address = script_to_p2wsh(script) + + # Fund that address and make the spend + txid = self.nodes[0].sendtoaddress(address, 1) + vout = find_vout_for_address(self.nodes[0], txid, address) + self.nodes[0].generate(1) + utxo = self.nodes[0].listunspent()[0] + amt = Decimal(1) + utxo["amount"] - Decimal(0.00001) + tx = self.nodes[0].createrawtransaction( + [{"txid": txid, "vout": vout, "sequence": 1},{"txid": utxo["txid"], "vout": utxo["vout"]}], + [{self.nodes[0].getnewaddress(): amt}], + self.nodes[0].getblockcount() + ) + + # Set the witness script + ctx = CTransaction() + ctx.deserialize(BytesIO(hex_str_to_bytes(tx))) + ctx.wit.vtxinwit.append(CTxInWitness()) + ctx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE]), script] + tx = ctx.serialize_with_witness().hex() + + # Sign and send the transaction + signed = self.nodes[0].signrawtransactionwithwallet(tx) + assert_equal(signed["complete"], True) + self.nodes[0].sendrawtransaction(signed["hex"]) + + def test_signing_with_cltv(self): + self.log.info("Test signing a transaction containing a fully signed CLTV input") + self.nodes[0].walletpassphrase("password", 9999) + getcontext().prec = 8 + + # Make sure CSV is active + self.nodes[0].generate(1500) + + # Create a P2WSH script with CLTV + script = CScript([1000, OP_CHECKLOCKTIMEVERIFY, OP_DROP]) + address = script_to_p2wsh(script) + + # Fund that address and make the spend + txid = self.nodes[0].sendtoaddress(address, 1) + vout = find_vout_for_address(self.nodes[0], txid, address) + self.nodes[0].generate(1) + utxo = self.nodes[0].listunspent()[0] + amt = Decimal(1) + utxo["amount"] - Decimal(0.00001) + tx = self.nodes[0].createrawtransaction( + [{"txid": txid, "vout": vout},{"txid": utxo["txid"], "vout": utxo["vout"]}], + [{self.nodes[0].getnewaddress(): amt}], + self.nodes[0].getblockcount() + ) + + # Set the witness script + ctx = CTransaction() + ctx.deserialize(BytesIO(hex_str_to_bytes(tx))) + ctx.wit.vtxinwit.append(CTxInWitness()) + ctx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE]), script] + tx = ctx.serialize_with_witness().hex() + + # Sign and send the transaction + signed = self.nodes[0].signrawtransactionwithwallet(tx) + assert_equal(signed["complete"], True) + self.nodes[0].sendrawtransaction(signed["hex"]) + def run_test(self): self.successful_signing_test() self.script_verification_error_test() @@ -245,6 +318,8 @@ class SignRawTransactionsTest(BitcoinTestFramework): self.OP_1NEGATE_test() self.test_with_lock_outputs() self.test_fully_signed_tx() + self.test_signing_with_csv() + self.test_signing_with_cltv() if __name__ == '__main__': -- cgit v1.2.3