Skip to content

Commit

Permalink
Implemented extra inputs coinstake validation check. Updated version.…
Browse files Browse the repository at this point in the history
… Updated consensus rules
  • Loading branch information
durkmurder committed May 31, 2021
1 parent d781599 commit 286522c
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 47 deletions.
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N)
AC_PREREQ([2.60])
define(_CLIENT_VERSION_MAJOR, 1)
define(_CLIENT_VERSION_MINOR, 0)
define(_CLIENT_VERSION_REVISION, 26)
define(_CLIENT_VERSION_REVISION, 28)
define(_CLIENT_VERSION_BUILD, 0)
define(_CLIENT_VERSION_IS_RELEASE, true)
define(_COPYRIGHT_YEAR, 2020)
Expand Down
3 changes: 3 additions & 0 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ class CMainParams : public CChainParams {
consensus.nPosTargetTimespan = 60 * 40;
consensus.nPoSUpdgradeHFHeight = 898488; // 4 December 2019
consensus.nTPoSSignatureUpgradeHFHeight = 1348224; // tpos signature update HF, 8 October 2020
consensus.nCoinstakeExtraInputsValidationHFHeight = 1688696; // coinstake validation HF, 29 May 2021
consensus.nMerchantnodeMinimumConfirmations = 1;
consensus.nMasternodeMinimumConfirmations = 15;
consensus.nStakeMinAge = 60 * 60;
Expand Down Expand Up @@ -245,6 +246,7 @@ class CTestNetParams : public CChainParams {
consensus.nStakeMaxAge = 60 * 60 * 24; // one day
consensus.nCoinbaseMaturity = 20;
consensus.nTPoSContractSignatureDeploymentTime = 1522782000;
consensus.nCoinstakeExtraInputsValidationHFHeight = 0;
consensus.nRuleChangeActivationThreshold = 30; // 75% for testchains
consensus.nMinerConfirmationWindow = 40; // nPowTargetTimespan / nPowTargetSpacing
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
Expand Down Expand Up @@ -360,6 +362,7 @@ class CRegTestParams : public CChainParams {
consensus.nPosTargetTimespan = 60 * 40;
consensus.nPoSUpdgradeHFHeight = 0;
consensus.nTPoSSignatureUpgradeHFHeight = 80;
consensus.nCoinstakeExtraInputsValidationHFHeight = 0;
consensus.fPowAllowMinDifficultyBlocks = true;
consensus.fPowNoRetargeting = true;
consensus.fPoSNoRetargeting = true;
Expand Down
1 change: 1 addition & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ struct Params {
int nMaxBlockSpacingFixDeploymentHeight;
int nPoSUpdgradeHFHeight;
int nTPoSSignatureUpgradeHFHeight;
int nCoinstakeExtraInputsValidationHFHeight;
};
} // namespace Consensus

Expand Down
19 changes: 19 additions & 0 deletions src/consensus/tx_verify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,21 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe
return true;
}

static bool IsInputGovernanceLocked(COutPoint outpoint, int nSpendHeight) {
static std::set<COutPoint> setLockedOutpoints {
COutPoint(uint256S("e59e8e9ad8f289ca15bcaea450d9579e4de070308a19785c8d461a33abbe2717"), 1),
COutPoint(uint256S("ca971cc6cc76c0686224d54dec2067160fae5d057df44a9ba3d296f85879e047"), 1),
COutPoint(uint256S("ca971cc6cc76c0686224d54dec2067160fae5d057df44a9ba3d296f85879e047"), 2),
COutPoint(uint256S("2a87b52d96f5d38932dcdf10fddb54781301a2707af4a217050fc96881e5a786"), 0),
};

if (nSpendHeight < Consensus::Params().nCoinstakeExtraInputsValidationHFHeight) {
return false;
}

return setLockedOutpoints.count(outpoint);
}

bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee)
{
// are the actual inputs available?
Expand All @@ -219,6 +234,10 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c
const Coin& coin = inputs.AccessCoin(prevout);
assert(!coin.IsSpent());

if (IsInputGovernanceLocked(prevout, nSpendHeight)) {
return state.Invalid(false, REJECT_INVALID, "governance-spend-locked-outpoint", strprintf("tried to spend locked outpoint %s", prevout.ToString().c_str()));
}

// If prev is coinbase, check that it's matured
if (coin.IsCoinBase() || coin.IsCoinStake()) {
if(nSpendHeight - coin.nHeight < COINBASE_MATURITY) {
Expand Down
36 changes: 36 additions & 0 deletions src/kernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,30 @@ bool CheckKernelScript(CScript scriptVin, CScript scriptVout)
return extractKeyID(scriptVin) == extractKeyID(scriptVout);
}


bool CheckKernelExtraInputs(const CTransactionRef& tx, const CScript& scriptKernel, const Consensus::Params& params)
{
if (!tx->IsCoinStake())
return error("CheckKernelExtraInputs() : called on non-coinstake %s", tx->GetHash().ToString().c_str());

const auto& vin = tx->vin;
for (size_t i = 0; i < vin.size(); ++i) {
const auto& in = vin[i];
uint256 hashBlock;
CTransactionRef txPrev;

if (!GetTransaction(in.prevout.hash, txPrev, params, hashBlock, true))
return error("CheckKernelExtraInputs() : INFO: read txPrev failed");

const auto& prevOut = txPrev->vout[in.prevout.n];
if (scriptKernel != prevOut.scriptPubKey) {
return error("CheckKernelExtraInputs() : invalid input at position %d for coinstake %s", i, tx->GetHash().ToString().c_str());
}
}

return true;
}

// Check kernel hash target and coinstake signature
bool CheckProofOfStake(const CBlockIndex *pindexPrev, const CBlock &block, uint256& hashProofOfStake, const Consensus::Params &params)
{
Expand Down Expand Up @@ -528,6 +552,13 @@ bool CheckProofOfStake(const CBlockIndex *pindexPrev, const CBlock &block, uint2
else
return error("CheckProofOfStake() : read block failed");

if(IsCoinstakeExtraInputValidationHardForkActivated(pindex->nHeight)) {
// any coinstake transaction has to have scripts only from kernel.
if (!CheckKernelExtraInputs(tx, prevTxOut.scriptPubKey, params)) {
return error("CheckProofOfStake() : extra inputs check failed");
}
}

// Read block header
CBlock blockprev;
if (!ReadBlockFromDisk(blockprev, pindex->GetBlockPos(), params))
Expand Down Expand Up @@ -573,3 +604,8 @@ bool CheckStakeModifierCheckpoints(int nHeight, unsigned int nStakeModifierCheck
return true;
}

bool IsCoinstakeExtraInputValidationHardForkActivated(int nChainHeight)
{
return nChainHeight >= Params().GetConsensus().nCoinstakeExtraInputsValidationHFHeight;
}

2 changes: 2 additions & 0 deletions src/kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ extern unsigned int getIntervalVersion(bool fTestNet);
// ratio of group interval length between the last group and the first group
static const int MODIFIER_INTERVAL_RATIO = 3;

bool IsCoinstakeExtraInputValidationHardForkActivated(int nChainHeight);

uint256 ComputeStakeModifierV3(const CBlockIndex* pindexPrev, const uint256& kernel);
bool ComputeNextStakeModifier(const CBlockIndex* pindexCurrent, uint64_t& nStakeModifier, bool& fGeneratedStakeModifier);

Expand Down
73 changes: 36 additions & 37 deletions src/test/txvalidationcache_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <consensus/validation.h>
#include <core_io.h>
#include <key.h>
#include <validation.h>
#include <keystore.h>
#include <miner.h>
#include <policy/policy.h>
#include <pubkey.h>
#include <txmempool.h>
#include <random.h>
#include <script/standard.h>
#include <script/sign.h>
#include <script/standard.h>
#include <test/test_xsn.h>
#include <txmempool.h>
#include <utiltime.h>
#include <core_io.h>
#include <keystore.h>
#include <policy/policy.h>
#include <validation.h>

#include <boost/test/unit_test.hpp>

bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks);
bool CheckInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, bool isTPoSBlock, PrecomputedTransactionData& txdata, std::vector<CScriptCheck>* pvChecks);

BOOST_AUTO_TEST_SUITE(tx_validationcache_tests)

Expand All @@ -30,9 +30,9 @@ ToMemPool(const CMutableTransaction& tx)

CValidationState state;
bool result = AcceptToMemoryPool(mempool, state, MakeTransactionRef(tx), nullptr /* pfMissingInputs */,
nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */);
nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */);

if(!result)
if (!result)
LogPrintf("Failed %s, reason: %s\n", __func__, state.GetRejectReason());

return result;
Expand All @@ -44,19 +44,18 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
// validated going into the memory pool does not allow
// double-spends in blocks to pass validation when they should not.

CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;

// Create a double-spend of mature coinbase txn:
std::vector<CMutableTransaction> spends;
spends.resize(2);
for (int i = 0; i < 2; i++)
{
for (int i = 0; i < 2; i++) {
spends[i].nVersion = 1;
spends[i].vin.resize(1);
spends[i].vin[0].prevout.hash = m_coinbase_txns[0]->GetHash();
spends[i].vin[0].prevout.n = 0;
spends[i].vout.resize(1);
spends[i].vout[0].nValue = 11*CENT;
spends[i].vout[0].nValue = 11 * CENT;
spends[i].vout[0].scriptPubKey = scriptPubKey;

// Sign:
Expand Down Expand Up @@ -107,12 +106,12 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
// should fail.
// Capture this interaction with the upgraded_nop argument: set it when evaluating
// any script flag that is implemented as an upgraded NOP code.
static void ValidateCheckInputsForAllFlags(CMutableTransaction &tx, uint32_t failing_flags, bool add_to_cache)
static void ValidateCheckInputsForAllFlags(CMutableTransaction& tx, uint32_t failing_flags, bool add_to_cache)
{
PrecomputedTransactionData txdata(tx);
// If we add many more flags, this loop can get too expensive, but we can
// rewrite in the future to randomly pick a set of flags to evaluate.
for (uint32_t test_flags=0; test_flags < (1U << 16); test_flags += 1) {
for (uint32_t test_flags = 0; test_flags < (1U << 16); test_flags += 1) {
CValidationState state;
// Filter out incompatible flag choices
if ((test_flags & SCRIPT_VERIFY_CLEANSTACK)) {
Expand All @@ -124,7 +123,7 @@ static void ValidateCheckInputsForAllFlags(CMutableTransaction &tx, uint32_t fai
// WITNESS requires P2SH
test_flags |= SCRIPT_VERIFY_P2SH;
}
bool ret = CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, txdata, nullptr);
bool ret = CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, false, txdata, nullptr);
// CheckInputs should succeed iff test_flags doesn't intersect with
// failing_flags
bool expected_return_value = !(test_flags & failing_flags);
Expand All @@ -134,13 +133,13 @@ static void ValidateCheckInputsForAllFlags(CMutableTransaction &tx, uint32_t fai
if (ret && add_to_cache) {
// Check that we get a cache hit if the tx was valid
std::vector<CScriptCheck> scriptchecks;
BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, txdata, &scriptchecks));
BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, false, txdata, &scriptchecks));
BOOST_CHECK(scriptchecks.empty());
} else {
// Check that we get script executions to check, if the transaction
// was invalid, or we didn't add to cache.
std::vector<CScriptCheck> scriptchecks;
BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, txdata, &scriptchecks));
BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, test_flags, true, add_to_cache, false, txdata, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size());
}
}
Expand Down Expand Up @@ -175,21 +174,21 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
spend_tx.vin[0].prevout.hash = m_coinbase_txns[0]->GetHash();
spend_tx.vin[0].prevout.n = 0;
spend_tx.vout.resize(4);
spend_tx.vout[0].nValue = 11*CENT;
spend_tx.vout[0].nValue = 11 * CENT;
spend_tx.vout[0].scriptPubKey = p2sh_scriptPubKey;
spend_tx.vout[1].nValue = 11*CENT;
spend_tx.vout[1].nValue = 11 * CENT;
spend_tx.vout[1].scriptPubKey = p2wpkh_scriptPubKey;
spend_tx.vout[2].nValue = 11*CENT;
spend_tx.vout[2].nValue = 11 * CENT;
spend_tx.vout[2].scriptPubKey = CScript() << OP_CHECKLOCKTIMEVERIFY << OP_DROP << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
spend_tx.vout[3].nValue = 11*CENT;
spend_tx.vout[3].nValue = 11 * CENT;
spend_tx.vout[3].scriptPubKey = CScript() << OP_CHECKSEQUENCEVERIFY << OP_DROP << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;

