| 
 | 1 | +// Copyright (c) 2013-2021 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 <core_io.h>  | 
 | 9 | +#include <hash.h>  | 
 | 10 | +#include <random.h>  | 
 | 11 | +#include <script/interpreter.h>  | 
 | 12 | +#include <serialize.h>  | 
 | 13 | +#include <streams.h>  | 
 | 14 | +#include <test/data/ctvhash.json.h>  | 
 | 15 | +#include <test/util/json.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 | +UniValue read_json(const std::string& jsondata);  | 
 | 26 | + | 
 | 27 | +BOOST_FIXTURE_TEST_SUITE(ctvhash_tests, BasicTestingSetup)  | 
 | 28 | + | 
 | 29 | +// Goal: check that CTV Hash Functions generate correct hash  | 
 | 30 | +BOOST_AUTO_TEST_CASE(ctvhash_from_data)  | 
 | 31 | +{  | 
 | 32 | +    UniValue tests = read_json(json_tests::ctvhash);  | 
 | 33 | + | 
 | 34 | +    for (unsigned int idx = 0; idx < tests.size(); idx++) {  | 
 | 35 | +        const UniValue& test = tests[idx];  | 
 | 36 | +        std::string strTest = test.write();  | 
 | 37 | +        // comment  | 
 | 38 | +        if (test.isStr())  | 
 | 39 | +            continue;  | 
 | 40 | +        else if (test.isObject()) {  | 
 | 41 | +            std::string raw_tx;  | 
 | 42 | +            std::vector<uint256> hash;  | 
 | 43 | +            std::vector<uint32_t> spend_index;  | 
 | 44 | +            try {  | 
 | 45 | +                auto& hash_arr = test["result"].get_array();  | 
 | 46 | +                for (size_t i = 0; i < hash_arr.size(); ++i) {  | 
 | 47 | +                    hash.emplace_back();  | 
 | 48 | +                    hash.back().SetHexDeprecated(hash_arr[i].get_str());  | 
 | 49 | +                    // reverse because python's sha256().digest().hex() is  | 
 | 50 | +                    // backwards  | 
 | 51 | +                    std::reverse(hash.back().begin(), hash.back().end());  | 
 | 52 | +                }  | 
 | 53 | +            } catch (...) {  | 
 | 54 | +                BOOST_ERROR("Bad test: Results could not be deserialized" << strTest);  | 
 | 55 | +                break;  | 
 | 56 | +            }  | 
 | 57 | +            try {  | 
 | 58 | +                auto& spend_index_arr = test["spend_index"].get_array();  | 
 | 59 | +                for (size_t i = 0; i < spend_index_arr.size(); ++i) {  | 
 | 60 | +                    spend_index.emplace_back(static_cast<uint32_t>(spend_index_arr[i].getInt<int64_t>()));  | 
 | 61 | +                }  | 
 | 62 | +            } catch (...) {  | 
 | 63 | +                BOOST_ERROR("Bad test: spend_index could not be deserialized" << strTest);  | 
 | 64 | +                break;  | 
 | 65 | +            }  | 
 | 66 | +            if (spend_index.size() != hash.size()) {  | 
 | 67 | +                BOOST_ERROR("Bad test: Spend Indexes not same length as Result Hashes: " << strTest);  | 
 | 68 | +                break;  | 
 | 69 | +            }  | 
 | 70 | +            CMutableTransaction tx;  | 
 | 71 | +            try {  | 
 | 72 | +                // deserialize test data  | 
 | 73 | +                BOOST_CHECK(DecodeHexTx(tx, test["hex_tx"].get_str()));  | 
 | 74 | +            } catch (...) {  | 
 | 75 | +                BOOST_ERROR("Bad test, couldn't deserialize hex_tx: " << strTest);  | 
 | 76 | +                continue;  | 
 | 77 | +            }  | 
 | 78 | +            const PrecomputedTransactionData data{tx};  | 
 | 79 | +            for (size_t i = 0; i < hash.size(); ++i) {  | 
 | 80 | +                uint256 sh = GetDefaultCheckTemplateVerifyHash(tx, data.m_outputs_single_hash, data.m_sequences_single_hash, spend_index[i]);  | 
 | 81 | +                if (sh != hash[i]) {  | 
 | 82 | +                    BOOST_ERROR("Expected: " << sh << " Got: " << hash[i] << " For:\n"  | 
 | 83 | +                                             << strTest);  | 
 | 84 | +                }  | 
 | 85 | +            }  | 
 | 86 | +            // Change all of the outpoints and there should be no difference.  | 
 | 87 | +            FastRandomContext fr;  | 
 | 88 | + | 
 | 89 | +            for (auto i = 0; i < 200; ++i) {  | 
 | 90 | +                CMutableTransaction txc = tx;  | 
 | 91 | +                bool hash_will_change = false;  | 
 | 92 | +                // do n mutations, 50% of being 1, 50% chance of being 2-11  | 
 | 93 | +                const uint64_t n_mutations = fr.randbool()? (fr.randrange(10)+2) : 1;  | 
 | 94 | +                for (uint64_t j = 0; j < n_mutations; ++j) {  | 
 | 95 | +                    // on the first 50 passes, modify in ways that will not change hash  | 
 | 96 | +                    const int mutate_field = i < 50 ? fr.randrange(2) : fr.randrange(8);  | 
 | 97 | +                    switch (mutate_field) {  | 
 | 98 | +                    case 0: {  | 
 | 99 | +                        // no need to rejection sample on 256 bits  | 
 | 100 | +                        auto which = fr.randrange(tx.vin.size());  | 
 | 101 | +                        tx.vin[which].prevout.hash = Txid::FromUint256(fr.rand256());  | 
 | 102 | +                        tx.vin[which].prevout.n = fr.rand32();  | 
 | 103 | +                        break;  | 
 | 104 | +                    }  | 
 | 105 | +                    case 1: {  | 
 | 106 | +                        auto which = fr.randrange(tx.vin.size());  | 
 | 107 | +                        tx.vin[which].scriptWitness.stack.push_back(fr.randbytes(500));  | 
 | 108 | +                        break;  | 
 | 109 | +                    }  | 
 | 110 | +                    case 2: {  | 
 | 111 | +                        // Mutate a scriptSig  | 
 | 112 | +                        txc.vin[0].scriptSig.push_back('x');  | 
 | 113 | +                        hash_will_change = true;  | 
 | 114 | +                        break;  | 
 | 115 | +                    }  | 
 | 116 | +                    case 3: {  | 
 | 117 | +                        // Mutate a sequence  | 
 | 118 | +                        do {  | 
 | 119 | +                            txc.vin.back().nSequence = fr.rand32();  | 
 | 120 | +                        } while (txc.vin.back().nSequence == tx.vin.back().nSequence);  | 
 | 121 | +                        hash_will_change = true;  | 
 | 122 | +                        break;  | 
 | 123 | +                    }  | 
 | 124 | +                    case 4: {  | 
 | 125 | +                        // Mutate nVersion  | 
 | 126 | +                        do {  | 
 | 127 | +                            txc.version = fr.rand32();  | 
 | 128 | +                        } while (txc.version == tx.version);  | 
 | 129 | +                        hash_will_change = true;  | 
 | 130 | +                        break;  | 
 | 131 | +                    }  | 
 | 132 | +                    case 5: {  | 
 | 133 | +                        // Mutate output amount  | 
 | 134 | +                        auto which = fr.randrange(tx.vout.size());  | 
 | 135 | +                        txc.vout[which].nValue += 1;  | 
 | 136 | +                        hash_will_change = true;  | 
 | 137 | +                        break;  | 
 | 138 | +                    }  | 
 | 139 | +                    case 6: {  | 
 | 140 | +                        // Mutate output script  | 
 | 141 | +                        auto which = fr.randrange(tx.vout.size());  | 
 | 142 | +                        txc.vout[which].scriptPubKey.push_back('x');  | 
 | 143 | +                        hash_will_change = true;  | 
 | 144 | +                        break;  | 
 | 145 | +                    }  | 
 | 146 | +                    case 7: {  | 
 | 147 | +                        // Mutate nLockTime  | 
 | 148 | +                        do {  | 
 | 149 | +                            txc.nLockTime = fr.rand32();  | 
 | 150 | +                        } while (txc.nLockTime == tx.nLockTime);  | 
 | 151 | +                        hash_will_change = true;  | 
 | 152 | +                        break;  | 
 | 153 | +                    }  | 
 | 154 | +                    default:  | 
 | 155 | +                        assert(0);  | 
 | 156 | +                    }  | 
 | 157 | +                }  | 
 | 158 | +                const PrecomputedTransactionData data_txc{txc};  | 
 | 159 | +                // iterate twice, one time with the correct spend indexes, one time with incorrect.  | 
 | 160 | +                for (auto use_random_index = 0; use_random_index < 2; ++use_random_index) {  | 
 | 161 | +                    hash_will_change |= use_random_index != 0;  | 
 | 162 | +                    for (size_t i = 0; i < hash.size(); ++i) {  | 
 | 163 | +                        uint32_t index{spend_index[i]};  | 
 | 164 | +                        while (use_random_index && index == spend_index[i]) {  | 
 | 165 | +                            index = fr.rand32();  | 
 | 166 | +                        }  | 
 | 167 | +                        uint256 sh = GetDefaultCheckTemplateVerifyHash(txc, data_txc.m_outputs_single_hash, data_txc.m_sequences_single_hash, index);  | 
 | 168 | +                        const bool hash_equals = sh == hash[i];  | 
 | 169 | +                        if (hash_will_change && hash_equals) {  | 
 | 170 | +                            BOOST_ERROR("Expected: NOT " << hash[i] << " Got: " << sh << " For:\n"  | 
 | 171 | +                                                         << strTest);  | 
 | 172 | +                        } else if (!hash_will_change && !hash_equals) {  | 
 | 173 | +                            BOOST_ERROR("Expected: " << hash[i] << " Got: " << sh << " For:\n"  | 
 | 174 | +                                                     << strTest);  | 
 | 175 | +                        }  | 
 | 176 | +                    }  | 
 | 177 | +                }  | 
 | 178 | +            }  | 
 | 179 | + | 
 | 180 | + | 
 | 181 | +        } else {  | 
 | 182 | +            BOOST_ERROR("Bad test: " << strTest);  | 
 | 183 | +            continue;  | 
 | 184 | +        }  | 
 | 185 | +    }  | 
 | 186 | +}  | 
 | 187 | +BOOST_AUTO_TEST_SUITE_END()  | 
0 commit comments