1 // Copyright (c) 2009-2020 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 <pubkey.h>
6 #include <script/interpreter.h>
7 #include <streams.h>
8 #include <util/memory.h>
9 #include <version.h>
10 
11 #include <test/fuzz/fuzz.h>
12 
13 /** Flags that are not forbidden by an assert */
14 static bool IsValidFlagCombination(unsigned flags);
15 
initialize()16 void initialize()
17 {
18     static const ECCVerifyHandle verify_handle;
19 }
20 
test_one_input(const std::vector<uint8_t> & buffer)21 void test_one_input(const std::vector<uint8_t>& buffer)
22 {
23     CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION);
24     try {
25         int nVersion;
26         ds >> nVersion;
27         ds.SetVersion(nVersion);
28     } catch (const std::ios_base::failure&) {
29         return;
30     }
31 
32     try {
33         const CTransaction tx(deserialize, ds);
34 
35         unsigned int verify_flags;
36         ds >> verify_flags;
37 
38         if (!IsValidFlagCombination(verify_flags)) return;
39 
40         unsigned int fuzzed_flags;
41         ds >> fuzzed_flags;
42 
43         std::vector<CTxOut> spent_outputs;
44         for (unsigned i = 0; i < tx.vin.size(); ++i) {
45             CTxOut prevout;
46             ds >> prevout;
47             spent_outputs.push_back(prevout);
48         }
49         PrecomputedTransactionData txdata;
50         txdata.Init(tx, std::move(spent_outputs));
51 
52         for (unsigned i = 0; i < tx.vin.size(); ++i) {
53             const CTxOut& prevout = txdata.m_spent_outputs.at(i);
54             const TransactionSignatureChecker checker{&tx, i, prevout.nValue, txdata};
55 
56             ScriptError serror;
57             const bool ret = VerifyScript(tx.vin.at(i).scriptSig, prevout.scriptPubKey, &tx.vin.at(i).scriptWitness, verify_flags, checker, &serror);
58             assert(ret == (serror == SCRIPT_ERR_OK));
59 
60             // Verify that removing flags from a passing test or adding flags to a failing test does not change the result
61             if (ret) {
62                 verify_flags &= ~fuzzed_flags;
63             } else {
64                 verify_flags |= fuzzed_flags;
65             }
66             if (!IsValidFlagCombination(verify_flags)) return;
67 
68             ScriptError serror_fuzzed;
69             const bool ret_fuzzed = VerifyScript(tx.vin.at(i).scriptSig, prevout.scriptPubKey, &tx.vin.at(i).scriptWitness, verify_flags, checker, &serror_fuzzed);
70             assert(ret_fuzzed == (serror_fuzzed == SCRIPT_ERR_OK));
71 
72             assert(ret_fuzzed == ret);
73         }
74     } catch (const std::ios_base::failure&) {
75         return;
76     }
77 }
78 
IsValidFlagCombination(unsigned flags)79 static bool IsValidFlagCombination(unsigned flags)
80 {
81     if (flags & SCRIPT_VERIFY_CLEANSTACK && ~flags & (SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS)) return false;
82     if (flags & SCRIPT_VERIFY_WITNESS && ~flags & SCRIPT_VERIFY_P2SH) return false;
83     return true;
84 }
85