// Sign, with a non-DER signature
{
std::vector<unsigned char> vchSig;
uint256 hash = SignatureHash(p2pk_scriptPubKey, spend_tx, 0, SIGHASH_ALL, 0, SigVersion::BASE);
BOOST_CHECK(coinbaseKey.Sign(hash, vchSig));
vchSig.push_back((unsigned char) 0); // padding byte makes this non-DER
vchSig.push_back((unsigned char)0); // padding byte makes this non-DER
vchSig.push_back((unsigned char)SIGHASH_ALL);
spend_tx.vin[0].scriptSig << vchSig;
}
Expand All @@ -203,13 +202,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
CValidationState state;
PrecomputedTransactionData ptd_spend_tx(spend_tx);

BOOST_CHECK(!CheckInputs(spend_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr));
BOOST_CHECK(!CheckInputs(spend_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, false, ptd_spend_tx, nullptr));

// If we call again asking for scriptchecks (as happens in
// ConnectBlock), we should add a script check object for this -- we're
// not caching invalidity (if that changes, delete this test case).
std::vector<CScriptCheck> scriptchecks;
BOOST_CHECK(CheckInputs(spend_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks));
BOOST_CHECK(CheckInputs(spend_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, false, ptd_spend_tx, &scriptchecks));
BOOST_CHECK_EQUAL(scriptchecks.size(), 1U);

