verify_script.cpp raw
1 // Copyright (c) 2016-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 <limenka-build-config.h> // IWYU pragma: keep
6
7 #include <bench/bench.h>
8 #include <hash.h>
9 #include <key.h>
10 #include <primitives/transaction.h>
11 #include <pubkey.h>
12 #if defined(HAVE_CONSENSUS_LIB)
13 #include <script/limenkaconsensus.h>
14 #endif
15 #include <script/interpreter.h>
16 #include <script/script.h>
17 #include <span.h>
18 #include <streams.h>
19 #include <test/util/transaction_utils.h>
20 #include <uint256.h>
21
22 #include <array>
23 #include <cassert>
24 #include <cstdint>
25 #include <vector>
26
27 // Microbenchmark for verification of a basic P2WPKH script. Can be easily
28 // modified to measure performance of other types of scripts.
29 static void VerifyScriptBench(benchmark::Bench& bench)
30 {
31 ECC_Context ecc_context{};
32
33 const uint32_t flags{SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH};
34 const int witnessversion = 0;
35
36 // Key pair.
37 CKey key;
38 static const std::array<unsigned char, 32> vchKey = {
39 {
40 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
41 }
42 };
43 key.Set(vchKey.begin(), vchKey.end(), false);
44 CPubKey pubkey = key.GetPubKey();
45 uint160 pubkeyHash;
46 CHash160().Write(pubkey).Finalize(pubkeyHash);
47
48 // Script.
49 CScript scriptPubKey = CScript() << witnessversion << ToByteVector(pubkeyHash);
50 CScript scriptSig;
51 CScript witScriptPubkey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(pubkeyHash) << OP_EQUALVERIFY << OP_CHECKSIG;
52 const CMutableTransaction& txCredit = BuildCreditingTransaction(scriptPubKey, 1);
53 CMutableTransaction txSpend = BuildSpendingTransaction(scriptSig, CScriptWitness(), CTransaction(txCredit));
54 CScriptWitness& witness = txSpend.vin[0].scriptWitness;
55 witness.stack.emplace_back();
56 key.Sign(SignatureHash(witScriptPubkey, txSpend, 0, SIGHASH_ALL, txCredit.vout[0].nValue, SigVersion::WITNESS_V0), witness.stack.back());
57 witness.stack.back().push_back(static_cast<unsigned char>(SIGHASH_ALL));
58 witness.stack.push_back(ToByteVector(pubkey));
59
60 // Benchmark.
61 bench.run([&] {
62 ScriptError err;
63 bool success = VerifyScript(
64 txSpend.vin[0].scriptSig,
65 txCredit.vout[0].scriptPubKey,
66 &txSpend.vin[0].scriptWitness,
67 flags,
68 MutableTransactionSignatureChecker(&txSpend, 0, txCredit.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL),
69 &err);
70 assert(err == SCRIPT_ERR_OK);
71 assert(success);
72
73 #if defined(HAVE_CONSENSUS_LIB)
74 DataStream stream;
75 stream << TX_WITH_WITNESS(txSpend);
76 int csuccess = limenkaconsensus_verify_script_with_amount(
77 txCredit.vout[0].scriptPubKey.data(),
78 txCredit.vout[0].scriptPubKey.size(),
79 txCredit.vout[0].nValue,
80 (const unsigned char*)stream.data(), stream.size(), 0, flags, nullptr);
81 assert(csuccess == 1);
82 #endif
83 });
84 }
85
86 static void VerifyNestedIfScript(benchmark::Bench& bench)
87 {
88 std::vector<std::vector<unsigned char>> stack;
89 CScript script;
90 for (int i = 0; i < 100; ++i) {
91 script << OP_1 << OP_IF;
92 }
93 for (int i = 0; i < 1000; ++i) {
94 script << OP_1;
95 }
96 for (int i = 0; i < 100; ++i) {
97 script << OP_ENDIF;
98 }
99 bench.run([&] {
100 auto stack_copy = stack;
101 ScriptError error;
102 bool ret = EvalScript(stack_copy, script, 0, BaseSignatureChecker(), SigVersion::BASE, &error);
103 assert(ret);
104 });
105 }
106
107 BENCHMARK(VerifyScriptBench, benchmark::PriorityLevel::HIGH);
108 BENCHMARK(VerifyNestedIfScript, benchmark::PriorityLevel::HIGH);
109