mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-15 12:51:00 +02:00
Several conflicts in the C++ code related to the new `flags` parameter to `CheckSignature` and the corresponding function being renamed upstream to `CheckSignatureECDSA`. Several conflicts in the test harness as Steven sorta pulled the new upstream ECKey module into the Python code, and the actual upstream code was slightly different. Also needed to update the feature_taproot code to always use the non-RANGEPROOF sighash since dynafed is not enabled in the Taproot test. Also had to pull the `set_wif` method out of `ECKey` and inline it because otherwise it triggers a "circular inclusion" error between script.py (which would pull in `base58_to_bytes` from address.py) and address.py (which now pulls in some taproot EC related stuff from script.py). Noticed that #960 does not test the "sighash rangeproof flag set but no witnesses" case.
209 lines
6.9 KiB
C++
209 lines
6.9 KiB
C++
// Copyright (c) 2013-2020 The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include <consensus/tx_check.h>
|
|
#include <consensus/validation.h>
|
|
#include <hash.h>
|
|
#include <script/interpreter.h>
|
|
#include <script/script.h>
|
|
#include <serialize.h>
|
|
#include <streams.h>
|
|
#include <test/data/sighash.json.h>
|
|
#include <test/util/setup_common.h>
|
|
#include <util/strencodings.h>
|
|
#include <util/system.h>
|
|
#include <version.h>
|
|
|
|
#include <iostream>
|
|
|
|
#include <boost/test/unit_test.hpp>
|
|
|
|
#include <univalue.h>
|
|
|
|
extern UniValue read_json(const std::string& jsondata);
|
|
|
|
// Old script.cpp SignatureHash function
|
|
uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType)
|
|
{
|
|
if (nIn >= txTo.vin.size())
|
|
{
|
|
return uint256::ONE;
|
|
}
|
|
CMutableTransaction txTmp(txTo);
|
|
|
|
// In case concatenating two scripts ends up with two codeseparators,
|
|
// or an extra one at the end, this prevents all those possible incompatibilities.
|
|
FindAndDelete(scriptCode, CScript(OP_CODESEPARATOR));
|
|
|
|
// Blank out other inputs' signatures
|
|
for (unsigned int i = 0; i < txTmp.vin.size(); i++)
|
|
txTmp.vin[i].scriptSig = CScript();
|
|
txTmp.vin[nIn].scriptSig = scriptCode;
|
|
|
|
// Blank out some of the outputs
|
|
if ((nHashType & 0x1f) == SIGHASH_NONE)
|
|
{
|
|
// Wildcard payee
|
|
txTmp.vout.clear();
|
|
|
|
// Let the others update at will
|
|
for (unsigned int i = 0; i < txTmp.vin.size(); i++)
|
|
if (i != nIn)
|
|
txTmp.vin[i].nSequence = 0;
|
|
}
|
|
else if ((nHashType & 0x1f) == SIGHASH_SINGLE)
|
|
{
|
|
// Only lock-in the txout payee at same index as txin
|
|
unsigned int nOut = nIn;
|
|
if (nOut >= txTmp.vout.size())
|
|
{
|
|
return uint256::ONE;
|
|
}
|
|
txTmp.vout.resize(nOut+1);
|
|
for (unsigned int i = 0; i < nOut; i++)
|
|
txTmp.vout[i].SetNull();
|
|
|
|
// Let the others update at will
|
|
for (unsigned int i = 0; i < txTmp.vin.size(); i++)
|
|
if (i != nIn)
|
|
txTmp.vin[i].nSequence = 0;
|
|
}
|
|
|
|
// Blank out other inputs completely, not recommended for open transactions
|
|
if (nHashType & SIGHASH_ANYONECANPAY)
|
|
{
|
|
txTmp.vin[0] = txTmp.vin[nIn];
|
|
txTmp.vin.resize(1);
|
|
}
|
|
|
|
// Serialize and hash
|
|
CHashWriter ss(SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS);
|
|
ss << txTmp << nHashType;
|
|
return ss.GetHash();
|
|
}
|
|
|
|
void static RandomScript(CScript &script) {
|
|
static const opcodetype oplist[] = {OP_FALSE, OP_1, OP_2, OP_3, OP_CHECKSIG, OP_IF, OP_VERIF, OP_RETURN, OP_CODESEPARATOR};
|
|
script = CScript();
|
|
int ops = (InsecureRandRange(10));
|
|
for (int i=0; i<ops; i++)
|
|
script << oplist[InsecureRandRange(sizeof(oplist)/sizeof(oplist[0]))];
|
|
}
|
|
|
|
void static RandomTransaction(CMutableTransaction &tx, bool fSingle) {
|
|
tx.nVersion = InsecureRand32();
|
|
tx.vin.clear();
|
|
tx.vout.clear();
|
|
tx.nLockTime = (InsecureRandBool()) ? InsecureRand32() : 0;
|
|
int ins = (InsecureRandBits(2)) + 1;
|
|
int outs = fSingle ? ins : (InsecureRandBits(2)) + 1;
|
|
for (int in = 0; in < ins; in++) {
|
|
tx.vin.push_back(CTxIn());
|
|
CTxIn &txin = tx.vin.back();
|
|
txin.prevout.hash = InsecureRand256();
|
|
txin.prevout.n = InsecureRandBits(2);
|
|
RandomScript(txin.scriptSig);
|
|
txin.nSequence = (InsecureRandBool()) ? InsecureRand32() : std::numeric_limits<uint32_t>::max();
|
|
}
|
|
for (int out = 0; out < outs; out++) {
|
|
tx.vout.push_back(CTxOut());
|
|
CTxOut &txout = tx.vout.back();
|
|
txout.nValue = InsecureRandRange(100000000);
|
|
RandomScript(txout.scriptPubKey);
|
|
}
|
|
}
|
|
|
|
BOOST_FIXTURE_TEST_SUITE(sighash_tests, BasicTestingSetup)
|
|
|
|
BOOST_AUTO_TEST_CASE(sighash_test)
|
|
{
|
|
#if defined(PRINT_SIGHASH_JSON)
|
|
std::cout << "[\n";
|
|
std::cout << "\t[\"raw_transaction, script, input_index, hashType, signature_hash (result)\"],\n";
|
|
int nRandomTests = 500;
|
|
#else
|
|
int nRandomTests = 50000;
|
|
#endif
|
|
for (int i=0; i<nRandomTests; i++) {
|
|
// In randomized test, we disable SIGHASH_RANGEPROOF.
|
|
int nHashType = InsecureRand32() & ~SIGHASH_RANGEPROOF;
|
|
CMutableTransaction txTo;
|
|
RandomTransaction(txTo, (nHashType & 0x1f) == SIGHASH_SINGLE);
|
|
CScript scriptCode;
|
|
RandomScript(scriptCode);
|
|
int nIn = InsecureRandRange(txTo.vin.size());
|
|
|
|
uint256 sh, sho;
|
|
sho = SignatureHashOld(scriptCode, CTransaction(txTo), nIn, nHashType);
|
|
sh = SignatureHash(scriptCode, txTo, nIn, nHashType, 0, SigVersion::BASE, SCRIPT_SIGHASH_RANGEPROOF);
|
|
#if defined(PRINT_SIGHASH_JSON)
|
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
|
ss << txTo;
|
|
|
|
std::cout << "\t[\"" ;
|
|
std::cout << HexStr(ss) << "\", \"";
|
|
std::cout << HexStr(scriptCode) << "\", ";
|
|
std::cout << nIn << ", ";
|
|
std::cout << nHashType << ", \"";
|
|
std::cout << sho.GetHex() << "\"]";
|
|
if (i+1 != nRandomTests) {
|
|
std::cout << ",";
|
|
}
|
|
std::cout << "\n";
|
|
#endif
|
|
BOOST_CHECK(sh == sho);
|
|
}
|
|
#if defined(PRINT_SIGHASH_JSON)
|
|
std::cout << "]\n";
|
|
#endif
|
|
}
|
|
|
|
// Goal: check that SignatureHash generates correct hash
|
|
BOOST_AUTO_TEST_CASE(sighash_from_data)
|
|
{
|
|
UniValue tests = read_json(std::string(json_tests::sighash, json_tests::sighash + sizeof(json_tests::sighash)));
|
|
|
|
for (unsigned int idx = 0; idx < tests.size(); idx++) {
|
|
UniValue test = tests[idx];
|
|
std::string strTest = test.write();
|
|
if (test.size() < 1) // Allow for extra stuff (useful for comments)
|
|
{
|
|
BOOST_ERROR("Bad test: " << strTest);
|
|
continue;
|
|
}
|
|
if (test.size() == 1) continue; // comment
|
|
|
|
std::string raw_tx, raw_script, sigHashHex;
|
|
int nIn, nHashType;
|
|
uint256 sh;
|
|
CTransactionRef tx;
|
|
CScript scriptCode = CScript();
|
|
|
|
try {
|
|
// deserialize test data
|
|
raw_tx = test[0].get_str();
|
|
raw_script = test[1].get_str();
|
|
nIn = test[2].get_int();
|
|
nHashType = test[3].get_int();
|
|
sigHashHex = test[4].get_str();
|
|
|
|
CDataStream stream(ParseHex(raw_tx), SER_NETWORK, PROTOCOL_VERSION);
|
|
stream >> tx;
|
|
|
|
TxValidationState state;
|
|
BOOST_CHECK_MESSAGE(CheckTransaction(*tx, state), strTest);
|
|
BOOST_CHECK(state.IsValid());
|
|
|
|
std::vector<unsigned char> raw = ParseHex(raw_script);
|
|
scriptCode.insert(scriptCode.end(), raw.begin(), raw.end());
|
|
} catch (...) {
|
|
BOOST_ERROR("Bad test, couldn't deserialize data: " << strTest);
|
|
continue;
|
|
}
|
|
|
|
sh = SignatureHash(scriptCode, *tx, nIn, nHashType, 0, SigVersion::BASE, 0);
|
|
BOOST_CHECK_MESSAGE(sh.GetHex() == sigHashHex, strTest);
|
|
}
|
|
}
|
|
BOOST_AUTO_TEST_SUITE_END()
|