// Test that CheckInputs returns true iff DERSIG-enforcing flags are
Expand Down Expand Up @@ -238,7 +237,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
invalid_under_p2sh_tx.vin[0].prevout.hash = spend_tx.GetHash();
invalid_under_p2sh_tx.vin[0].prevout.n = 0;
invalid_under_p2sh_tx.vout.resize(1);
invalid_under_p2sh_tx.vout[0].nValue = 11*CENT;
invalid_under_p2sh_tx.vout[0].nValue = 11 * CENT;
invalid_under_p2sh_tx.vout[0].scriptPubKey = p2pk_scriptPubKey;
std::vector<unsigned char> vchSig2(p2pk_scriptPubKey.begin(), p2pk_scriptPubKey.end());
invalid_under_p2sh_tx.vin[0].scriptSig << vchSig2;
Expand All @@ -256,7 +255,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
invalid_with_cltv_tx.vin[0].prevout.n = 2;
invalid_with_cltv_tx.vin[0].nSequence = 0;
invalid_with_cltv_tx.vout.resize(1);
invalid_with_cltv_tx.vout[0].nValue = 11*CENT;
invalid_with_cltv_tx.vout[0].nValue = 11 * CENT;
invalid_with_cltv_tx.vout[0].scriptPubKey = p2pk_scriptPubKey;

