Consensus: Refactor: Separate Consensus::CheckTxInputs and GetSpendHeight in CheckInputs
Zcash: - Replaces some of the refactoring performed in #1017 - Exposed Consensus::CheckTxInputs for testing
This commit is contained in:
67
src/main.cpp
67
src/main.cpp
@@ -1607,13 +1607,16 @@ bool CScriptCheck::operator()() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NonContextualCheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, const Consensus::Params& consensusParams, std::vector<CScriptCheck> *pvChecks)
|
int GetSpendHeight(const CCoinsViewCache& inputs)
|
||||||
{
|
{
|
||||||
if (!tx.IsCoinBase())
|
LOCK(cs_main);
|
||||||
{
|
CBlockIndex* pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second;
|
||||||
if (pvChecks)
|
return pindexPrev->nHeight + 1;
|
||||||
pvChecks->reserve(tx.vin.size());
|
}
|
||||||
|
|
||||||
|
namespace Consensus {
|
||||||
|
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, const Consensus::Params& consensusParams)
|
||||||
|
{
|
||||||
// This doesn't trigger the DoS code on purpose; if it did, it would make it easier
|
// This doesn't trigger the DoS code on purpose; if it did, it would make it easier
|
||||||
// for an attacker to attempt to split the network.
|
// for an attacker to attempt to split the network.
|
||||||
if (!inputs.HaveInputs(tx))
|
if (!inputs.HaveInputs(tx))
|
||||||
@@ -1632,6 +1635,13 @@ bool NonContextualCheckInputs(const CTransaction& tx, CValidationState &state, c
|
|||||||
assert(coins);
|
assert(coins);
|
||||||
|
|
||||||
if (coins->IsCoinBase()) {
|
if (coins->IsCoinBase()) {
|
||||||
|
// Ensure that coinbases are matured
|
||||||
|
if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) {
|
||||||
|
return state.Invalid(
|
||||||
|
error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight),
|
||||||
|
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase");
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure that coinbases cannot be spent to transparent outputs
|
// Ensure that coinbases cannot be spent to transparent outputs
|
||||||
// Disabled on regtest
|
// Disabled on regtest
|
||||||
if (fCoinbaseEnforcedProtectionEnabled &&
|
if (fCoinbaseEnforcedProtectionEnabled &&
|
||||||
@@ -1670,6 +1680,19 @@ bool NonContextualCheckInputs(const CTransaction& tx, CValidationState &state, c
|
|||||||
if (!MoneyRange(nFees))
|
if (!MoneyRange(nFees))
|
||||||
return state.DoS(100, error("CheckInputs(): nFees out of range"),
|
return state.DoS(100, error("CheckInputs(): nFees out of range"),
|
||||||
REJECT_INVALID, "bad-txns-fee-outofrange");
|
REJECT_INVALID, "bad-txns-fee-outofrange");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}// namespace Consensus
|
||||||
|
|
||||||
|
bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, const Consensus::Params& consensusParams, std::vector<CScriptCheck> *pvChecks)
|
||||||
|
{
|
||||||
|
if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs), consensusParams))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!tx.IsCoinBase())
|
||||||
|
{
|
||||||
|
if (pvChecks)
|
||||||
|
pvChecks->reserve(tx.vin.size());
|
||||||
|
|
||||||
// The first loop above does all the inexpensive checks.
|
// The first loop above does all the inexpensive checks.
|
||||||
// Only if ALL inputs pass do we perform expensive ECDSA signature checks.
|
// Only if ALL inputs pass do we perform expensive ECDSA signature checks.
|
||||||
@@ -1718,40 +1741,6 @@ bool NonContextualCheckInputs(const CTransaction& tx, CValidationState &state, c
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, const Consensus::Params& consensusParams, std::vector<CScriptCheck> *pvChecks)
|
|
||||||
{
|
|
||||||
if (!NonContextualCheckInputs(tx, state, inputs, fScriptChecks, flags, cacheStore, consensusParams, pvChecks)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tx.IsCoinBase())
|
|
||||||
{
|
|
||||||
// While checking, GetBestBlock() refers to the parent block.
|
|
||||||
// This is also true for mempool checks.
|
|
||||||
CBlockIndex *pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second;
|
|
||||||
int nSpendHeight = pindexPrev->nHeight + 1;
|
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
|
||||||
{
|
|
||||||
const COutPoint &prevout = tx.vin[i].prevout;
|
|
||||||
const CCoins *coins = inputs.AccessCoins(prevout.hash);
|
|
||||||
// Assertion is okay because NonContextualCheckInputs ensures the inputs
|
|
||||||
// are available.
|
|
||||||
assert(coins);
|
|
||||||
|
|
||||||
// If prev is coinbase, check that it's matured
|
|
||||||
if (coins->IsCoinBase()) {
|
|
||||||
if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) {
|
|
||||||
return state.Invalid(
|
|
||||||
error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight),
|
|
||||||
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart)
|
bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart)
|
||||||
|
|||||||
15
src/main.h
15
src/main.h
@@ -332,10 +332,6 @@ bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, cons
|
|||||||
unsigned int flags, bool cacheStore, const Consensus::Params& consensusParams,
|
unsigned int flags, bool cacheStore, const Consensus::Params& consensusParams,
|
||||||
std::vector<CScriptCheck> *pvChecks = NULL);
|
std::vector<CScriptCheck> *pvChecks = NULL);
|
||||||
|
|
||||||
bool NonContextualCheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &view, bool fScriptChecks,
|
|
||||||
unsigned int flags, bool cacheStore, const Consensus::Params& consensusParams,
|
|
||||||
std::vector<CScriptCheck> *pvChecks = NULL);
|
|
||||||
|
|
||||||
/** Apply the effects of this transaction on the UTXO set represented by view */
|
/** Apply the effects of this transaction on the UTXO set represented by view */
|
||||||
void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, int nHeight);
|
void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCache &inputs, int nHeight);
|
||||||
|
|
||||||
@@ -520,4 +516,15 @@ extern CCoinsViewCache *pcoinsTip;
|
|||||||
/** Global variable that points to the active block tree (protected by cs_main) */
|
/** Global variable that points to the active block tree (protected by cs_main) */
|
||||||
extern CBlockTreeDB *pblocktree;
|
extern CBlockTreeDB *pblocktree;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the spend height, which is one more than the inputs.GetBestBlock().
|
||||||
|
* While checking, GetBestBlock() refers to the parent block. (protected by cs_main)
|
||||||
|
* This is also true for mempool checks.
|
||||||
|
*/
|
||||||
|
int GetSpendHeight(const CCoinsViewCache& inputs);
|
||||||
|
|
||||||
|
namespace Consensus {
|
||||||
|
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, const Consensus::Params& consensusParams);
|
||||||
|
}
|
||||||
|
|
||||||
#endif // BITCOIN_MAIN_H
|
#endif // BITCOIN_MAIN_H
|
||||||
|
|||||||
@@ -756,7 +756,7 @@ BOOST_AUTO_TEST_CASE(coins_coinbase_spends)
|
|||||||
|
|
||||||
{
|
{
|
||||||
CTransaction tx2(mtx2);
|
CTransaction tx2(mtx2);
|
||||||
BOOST_CHECK(NonContextualCheckInputs(tx2, state, cache, false, SCRIPT_VERIFY_NONE, false, Params().GetConsensus()));
|
BOOST_CHECK(Consensus::CheckTxInputs(tx2, state, cache, 100+COINBASE_MATURITY, Params().GetConsensus()));
|
||||||
}
|
}
|
||||||
|
|
||||||
mtx2.vout.resize(1);
|
mtx2.vout.resize(1);
|
||||||
@@ -765,7 +765,7 @@ BOOST_AUTO_TEST_CASE(coins_coinbase_spends)
|
|||||||
|
|
||||||
{
|
{
|
||||||
CTransaction tx2(mtx2);
|
CTransaction tx2(mtx2);
|
||||||
BOOST_CHECK(!NonContextualCheckInputs(tx2, state, cache, false, SCRIPT_VERIFY_NONE, false, Params().GetConsensus()));
|
BOOST_CHECK(!Consensus::CheckTxInputs(tx2, state, cache, 100+COINBASE_MATURITY, Params().GetConsensus()));
|
||||||
BOOST_CHECK(state.GetRejectReason() == "bad-txns-coinbase-spend-has-transparent-outputs");
|
BOOST_CHECK(state.GetRejectReason() == "bad-txns-coinbase-spend-has-transparent-outputs");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user