p2spkh_tests.cpp raw

   1  // Copyright (c) 2024-present The Limenka developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  
   5  #include <addresstype.h>
   6  #include <key.h>
   7  #include <key_io.h>
   8  #include <script/descriptor.h>
   9  #include <script/interpreter.h>
  10  #include <script/signingprovider.h>
  11  #include <script/solver.h>
  12  #include <policy/policy.h>
  13  #include <test/util/setup_common.h>
  14  #include <test/util/transaction_utils.h>
  15  #include <util/strencodings.h>
  16  
  17  #include <boost/test/unit_test.hpp>
  18  
  19  BOOST_FIXTURE_TEST_SUITE(p2spkh_tests, BasicTestingSetup)
  20  
  21  static XOnlyPubKey NewXOnlyPubKey()
  22  {
  23      CKey key = GenerateRandomKey();
  24      return XOnlyPubKey(key.GetPubKey());
  25  }
  26  
  27  static std::vector<unsigned char> XOnlyPubKeyToBytes(const XOnlyPubKey& xpk)
  28  {
  29      return {xpk.data(), xpk.data() + XOnlyPubKey::size()};
  30  }
  31  
  32  BOOST_AUTO_TEST_CASE(solver_detects_p2spkh)
  33  {
  34      XOnlyPubKey xpk = NewXOnlyPubKey();
  35      WitnessV3SpkHash hash(xpk);
  36      CScript script = GetScriptForDestination(hash);
  37      std::vector<std::vector<unsigned char>> solutions;
  38      BOOST_CHECK_EQUAL(Solver(script, solutions), TxoutType::WITNESS_V3_SPKHASH);
  39      BOOST_CHECK_EQUAL(solutions.size(), 1U);
  40      BOOST_CHECK_EQUAL(solutions[0].size(), 32U);
  41  }
  42  
  43  BOOST_AUTO_TEST_CASE(solver_rejects_v3_not32)
  44  {
  45      std::vector<unsigned char> prog_31(31, 0x42);
  46      CScript script = GetScriptForDestination(WitnessUnknown{3, prog_31});
  47      std::vector<std::vector<unsigned char>> solutions;
  48      BOOST_CHECK_EQUAL(Solver(script, solutions), TxoutType::WITNESS_UNKNOWN);
  49  }
  50  
  51  BOOST_AUTO_TEST_CASE(solver_rejects_v5_32)
  52  {
  53      // v3 is P2SPKH, v4 is P2BPCT; v5 + 32 bytes is still unknown.
  54      std::vector<unsigned char> prog_32(32, 0x42);
  55      CScript script = GetScriptForDestination(WitnessUnknown{5, prog_32});
  56      std::vector<std::vector<unsigned char>> solutions;
  57      BOOST_CHECK_EQUAL(Solver(script, solutions), TxoutType::WITNESS_UNKNOWN);
  58  }
  59  
  60  BOOST_AUTO_TEST_CASE(address_encode_decode_roundtrip)
  61  {
  62      XOnlyPubKey xpk = NewXOnlyPubKey();
  63      WitnessV3SpkHash hash(xpk);
  64      std::string addr = EncodeDestination(hash);
  65      CTxDestination decoded = DecodeDestination(addr);
  66      BOOST_CHECK(IsValidDestination(decoded));
  67      BOOST_CHECK(std::holds_alternative<WitnessV3SpkHash>(decoded));
  68      BOOST_CHECK_EQUAL(uint256(std::get<WitnessV3SpkHash>(decoded)), uint256(hash));
  69  }
  70  
  71  BOOST_AUTO_TEST_CASE(hash256_matches_manual)
  72  {
  73      XOnlyPubKey xpk = NewXOnlyPubKey();
  74      WitnessV3SpkHash hash(xpk);
  75      CSHA256 hasher;
  76      hasher.Write(xpk.data(), 32);
  77      unsigned char mid[CSHA256::OUTPUT_SIZE];
  78      hasher.Finalize(mid);
  79      hasher.Reset();
  80      hasher.Write(mid, CSHA256::OUTPUT_SIZE);
  81      unsigned char final[CSHA256::OUTPUT_SIZE];
  82      hasher.Finalize(final);
  83      BOOST_CHECK_EQUAL(uint256(hash), uint256(final));
  84  }
  85  
  86  BOOST_AUTO_TEST_CASE(hash256_different_keys_different_hash)
  87  {
  88      XOnlyPubKey xpk1 = NewXOnlyPubKey();
  89      XOnlyPubKey xpk2 = NewXOnlyPubKey();
  90      BOOST_CHECK_NE(uint256(WitnessV3SpkHash(xpk1)), uint256(WitnessV3SpkHash(xpk2)));
  91  }
  92  
  93  BOOST_AUTO_TEST_CASE(scriptpubkey_size)
  94  {
  95      CScript script = GetScriptForDestination(WitnessV3SpkHash(NewXOnlyPubKey()));
  96      BOOST_CHECK_EQUAL(script.size(), 34U);
  97  }
  98  
  99  BOOST_AUTO_TEST_CASE(valid_destination_visitor)
 100  {
 101      WitnessV3SpkHash hash(NewXOnlyPubKey());
 102      CTxDestination dest{hash};
 103      BOOST_CHECK(IsValidDestination(dest));
 104  }
 105  
 106  BOOST_AUTO_TEST_CASE(output_type_from_destination)
 107  {
 108      WitnessV3SpkHash hash(NewXOnlyPubKey());
 109      std::optional<OutputType> type = OutputTypeFromDestination(hash);
 110      BOOST_CHECK(type.has_value());
 111      BOOST_CHECK_EQUAL(*type, OutputType::P2SPKH);
 112  }
 113  
 114  BOOST_AUTO_TEST_CASE(descriptor_spk_parsing)
 115  {
 116      FlatSigningProvider provider;
 117      std::string error;
 118      auto descs = Parse("spk(" + HexStr(NewXOnlyPubKey()) + ")", provider, error, false);
 119      BOOST_CHECK_MESSAGE(!descs.empty(), error);
 120      BOOST_CHECK(descs[0]->GetOutputType() == OutputType::P2SPKH);
 121  }
 122  
 123  BOOST_AUTO_TEST_CASE(descriptor_spk_reject_not_top_level)
 124  {
 125      FlatSigningProvider provider;
 126      std::string error;
 127      auto descs = Parse("sh(spk(020202020202020202020202020202020202020202020202020202020202020202))", provider, error, false);
 128      BOOST_CHECK(descs.empty());
 129  }
 130  
 131  BOOST_AUTO_TEST_CASE(p2spkh_valid_signature)
 132  {
 133      CKey key = GenerateRandomKey();
 134      XOnlyPubKey xpk(key.GetPubKey());
 135      WitnessV3SpkHash hash(xpk);
 136      CScript scriptPubKey = GetScriptForDestination(hash);
 137  
 138      const auto txCredit = MakeTransactionRef(BuildCreditingTransaction(scriptPubKey, 10000));
 139      CMutableTransaction mtxSpend = BuildCreditingTransaction(CScript() << OP_RETURN, 5000);
 140      mtxSpend.vin.resize(1);
 141      mtxSpend.vin[0].prevout.hash = txCredit->GetHash();
 142      mtxSpend.vin[0].prevout.n = 0;
 143      const auto txSpend = MakeTransactionRef(mtxSpend);
 144  
 145      PrecomputedTransactionData txdata;
 146      txdata.Init(*txSpend, {txCredit->vout[0]}, true);
 147      ScriptExecutionData execdata;
 148      execdata.m_annex_init = true;
 149      execdata.m_annex_present = false;
 150      uint256 sighash;
 151      BOOST_REQUIRE(SignatureHashSchnorr(sighash, execdata, *txSpend, 0, SIGHASH_DEFAULT,
 152                                          SigVersion::TAPROOT, txdata, MissingDataBehavior::FAIL));
 153  
 154      std::vector<unsigned char> sig(64);
 155      BOOST_REQUIRE(key.SignSchnorr(sighash, sig, nullptr, uint256::ZERO));
 156  
 157      CMutableTransaction mtxSpendSigned = mtxSpend;
 158      mtxSpendSigned.vin[0].scriptSig.clear();
 159      mtxSpendSigned.vin[0].scriptWitness.stack.push_back(sig);
 160      mtxSpendSigned.vin[0].scriptWitness.stack.push_back(XOnlyPubKeyToBytes(xpk));
 161      const auto txSpendSigned = MakeTransactionRef(mtxSpendSigned);
 162  
 163      ScriptError serror;
 164      bool result = VerifyScript(txSpendSigned->vin[0].scriptSig, scriptPubKey,
 165                                 &txSpendSigned->vin[0].scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS,
 166                                 TransactionSignatureChecker(txSpendSigned.get(), 0, txCredit->vout[0].nValue, txdata, MissingDataBehavior::FAIL),
 167                                 &serror);
 168      BOOST_CHECK_MESSAGE(result, ScriptErrorString(serror));
 169  }
 170  
 171  BOOST_AUTO_TEST_CASE(p2spkh_accept_no_flag)
 172  {
 173      // Without SCRIPT_VERIFY_P2SPKH, v3+32-byte programs are treated as
 174      // unknown witness programs and always succeed (forward-compatibility).
 175      CKey key = GenerateRandomKey();
 176      XOnlyPubKey xpk(key.GetPubKey());
 177      WitnessV3SpkHash hash(xpk);
 178      CScript scriptPubKey = GetScriptForDestination(hash);
 179  
 180      const auto txCredit = MakeTransactionRef(BuildCreditingTransaction(scriptPubKey, 10000));
 181      CMutableTransaction mtxSpend = BuildCreditingTransaction(CScript() << OP_RETURN, 5000);
 182      mtxSpend.vin.resize(1);
 183      mtxSpend.vin[0].prevout.hash = txCredit->GetHash();
 184      mtxSpend.vin[0].prevout.n = 0;
 185  
 186      // Build a valid-looking witness with a wrong signature (should still succeed without flag)
 187      mtxSpend.vin[0].scriptWitness.stack.emplace_back(72, 0x00); // nonsense sig
 188      mtxSpend.vin[0].scriptWitness.stack.emplace_back(XOnlyPubKeyToBytes(xpk));
 189  
 190      ScriptError serror;
 191      bool result = VerifyScript(mtxSpend.vin[0].scriptSig, scriptPubKey,
 192                                 &mtxSpend.vin[0].scriptWitness, 0,
 193                                 MutableTransactionSignatureChecker(&mtxSpend, 0, txCredit->vout[0].nValue,
 194                                                                     PrecomputedTransactionData{}, MissingDataBehavior::FAIL),
 195                                 &serror);
 196      BOOST_CHECK(result);
 197  }
 198  
 199  BOOST_AUTO_TEST_SUITE_END()
 200