// Sign
Expand All @@ -272,7 +271,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
CValidationState state;
PrecomputedTransactionData txdata(invalid_with_cltv_tx);
BOOST_CHECK(CheckInputs(invalid_with_cltv_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr));
BOOST_CHECK(CheckInputs(invalid_with_cltv_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, false, txdata, nullptr));
}

// TEST CHECKSEQUENCEVERIFY
Expand All @@ -284,7 +283,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
invalid_with_csv_tx.vin[0].prevout.n = 3;
invalid_with_csv_tx.vin[0].nSequence = 100;
invalid_with_csv_tx.vout.resize(1);
invalid_with_csv_tx.vout[0].nValue = 11*CENT;
invalid_with_csv_tx.vout[0].nValue = 11 * CENT;
invalid_with_csv_tx.vout[0].scriptPubKey = p2pk_scriptPubKey;

// Sign
Expand All @@ -300,7 +299,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100;
CValidationState state;
PrecomputedTransactionData txdata(invalid_with_csv_tx);
BOOST_CHECK(CheckInputs(invalid_with_csv_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr));
BOOST_CHECK(CheckInputs(invalid_with_csv_tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, false, txdata, nullptr));
}

// TODO: add tests for remaining script flags
Expand All @@ -314,12 +313,12 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
valid_with_witness_tx.vin[0].prevout.hash = spend_tx.GetHash();
valid_with_witness_tx.vin[0].prevout.n = 1;
valid_with_witness_tx.vout.resize(1);
valid_with_witness_tx.vout[0].nValue = 11*CENT;
valid_with_witness_tx.vout[0].nValue = 11 * CENT;
valid_with_witness_tx.vout[0].scriptPubKey = p2pk_scriptPubKey;

