verify_script.cpp
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 <key.h> 8 #include <policy/policy.h> 9 #include <primitives/transaction.h> 10 #include <pubkey.h> 11 #include <script/interpreter.h> 12 #include <script/script.h> 13 #include <span.h> 14 #include <test/util/transaction_utils.h> 15 #include <uint256.h> 16 #include <util/translation.h> 17 18 #include <array> 19 #include <cassert> 20 #include <cstdint> 21 #include <vector> 22 23 enum class ScriptType { 24 P2WPKH, // segwitv0, witness-pubkey-hash (ECDSA signature) 25 P2TR, // segwitv1, taproot key-path spend (Schnorr signature) 26 }; 27 28 // Microbenchmark for verification of standard scripts. 29 static void VerifyScriptBench(benchmark::Bench& bench, ScriptType script_type) 30 { 31 ECC_Context ecc_context{}; 32 33 // Create deterministic key material needed for output script creation / signing 34 CKey privkey; 35 privkey.Set(uint256::ONE.begin(), uint256::ONE.end(), /*fCompressedIn=*/true); 36 CPubKey pubkey = privkey.GetPubKey(); 37 CKeyID key_id = pubkey.GetID(); 38 39 FlatSigningProvider keystore; 40 keystore.keys.emplace(key_id, privkey); 41 keystore.pubkeys.emplace(key_id, pubkey); 42 43 // Create crediting and spending transactions with provided input type 44 CTxDestination dest; 45 switch (script_type) { 46 case ScriptType::P2WPKH: dest = WitnessV0KeyHash(pubkey); break; 47 case ScriptType::P2TR: dest = WitnessV1Taproot(XOnlyPubKey{pubkey}); break; 48 default: assert(false); 49 } 50 const CMutableTransaction& txCredit = BuildCreditingTransaction(GetScriptForDestination(dest), 1); 51 CMutableTransaction txSpend = BuildSpendingTransaction(/*scriptSig=*/{}, /*scriptWitness=*/{}, CTransaction(txCredit)); 52 53 // Sign spending transaction, precompute transaction data 54 PrecomputedTransactionData txdata; 55 { 56 std::map<COutPoint, Coin> coins; 57 coins[txSpend.vin[0].prevout] = Coin(txCredit.vout[0], /*nHeightIn=*/100, /*fCoinBaseIn=*/false); 58 std::map<int, bilingual_str> input_errors; 59 bool complete = SignTransaction(txSpend, &keystore, coins, SIGHASH_ALL, input_errors); 60 assert(complete); 61 txdata.Init(txSpend, /*spent_outputs=*/{txCredit.vout[0]}); 62 } 63 64 // Benchmark. 65 bench.run([&] { 66 ScriptError err; 67 bool success = VerifyScript( 68 txSpend.vin[0].scriptSig, 69 txCredit.vout[0].scriptPubKey, 70 &txSpend.vin[0].scriptWitness, 71 STANDARD_SCRIPT_VERIFY_FLAGS, 72 MutableTransactionSignatureChecker(&txSpend, 0, txCredit.vout[0].nValue, txdata, MissingDataBehavior::ASSERT_FAIL), 73 &err); 74 assert(err == SCRIPT_ERR_OK); 75 assert(success); 76 }); 77 } 78 79 static void VerifyScriptP2WPKH(benchmark::Bench& bench) { VerifyScriptBench(bench, ScriptType::P2WPKH); } 80 static void VerifyScriptP2TR(benchmark::Bench& bench) { VerifyScriptBench(bench, ScriptType::P2TR); } 81 82 static void VerifyNestedIfScript(benchmark::Bench& bench) 83 { 84 std::vector<std::vector<unsigned char>> stack; 85 CScript script; 86 for (int i = 0; i < 100; ++i) { 87 script << OP_1 << OP_IF; 88 } 89 for (int i = 0; i < 1000; ++i) { 90 script << OP_1; 91 } 92 for (int i = 0; i < 100; ++i) { 93 script << OP_ENDIF; 94 } 95 bench.run([&] { 96 auto stack_copy = stack; 97 ScriptError error; 98 bool ret = EvalScript(stack_copy, script, 0, BaseSignatureChecker(), SigVersion::BASE, &error); 99 assert(ret); 100 }); 101 } 102 103 BENCHMARK(VerifyScriptP2WPKH); 104 BENCHMARK(VerifyScriptP2TR); 105 BENCHMARK(VerifyNestedIfScript);