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