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 BOOST_AUTO_TEST_SUITE_END()