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 void static RandomScript(CScript &script) { 86 static const opcodetype oplist[] = {OP_FALSE, OP_1, OP_2, OP_3, OP_CHECKSIG, OP_IF, OP_VERIF, OP_RETURN, OP_CODESEPARATOR}; 87 script = CScript(); 88 int ops = (InsecureRandRange(10)); 89 for (int i=0; i<ops; i++) 90 script << oplist[InsecureRandRange(std::size(oplist))]; 91 } 92 93 void static RandomTransaction(CMutableTransaction& tx, bool fSingle) 94 { 95 tx.nVersion = int(InsecureRand32()); 96 tx.vin.clear(); 97 tx.vout.clear(); 98 tx.nLockTime = (InsecureRandBool()) ? InsecureRand32() : 0; 99 int ins = (InsecureRandBits(2)) + 1; 100 int outs = fSingle ? ins : (InsecureRandBits(2)) + 1; 101 for (int in = 0; in < ins; in++) { 102 tx.vin.emplace_back(); 103 CTxIn &txin = tx.vin.back(); 104 txin.prevout.hash = Txid::FromUint256(InsecureRand256()); 105 txin.prevout.n = InsecureRandBits(2); 106 RandomScript(txin.scriptSig); 107 txin.nSequence = (InsecureRandBool()) ? InsecureRand32() : std::numeric_limits<uint32_t>::max(); 108 } 109 for (int out = 0; out < outs; out++) { 110 tx.vout.emplace_back(); 111 CTxOut &txout = tx.vout.back(); 112 txout.nValue = InsecureRandMoneyAmount(); 113 RandomScript(txout.scriptPubKey); 114 } 115 } 116 117 BOOST_FIXTURE_TEST_SUITE(sighash_tests, BasicTestingSetup) 118 119 BOOST_AUTO_TEST_CASE(sighash_test) 120 { 121 #if defined(PRINT_SIGHASH_JSON) 122 std::cout << "[\n"; 123 std::cout << "\t[\"raw_transaction, script, input_index, hashType, signature_hash (result)\"],\n"; 124 int nRandomTests = 500; 125 #else 126 int nRandomTests = 50000; 127 #endif 128 for (int i=0; i<nRandomTests; i++) { 129 int nHashType{int(InsecureRand32())}; 130 CMutableTransaction txTo; 131 RandomTransaction(txTo, (nHashType & 0x1f) == SIGHASH_SINGLE); 132 CScript scriptCode; 133 RandomScript(scriptCode); 134 int nIn = InsecureRandRange(txTo.vin.size()); 135 136 uint256 sh, sho; 137 sho = SignatureHashOld(scriptCode, CTransaction(txTo), nIn, nHashType); 138 sh = SignatureHash(scriptCode, txTo, nIn, nHashType, 0, SigVersion::BASE); 139 #if defined(PRINT_SIGHASH_JSON) 140 DataStream ss; 141 ss << TX_WITH_WITNESS(txTo); 142 143 std::cout << "\t[\"" ; 144 std::cout << HexStr(ss) << "\", \""; 145 std::cout << HexStr(scriptCode) << "\", "; 146 std::cout << nIn << ", "; 147 std::cout << nHashType << ", \""; 148 std::cout << sho.GetHex() << "\"]"; 149 if (i+1 != nRandomTests) { 150 std::cout << ","; 151 } 152 std::cout << "\n"; 153 #endif 154 BOOST_CHECK(sh == sho); 155 } 156 #if defined(PRINT_SIGHASH_JSON) 157 std::cout << "]\n"; 158 #endif 159 } 160 161 // Goal: check that SignatureHash generates correct hash 162 BOOST_AUTO_TEST_CASE(sighash_from_data) 163 { 164 UniValue tests = read_json(json_tests::sighash); 165 166 for (unsigned int idx = 0; idx < tests.size(); idx++) { 167 const UniValue& test = tests[idx]; 168 std::string strTest = test.write(); 169 if (test.size() < 1) // Allow for extra stuff (useful for comments) 170 { 171 BOOST_ERROR("Bad test: " << strTest); 172 continue; 173 } 174 if (test.size() == 1) continue; // comment 175 176 std::string raw_tx, raw_script, sigHashHex; 177 int nIn, nHashType; 178 uint256 sh; 179 CTransactionRef tx; 180 CScript scriptCode = CScript(); 181 182 try { 183 // deserialize test data 184 raw_tx = test[0].get_str(); 185 raw_script = test[1].get_str(); 186 nIn = test[2].getInt<int>(); 187 nHashType = test[3].getInt<int>(); 188 sigHashHex = test[4].get_str(); 189 190 DataStream stream(ParseHex(raw_tx)); 191 stream >> TX_WITH_WITNESS(tx); 192 193 TxValidationState state; 194 BOOST_CHECK_MESSAGE(CheckTransaction(*tx, state), strTest); 195 BOOST_CHECK(state.IsValid()); 196 197 std::vector<unsigned char> raw = ParseHex(raw_script); 198 scriptCode.insert(scriptCode.end(), raw.begin(), raw.end()); 199 } catch (...) { 200 BOOST_ERROR("Bad test, couldn't deserialize data: " << strTest); 201 continue; 202 } 203 204 sh = SignatureHash(scriptCode, *tx, nIn, nHashType, 0, SigVersion::BASE); 205 BOOST_CHECK_MESSAGE(sh.GetHex() == sigHashHex, strTest); 206 } 207 } 208 BOOST_AUTO_TEST_SUITE_END()