verify_script.cpp raw

   1  // Copyright (c) 2016-present The Bitcoin Core 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 <bench/bench.h>
   7  #include <coins.h>
   8  #include <key.h>
   9  #include <policy/policy.h>
  10  #include <primitives/transaction.h>
  11  #include <pubkey.h>
  12  #include <script/interpreter.h>
  13  #include <script/script.h>
  14  #include <script/script_error.h>
  15  #include <script/sign.h>
  16  #include <script/signingprovider.h>
  17  #include <script/verify_flags.h>
  18  #include <span.h>
  19  #include <test/util/transaction_utils.h>
  20  #include <uint256.h>
  21  #include <util/check.h>
  22  #include <util/translation.h>
  23  
  24  #include <cstddef>
  25  #include <map>
  26  #include <span>
  27  #include <vector>
  28  
  29  enum class ScriptType {
  30      P2WPKH, // segwitv0, witness-pubkey-hash (ECDSA signature)
  31      P2TR_KeyPath, // segwitv1, taproot key-path spend (Schnorr signature)
  32      P2TR_ScriptPath, // segwitv1, taproot script-path spend (Tapscript leaf with a single OP_CHECKSIG)
  33  };
  34  
  35  static size_t ExpectedWitnessStackSize(ScriptType script_type)
  36  {
  37      switch (script_type) {
  38      case ScriptType::P2WPKH: return 2; // [pubkey, signature]
  39      case ScriptType::P2TR_KeyPath: return 1; // [signature]
  40      case ScriptType::P2TR_ScriptPath: return 3; // [signature, tapscript, control block]
  41      } // no default case, so the compiler can warn about missing cases
  42      assert(false);
  43  }
  44  
  45  // Microbenchmark for verification of standard scripts.
  46  static void VerifyScriptBench(benchmark::Bench& bench, ScriptType script_type)
  47  {
  48      ECC_Context ecc_context{};
  49  
  50      // Create deterministic key material needed for output script creation / signing
  51      CKey privkey;
  52      privkey.Set(uint256::ONE.begin(), uint256::ONE.end(), /*fCompressedIn=*/true);
  53      CPubKey pubkey = privkey.GetPubKey();
  54      XOnlyPubKey xonly_pubkey{pubkey};
  55      CKeyID key_id = pubkey.GetID();
  56  
  57      FlatSigningProvider keystore;
  58      keystore.keys.emplace(key_id, privkey);
  59      keystore.pubkeys.emplace(key_id, pubkey);
  60  
  61      // Create crediting and spending transactions with provided input type
  62      const auto dest{[&]() -> CTxDestination {
  63          switch (script_type) {
  64          case ScriptType::P2WPKH: return WitnessV0KeyHash(pubkey);
  65          case ScriptType::P2TR_KeyPath: return WitnessV1Taproot(xonly_pubkey);
  66          case ScriptType::P2TR_ScriptPath:
  67              TaprootBuilder builder;
  68              builder.Add(0, CScript() << ToByteVector(xonly_pubkey) << OP_CHECKSIG, TAPROOT_LEAF_TAPSCRIPT);
  69              builder.Finalize(XOnlyPubKey::NUMS_H); // effectively unspendable key-path
  70              const auto output{builder.GetOutput()};
  71              keystore.tr_trees.emplace(output, builder);
  72              return output;
  73          } // no default case, so the compiler can warn about missing cases
  74          assert(false);
  75      }()};
  76      const CMutableTransaction& txCredit = BuildCreditingTransaction(GetScriptForDestination(dest), 1);
  77      CMutableTransaction txSpend = BuildSpendingTransaction(/*scriptSig=*/{}, /*scriptWitness=*/{}, CTransaction(txCredit));
  78  
  79      // Sign spending transaction, precompute transaction data
  80      PrecomputedTransactionData txdata;
  81      {
  82          const std::map<COutPoint, Coin> coins{
  83              {txSpend.vin[0].prevout, Coin(txCredit.vout[0], /*nHeightIn=*/100, /*fCoinBaseIn=*/false)}
  84          };
  85          std::map<int, bilingual_str> input_errors;
  86          bool complete = SignTransaction(txSpend, &keystore, coins, {.sighash_type = SIGHASH_ALL}, input_errors);
  87          assert(complete);
  88          // Weak sanity check on witness data to ensure we produced the intended spending type
  89          assert(txSpend.vin[0].scriptWitness.stack.size() == ExpectedWitnessStackSize(script_type));
  90          txdata.Init(txSpend, /*spent_outputs=*/{txCredit.vout[0]});
  91      }
  92  
  93      // Benchmark.
  94      bench.unit("script").run([&] {
  95          ScriptError err;
  96          bool success = VerifyScript(
  97              txSpend.vin[0].scriptSig,
  98              txCredit.vout[0].scriptPubKey,
  99              &txSpend.vin[0].scriptWitness,
 100              STANDARD_SCRIPT_VERIFY_FLAGS,
 101              MutableTransactionSignatureChecker(&txSpend, 0, txCredit.vout[0].nValue, txdata, MissingDataBehavior::ASSERT_FAIL),
 102              &err);
 103          assert(err == SCRIPT_ERR_OK);
 104          assert(success);
 105      });
 106  }
 107  
 108  static void VerifyScriptP2WPKH(benchmark::Bench& bench) { VerifyScriptBench(bench, ScriptType::P2WPKH); }
 109  static void VerifyScriptP2TR_KeyPath(benchmark::Bench& bench) { VerifyScriptBench(bench, ScriptType::P2TR_KeyPath); }
 110  static void VerifyScriptP2TR_ScriptPath(benchmark::Bench& bench) { VerifyScriptBench(bench, ScriptType::P2TR_ScriptPath); }
 111  
 112  static void VerifyNestedIfScript(benchmark::Bench& bench)
 113  {
 114      std::vector<std::vector<unsigned char>> stack;
 115      CScript script;
 116      for (int i = 0; i < 100; ++i) {
 117          script << OP_1 << OP_IF;
 118      }
 119      for (int i = 0; i < 1000; ++i) {
 120          script << OP_1;
 121      }
 122      for (int i = 0; i < 100; ++i) {
 123          script << OP_ENDIF;
 124      }
 125      bench.unit("script")
 126          .setup([&] { stack.clear(); })
 127          .run([&] {
 128              ScriptError error;
 129              const bool ret{EvalScript(stack, script, /*flags=*/0, BaseSignatureChecker(), SigVersion::BASE, &error)};
 130              assert(ret && error == SCRIPT_ERR_OK);
 131          });
 132  }
 133  
 134  BENCHMARK(VerifyScriptP2WPKH);
 135  BENCHMARK(VerifyScriptP2TR_KeyPath);
 136  BENCHMARK(VerifyScriptP2TR_ScriptPath);
 137  BENCHMARK(VerifyNestedIfScript);
 138