Bitcoin Core  31.0.0
P2P Digital Currency
script_assets_tests.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-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 
6 #include <script/interpreter.h>
7 #include <script/script.h>
8 #include <script/sigcache.h>
9 #include <script/sign.h>
10 #include <script/signingprovider.h>
11 #include <span.h>
12 #include <streams.h>
13 #include <test/util/json.h>
14 #include <util/check.h>
15 #include <util/fs.h>
16 #include <util/strencodings.h>
17 
18 #include <cstdint>
19 #include <cstdlib>
20 #include <fstream>
21 #include <utility>
22 #include <vector>
23 
24 #include <boost/test/unit_test.hpp>
25 
26 #include <univalue.h>
27 
28 script_verify_flags ParseScriptFlags(std::string strFlags);
29 
30 BOOST_AUTO_TEST_SUITE(script_assets_tests)
31 
32 template <typename T>
33 CScript ToScript(const T& byte_container)
34 {
35  auto span{MakeUCharSpan(byte_container)};
36  return {span.begin(), span.end()};
37 }
38 
39 static CScript ScriptFromHex(const std::string& str)
40 {
41  return ToScript(*Assert(TryParseHex(str)));
42 }
43 
44 static CMutableTransaction TxFromHex(const std::string& str)
45 {
47  SpanReader{ParseHex(str)} >> TX_NO_WITNESS(tx);
48  return tx;
49 }
50 
51 static std::vector<CTxOut> TxOutsFromJSON(const UniValue& univalue)
52 {
53  assert(univalue.isArray());
54  std::vector<CTxOut> prevouts;
55  for (size_t i = 0; i < univalue.size(); ++i) {
56  CTxOut txout;
57  SpanReader{ParseHex(univalue[i].get_str())} >> txout;
58  prevouts.push_back(std::move(txout));
59  }
60  return prevouts;
61 }
62 
64 {
65  assert(univalue.isArray());
66  CScriptWitness scriptwitness;
67  for (size_t i = 0; i < univalue.size(); ++i) {
68  auto bytes = ParseHex(univalue[i].get_str());
69  scriptwitness.stack.push_back(std::move(bytes));
70  }
71  return scriptwitness;
72 }
73 
74 static std::vector<script_verify_flags> AllConsensusFlags()
75 {
76  std::vector<script_verify_flags> ret;
77 
78  for (unsigned int i = 0; i < 128; ++i) {
79  script_verify_flags flag = 0;
80  if (i & 1) flag |= SCRIPT_VERIFY_P2SH;
81  if (i & 2) flag |= SCRIPT_VERIFY_DERSIG;
82  if (i & 4) flag |= SCRIPT_VERIFY_NULLDUMMY;
83  if (i & 8) flag |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
84  if (i & 16) flag |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY;
85  if (i & 32) flag |= SCRIPT_VERIFY_WITNESS;
86  if (i & 64) flag |= SCRIPT_VERIFY_TAPROOT;
87 
88  // SCRIPT_VERIFY_WITNESS requires SCRIPT_VERIFY_P2SH
89  if (flag & SCRIPT_VERIFY_WITNESS && !(flag & SCRIPT_VERIFY_P2SH)) continue;
90  // SCRIPT_VERIFY_TAPROOT requires SCRIPT_VERIFY_WITNESS
91  if (flag & SCRIPT_VERIFY_TAPROOT && !(flag & SCRIPT_VERIFY_WITNESS)) continue;
92 
93  ret.push_back(flag);
94  }
95 
96  return ret;
97 }
98 
100 static const std::vector<script_verify_flags> ALL_CONSENSUS_FLAGS = AllConsensusFlags();
101 
102 static void AssetTest(const UniValue& test, SignatureCache& signature_cache)
103 {
104  BOOST_CHECK(test.isObject());
105 
106  CMutableTransaction mtx = TxFromHex(test["tx"].get_str());
107  const std::vector<CTxOut> prevouts = TxOutsFromJSON(test["prevouts"]);
108  BOOST_CHECK(prevouts.size() == mtx.vin.size());
109  size_t idx = test["index"].getInt<int64_t>();
110  script_verify_flags test_flags{ParseScriptFlags(test["flags"].get_str())};
111  bool fin = test.exists("final") && test["final"].get_bool();
112 
113  if (test.exists("success")) {
114  mtx.vin[idx].scriptSig = ScriptFromHex(test["success"]["scriptSig"].get_str());
115  mtx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["success"]["witness"]);
116  CTransaction tx(mtx);
118  txdata.Init(tx, std::vector<CTxOut>(prevouts));
119  CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, signature_cache, txdata);
120 
121  for (const auto flags : ALL_CONSENSUS_FLAGS) {
122  // "final": true tests are valid for all flags. Others are only valid with flags that are
123  // a subset of test_flags.
124  if (fin || ((flags & test_flags) == flags)) {
125  bool ret = VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr);
126  BOOST_CHECK(ret);
127  }
128  }
129  }
130 
131  if (test.exists("failure")) {
132  mtx.vin[idx].scriptSig = ScriptFromHex(test["failure"]["scriptSig"].get_str());
133  mtx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["failure"]["witness"]);
134  CTransaction tx(mtx);
136  txdata.Init(tx, std::vector<CTxOut>(prevouts));
137  CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, signature_cache, txdata);
138 
139  for (const auto flags : ALL_CONSENSUS_FLAGS) {
140  // If a test is supposed to fail with test_flags, it should also fail with any superset thereof.
141  if ((flags & test_flags) == test_flags) {
142  bool ret = VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr);
143  BOOST_CHECK(!ret);
144  }
145  }
146  }
147 }
148 
149 BOOST_AUTO_TEST_CASE(script_assets_test)
150 {
151  // See src/test/fuzz/script_assets_test_minimizer.cpp for information on how to generate
152  // the script_assets_test.json file used by this test.
154 
155  const char* dir = std::getenv("DIR_UNIT_TEST_DATA");
156  BOOST_WARN_MESSAGE(dir != nullptr, "Variable DIR_UNIT_TEST_DATA unset, skipping script_assets_test");
157  if (dir == nullptr) return;
158  auto path = fs::path(dir) / "script_assets_test.json";
159  bool exists = fs::exists(path);
160  BOOST_WARN_MESSAGE(exists, "File $DIR_UNIT_TEST_DATA/script_assets_test.json not found, skipping script_assets_test");
161  if (!exists) return;
162  std::ifstream file{path.std_path()};
163  BOOST_CHECK(file.is_open());
164  file.seekg(0, std::ios::end);
165  size_t length = file.tellg();
166  file.seekg(0, std::ios::beg);
167  std::string data(length, '\0');
168  file.read(data.data(), data.size());
170  BOOST_CHECK(tests.isArray());
171  BOOST_CHECK(tests.size() > 0);
172 
173  for (size_t i = 0; i < tests.size(); i++) {
174  AssetTest(tests[i], signature_cache);
175  }
176  file.close();
177 }
178 
bool isObject() const
Definition: univalue.h:88
int ret
Valid signature cache, to avoid doing expensive ECDSA signature checking twice for every transaction ...
Definition: sigcache.h:38
constexpr auto MakeUCharSpan(const V &v) -> decltype(UCharSpanCast(std::span
Like the std::span constructor, but for (const) unsigned char member types only.
Definition: span.h:111
static std::vector< CTxOut > TxOutsFromJSON(const UniValue &univalue)
std::vector< Byte > ParseHex(std::string_view hex_str)
Like TryParseHex, but returns an empty vector on invalid input.
Definition: strencodings.h:69
assert(!tx.IsCoinBase())
bool get_bool() const
static void AssetTest(const UniValue &test, SignatureCache &signature_cache)
UniValue read_json(std::string_view jsondata)
Definition: json.cpp:12
Int getInt() const
Definition: univalue.h:140
bool VerifyScript(const CScript &scriptSig, const CScript &scriptPubKey, const CScriptWitness *witness, script_verify_flags flags, const BaseSignatureChecker &checker, ScriptError *serror)
const std::vector< CTxIn > vin
Definition: transaction.h:291
Minimal stream for reading from an existing byte array by std::span.
Definition: streams.h:82
static CScript ScriptFromHex(const std::string &str)
constexpr std::array tests
Definition: unitester.cpp:101
static bool exists(const path &p)
Definition: fs.h:95
void Init(const T &tx, std::vector< CTxOut > &&spent_outputs, bool force=false)
Initialize this PrecomputedTransactionData with transaction data.
bool exists(const std::string &key) const
Definition: univalue.h:79
BOOST_AUTO_TEST_SUITE_END()
static CScriptWitness ScriptWitnessFromJSON(const UniValue &univalue)
An output of a transaction.
Definition: transaction.h:139
static CMutableTransaction TxFromHex(const std::string &str)
static const std::vector< script_verify_flags > ALL_CONSENSUS_FLAGS
Precomputed list of all valid combinations of consensus-relevant script validation flags...
int flags
Definition: bitcoin-tx.cpp:529
static std::vector< script_verify_flags > AllConsensusFlags()
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:404
CScript ToScript(const T &byte_container)
A mutable version of CTransaction.
Definition: transaction.h:357
static constexpr size_t DEFAULT_SIGNATURE_CACHE_BYTES
Definition: sigcache.h:29
std::optional< std::vector< Byte > > TryParseHex(std::string_view str)
Parse the hex string into bytes (uint8_t or std::byte).
size_t size() const
Definition: univalue.h:71
The basic transaction that is broadcasted on the network and contained in blocks. ...
Definition: transaction.h:280
BOOST_AUTO_TEST_CASE(script_assets_test)
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:33
bool isArray() const
Definition: univalue.h:87
#define Assert(val)
Identity function.
Definition: check.h:113
static constexpr TransactionSerParams TX_NO_WITNESS
Definition: transaction.h:181
#define BOOST_CHECK(expr)
Definition: object.cpp:16
script_verify_flags ParseScriptFlags(std::string strFlags)