script_p2sh_tests.cpp raw

   1  // Copyright (c) 2012-2022 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 <consensus/tx_verify.h>
   6  #include <kernel/mempool_options.h>
   7  #include <key.h>
   8  #include <policy/policy.h>
   9  #include <policy/settings.h>
  10  #include <script/script.h>
  11  #include <script/script_error.h>
  12  #include <script/sign.h>
  13  #include <script/signingprovider.h>
  14  #include <test/util/setup_common.h>
  15  #include <test/util/transaction_utils.h>
  16  #include <validation.h>
  17  
  18  #include <vector>
  19  
  20  #include <boost/test/unit_test.hpp>
  21  
  22  // Helpers:
  23  static bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, std::string& reason)
  24  {
  25      const kernel::MemPoolOptions opts{
  26          .permit_bare_pubkey = true,
  27          .permit_bare_multisig = permit_bare_multisig,
  28      };
  29      return IsStandardTx(tx, opts, reason);
  30  }
  31  
  32  static bool IsStandardTx(const CTransaction& tx, std::string& reason)
  33  {
  34      kernel::MemPoolOptions opts{
  35          .permit_bare_pubkey = true,
  36          .permit_bare_multisig = true,
  37      };
  38      if (!IsStandardTx(tx, opts, reason)) return false;
  39      opts.permit_bare_multisig = false;
  40      return IsStandardTx(tx, opts, reason);
  41  }
  42  
  43  static std::vector<unsigned char> Serialize(const CScript& s)
  44  {
  45      std::vector<unsigned char> sSerialized(s.begin(), s.end());
  46      return sSerialized;
  47  }
  48  
  49  static bool Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict, ScriptError& err)
  50  {
  51      // Create dummy to/from transactions:
  52      CMutableTransaction txFrom;
  53      txFrom.vout.resize(1);
  54      txFrom.vout[0].scriptPubKey = scriptPubKey;
  55  
  56      CMutableTransaction txTo;
  57      txTo.vin.resize(1);
  58      txTo.vout.resize(1);
  59      txTo.vin[0].prevout.n = 0;
  60      txTo.vin[0].prevout.hash = txFrom.GetHash();
  61      txTo.vin[0].scriptSig = scriptSig;
  62      txTo.vout[0].nValue = 1;
  63  
  64      return VerifyScript(scriptSig, scriptPubKey, nullptr, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, MutableTransactionSignatureChecker(&txTo, 0, txFrom.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err);
  65  }
  66  
  67  
  68  BOOST_FIXTURE_TEST_SUITE(script_p2sh_tests, BasicTestingSetup)
  69  
  70  BOOST_AUTO_TEST_CASE(sign)
  71  {
  72      // Pay-to-script-hash looks like this:
  73      // scriptSig:    <sig> <sig...> <serialized_script>
  74      // scriptPubKey: HASH160 <hash> EQUAL
  75  
  76      // Test SignSignature() (and therefore the version of Solver() that signs transactions)
  77      FillableSigningProvider keystore;
  78      CKey key[4];
  79      for (int i = 0; i < 4; i++)
  80      {
  81          key[i].MakeNewKey(true);
  82          BOOST_CHECK(keystore.AddKey(key[i]));
  83      }
  84  
  85      // 8 Scripts: checking all combinations of
  86      // different keys, straight/P2SH, pubkey/pubkeyhash
  87      CScript standardScripts[4];
  88      standardScripts[0] << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG;
  89      standardScripts[1] = GetScriptForDestination(PKHash(key[1].GetPubKey()));
  90      standardScripts[2] << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG;
  91      standardScripts[3] = GetScriptForDestination(PKHash(key[2].GetPubKey()));
  92      CScript evalScripts[4];
  93      for (int i = 0; i < 4; i++)
  94      {
  95          BOOST_CHECK(keystore.AddCScript(standardScripts[i]));
  96          evalScripts[i] = GetScriptForDestination(ScriptHash(standardScripts[i]));
  97      }
  98  
  99      CMutableTransaction txFrom;  // Funding transaction:
 100      std::string reason;
 101      txFrom.vout.resize(8);
 102      for (int i = 0; i < 4; i++)
 103      {
 104          txFrom.vout[i].scriptPubKey = evalScripts[i];
 105          txFrom.vout[i].nValue = COIN;
 106          txFrom.vout[i+4].scriptPubKey = standardScripts[i];
 107          txFrom.vout[i+4].nValue = COIN;
 108      }
 109      BOOST_CHECK(IsStandardTx(CTransaction(txFrom), reason));
 110  
 111      CMutableTransaction txTo[8]; // Spending transactions
 112      for (int i = 0; i < 8; i++)
 113      {
 114          txTo[i].vin.resize(1);
 115          txTo[i].vout.resize(1);
 116          txTo[i].vin[0].prevout.n = i;
 117          txTo[i].vin[0].prevout.hash = txFrom.GetHash();
 118          txTo[i].vout[0].nValue = 1;
 119      }
 120      for (int i = 0; i < 8; i++)
 121      {
 122          SignatureData empty;
 123          BOOST_CHECK_MESSAGE(SignSignature(keystore, CTransaction(txFrom), txTo[i], 0, SIGHASH_ALL, empty), strprintf("SignSignature %d", i));
 124      }
 125      // All of the above should be OK, and the txTos have valid signatures
 126      // Check to make sure signature verification fails if we use the wrong ScriptSig:
 127      SignatureCache signature_cache{DEFAULT_SIGNATURE_CACHE_BYTES};
 128      for (int i = 0; i < 8; i++) {
 129          PrecomputedTransactionData txdata(txTo[i]);
 130          for (int j = 0; j < 8; j++)
 131          {
 132              CScript sigSave = txTo[i].vin[0].scriptSig;
 133              txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig;
 134              bool sigOK = !CScriptCheck(txFrom.vout[txTo[i].vin[0].prevout.n], CTransaction(txTo[i]), signature_cache, 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata)().has_value();
 135              if (i == j)
 136                  BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j));
 137              else
 138                  BOOST_CHECK_MESSAGE(!sigOK, strprintf("VerifySignature %d %d", i, j));
 139              txTo[i].vin[0].scriptSig = sigSave;
 140          }
 141      }
 142  }
 143  
 144  BOOST_AUTO_TEST_CASE(norecurse)
 145  {
 146      ScriptError err;
 147      // Make sure only the outer pay-to-script-hash does the
 148      // extra-validation thing:
 149      CScript invalidAsScript;
 150      invalidAsScript << OP_INVALIDOPCODE << OP_INVALIDOPCODE;
 151  
 152      CScript p2sh = GetScriptForDestination(ScriptHash(invalidAsScript));
 153  
 154      CScript scriptSig;
 155      scriptSig << Serialize(invalidAsScript);
 156  
 157      // Should not verify, because it will try to execute OP_INVALIDOPCODE
 158      BOOST_CHECK(!Verify(scriptSig, p2sh, true, err));
 159      BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_BAD_OPCODE, ScriptErrorString(err));
 160  
 161      // Try to recur, and verification should succeed because
 162      // the inner HASH160 <> EQUAL should only check the hash:
 163      CScript p2sh2 = GetScriptForDestination(ScriptHash(p2sh));
 164      CScript scriptSig2;
 165      scriptSig2 << Serialize(invalidAsScript) << Serialize(p2sh);
 166  
 167      BOOST_CHECK(Verify(scriptSig2, p2sh2, true, err));
 168      BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
 169  }
 170  
 171  BOOST_AUTO_TEST_CASE(set)
 172  {
 173      // Test the CScript::Set* methods
 174      FillableSigningProvider keystore;
 175      CKey key[4];
 176      std::vector<CPubKey> keys;
 177      keys.reserve(4);
 178      for (int i = 0; i < 4; i++)
 179      {
 180          key[i].MakeNewKey(true);
 181          BOOST_CHECK(keystore.AddKey(key[i]));
 182          keys.push_back(key[i].GetPubKey());
 183      }
 184  
 185      CScript inner[4];
 186      inner[0] = GetScriptForDestination(PKHash(key[0].GetPubKey()));
 187      inner[1] = GetScriptForMultisig(2, std::vector<CPubKey>(keys.begin(), keys.begin()+2));
 188      inner[2] = GetScriptForMultisig(1, std::vector<CPubKey>(keys.begin(), keys.begin()+2));
 189      inner[3] = GetScriptForMultisig(2, std::vector<CPubKey>(keys.begin(), keys.begin()+3));
 190  
 191      CScript outer[4];
 192      for (int i = 0; i < 4; i++)
 193      {
 194          outer[i] = GetScriptForDestination(ScriptHash(inner[i]));
 195          BOOST_CHECK(keystore.AddCScript(inner[i]));
 196      }
 197  
 198      CMutableTransaction txFrom;  // Funding transaction:
 199      std::string reason;
 200      txFrom.vout.resize(4);
 201      for (int i = 0; i < 4; i++)
 202      {
 203          txFrom.vout[i].scriptPubKey = outer[i];
 204          txFrom.vout[i].nValue = CENT;
 205      }
 206      BOOST_CHECK(IsStandardTx(CTransaction(txFrom), reason));
 207  
 208      CMutableTransaction txTo[4]; // Spending transactions
 209      for (int i = 0; i < 4; i++)
 210      {
 211          txTo[i].vin.resize(1);
 212          txTo[i].vout.resize(1);
 213          txTo[i].vin[0].prevout.n = i;
 214          txTo[i].vin[0].prevout.hash = txFrom.GetHash();
 215          txTo[i].vout[0].nValue = 1*CENT;
 216          txTo[i].vout[0].scriptPubKey = inner[i];
 217      }
 218      for (int i = 0; i < 4; i++)
 219      {
 220          SignatureData empty;
 221          BOOST_CHECK_MESSAGE(SignSignature(keystore, CTransaction(txFrom), txTo[i], 0, SIGHASH_ALL, empty), strprintf("SignSignature %d", i));
 222          BOOST_CHECK_MESSAGE(IsStandardTx(CTransaction(txTo[i]), /*permit_bare_multisig=*/true, reason), strprintf("txTo[%d].IsStandard", i));
 223          bool no_pbms_is_std = IsStandardTx(CTransaction(txTo[i]), /*permit_bare_multisig=*/false, reason);
 224          BOOST_CHECK_MESSAGE((i == 0 ? no_pbms_is_std : !no_pbms_is_std), strprintf("txTo[%d].IsStandard(permbaremulti=false)", i));
 225      }
 226  }
 227  
 228  BOOST_AUTO_TEST_CASE(is)
 229  {
 230      // Test CScript::IsPayToScriptHash()
 231      uint160 dummy;
 232      CScript p2sh;
 233      p2sh << OP_HASH160 << ToByteVector(dummy) << OP_EQUAL;
 234      BOOST_CHECK(p2sh.IsPayToScriptHash());
 235  
 236      std::vector<unsigned char> direct = {OP_HASH160, 20};
 237      direct.insert(direct.end(), 20, 0);
 238      direct.push_back(OP_EQUAL);
 239      BOOST_CHECK(CScript(direct.begin(), direct.end()).IsPayToScriptHash());
 240  
 241      // Not considered pay-to-script-hash if using one of the OP_PUSHDATA opcodes:
 242      std::vector<unsigned char> pushdata1 = {OP_HASH160, OP_PUSHDATA1, 20};
 243      pushdata1.insert(pushdata1.end(), 20, 0);
 244      pushdata1.push_back(OP_EQUAL);
 245      BOOST_CHECK(!CScript(pushdata1.begin(), pushdata1.end()).IsPayToScriptHash());
 246      std::vector<unsigned char> pushdata2 = {OP_HASH160, OP_PUSHDATA2, 20, 0};
 247      pushdata2.insert(pushdata2.end(), 20, 0);
 248      pushdata2.push_back(OP_EQUAL);
 249      BOOST_CHECK(!CScript(pushdata2.begin(), pushdata2.end()).IsPayToScriptHash());
 250      std::vector<unsigned char> pushdata4 = {OP_HASH160, OP_PUSHDATA4, 20, 0, 0, 0};
 251      pushdata4.insert(pushdata4.end(), 20, 0);
 252      pushdata4.push_back(OP_EQUAL);
 253      BOOST_CHECK(!CScript(pushdata4.begin(), pushdata4.end()).IsPayToScriptHash());
 254  
 255      CScript not_p2sh;
 256      BOOST_CHECK(!not_p2sh.IsPayToScriptHash());
 257  
 258      not_p2sh.clear(); not_p2sh << OP_HASH160 << ToByteVector(dummy) << ToByteVector(dummy) << OP_EQUAL;
 259      BOOST_CHECK(!not_p2sh.IsPayToScriptHash());
 260  
 261      not_p2sh.clear(); not_p2sh << OP_NOP << ToByteVector(dummy) << OP_EQUAL;
 262      BOOST_CHECK(!not_p2sh.IsPayToScriptHash());
 263  
 264      not_p2sh.clear(); not_p2sh << OP_HASH160 << ToByteVector(dummy) << OP_CHECKSIG;
 265      BOOST_CHECK(!not_p2sh.IsPayToScriptHash());
 266  }
 267  
 268  BOOST_AUTO_TEST_CASE(switchover)
 269  {
 270      // Test switch over code
 271      CScript notValid;
 272      ScriptError err;
 273      notValid << OP_11 << OP_12 << OP_EQUALVERIFY;
 274      CScript scriptSig;
 275      scriptSig << Serialize(notValid);
 276  
 277      CScript fund = GetScriptForDestination(ScriptHash(notValid));
 278  
 279  
 280      // Validation should succeed under old rules (hash is correct):
 281      BOOST_CHECK(Verify(scriptSig, fund, false, err));
 282      BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
 283      // Fail under new:
 284      BOOST_CHECK(!Verify(scriptSig, fund, true, err));
 285      BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_EQUALVERIFY, ScriptErrorString(err));
 286  }
 287  
 288  BOOST_AUTO_TEST_CASE(AreInputsStandard)
 289  {
 290      CCoinsView coinsDummy;
 291      CCoinsViewCache coins(&coinsDummy);
 292      FillableSigningProvider keystore;
 293      CKey key[6];
 294      for (int i = 0; i < 6; i++)
 295      {
 296          key[i].MakeNewKey(true);
 297          BOOST_CHECK(keystore.AddKey(key[i]));
 298      }
 299      std::vector<CPubKey> keys;
 300      keys.reserve(3);
 301      for (int i = 0; i < 3; i++)
 302          keys.push_back(key[i].GetPubKey());
 303  
 304      CMutableTransaction txFrom;
 305      txFrom.vout.resize(7);
 306  
 307      // First three are standard:
 308      CScript pay1 = GetScriptForDestination(PKHash(key[0].GetPubKey()));
 309      BOOST_CHECK(keystore.AddCScript(pay1));
 310      CScript pay1of3 = GetScriptForMultisig(1, keys);
 311  
 312      txFrom.vout[0].scriptPubKey = GetScriptForDestination(ScriptHash(pay1)); // P2SH (OP_CHECKSIG)
 313      txFrom.vout[0].nValue = 1000;
 314      txFrom.vout[1].scriptPubKey = pay1; // ordinary OP_CHECKSIG
 315      txFrom.vout[1].nValue = 2000;
 316      txFrom.vout[2].scriptPubKey = pay1of3; // ordinary OP_CHECKMULTISIG
 317      txFrom.vout[2].nValue = 3000;
 318  
 319      // vout[3] is complicated 1-of-3 AND 2-of-3
 320      // ... that is OK if wrapped in P2SH:
 321      CScript oneAndTwo;
 322      oneAndTwo << OP_1 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << ToByteVector(key[2].GetPubKey());
 323      oneAndTwo << OP_3 << OP_CHECKMULTISIGVERIFY;
 324      oneAndTwo << OP_2 << ToByteVector(key[3].GetPubKey()) << ToByteVector(key[4].GetPubKey()) << ToByteVector(key[5].GetPubKey());
 325      oneAndTwo << OP_3 << OP_CHECKMULTISIG;
 326      BOOST_CHECK(keystore.AddCScript(oneAndTwo));
 327      txFrom.vout[3].scriptPubKey = GetScriptForDestination(ScriptHash(oneAndTwo));
 328      txFrom.vout[3].nValue = 4000;
 329  
 330      // vout[4] is max sigops:
 331      CScript fifteenSigops; fifteenSigops << OP_1;
 332      for (unsigned i = 0; i < MAX_P2SH_SIGOPS; i++)
 333          fifteenSigops << ToByteVector(key[i%3].GetPubKey());
 334      fifteenSigops << OP_15 << OP_CHECKMULTISIG;
 335      BOOST_CHECK(keystore.AddCScript(fifteenSigops));
 336      txFrom.vout[4].scriptPubKey = GetScriptForDestination(ScriptHash(fifteenSigops));
 337      txFrom.vout[4].nValue = 5000;
 338  
 339      // vout[5/6] are non-standard because they exceed MAX_P2SH_SIGOPS
 340      CScript sixteenSigops; sixteenSigops << OP_16 << OP_CHECKMULTISIG;
 341      BOOST_CHECK(keystore.AddCScript(sixteenSigops));
 342      txFrom.vout[5].scriptPubKey = GetScriptForDestination(ScriptHash(sixteenSigops));
 343      txFrom.vout[5].nValue = 5000;
 344      CScript twentySigops; twentySigops << OP_CHECKMULTISIG;
 345      BOOST_CHECK(keystore.AddCScript(twentySigops));
 346      txFrom.vout[6].scriptPubKey = GetScriptForDestination(ScriptHash(twentySigops));
 347      txFrom.vout[6].nValue = 6000;
 348  
 349      AddCoins(coins, CTransaction(txFrom), 0);
 350  
 351      CMutableTransaction txTo;
 352      txTo.vout.resize(1);
 353      txTo.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[1].GetPubKey()));
 354  
 355      txTo.vin.resize(5);
 356      for (int i = 0; i < 5; i++)
 357      {
 358          txTo.vin[i].prevout.n = i;
 359          txTo.vin[i].prevout.hash = txFrom.GetHash();
 360      }
 361      SignatureData empty;
 362      BOOST_CHECK(SignSignature(keystore, CTransaction(txFrom), txTo, 0, SIGHASH_ALL, empty));
 363      SignatureData empty_b;
 364      BOOST_CHECK(SignSignature(keystore, CTransaction(txFrom), txTo, 1, SIGHASH_ALL, empty_b));
 365      SignatureData empty_c;
 366      BOOST_CHECK(SignSignature(keystore, CTransaction(txFrom), txTo, 2, SIGHASH_ALL, empty_c));
 367      // SignSignature doesn't know how to sign these. We're
 368      // not testing validating signatures, so just create
 369      // dummy signatures that DO include the correct P2SH scripts:
 370      txTo.vin[3].scriptSig << OP_11 << OP_11 << std::vector<unsigned char>(oneAndTwo.begin(), oneAndTwo.end());
 371      txTo.vin[4].scriptSig << std::vector<unsigned char>(fifteenSigops.begin(), fifteenSigops.end());
 372  
 373      BOOST_CHECK(::AreInputsStandard(CTransaction(txTo), coins));
 374      // 22 P2SH sigops for all inputs (1 for vin[0], 6 for vin[3], 15 for vin[4]
 375      BOOST_CHECK_EQUAL(GetP2SHSigOpCount(CTransaction(txTo), coins), 22U);
 376  
 377      CMutableTransaction coinbase_tx_mut;
 378      coinbase_tx_mut.vin.resize(1);
 379      CTransaction coinbase_tx{coinbase_tx_mut};
 380      BOOST_CHECK(coinbase_tx.IsCoinBase());
 381      BOOST_CHECK_EQUAL(GetP2SHSigOpCount(coinbase_tx, coins), 0U);
 382  
 383      CMutableTransaction txToNonStd1;
 384      txToNonStd1.vout.resize(1);
 385      txToNonStd1.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[1].GetPubKey()));
 386      txToNonStd1.vout[0].nValue = 1000;
 387      txToNonStd1.vin.resize(1);
 388      txToNonStd1.vin[0].prevout.n = 5;
 389      txToNonStd1.vin[0].prevout.hash = txFrom.GetHash();
 390      txToNonStd1.vin[0].scriptSig << std::vector<unsigned char>(sixteenSigops.begin(), sixteenSigops.end());
 391  
 392      BOOST_CHECK(!::AreInputsStandard(CTransaction(txToNonStd1), coins));
 393      BOOST_CHECK_EQUAL(GetP2SHSigOpCount(CTransaction(txToNonStd1), coins), 16U);
 394  
 395      CMutableTransaction txToNonStd2;
 396      txToNonStd2.vout.resize(1);
 397      txToNonStd2.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key[1].GetPubKey()));
 398      txToNonStd2.vout[0].nValue = 1000;
 399      txToNonStd2.vin.resize(1);
 400      txToNonStd2.vin[0].prevout.n = 6;
 401      txToNonStd2.vin[0].prevout.hash = txFrom.GetHash();
 402      txToNonStd2.vin[0].scriptSig << std::vector<unsigned char>(twentySigops.begin(), twentySigops.end());
 403  
 404      BOOST_CHECK(!::AreInputsStandard(CTransaction(txToNonStd2), coins));
 405      BOOST_CHECK_EQUAL(GetP2SHSigOpCount(CTransaction(txToNonStd2), coins), 20U);
 406  }
 407  
 408  BOOST_AUTO_TEST_SUITE_END()
 409