sighash_tests.cpp
1 // Copyright (c) 2013-2022 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 <common/system.h> 6 #include <consensus/tx_check.h> 7 #include <consensus/validation.h> 8 #include <hash.h> 9 #include <script/interpreter.h> 10 #include <script/script.h> 11 #include <serialize.h> 12 #include <streams.h> 13 #include <test/data/sighash.json.h> 14 #include <test/util/json.h> 15 #include <test/util/random.h> 16 #include <test/util/setup_common.h> 17 #include <util/strencodings.h> 18 19 #include <iostream> 20 21 #include <boost/test/unit_test.hpp> 22 23 #include <univalue.h> 24 25 // Old script.cpp SignatureHash function 26 uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) 27 { 28 if (nIn >= txTo.vin.size()) 29 { 30 return uint256::ONE; 31 } 32 CMutableTransaction txTmp(txTo); 33 34 // In case concatenating two scripts ends up with two codeseparators, 35 // or an extra one at the end, this prevents all those possible incompatibilities. 36 FindAndDelete(scriptCode, CScript(OP_CODESEPARATOR)); 37 38 // Blank out other inputs' signatures 39 for (unsigned int i = 0; i < txTmp.vin.size(); i++) 40 txTmp.vin[i].scriptSig = CScript(); 41 txTmp.vin[nIn].scriptSig = scriptCode; 42 43 // Blank out some of the outputs 44 if ((nHashType & 0x1f) == SIGHASH_NONE) 45 { 46 // Wildcard payee 47 txTmp.vout.clear(); 48 49 // Let the others update at will 50 for (unsigned int i = 0; i < txTmp.vin.size(); i++) 51 if (i != nIn) 52 txTmp.vin[i].nSequence = 0; 53 } 54 else if ((nHashType & 0x1f) == SIGHASH_SINGLE) 55 { 56 // Only lock-in the txout payee at same index as txin 57 unsigned int nOut = nIn; 58 if (nOut >= txTmp.vout.size()) 59 { 60 return uint256::ONE; 61 } 62 txTmp.vout.resize(nOut+1); 63 for (unsigned int i = 0; i < nOut; i++) 64 txTmp.vout[i].SetNull(); 65 66 // Let the others update at will 67 for (unsigned int i = 0; i < txTmp.vin.size(); i++) 68 if (i != nIn) 69 txTmp.vin[i].nSequence = 0; 70 } 71 72 // Blank out other inputs completely, not recommended for open transactions 73 if (nHashType & SIGHASH_ANYONECANPAY) 74 { 75 txTmp.vin[0] = txTmp.vin[nIn]; 76 txTmp.vin.resize(1); 77 } 78 79 // Serialize and hash 80 HashWriter ss{}; 81 ss << TX_NO_WITNESS(txTmp) << nHashType; 82 return ss.GetHash(); 83 } 84 85 struct SigHashTest : BasicTestingSetup { 86 void RandomScript(CScript &script) { 87 static const opcodetype oplist[] = {OP_FALSE, OP_1, OP_2, OP_3, OP_CHECKSIG, OP_IF, OP_VERIF, OP_RETURN, OP_CODESEPARATOR}; 88 script = CScript(); 89 int ops = (m_rng.randrange(10)); 90 for (int i=0; i<ops; i++) 91 script << oplist[m_rng.randrange(std::size(oplist))]; 92 } 93 94 void RandomTransaction(CMutableTransaction& tx, bool fSingle) 95 { 96 tx.version = m_rng.rand32(); 97 tx.vin.clear(); 98 tx.vout.clear(); 99 tx.nLockTime = (m_rng.randbool()) ? m_rng.rand32() : 0; 100 int ins = (m_rng.randbits(2)) + 1; 101 int outs = fSingle ? ins : (m_rng.randbits(2)) + 1; 102 for (int in = 0; in < ins; in++) { 103 tx.vin.emplace_back(); 104 CTxIn &txin = tx.vin.back(); 105 txin.prevout.hash = Txid::FromUint256(m_rng.rand256()); 106 txin.prevout.n = m_rng.randbits(2); 107 RandomScript(txin.scriptSig); 108 txin.nSequence = (m_rng.randbool()) ? m_rng.rand32() : std::numeric_limits<uint32_t>::max(); 109 } 110 for (int out = 0; out < outs; out++) { 111 tx.vout.emplace_back(); 112 CTxOut &txout = tx.vout.back(); 113 txout.nValue = RandMoney(m_rng); 114 RandomScript(txout.scriptPubKey); 115 } 116 } 117 }; // struct SigHashTest 118 119 BOOST_FIXTURE_TEST_SUITE(sighash_tests, SigHashTest) 120 121 BOOST_AUTO_TEST_CASE(sighash_test) 122 { 123 #if defined(PRINT_SIGHASH_JSON) 124 std::cout << "[\n"; 125 std::cout << "\t[\"raw_transaction, script, input_index, hashType, signature_hash (result)\"],\n"; 126 int nRandomTests = 500; 127 #else 128 int nRandomTests = 50000; 129 #endif 130 for (int i=0; i<nRandomTests; i++) { 131 int nHashType{int(m_rng.rand32())}; 132 CMutableTransaction txTo; 133 RandomTransaction(txTo, (nHashType & 0x1f) == SIGHASH_SINGLE); 134 CScript scriptCode; 135 RandomScript(scriptCode); 136 int nIn = m_rng.randrange(txTo.vin.size()); 137 138 uint256 sh, sho; 139 sho = SignatureHashOld(scriptCode, CTransaction(txTo), nIn, nHashType); 140 sh = SignatureHash(scriptCode, txTo, nIn, nHashType, 0, SigVersion::BASE); 141 #if defined(PRINT_SIGHASH_JSON) 142 DataStream ss; 143 ss << TX_WITH_WITNESS(txTo); 144 145 std::cout << "\t[\"" ; 146 std::cout << HexStr(ss) << "\", \""; 147 std::cout << HexStr(scriptCode) << "\", "; 148 std::cout << nIn << ", "; 149 std::cout << nHashType << ", \""; 150 std::cout << sho.GetHex() << "\"]"; 151 if (i+1 != nRandomTests) { 152 std::cout << ","; 153 } 154 std::cout << "\n"; 155 #endif 156 BOOST_CHECK(sh == sho); 157 } 158 #if defined(PRINT_SIGHASH_JSON) 159 std::cout << "]\n"; 160 #endif 161 } 162 163 // Goal: check that SignatureHash generates correct hash 164 BOOST_AUTO_TEST_CASE(sighash_from_data) 165 { 166 UniValue tests = read_json(json_tests::sighash); 167 168 for (unsigned int idx = 0; idx < tests.size(); idx++) { 169 const UniValue& test = tests[idx]; 170 std::string strTest = test.write(); 171 if (test.size() < 1) // Allow for extra stuff (useful for comments) 172 { 173 BOOST_ERROR("Bad test: " << strTest); 174 continue; 175 } 176 if (test.size() == 1) continue; // comment 177 178 std::string raw_tx, raw_script, sigHashHex; 179 int nIn, nHashType; 180 uint256 sh; 181 CTransactionRef tx; 182 CScript scriptCode = CScript(); 183 184 try { 185 // deserialize test data 186 raw_tx = test[0].get_str(); 187 raw_script = test[1].get_str(); 188 nIn = test[2].getInt<int>(); 189 nHashType = test[3].getInt<int>(); 190 sigHashHex = test[4].get_str(); 191 192 DataStream stream(ParseHex(raw_tx)); 193 stream >> TX_WITH_WITNESS(tx); 194 195 TxValidationState state; 196 BOOST_CHECK_MESSAGE(CheckTransaction(*tx, state), strTest); 197 BOOST_CHECK(state.IsValid()); 198 199 std::vector<unsigned char> raw = ParseHex(raw_script); 200 scriptCode.insert(scriptCode.end(), raw.begin(), raw.end()); 201 } catch (...) { 202 BOOST_ERROR("Bad test, couldn't deserialize data: " << strTest); 203 continue; 204 } 205 206 sh = SignatureHash(scriptCode, *tx, nIn, nHashType, 0, SigVersion::BASE); 207 BOOST_CHECK_MESSAGE(sh.GetHex() == sigHashHex, strTest); 208 } 209 } 210 211 BOOST_AUTO_TEST_CASE(sighash_caching) 212 { 213 // Get a script, transaction and parameters as inputs to the sighash function. 214 CScript scriptcode; 215 RandomScript(scriptcode); 216 CScript diff_scriptcode{scriptcode}; 217 diff_scriptcode << OP_1; 218 CMutableTransaction tx; 219 RandomTransaction(tx, /*fSingle=*/false); 220 const auto in_index{static_cast<uint32_t>(m_rng.randrange(tx.vin.size()))}; 221 const auto amount{m_rng.rand<CAmount>()}; 222 223 // Exercise the sighash function under both legacy and segwit v0. 224 for (const auto sigversion: {SigVersion::BASE, SigVersion::WITNESS_V0}) { 225 // For each, run it against all the 6 standard hash types and a few additional random ones. 226 std::vector<int32_t> hash_types{{SIGHASH_ALL, SIGHASH_SINGLE, SIGHASH_NONE, SIGHASH_ALL | SIGHASH_ANYONECANPAY, 227 SIGHASH_SINGLE | SIGHASH_ANYONECANPAY, SIGHASH_NONE | SIGHASH_ANYONECANPAY, 228 SIGHASH_ANYONECANPAY, 0, std::numeric_limits<int32_t>::max()}}; 229 for (int i{0}; i < 10; ++i) { 230 hash_types.push_back(i % 2 == 0 ? m_rng.rand<int8_t>() : m_rng.rand<int32_t>()); 231 } 232 233 // Reuse the same cache across script types. This must not cause any issue as the cached value for one hash type must never 234 // be confused for another (instantiating the cache within the loop instead would prevent testing this). 235 SigHashCache cache; 236 for (const auto hash_type: hash_types) { 237 const bool expect_one{sigversion == SigVersion::BASE && ((hash_type & 0x1f) == SIGHASH_SINGLE) && in_index >= tx.vout.size()}; 238 239 // The result of computing the sighash should be the same with or without cache. 240 const auto sighash_with_cache{SignatureHash(scriptcode, tx, in_index, hash_type, amount, sigversion, nullptr, &cache)}; 241 const auto sighash_no_cache{SignatureHash(scriptcode, tx, in_index, hash_type, amount, sigversion, nullptr, nullptr)}; 242 BOOST_CHECK_EQUAL(sighash_with_cache, sighash_no_cache); 243 244 // Calling the cached version again should return the same value again. 245 BOOST_CHECK_EQUAL(sighash_with_cache, SignatureHash(scriptcode, tx, in_index, hash_type, amount, sigversion, nullptr, &cache)); 246 247 // While here we might as well also check that the result for legacy is the same as for the old SignatureHash() function. 248 if (sigversion == SigVersion::BASE) { 249 BOOST_CHECK_EQUAL(sighash_with_cache, SignatureHashOld(scriptcode, CTransaction(tx), in_index, hash_type)); 250 } 251 252 // Calling with a different scriptcode (for instance in case a CODESEP is encountered) will not return the cache value but 253 // overwrite it. The sighash will always be different except in case of legacy SIGHASH_SINGLE bug. 254 const auto sighash_with_cache2{SignatureHash(diff_scriptcode, tx, in_index, hash_type, amount, sigversion, nullptr, &cache)}; 255 const auto sighash_no_cache2{SignatureHash(diff_scriptcode, tx, in_index, hash_type, amount, sigversion, nullptr, nullptr)}; 256 BOOST_CHECK_EQUAL(sighash_with_cache2, sighash_no_cache2); 257 if (!expect_one) { 258 BOOST_CHECK_NE(sighash_with_cache, sighash_with_cache2); 259 } else { 260 BOOST_CHECK_EQUAL(sighash_with_cache, sighash_with_cache2); 261 BOOST_CHECK_EQUAL(sighash_with_cache, uint256::ONE); 262 } 263 264 // Calling the cached version again should return the same value again. 265 BOOST_CHECK_EQUAL(sighash_with_cache2, SignatureHash(diff_scriptcode, tx, in_index, hash_type, amount, sigversion, nullptr, &cache)); 266 267 // And if we store a different value for this scriptcode and hash type it will return that instead. 268 { 269 HashWriter h{}; 270 h << 42; 271 cache.Store(hash_type, scriptcode, h); 272 const auto stored_hash{h.GetHash()}; 273 BOOST_CHECK(cache.Load(hash_type, scriptcode, h)); 274 const auto loaded_hash{h.GetHash()}; 275 BOOST_CHECK_EQUAL(stored_hash, loaded_hash); 276 } 277 278 // And using this mutated cache with the sighash function will return the new value (except in the legacy SIGHASH_SINGLE bug 279 // case in which it'll return 1). 280 if (!expect_one) { 281 BOOST_CHECK_NE(SignatureHash(scriptcode, tx, in_index, hash_type, amount, sigversion, nullptr, &cache), sighash_with_cache); 282 HashWriter h{}; 283 BOOST_CHECK(cache.Load(hash_type, scriptcode, h)); 284 h << hash_type; 285 const auto new_hash{h.GetHash()}; 286 BOOST_CHECK_EQUAL(SignatureHash(scriptcode, tx, in_index, hash_type, amount, sigversion, nullptr, &cache), new_hash); 287 } else { 288 BOOST_CHECK_EQUAL(SignatureHash(scriptcode, tx, in_index, hash_type, amount, sigversion, nullptr, &cache), uint256::ONE); 289 } 290 291 // Wipe the cache and restore the correct cached value for this scriptcode and hash_type before starting the next iteration. 292 HashWriter dummy{}; 293 cache.Store(hash_type, diff_scriptcode, dummy); 294 (void)SignatureHash(scriptcode, tx, in_index, hash_type, amount, sigversion, nullptr, &cache); 295 BOOST_CHECK(cache.Load(hash_type, scriptcode, dummy) || expect_one); 296 } 297 } 298 } 299 300 BOOST_AUTO_TEST_SUITE_END()