// Sign
SignatureData sigdata;
ProduceSignature(keystore, MutableTransactionSignatureCreator(&valid_with_witness_tx, 0, 11*CENT, SIGHASH_ALL), spend_tx.vout[1].scriptPubKey, sigdata);
ProduceSignature(keystore, MutableTransactionSignatureCreator(&valid_with_witness_tx, 0, 11 * CENT, SIGHASH_ALL), spend_tx.vout[1].scriptPubKey, sigdata);
UpdateTransaction(valid_with_witness_tx, 0, sigdata);

// This should be valid under all script flags.
Expand All @@ -341,13 +340,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
tx.vin[1].prevout.hash = spend_tx.GetHash();
tx.vin[1].prevout.n = 1;
tx.vout.resize(1);
tx.vout[0].nValue = 22*CENT;
tx.vout[0].nValue = 22 * CENT;
tx.vout[0].scriptPubKey = p2pk_scriptPubKey;

// Sign
for (int i=0; i<2; ++i) {
for (int i = 0; i < 2; ++i) {
SignatureData sigdata;
ProduceSignature(keystore, MutableTransactionSignatureCreator(&tx, i, 11*CENT, SIGHASH_ALL), spend_tx.vout[i].scriptPubKey, sigdata);
ProduceSignature(keystore, MutableTransactionSignatureCreator(&tx, i, 11 * CENT, SIGHASH_ALL), spend_tx.vout[i].scriptPubKey, sigdata);
UpdateTransaction(tx, i, sigdata);
}

Expand All @@ -362,12 +361,12 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup)
CValidationState state;
PrecomputedTransactionData txdata(tx);
// This transaction is now invalid under segwit, because of the second input.
BOOST_CHECK(!CheckInputs(tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, nullptr));
BOOST_CHECK(!CheckInputs(tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, false, txdata, nullptr));

std::vector<CScriptCheck> scriptchecks;
// Make sure this transaction was not cached (ie because the first
// input was valid)
BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, txdata, &scriptchecks));
BOOST_CHECK(CheckInputs(tx, state, pcoinsTip.get(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true, true, false, txdata, &scriptchecks));
// Should get 2 script checks back -- caching is on a whole-transaction basis.
BOOST_CHECK_EQUAL(scriptchecks.size(), 2U);
}
Expand Down
Loading

0 comments on commit 286522c

Please sign in to comment.