diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp --- a/src/bench/block_assemble.cpp +++ b/src/bench/block_assemble.cpp @@ -42,9 +42,8 @@ LOCK(::cs_main); for (const auto &txr : txs) { - CValidationState vstate; + TxValidationState vstate; bool ret{::AcceptToMemoryPool(config, ::g_mempool, vstate, txr, - nullptr /* pfMissingInputs */, false /* bypass_limits */, /* nAbsurdFee */ Amount::zero())}; assert(ret); diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp --- a/src/bench/checkblock.cpp +++ b/src/bench/checkblock.cpp @@ -46,7 +46,7 @@ bool rewound = stream.Rewind(benchmark::data::block413567.size()); assert(rewound); - CValidationState validationState; + BlockValidationState validationState; bool checked = CheckBlock(block, validationState, params, options); assert(checked); } diff --git a/src/bench/duplicate_inputs.cpp b/src/bench/duplicate_inputs.cpp --- a/src/bench/duplicate_inputs.cpp +++ b/src/bench/duplicate_inputs.cpp @@ -57,7 +57,7 @@ block.hashMerkleRoot = BlockMerkleRoot(block); while (state.KeepRunning()) { - CValidationState cvstate{}; + BlockValidationState cvstate{}; assert(!CheckBlock(block, cvstate, chainparams.GetConsensus(), BlockValidationOptions(GetConfig()) .withCheckPoW(false) diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp --- a/src/blockencodings.cpp +++ b/src/blockencodings.cpp @@ -240,14 +240,14 @@ return READ_STATUS_INVALID; } - CValidationState state; + BlockValidationState state; if (!CheckBlock(block, state, config->GetChainParams().GetConsensus(), BlockValidationOptions(*config))) { // TODO: We really want to just check merkle tree manually here, but // that is expensive, and CheckBlock caches a block's "checked-status" // (in the CBlock?). CBlock should be able to check its own merkle root // and cache that check. - if (state.GetReason() == ValidationInvalidReason::BLOCK_MUTATED) { + if (state.GetResult() == BlockValidationResult::BLOCK_MUTATED) { // Possible Short ID collision. return READ_STATUS_FAILED; } diff --git a/src/consensus/tx_check.h b/src/consensus/tx_check.h --- a/src/consensus/tx_check.h +++ b/src/consensus/tx_check.h @@ -13,14 +13,14 @@ */ class CTransaction; -class CValidationState; +class TxValidationState; /** * Context-independent validity checks for coinbase and non-coinbase * transactions. */ -bool CheckRegularTransaction(const CTransaction &tx, CValidationState &state); -bool CheckCoinbase(const CTransaction &tx, CValidationState &state); +bool CheckRegularTransaction(const CTransaction &tx, TxValidationState &state); +bool CheckCoinbase(const CTransaction &tx, TxValidationState &state); #endif // BITCOIN_CONSENSUS_TX_CHECK_H diff --git a/src/consensus/tx_check.cpp b/src/consensus/tx_check.cpp --- a/src/consensus/tx_check.cpp +++ b/src/consensus/tx_check.cpp @@ -11,40 +11,40 @@ #include static bool CheckTransactionCommon(const CTransaction &tx, - CValidationState &state) { + TxValidationState &state) { // Basic checks that don't depend on any context if (tx.vin.empty()) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, - REJECT_INVALID, "bad-txns-vin-empty"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, REJECT_INVALID, + "bad-txns-vin-empty"); } if (tx.vout.empty()) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, - REJECT_INVALID, "bad-txns-vout-empty"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, REJECT_INVALID, + "bad-txns-vout-empty"); } // Size limit if (::GetSerializeSize(tx, PROTOCOL_VERSION) > MAX_TX_SIZE) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, - REJECT_INVALID, "bad-txns-oversize"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, REJECT_INVALID, + "bad-txns-oversize"); } // Check for negative or overflow output values Amount nValueOut = Amount::zero(); for (const auto &txout : tx.vout) { if (txout.nValue < Amount::zero()) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, + return state.Invalid(TxValidationResult::TX_CONSENSUS, REJECT_INVALID, "bad-txns-vout-negative"); } if (txout.nValue > MAX_MONEY) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, + return state.Invalid(TxValidationResult::TX_CONSENSUS, REJECT_INVALID, "bad-txns-vout-toolarge"); } nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, + return state.Invalid(TxValidationResult::TX_CONSENSUS, REJECT_INVALID, "bad-txns-txouttotal-toolarge"); } @@ -53,11 +53,10 @@ return true; } -bool CheckCoinbase(const CTransaction &tx, CValidationState &state) { +bool CheckCoinbase(const CTransaction &tx, TxValidationState &state) { if (!tx.IsCoinBase()) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, - REJECT_INVALID, "bad-cb-missing", - "first tx is not coinbase"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, REJECT_INVALID, + "bad-cb-missing", "first tx is not coinbase"); } if (!CheckTransactionCommon(tx, state)) { @@ -67,17 +66,17 @@ if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > MAX_COINBASE_SCRIPTSIG_SIZE) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, - REJECT_INVALID, "bad-cb-length"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, REJECT_INVALID, + "bad-cb-length"); } return true; } -bool CheckRegularTransaction(const CTransaction &tx, CValidationState &state) { +bool CheckRegularTransaction(const CTransaction &tx, TxValidationState &state) { if (tx.IsCoinBase()) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, - REJECT_INVALID, "bad-tx-coinbase"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, REJECT_INVALID, + "bad-tx-coinbase"); } if (!CheckTransactionCommon(tx, state)) { @@ -88,12 +87,12 @@ std::unordered_set vInOutPoints; for (const auto &txin : tx.vin) { if (txin.prevout.IsNull()) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, + return state.Invalid(TxValidationResult::TX_CONSENSUS, REJECT_INVALID, "bad-txns-prevout-null"); } if (!vInOutPoints.insert(txin.prevout).second) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, + return state.Invalid(TxValidationResult::TX_CONSENSUS, REJECT_INVALID, "bad-txns-inputs-duplicate"); } } diff --git a/src/consensus/tx_verify.h b/src/consensus/tx_verify.h --- a/src/consensus/tx_verify.h +++ b/src/consensus/tx_verify.h @@ -12,7 +12,7 @@ class CBlockIndex; class CCoinsViewCache; class CTransaction; -class CValidationState; +class TxValidationState; namespace Consensus { struct Params; @@ -24,7 +24,7 @@ * @param[out] txfee Set to the transaction fee if successful. * Preconditions: tx.IsCoinBase() is false. */ -bool CheckTxInputs(const CTransaction &tx, CValidationState &state, +bool CheckTxInputs(const CTransaction &tx, TxValidationState &state, const CCoinsViewCache &inputs, int nSpendHeight, Amount &txfee); @@ -37,8 +37,9 @@ * activation/deactivation and CLTV. */ bool ContextualCheckTransaction(const Consensus::Params ¶ms, - const CTransaction &tx, CValidationState &state, - int nHeight, int64_t nLockTimeCutoff, + const CTransaction &tx, + TxValidationState &state, int nHeight, + int64_t nLockTimeCutoff, int64_t nMedianTimePast); /** diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -38,21 +38,21 @@ } bool ContextualCheckTransaction(const Consensus::Params ¶ms, - const CTransaction &tx, CValidationState &state, - int nHeight, int64_t nLockTimeCutoff, + const CTransaction &tx, + TxValidationState &state, int nHeight, + int64_t nLockTimeCutoff, int64_t nMedianTimePast) { if (!IsFinalTx(tx, nHeight, nLockTimeCutoff)) { // While this is only one transaction, we use txns in the error to // ensure continuity with other clients. - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, - REJECT_INVALID, "bad-txns-nonfinal", - "non-final transaction"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, REJECT_INVALID, + "bad-txns-nonfinal", "non-final transaction"); } if (IsMagneticAnomalyEnabled(params, nHeight)) { // Size limit if (::GetSerializeSize(tx, PROTOCOL_VERSION) < MIN_TX_SIZE) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, + return state.Invalid(TxValidationResult::TX_CONSENSUS, REJECT_INVALID, "bad-txns-undersize"); } } @@ -155,12 +155,12 @@ } namespace Consensus { -bool CheckTxInputs(const CTransaction &tx, CValidationState &state, +bool CheckTxInputs(const CTransaction &tx, TxValidationState &state, const CCoinsViewCache &inputs, int nSpendHeight, Amount &txfee) { // are the actual inputs available? if (!inputs.HaveInputs(tx)) { - return state.Invalid(ValidationInvalidReason::TX_MISSING_INPUTS, false, + return state.Invalid(TxValidationResult::TX_MISSING_INPUTS, REJECT_INVALID, "bad-txns-inputs-missingorspent", strprintf("%s: inputs missing/spent", __func__)); } @@ -175,8 +175,8 @@ if (coin.IsCoinBase() && nSpendHeight - coin.GetHeight() < COINBASE_MATURITY) { return state.Invalid( - ValidationInvalidReason::TX_PREMATURE_SPEND, false, - REJECT_INVALID, "bad-txns-premature-spend-of-coinbase", + TxValidationResult::TX_PREMATURE_SPEND, REJECT_INVALID, + "bad-txns-premature-spend-of-coinbase", strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.GetHeight())); } @@ -184,7 +184,7 @@ // Check for negative or overflow input values nValueIn += coin.GetTxOut().nValue; if (!MoneyRange(coin.GetTxOut().nValue) || !MoneyRange(nValueIn)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, + return state.Invalid(TxValidationResult::TX_CONSENSUS, REJECT_INVALID, "bad-txns-inputvalues-outofrange"); } @@ -192,8 +192,8 @@ const Amount value_out = tx.GetValueOut(); if (nValueIn < value_out) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, - REJECT_INVALID, "bad-txns-in-belowout", + return state.Invalid(TxValidationResult::TX_CONSENSUS, REJECT_INVALID, + "bad-txns-in-belowout", strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out))); @@ -202,8 +202,8 @@ // Tally transaction fees const Amount txfee_aux = nValueIn - value_out; if (!MoneyRange(txfee_aux)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, - REJECT_INVALID, "bad-txns-fee-outofrange"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, REJECT_INVALID, + "bad-txns-fee-outofrange"); } txfee = txfee_aux; diff --git a/src/consensus/validation.h b/src/consensus/validation.h --- a/src/consensus/validation.h +++ b/src/consensus/validation.h @@ -19,130 +19,154 @@ static const uint8_t REJECT_CHECKPOINT = 0x43; /** - * A "reason" why something was invalid, suitable for determining whether the - * provider of the object should be banned/ignored/disconnected/etc. These are - * much more granular than the rejection codes, which may be more useful for - * some other use-cases. + * A "reason" why a transaction was invalid, suitable for determining whether + * the provider of the transaction should be banned/ignored/disconnected/etc. + * These are much more granular than the rejection codes, which may be more + * useful for some other use-cases. */ -enum class ValidationInvalidReason { - // txn and blocks: - //! not actually invalid - NONE, - //! invalid by consensus rules (excluding any below reasons) - CONSENSUS, +enum class TxValidationResult { + //! initial value. Tx has not yet been rejected + TX_RESULT_UNSET, + //! invalid by consensus rules + TX_CONSENSUS, /** * Invalid by a recent change to consensus rules. * Currently unused as there are no such consensus rule changes. */ - RECENT_CONSENSUS_CHANGE, - // Only blocks (or headers): - //! this object was cached as being invalid, but we don't know why - CACHED_INVALID, - //! invalid proof of work or time too old - BLOCK_INVALID_HEADER, - //! the block's data didn't match the data committed to by the PoW - BLOCK_MUTATED, - //! We don't have the previous block the checked one is built on - BLOCK_MISSING_PREV, - //! A block this one builds on is invalid - BLOCK_INVALID_PREV, - //! block timestamp was > 2 hours in the future (or our clock is bad) - BLOCK_TIME_FUTURE, - //! the block failed to meet one of our checkpoints - BLOCK_CHECKPOINT, - //! block finalization problems. - BLOCK_FINALIZATION, - // Only loose txn: + TX_RECENT_CONSENSUS_CHANGE, //! didn't meet our local policy rules TX_NOT_STANDARD, - //! a transaction was missing some of its inputs + //! transaction was missing some of its inputs TX_MISSING_INPUTS, //! transaction spends a coinbase too early, or violates locktime/sequence //! locks TX_PREMATURE_SPEND, /** * Tx already in mempool or conflicts with a tx in the chain - * TODO: Currently this is only used if the transaction already exists in - * the mempool or on chain, - * TODO: ATMP's fMissingInputs and a valid CValidationState being used to - * indicate missing inputs + * Currently this is only used if the transaction already exists in the + * mempool or on chain. */ TX_CONFLICT, //! violated mempool's fee/size/descendant/etc limits TX_MEMPOOL_POLICY, }; -inline bool IsTransactionReason(ValidationInvalidReason r) { - return r == ValidationInvalidReason::NONE || - r == ValidationInvalidReason::CONSENSUS || - r == ValidationInvalidReason::RECENT_CONSENSUS_CHANGE || - r == ValidationInvalidReason::TX_NOT_STANDARD || - r == ValidationInvalidReason::TX_PREMATURE_SPEND || - r == ValidationInvalidReason::TX_MISSING_INPUTS || - r == ValidationInvalidReason::TX_CONFLICT || - r == ValidationInvalidReason::TX_MEMPOOL_POLICY; -} - -inline bool IsBlockReason(ValidationInvalidReason r) { - return r == ValidationInvalidReason::NONE || - r == ValidationInvalidReason::CONSENSUS || - r == ValidationInvalidReason::RECENT_CONSENSUS_CHANGE || - r == ValidationInvalidReason::CACHED_INVALID || - r == ValidationInvalidReason::BLOCK_INVALID_HEADER || - r == ValidationInvalidReason::BLOCK_MUTATED || - r == ValidationInvalidReason::BLOCK_MISSING_PREV || - r == ValidationInvalidReason::BLOCK_INVALID_PREV || - r == ValidationInvalidReason::BLOCK_TIME_FUTURE || - r == ValidationInvalidReason::BLOCK_CHECKPOINT || - r == ValidationInvalidReason::BLOCK_FINALIZATION; -} +/** + * A "reason" why a block was invalid, suitable for determining whether the + * provider of the block should be banned/ignored/disconnected/etc. + * These are much more granular than the rejection codes, which may be more + * useful for some other use-cases. + */ +enum class BlockValidationResult { + //! initial value. Block has not yet been rejected + BLOCK_RESULT_UNSET, + //! invalid by consensus rules (excluding any below reasons) + BLOCK_CONSENSUS, + /** + * Invalid by a change to consensus rules more recent than SegWit. + * Currently unused as there are no such consensus rule changes, and any + * download sources realistically need to support SegWit in order to provide + * useful data, so differentiating between always-invalid and + * invalid-by-pre-SegWit-soft-fork is uninteresting. + */ + BLOCK_RECENT_CONSENSUS_CHANGE, + //! this block was cached as being invalid and we didn't store the reason + //! why + BLOCK_CACHED_INVALID, + //! invalid proof of work or time too old + BLOCK_INVALID_HEADER, + //! the block's data didn't match the data committed to by the PoW + BLOCK_MUTATED, + //! We don't have the previous block the checked one is built on + BLOCK_MISSING_PREV, + //! A block this one builds on is invalid + BLOCK_INVALID_PREV, + //! block timestamp was > 2 hours in the future (or our clock is bad) + BLOCK_TIME_FUTURE, + //! the block failed to meet one of our checkpoints + BLOCK_CHECKPOINT, + //! block finalization problems + BLOCK_FINALIZATION, +}; -/** Capture information about block/transaction validation */ -class CValidationState { +/** + * Base class for capturing information about block/transaction validation. + * This is subclassed by TxValidationState and BlockValidationState for + * validation information on transactions and blocks respectively. + */ +class ValidationState { private: enum mode_state { MODE_VALID, //!< everything ok MODE_INVALID, //!< network rule violation (DoS value may be set) MODE_ERROR, //!< run-time error - } mode; - ValidationInvalidReason m_reason; - std::string strRejectReason; + } m_mode; + std::string m_reject_reason; unsigned int chRejectCode; - std::string strDebugMessage; + std::string m_debug_message; -public: - CValidationState() - : mode(MODE_VALID), m_reason(ValidationInvalidReason::NONE), - chRejectCode(0) {} - bool Invalid(ValidationInvalidReason reasonIn, bool ret = false, - unsigned int chRejectCodeIn = 0, - const std::string &strRejectReasonIn = "", - const std::string &strDebugMessageIn = "") { - m_reason = reasonIn; +protected: + void Invalid(unsigned int chRejectCodeIn = 0, + const std::string &reject_reason = "", + const std::string &debug_message = "") { chRejectCode = chRejectCodeIn; - strRejectReason = strRejectReasonIn; - strDebugMessage = strDebugMessageIn; - if (mode == MODE_ERROR) { - return ret; + m_reject_reason = reject_reason; + m_debug_message = debug_message; + if (m_mode != MODE_ERROR) { + m_mode = MODE_INVALID; } - mode = MODE_INVALID; - return ret; } - bool Error(const std::string &strRejectReasonIn) { - if (mode == MODE_VALID) { - strRejectReason = strRejectReasonIn; - } - mode = MODE_ERROR; +public: + // ValidationState is abstract. Have a pure virtual destructor. + virtual ~ValidationState() = 0; + + ValidationState() : m_mode(MODE_VALID), chRejectCode(0) {} + bool Error(const std::string &reject_reason) { + if (m_mode == MODE_VALID) { + m_reject_reason = reject_reason; + } + m_mode = MODE_ERROR; return false; } - bool IsValid() const { return mode == MODE_VALID; } - bool IsInvalid() const { return mode == MODE_INVALID; } - bool IsError() const { return mode == MODE_ERROR; } - ValidationInvalidReason GetReason() const { return m_reason; } + bool IsValid() const { return m_mode == MODE_VALID; } + bool IsInvalid() const { return m_mode == MODE_INVALID; } + bool IsError() const { return m_mode == MODE_ERROR; } unsigned int GetRejectCode() const { return chRejectCode; } - std::string GetRejectReason() const { return strRejectReason; } - std::string GetDebugMessage() const { return strDebugMessage; } + std::string GetRejectReason() const { return m_reject_reason; } + std::string GetDebugMessage() const { return m_debug_message; } +}; + +inline ValidationState::~ValidationState(){}; + +class TxValidationState : public ValidationState { +private: + TxValidationResult m_result = TxValidationResult::TX_RESULT_UNSET; + +public: + bool Invalid(TxValidationResult result, unsigned int chRejectCodeIn = 0, + const std::string &reject_reason = "", + const std::string &debug_message = "") { + m_result = result; + ValidationState::Invalid(chRejectCodeIn, reject_reason, debug_message); + return false; + } + TxValidationResult GetResult() const { return m_result; } +}; + +class BlockValidationState : public ValidationState { +private: + BlockValidationResult m_result = BlockValidationResult::BLOCK_RESULT_UNSET; + +public: + bool Invalid(BlockValidationResult result, unsigned int chRejectCodeIn = 0, + const std::string &reject_reason = "", + const std::string &debug_message = "") { + m_result = result; + ValidationState::Invalid(chRejectCodeIn, reject_reason, debug_message); + return false; + } + BlockValidationResult GetResult() const { return m_result; } }; #endif // BITCOIN_CONSENSUS_VALIDATION_H diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -1339,7 +1339,7 @@ // scan for better chains in the block chain database, that are not yet // connected in the active best chain - CValidationState state; + BlockValidationState state; if (!ActivateBestChain(config, state)) { LogPrintf("Failed to connect best block (%s)\n", FormatStateMessage(state)); diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -15,16 +15,18 @@ #include #include -struct BlockHash; class CBlock; -struct CBlockLocator; class CChainParams; class Coin; class Config; class CRPCCommand; class CScheduler; -class CValidationState; +class TxValidationState; + +struct BlockHash; +struct CBlockLocator; struct NodeContext; + namespace Consensus { struct Params; } @@ -131,7 +133,7 @@ //! Check if transaction will be final given chain height current time. virtual bool contextualCheckTransactionForCurrentBlock( const Consensus::Params ¶ms, const CTransaction &tx, - CValidationState &state) = 0; + TxValidationState &state) = 0; }; //! Return Lock interface. Chain is locked when this is called, and diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp --- a/src/interfaces/chain.cpp +++ b/src/interfaces/chain.cpp @@ -141,7 +141,7 @@ } bool contextualCheckTransactionForCurrentBlock( const Consensus::Params ¶ms, const CTransaction &tx, - CValidationState &state) override { + TxValidationState &state) override { LockAssertion lock(::cs_main); return ContextualCheckTransactionForCurrentBlock(params, tx, state); } diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp --- a/src/interfaces/wallet.cpp +++ b/src/interfaces/wallet.cpp @@ -68,7 +68,7 @@ result.depth_in_main_chain = wtx.GetDepthInMainChain(locked_chain); result.time_received = wtx.nTimeReceived; result.lock_time = wtx.tx->nLockTime; - CValidationState state; + TxValidationState state; result.is_final = locked_chain.contextualCheckTransactionForCurrentBlock( Params().GetConsensus(), *wtx.tx, state); @@ -233,7 +233,7 @@ std::string &reject_reason) override { auto locked_chain = m_wallet->chain().lock(); LOCK(m_wallet->cs_wallet); - CValidationState state; + TxValidationState state; if (!m_wallet->CommitTransaction(std::move(tx), std::move(value_map), std::move(order_form), state)) { diff --git a/src/miner.cpp b/src/miner.cpp --- a/src/miner.cpp +++ b/src/miner.cpp @@ -223,7 +223,7 @@ pblock->nNonce = 0; pblocktemplate->entries[0].sigOpCount = 0; - CValidationState state; + BlockValidationState state; if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, BlockValidationOptions(nMaxGeneratedBlockSize) .withCheckPoW(false) @@ -279,7 +279,7 @@ const CTxMemPool::setEntries &package) { uint64_t nPotentialBlockSize = nBlockSize; for (CTxMemPool::txiter it : package) { - CValidationState state; + TxValidationState state; if (!ContextualCheckTransaction(chainparams.GetConsensus(), it->GetTx(), state, nHeight, nLockTimeCutoff, nMedianTimePast)) { diff --git a/src/net_processing.h b/src/net_processing.h --- a/src/net_processing.h +++ b/src/net_processing.h @@ -59,7 +59,7 @@ * Overridden from CValidationInterface. */ void BlockChecked(const CBlock &block, - const CValidationState &state) override; + const BlockValidationState &state) override; /** * Overridden from CValidationInterface. */ diff --git a/src/net_processing.cpp b/src/net_processing.cpp --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1201,13 +1201,12 @@ * banning/disconnecting us. We use this to determine which unaccepted * transactions from a whitelisted peer that we can safely relay. */ -static bool TxRelayMayResultInDisconnect(const CValidationState &state) { - assert(IsTransactionReason(state.GetReason())); - return state.GetReason() == ValidationInvalidReason::CONSENSUS; +static bool TxRelayMayResultInDisconnect(const TxValidationState &state) { + return state.GetResult() == TxValidationResult::TX_CONSENSUS; } /** - * Potentially ban a node based on the contents of a CValidationState object + * Potentially ban a node based on the contents of a BlockValidationState object * * @param[in] via_compact_block: this bool is passed in because net_processing * should punish peers differently depending on whether the data was provided in @@ -1215,25 +1214,24 @@ * contained invalid txs, the peer should not be punished. See BIP 152. * * @return Returns true if the peer was punished (probably disconnected) - * - * Changes here may need to be reflected in TxRelayMayResultInDisconnect(). */ -static bool MaybePunishNode(NodeId nodeid, const CValidationState &state, - bool via_compact_block, - const std::string &message = "") { - switch (state.GetReason()) { - case ValidationInvalidReason::NONE: +static bool MaybePunishNodeForBlock(NodeId nodeid, + const BlockValidationState &state, + bool via_compact_block, + const std::string &message = "") { + switch (state.GetResult()) { + case BlockValidationResult::BLOCK_RESULT_UNSET: break; // The node is providing invalid data: - case ValidationInvalidReason::CONSENSUS: - case ValidationInvalidReason::BLOCK_MUTATED: + case BlockValidationResult::BLOCK_CONSENSUS: + case BlockValidationResult::BLOCK_MUTATED: if (!via_compact_block) { LOCK(cs_main); Misbehaving(nodeid, 100, message); return true; } break; - case ValidationInvalidReason::CACHED_INVALID: { + case BlockValidationResult::BLOCK_CACHED_INVALID: { LOCK(cs_main); CNodeState *node_state = State(nodeid); if (node_state == nullptr) { @@ -1249,14 +1247,14 @@ } break; } - case ValidationInvalidReason::BLOCK_INVALID_HEADER: - case ValidationInvalidReason::BLOCK_CHECKPOINT: - case ValidationInvalidReason::BLOCK_INVALID_PREV: { + case BlockValidationResult::BLOCK_INVALID_HEADER: + case BlockValidationResult::BLOCK_CHECKPOINT: + case BlockValidationResult::BLOCK_INVALID_PREV: { LOCK(cs_main); Misbehaving(nodeid, 100, message); } return true; - case ValidationInvalidReason::BLOCK_FINALIZATION: { + case BlockValidationResult::BLOCK_FINALIZATION: { // TODO: Use the state object to report this is probably not the // best idea. This is effectively unreachable, unless there is a bug // somewhere. @@ -1265,20 +1263,48 @@ } return true; // Conflicting (but not necessarily invalid) data or different policy: - case ValidationInvalidReason::BLOCK_MISSING_PREV: { + case BlockValidationResult::BLOCK_MISSING_PREV: { // TODO: Handle this much more gracefully (10 DoS points is super // arbitrary) LOCK(cs_main); Misbehaving(nodeid, 10, message); } return true; - case ValidationInvalidReason::RECENT_CONSENSUS_CHANGE: - case ValidationInvalidReason::BLOCK_TIME_FUTURE: - case ValidationInvalidReason::TX_NOT_STANDARD: - case ValidationInvalidReason::TX_MISSING_INPUTS: - case ValidationInvalidReason::TX_PREMATURE_SPEND: - case ValidationInvalidReason::TX_CONFLICT: - case ValidationInvalidReason::TX_MEMPOOL_POLICY: + case BlockValidationResult::BLOCK_RECENT_CONSENSUS_CHANGE: + case BlockValidationResult::BLOCK_TIME_FUTURE: + break; + } + if (message != "") { + LogPrint(BCLog::NET, "peer=%d: %s\n", nodeid, message); + } + return false; +} + +/** + * Potentially ban a node based on the contents of a TxValidationState object + * + * @return Returns true if the peer was punished (probably disconnected) + * + * Changes here may need to be reflected in TxRelayMayResultInDisconnect(). + */ +static bool MaybePunishNodeForTx(NodeId nodeid, const TxValidationState &state, + const std::string &message = "") { + switch (state.GetResult()) { + case TxValidationResult::TX_RESULT_UNSET: + break; + // The node is providing invalid data: + case TxValidationResult::TX_CONSENSUS: { + LOCK(cs_main); + Misbehaving(nodeid, 100, message); + return true; + } + // Conflicting (but not necessarily invalid) data or different policy: + case TxValidationResult::TX_RECENT_CONSENSUS_CHANGE: + case TxValidationResult::TX_NOT_STANDARD: + case TxValidationResult::TX_MISSING_INPUTS: + case TxValidationResult::TX_PREMATURE_SPEND: + case TxValidationResult::TX_CONFLICT: + case TxValidationResult::TX_MEMPOOL_POLICY: break; } if (message != "") { @@ -1485,7 +1511,7 @@ * peers announce compact blocks. */ void PeerLogicValidation::BlockChecked(const CBlock &block, - const CValidationState &state) { + const BlockValidationState &state) { LOCK(cs_main); const BlockHash hash = block.GetHash(); @@ -1502,8 +1528,8 @@ state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), hash}; State(it->second.first)->rejects.push_back(reject); - MaybePunishNode(/*nodeid=*/it->second.first, state, - /*via_compact_block=*/!it->second.second); + MaybePunishNodeForBlock(/*nodeid=*/it->second.first, state, + /*via_compact_block=*/!it->second.second); } } // Check that: @@ -1653,7 +1679,7 @@ } } // release cs_main before calling ActivateBestChain if (need_activate_chain) { - CValidationState state; + BlockValidationState state; if (!ActivateBestChain(config, state, a_recent_block)) { LogPrint(BCLog::NET, "failed to activate chain (%s)\n", FormatStateMessage(state)); @@ -1987,13 +2013,11 @@ } } - CValidationState state; - CBlockHeader first_invalid_header; - if (!ProcessNewBlockHeaders(config, headers, state, &pindexLast, - &first_invalid_header)) { + BlockValidationState state; + if (!ProcessNewBlockHeaders(config, headers, state, &pindexLast)) { if (state.IsInvalid()) { - MaybePunishNode(pfrom->GetId(), state, via_compact_block, - "invalid header received"); + MaybePunishNodeForBlock(pfrom->GetId(), state, via_compact_block, + "invalid header received"); return false; } } @@ -2167,11 +2191,11 @@ const CTransactionRef porphanTx = orphan_it->second.tx; const CTransaction &orphanTx = *porphanTx; NodeId fromPeer = orphan_it->second.fromPeer; - bool fMissingInputs2 = false; - // Use a new CValidationState because orphans come from different peers - // (and we call MaybePunishNode based on the source peer from the orphan - // map, not based on the peer that relayed the previous transaction). - CValidationState orphan_state; + // Use a new TxValidationState because orphans come from different peers + // (and we call MaybePunishNodeForTx based on the source peer from the + // orphan map, not based on the peer that relayed the previous + // transaction). + TxValidationState orphan_state; auto it = rejectCountPerNode.find(fromPeer); if (it != rejectCountPerNode.end() && @@ -2180,7 +2204,7 @@ } if (AcceptToMemoryPool(config, g_mempool, orphan_state, porphanTx, - &fMissingInputs2, false /* bypass_limits */, + false /* bypass_limits */, Amount::zero() /* nAbsurdFee */)) { LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanTxId.ToString()); @@ -2196,11 +2220,11 @@ } EraseOrphanTx(orphanTxId); done = true; - } else if (!fMissingInputs2) { + } else if (orphan_state.GetResult() != + TxValidationResult::TX_MISSING_INPUTS) { if (orphan_state.IsInvalid()) { // Punish peer that gave us an invalid orphan tx - MaybePunishNode(fromPeer, orphan_state, - /*via_compact_block*/ false); + MaybePunishNodeForTx(fromPeer, orphan_state); LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s\n", orphanTxId.ToString()); } @@ -2208,7 +2232,6 @@ // Probably non-standard or insufficient fee LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", orphanTxId.ToString()); - assert(IsTransactionReason(orphan_state.GetReason())); assert(recentRejects); recentRejects->insert(orphanTxId); @@ -2750,7 +2773,7 @@ LOCK(cs_most_recent_block); a_recent_block = most_recent_block; } - CValidationState state; + BlockValidationState state; if (!ActivateBestChain(config, state, a_recent_block)) { LogPrint(BCLog::NET, "failed to activate chain (%s)\n", FormatStateMessage(state)); @@ -2967,8 +2990,7 @@ LOCK2(cs_main, g_cs_orphans); - bool fMissingInputs = false; - CValidationState state; + TxValidationState state; CNodeState *nodestate = State(pfrom->GetId()); nodestate->m_tx_download.m_tx_announced.erase(txid); @@ -2976,7 +2998,7 @@ EraseTxRequest(txid); if (!AlreadyHave(inv) && - AcceptToMemoryPool(config, g_mempool, state, ptx, &fMissingInputs, + AcceptToMemoryPool(config, g_mempool, state, ptx, false /* bypass_limits */, Amount::zero() /* nAbsurdFee */)) { g_mempool.check(pcoinsTip.get()); @@ -3002,7 +3024,7 @@ // Recursively process any orphan transactions that depended on this // one ProcessOrphanTx(config, connman, pfrom->orphan_work_set); - } else if (fMissingInputs) { + } else if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) { // It may be the case that the orphans parents have all been // rejected. bool fRejectedParents = false; @@ -3045,8 +3067,6 @@ recentRejects->insert(tx.GetId()); } } else { - assert(IsTransactionReason(state.GetReason())); - assert(recentRejects); recentRejects->insert(tx.GetId()); @@ -3107,7 +3127,7 @@ 0, MAX_REJECT_MESSAGE_LENGTH), inv.hash)); } - MaybePunishNode(pfrom->GetId(), state, /*via_compact_block*/ false); + MaybePunishNodeForTx(pfrom->GetId(), state); } return true; } @@ -3148,13 +3168,13 @@ } const CBlockIndex *pindex = nullptr; - CValidationState state; + BlockValidationState state; if (!ProcessNewBlockHeaders(config, {cmpctblock.header}, state, &pindex)) { if (state.IsInvalid()) { - MaybePunishNode(pfrom->GetId(), state, - /*via_compact_block*/ true, - "invalid header via cmpctblock"); + MaybePunishNodeForBlock(pfrom->GetId(), state, + /*via_compact_block*/ true, + "invalid header via cmpctblock"); return true; } } @@ -3657,7 +3677,7 @@ switch (u.getStatus()) { case avalanche::BlockUpdate::Status::Invalid: case avalanche::BlockUpdate::Status::Rejected: { - CValidationState state; + BlockValidationState state; ParkBlock(config, state, pindex); if (!state.IsValid()) { return error("Database error: %s", @@ -3672,7 +3692,7 @@ } } - CValidationState state; + BlockValidationState state; if (!ActivateBestChain(config, state)) { LogPrint(BCLog::NET, "failed to activate chain (%s)\n", FormatStateMessage(state)); diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp --- a/src/node/transaction.cpp +++ b/src/node/transaction.cpp @@ -45,21 +45,17 @@ if (!g_mempool.exists(txid)) { // Transaction is not already in the mempool. Submit it. - CValidationState state; - bool fMissingInputs; + TxValidationState state; if (!AcceptToMemoryPool(config, g_mempool, state, std::move(tx), - &fMissingInputs, false /* bypass_limits */, - max_tx_fee)) { + false /* bypass_limits */, max_tx_fee)) { + err_string = FormatStateMessage(state); if (state.IsInvalid()) { - err_string = FormatStateMessage(state); + if (state.GetResult() == + TxValidationResult::TX_MISSING_INPUTS) { + return TransactionError::MISSING_INPUTS; + } return TransactionError::MEMPOOL_REJECTED; } - - if (fMissingInputs) { - return TransactionError::MISSING_INPUTS; - } - - err_string = FormatStateMessage(state); return TransactionError::MEMPOOL_ERROR; } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1657,7 +1657,7 @@ } } - CValidationState state; + BlockValidationState state; PreciousBlock(config, state, pblockindex); if (!state.IsValid()) { @@ -1686,7 +1686,7 @@ std::string strHash = request.params[0].get_str(); BlockHash hash(uint256S(strHash)); - CValidationState state; + BlockValidationState state; { LOCK(cs_main); @@ -1726,7 +1726,7 @@ .Check(request); const BlockHash hash(ParseHashV(request.params[0], "blockhash")); - CValidationState state; + BlockValidationState state; CBlockIndex *pblockindex; { @@ -1765,7 +1765,6 @@ const std::string strHash = request.params[0].get_str(); const BlockHash hash(uint256S(strHash)); - CValidationState state; CBlockIndex *pblockindex; { @@ -1776,6 +1775,7 @@ pblockindex = mapBlockIndex[hash]; } + BlockValidationState state; ParkBlock(config, state, pblockindex); if (state.IsValid()) { @@ -1818,7 +1818,7 @@ ResetBlockFailureFlags(pblockindex); } - CValidationState state; + BlockValidationState state; ActivateBestChain(config, state); if (!state.IsValid()) { @@ -1857,7 +1857,7 @@ UnparkBlockAndChildren(pblockindex); } - CValidationState state; + BlockValidationState state; ActivateBestChain(config, state); if (!state.IsValid()) { diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -313,7 +313,7 @@ // NOTE: Assumes a conclusive result; if result is inconclusive, it must be // handled by caller static UniValue BIP22ValidationResult(const Config &config, - const CValidationState &state) { + const BlockValidationState &state) { if (state.IsValid()) { return NullUniValue; } @@ -502,7 +502,7 @@ if (block.hashPrevBlock != pindexPrev->GetBlockHash()) { return "inconclusive-not-best-prevblk"; } - CValidationState state; + BlockValidationState state; TestBlockValidity(state, config.GetChainParams(), block, pindexPrev, BlockValidationOptions(config) .withCheckPoW(false) @@ -687,14 +687,14 @@ public: uint256 hash; bool found; - CValidationState state; + BlockValidationState state; explicit submitblock_StateCatcher(const uint256 &hashIn) : hash(hashIn), found(false), state() {} protected: void BlockChecked(const CBlock &block, - const CValidationState &stateIn) override { + const BlockValidationState &stateIn) override { if (block.GetHash() != hash) { return; } @@ -803,9 +803,8 @@ } } - CValidationState state; - ProcessNewBlockHeaders(config, {h}, state, /* ppindex */ nullptr, - /* first_invalid */ nullptr); + BlockValidationState state; + ProcessNewBlockHeaders(config, {h}, state); if (state.IsValid()) { return NullUniValue; } diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -1036,23 +1036,24 @@ UniValue result_0(UniValue::VOBJ); result_0.pushKV("txid", txid.GetHex()); - CValidationState state; - bool missing_inputs; + TxValidationState state; bool test_accept_res; { LOCK(cs_main); test_accept_res = AcceptToMemoryPool( - config, g_mempool, state, std::move(tx), &missing_inputs, - false /* bypass_limits */, max_raw_tx_fee, true /* test_accept */); + config, g_mempool, state, std::move(tx), false /* bypass_limits */, + max_raw_tx_fee, true /* test_accept */); } result_0.pushKV("allowed", test_accept_res); if (!test_accept_res) { if (state.IsInvalid()) { - result_0.pushKV("reject-reason", - strprintf("%i: %s", state.GetRejectCode(), - state.GetRejectReason())); - } else if (missing_inputs) { - result_0.pushKV("reject-reason", "missing-inputs"); + if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) { + result_0.pushKV("reject-reason", "missing-inputs"); + } else { + result_0.pushKV("reject-reason", + strprintf("%i: %s", state.GetRejectCode(), + state.GetRejectReason())); + } } else { result_0.pushKV("reject-reason", state.GetRejectReason()); } diff --git a/src/test/blockcheck_tests.cpp b/src/test/blockcheck_tests.cpp --- a/src/test/blockcheck_tests.cpp +++ b/src/test/blockcheck_tests.cpp @@ -15,7 +15,7 @@ BOOST_FIXTURE_TEST_SUITE(blockcheck_tests, BasicTestingSetup) static void RunCheckOnBlockImpl(const GlobalConfig &config, const CBlock &block, - CValidationState &state, bool expected) { + BlockValidationState &state, bool expected) { block.fChecked = false; bool fValid = CheckBlock( block, state, config.GetChainParams().GetConsensus(), @@ -27,13 +27,13 @@ } static void RunCheckOnBlock(const GlobalConfig &config, const CBlock &block) { - CValidationState state; + BlockValidationState state; RunCheckOnBlockImpl(config, block, state, true); } static void RunCheckOnBlock(const GlobalConfig &config, const CBlock &block, const std::string &reason) { - CValidationState state; + BlockValidationState state; RunCheckOnBlockImpl(config, block, state, false); BOOST_CHECK_EQUAL(state.GetRejectCode(), REJECT_INVALID); diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp --- a/src/test/blockfilter_index_tests.cpp +++ b/src/test/blockfilter_index_tests.cpp @@ -95,9 +95,8 @@ CreateBlock(pindex, no_txns, coinbase_script_pub_key, mempool)); CBlockHeader header = block->GetBlockHeader(); - CValidationState state; - if (!ProcessNewBlockHeaders(GetConfig(), {header}, state, &pindex, - nullptr)) { + BlockValidationState state; + if (!ProcessNewBlockHeaders(GetConfig(), {header}, state, &pindex)) { return false; } } diff --git a/src/test/checkpoints_tests.cpp b/src/test/checkpoints_tests.cpp --- a/src/test/checkpoints_tests.cpp +++ b/src/test/checkpoints_tests.cpp @@ -81,8 +81,6 @@ */ BOOST_AUTO_TEST_CASE(ban_fork_prior_to_and_at_checkpoints) { MainnetConfigWithTestCheckpoints config; - - CBlockHeader invalid; const CBlockIndex *pindex = nullptr; // Start with mainnet genesis block @@ -92,9 +90,8 @@ "1b60a8ce26f")); { - CValidationState state; - BOOST_CHECK(ProcessNewBlockHeaders(config, {headerG}, state, &pindex, - &invalid)); + BlockValidationState state; + BOOST_CHECK(ProcessNewBlockHeaders(config, {headerG}, state, &pindex)); pindex = nullptr; } @@ -157,35 +154,29 @@ // Headers A and AA should be accepted { - CValidationState state; - BOOST_CHECK(ProcessNewBlockHeaders(config, {headerA}, state, &pindex, - &invalid)); + BlockValidationState state; + BOOST_CHECK(ProcessNewBlockHeaders(config, {headerA}, state, &pindex)); BOOST_CHECK(state.IsValid()); BOOST_CHECK(pindex != nullptr); pindex = nullptr; - BOOST_CHECK(invalid.IsNull()); } { - CValidationState state; - BOOST_CHECK(ProcessNewBlockHeaders(config, {headerAA}, state, &pindex, - &invalid)); + BlockValidationState state; + BOOST_CHECK(ProcessNewBlockHeaders(config, {headerAA}, state, &pindex)); BOOST_CHECK(state.IsValid()); BOOST_CHECK(pindex != nullptr); pindex = nullptr; - BOOST_CHECK(invalid.IsNull()); } // Header B should be rejected { - CValidationState state; - BOOST_CHECK(!ProcessNewBlockHeaders(config, {headerB}, state, &pindex, - &invalid)); + BlockValidationState state; + BOOST_CHECK(!ProcessNewBlockHeaders(config, {headerB}, state, &pindex)); BOOST_CHECK(state.IsInvalid()); BOOST_CHECK(state.GetRejectCode() == REJECT_CHECKPOINT); BOOST_CHECK(state.GetRejectReason() == "bad-fork-prior-to-checkpoint"); BOOST_CHECK(pindex == nullptr); - BOOST_CHECK(invalid.GetHash() == headerB.GetHash()); } // Sanity check to ensure header was not saved in memory @@ -196,14 +187,13 @@ // Header AB should be rejected { - CValidationState state; - BOOST_CHECK(!ProcessNewBlockHeaders(config, {headerAB}, state, &pindex, - &invalid)); + BlockValidationState state; + BOOST_CHECK( + !ProcessNewBlockHeaders(config, {headerAB}, state, &pindex)); BOOST_CHECK(state.IsInvalid()); BOOST_CHECK(state.GetRejectCode() == REJECT_CHECKPOINT); BOOST_CHECK(state.GetRejectReason() == "checkpoint mismatch"); BOOST_CHECK(pindex == nullptr); - BOOST_CHECK(invalid.GetHash() == headerAB.GetHash()); } // Sanity check to ensure header was not saved in memory diff --git a/src/test/fuzz/transaction.cpp b/src/test/fuzz/transaction.cpp --- a/src/test/fuzz/transaction.cpp +++ b/src/test/fuzz/transaction.cpp @@ -41,7 +41,7 @@ return; } - CValidationState state; + TxValidationState state; (void)CheckRegularTransaction(tx, state); const CFeeRate dust_relay_fee{DUST_RELAY_TX_FEE}; diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -520,7 +520,7 @@ { // Locktime passes. - CValidationState state; + TxValidationState state; BOOST_CHECK(ContextualCheckTransactionForCurrentBlock( params, CTransaction(tx), state, flags)); } @@ -546,7 +546,7 @@ { // Locktime passes. - CValidationState state; + TxValidationState state; BOOST_CHECK(ContextualCheckTransactionForCurrentBlock( params, CTransaction(tx), state, flags)); } @@ -583,7 +583,7 @@ { // Locktime fails. - CValidationState state; + TxValidationState state; BOOST_CHECK(!ContextualCheckTransactionForCurrentBlock( params, CTransaction(tx), state, flags)); BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-txns-nonfinal"); @@ -594,7 +594,7 @@ { // Locktime passes on 2nd block. - CValidationState state; + TxValidationState state; int64_t nMedianTimePast = ::ChainActive().Tip()->GetMedianTimePast(); BOOST_CHECK(ContextualCheckTransaction( params, CTransaction(tx), state, ::ChainActive().Tip()->nHeight + 2, @@ -611,7 +611,7 @@ { // Locktime fails. - CValidationState state; + TxValidationState state; BOOST_CHECK(!ContextualCheckTransactionForCurrentBlock( params, CTransaction(tx), state, flags)); BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-txns-nonfinal"); @@ -622,7 +622,7 @@ { // Locktime passes 1 second later. - CValidationState state; + TxValidationState state; int64_t nMedianTimePast = ::ChainActive().Tip()->GetMedianTimePast() + 1; BOOST_CHECK(ContextualCheckTransaction( @@ -638,7 +638,7 @@ { // Locktime passes. - CValidationState state; + TxValidationState state; BOOST_CHECK(ContextualCheckTransactionForCurrentBlock( params, CTransaction(tx), state, flags)); } diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -252,7 +252,7 @@ CDataStream stream(ParseHex(raw_tx), SER_NETWORK, PROTOCOL_VERSION); stream >> tx; - CValidationState state; + TxValidationState state; BOOST_CHECK_MESSAGE(CheckRegularTransaction(*tx, state), strTest); BOOST_CHECK(state.IsValid()); diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -104,7 +104,7 @@ PROTOCOL_VERSION); CTransaction tx(deserialize, stream); - CValidationState state; + TxValidationState state; BOOST_CHECK_MESSAGE(tx.IsCoinBase() ? CheckCoinbase(tx, state) : CheckRegularTransaction(tx, state), @@ -206,7 +206,7 @@ PROTOCOL_VERSION); CTransaction tx(deserialize, stream); - CValidationState state; + TxValidationState state; fValid = CheckRegularTransaction(tx, state) && state.IsValid(); PrecomputedTransactionData txdata(tx); @@ -263,7 +263,7 @@ CDataStream stream(vch, SER_DISK, CLIENT_VERSION); CMutableTransaction tx; stream >> tx; - CValidationState state; + TxValidationState state; BOOST_CHECK_MESSAGE(CheckRegularTransaction(CTransaction(tx), state) && state.IsValid(), "Simple deserialized transaction should be valid."); @@ -796,7 +796,7 @@ // A minimaly sized transction. CTransaction minTx; - CValidationState state; + TxValidationState state; BOOST_CHECK(ContextualCheckTransaction( params, minTx, state, magneticAnomalyActivationHeight - 1, 5678, 1234)); diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp --- a/src/test/txvalidation_tests.cpp +++ b/src/test/txvalidation_tests.cpp @@ -32,7 +32,7 @@ BOOST_CHECK(CTransaction(coinbaseTx).IsCoinBase()); - CValidationState state; + TxValidationState state; LOCK(cs_main); @@ -41,7 +41,6 @@ BOOST_CHECK_EQUAL(false, AcceptToMemoryPool(GetConfig(), *m_node.mempool, state, MakeTransactionRef(coinbaseTx), - nullptr /* pfMissingInputs */, true /* bypass_limits */, Amount::zero() /* nAbsurdFee */)); @@ -51,7 +50,7 @@ // Check that the validation state reflects the unsuccesful attempt. BOOST_CHECK(state.IsInvalid()); BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-tx-coinbase"); - BOOST_CHECK(state.GetReason() == ValidationInvalidReason::CONSENSUS); + BOOST_CHECK(state.GetResult() == TxValidationResult::TX_CONSENSUS); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -32,11 +32,10 @@ const auto ToMemPool = [this](const CMutableTransaction &tx) { LOCK(cs_main); - CValidationState state; + TxValidationState state; return AcceptToMemoryPool( GetConfig(), *m_node.mempool, state, MakeTransactionRef(tx), - nullptr /* pfMissingInputs */, true /* bypass_limits */, - Amount::zero() /* nAbsurdFee */); + true /* bypass_limits */, Amount::zero() /* nAbsurdFee */); }; // Create a double-spend of mature coinbase txn: @@ -102,7 +101,7 @@ } static inline bool -CheckInputs(const CTransaction &tx, CValidationState &state, +CheckInputs(const CTransaction &tx, TxValidationState &state, const CCoinsViewCache &view, bool fScriptChecks, const uint32_t flags, bool sigCacheStore, bool scriptCacheStore, const PrecomputedTransactionData &txdata, int &nSigChecksOut, @@ -139,7 +138,7 @@ MMIXLinearCongruentialGenerator lcg; for (int i = 0; i < 4096; i++) { uint32_t test_flags = lcg.next() | required_flags; - CValidationState state; + TxValidationState state; // Filter out incompatible flag choices if ((test_flags & SCRIPT_VERIFY_CLEANSTACK)) { @@ -273,7 +272,7 @@ LOCK(cs_main); - CValidationState state; + TxValidationState state; PrecomputedTransactionData ptd_spend_tx(tx); int nSigChecksDummy; @@ -353,7 +352,7 @@ // Make it valid, and check again invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100; - CValidationState state; + TxValidationState state; CTransaction transaction(invalid_with_cltv_tx); PrecomputedTransactionData txdata(transaction); @@ -392,7 +391,7 @@ // Make it valid, and check again invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100; - CValidationState state; + TxValidationState state; CTransaction transaction(invalid_with_csv_tx); PrecomputedTransactionData txdata(transaction); @@ -448,7 +447,7 @@ // Try checking this valid transaction with sigchecks limiter // supplied. Each input consumes 1 sigcheck. - CValidationState state; + TxValidationState state; CTransaction transaction(tx); PrecomputedTransactionData txdata(transaction); const uint32_t flags = @@ -475,7 +474,7 @@ // Serial validation fails with the limiter. CheckInputsLimiter sigchecklimiter2(1); - CValidationState state2; + TxValidationState state2; BOOST_CHECK(!CheckInputs(transaction, state2, pcoinsTip.get(), true, flags, true, true, txdata, nSigChecksDummy, nullptr, &sigchecklimiter2)); @@ -517,24 +516,24 @@ * caching. */ CheckInputsLimiter sigchecklimiter6(1); - CValidationState state6; + TxValidationState state6; BOOST_CHECK(!CheckInputs(transaction, state6, pcoinsTip.get(), true, flags, true, true, txdata, nSigChecksDummy, nullptr, &sigchecklimiter6)); BOOST_CHECK_EQUAL(state6.GetRejectReason(), "too-many-sigchecks"); - BOOST_CHECK_EQUAL(state6.GetReason(), - ValidationInvalidReason::CONSENSUS); + BOOST_CHECK_EQUAL(state6.GetResult(), + TxValidationResult::TX_CONSENSUS); BOOST_CHECK(!sigchecklimiter6.check()); // even in parallel validation, immediate fail from the cache. std::vector scriptchecks7; CheckInputsLimiter sigchecklimiter7(1); - CValidationState state7; + TxValidationState state7; BOOST_CHECK(!CheckInputs(transaction, state7, pcoinsTip.get(), true, flags, true, true, txdata, nSigChecksDummy, &scriptchecks7, &sigchecklimiter7)); BOOST_CHECK_EQUAL(state7.GetRejectReason(), "too-many-sigchecks"); - BOOST_CHECK_EQUAL(state6.GetReason(), - ValidationInvalidReason::CONSENSUS); + BOOST_CHECK_EQUAL(state6.GetResult(), + TxValidationResult::TX_CONSENSUS); BOOST_CHECK(!sigchecklimiter7.check()); BOOST_CHECK(scriptchecks7.empty()); } @@ -544,7 +543,7 @@ // Invalidate vin[1] tx.vin[1].scriptSig = CScript(); - CValidationState state; + TxValidationState state; CTransaction transaction(tx); PrecomputedTransactionData txdata(transaction); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -118,7 +118,7 @@ throw std::runtime_error("LoadGenesisBlock failed."); } { - CValidationState state; + BlockValidationState state; if (!ActivateBestChain(config, state)) { throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", FormatStateMessage(state))); diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp --- a/src/test/validation_block_tests.cpp +++ b/src/test/validation_block_tests.cpp @@ -149,7 +149,7 @@ } bool ignored; - CValidationState state; + BlockValidationState state; std::vector headers; std::transform( blocks.begin(), blocks.end(), std::back_inserter(headers), diff --git a/src/test/validationinterface_tests.cpp b/src/test/validationinterface_tests.cpp --- a/src/test/validationinterface_tests.cpp +++ b/src/test/validationinterface_tests.cpp @@ -23,14 +23,14 @@ } } void BlockChecked(const CBlock &block, - const CValidationState &state) override { + const BlockValidationState &state) override { if (m_on_call) { m_on_call(); } } static void Call() { CBlock block; - CValidationState state; + BlockValidationState state; GetMainSignals().BlockChecked(block, state); } std::function m_on_call; diff --git a/src/txmempool.cpp b/src/txmempool.cpp --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -571,7 +571,7 @@ LockPoints lp = it->GetLockPoints(); bool validLP = TestLockPointValidity(&lp); - CValidationState state; + TxValidationState state; if (!ContextualCheckTransactionForCurrentBlock( config.GetChainParams().GetConsensus(), tx, state, flags) || !CheckSequenceLocks(*this, tx, flags, &lp, validLP)) { @@ -688,11 +688,13 @@ static void CheckInputsAndUpdateCoins(const CTransaction &tx, CCoinsViewCache &mempoolDuplicate, const int64_t spendheight) { - CValidationState state; + // Not used. CheckTxInputs() should always pass + TxValidationState dummy_state; Amount txfee = Amount::zero(); bool fCheckResult = - tx.IsCoinBase() || Consensus::CheckTxInputs(tx, state, mempoolDuplicate, - spendheight, txfee); + tx.IsCoinBase() || + Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, + txfee); assert(fCheckResult); UpdateCoins(mempoolDuplicate, tx, std::numeric_limits::max()); } @@ -1391,10 +1393,9 @@ for (const CTransactionRef &tx : reverse_iterate(queuedTx.get())) { // ignore validation errors in resurrected transactions - CValidationState stateDummy; + TxValidationState stateDummy; if (!fAddToMempool || tx->IsCoinBase() || !AcceptToMemoryPool(config, g_mempool, stateDummy, tx, - nullptr /* pfMissingInputs */, true /* bypass_limits */, Amount::zero() /* nAbsurdFee */)) { // If the transaction doesn't make it in to the mempool, remove any diff --git a/src/undo.h b/src/undo.h --- a/src/undo.h +++ b/src/undo.h @@ -17,7 +17,7 @@ class CBlock; class CBlockIndex; class CCoinsViewCache; -class CValidationState; +class BlockValidationState; /** * Undo information for a CTxIn diff --git a/src/util/validation.h b/src/util/validation.h --- a/src/util/validation.h +++ b/src/util/validation.h @@ -8,10 +8,10 @@ #include -class CValidationState; +class ValidationState; -/** Convert CValidationState to a human-readable message for logging */ -std::string FormatStateMessage(const CValidationState &state); +/** Convert ValidationState to a human-readable message for logging */ +std::string FormatStateMessage(const ValidationState &state); extern const std::string strMessageMagic; diff --git a/src/util/validation.cpp b/src/util/validation.cpp --- a/src/util/validation.cpp +++ b/src/util/validation.cpp @@ -8,8 +8,8 @@ #include #include -/** Convert CValidationState to a human-readable message for logging */ -std::string FormatStateMessage(const CValidationState &state) { +/** Convert ValidationState to a human-readable message for logging */ +std::string FormatStateMessage(const ValidationState &state) { return strprintf( "%s%s (code %i)", state.GetRejectReason(), state.GetDebugMessage().empty() ? "" : ", " + state.GetDebugMessage(), diff --git a/src/validation.h b/src/validation.h --- a/src/validation.h +++ b/src/validation.h @@ -34,6 +34,7 @@ #include #include +class BlockValidationState; class CBlockIndex; class CBlockTreeDB; class CBlockUndo; @@ -46,8 +47,8 @@ class CScriptCheck; class CTxMemPool; class CTxUndo; -class CValidationState; class DisconnectedBlockTransactions; +class TxValidationState; struct ChainTxData; struct FlatFilePos; @@ -311,14 +312,12 @@ * occurred processing them. * @param[out] ppindex If set, the pointer will be set to point to the * last new block index object for the given headers. - * @param[out] first_invalid First header that fails validation, if one exists. * @return True if block headers were accepted as valid. */ bool ProcessNewBlockHeaders(const Config &config, const std::vector &block, - CValidationState &state, - const CBlockIndex **ppindex = nullptr, - CBlockHeader *first_invalid = nullptr) + BlockValidationState &state, + const CBlockIndex **ppindex = nullptr) LOCKS_EXCLUDED(cs_main); /** @@ -379,7 +378,7 @@ * validationinterface callback. */ bool ActivateBestChain( - const Config &config, CValidationState &state, + const Config &config, BlockValidationState &state, std::shared_ptr pblock = std::shared_ptr()); Amount GetBlockSubsidy(int nHeight, const Consensus::Params &consensusParams); @@ -412,9 +411,9 @@ * (try to) add transaction to memory pool */ bool AcceptToMemoryPool(const Config &config, CTxMemPool &pool, - CValidationState &state, const CTransactionRef &tx, - bool *pfMissingInputs, bool bypass_limits, - const Amount nAbsurdFee, bool test_accept = false) + TxValidationState &state, const CTransactionRef &tx, + bool bypass_limits, const Amount nAbsurdFee, + bool test_accept = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** @@ -486,7 +485,7 @@ * returned pvChecks must be executed exactly once in order to probe the limit * accurately. */ -bool CheckInputs(const CTransaction &tx, CValidationState &state, +bool CheckInputs(const CTransaction &tx, TxValidationState &state, const CCoinsViewCache &view, bool fScriptChecks, const uint32_t flags, bool sigCacheStore, bool scriptCacheStore, @@ -500,7 +499,7 @@ * Handy shortcut to full fledged CheckInputs call. */ static inline bool -CheckInputs(const CTransaction &tx, CValidationState &state, +CheckInputs(const CTransaction &tx, TxValidationState &state, const CCoinsViewCache &view, bool fScriptChecks, const uint32_t flags, bool sigCacheStore, bool scriptCacheStore, const PrecomputedTransactionData &txdata, int &nSigChecksOut) @@ -649,7 +648,7 @@ * Returns true if the provided block is valid (has valid header, * transactions are valid, block is a valid size, etc.) */ -bool CheckBlock(const CBlock &block, CValidationState &state, +bool CheckBlock(const CBlock &block, BlockValidationState &state, const Consensus::Params ¶ms, BlockValidationOptions validationOptions); @@ -661,14 +660,14 @@ */ bool ContextualCheckTransactionForCurrentBlock(const Consensus::Params ¶ms, const CTransaction &tx, - CValidationState &state, + TxValidationState &state, int flags = -1); /** * Check a block is completely valid from start to finish (only works on top of * our current best block) */ -bool TestBlockValidity(CValidationState &state, const CChainParams ¶ms, +bool TestBlockValidity(BlockValidationState &state, const CChainParams ¶ms, const CBlock &block, CBlockIndex *pindexPrev, BlockValidationOptions validationOptions) EXCLUSIVE_LOCKS_REQUIRED(cs_main); @@ -789,7 +788,7 @@ * anything besides checking if we need to prune. */ bool FlushStateToDisk(const CChainParams &chainparams, - CValidationState &state, FlushStateMode mode, + BlockValidationState &state, FlushStateMode mode, int nManualPruneHeight = 0); //! Unconditionally flush all changes to disk. @@ -800,7 +799,7 @@ void PruneAndFlush(); bool ActivateBestChain( - const Config &config, CValidationState &state, + const Config &config, BlockValidationState &state, std::shared_ptr pblock = std::shared_ptr()); /** @@ -809,11 +808,11 @@ * mapBlockIndex. */ bool AcceptBlockHeader(const Config &config, const CBlockHeader &block, - CValidationState &state, CBlockIndex **ppindex) + BlockValidationState &state, CBlockIndex **ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool AcceptBlock(const Config &config, const std::shared_ptr &pblock, - CValidationState &state, bool fRequested, + BlockValidationState &state, bool fRequested, const FlatFilePos *dbp, bool *fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main); @@ -821,21 +820,21 @@ DisconnectResult DisconnectBlock(const CBlock &block, const CBlockIndex *pindex, CCoinsViewCache &view); - bool ConnectBlock(const CBlock &block, CValidationState &state, + bool ConnectBlock(const CBlock &block, BlockValidationState &state, CBlockIndex *pindex, CCoinsViewCache &view, const CChainParams ¶ms, BlockValidationOptions options, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main); // Block disconnection on our pcoinsTip: - bool DisconnectTip(const CChainParams ¶ms, CValidationState &state, + bool DisconnectTip(const CChainParams ¶ms, BlockValidationState &state, DisconnectedBlockTransactions *disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main); // Manual block validity manipulation: - bool PreciousBlock(const Config &config, CValidationState &state, + bool PreciousBlock(const Config &config, BlockValidationState &state, CBlockIndex *pindex) LOCKS_EXCLUDED(cs_main); - bool UnwindBlock(const Config &config, CValidationState &state, + bool UnwindBlock(const Config &config, BlockValidationState &state, CBlockIndex *pindex, bool invalidate); void ResetBlockFailureFlags(CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main); @@ -864,12 +863,13 @@ bool IsInitialBlockDownload() const; private: - bool ActivateBestChainStep(const Config &config, CValidationState &state, + bool ActivateBestChainStep(const Config &config, + BlockValidationState &state, CBlockIndex *pindexMostWork, const std::shared_ptr &pblock, bool &fInvalidFound, ConnectTrace &connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - bool ConnectTip(const Config &config, CValidationState &state, + bool ConnectTip(const Config &config, BlockValidationState &state, CBlockIndex *pindexNew, const std::shared_ptr &pblock, ConnectTrace &connectTrace, @@ -889,7 +889,8 @@ */ void CheckBlockIndex(const Consensus::Params &consensusParams); - void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) + void InvalidBlockFound(CBlockIndex *pindex, + const BlockValidationState &state) EXCLUSIVE_LOCKS_REQUIRED(cs_main); CBlockIndex *FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main); void ReceivedBlockTransactions(const CBlock &block, CBlockIndex *pindexNew, @@ -906,23 +907,24 @@ * * May not be called in a validationinterface callback. */ -bool PreciousBlock(const Config &config, CValidationState &state, +bool PreciousBlock(const Config &config, BlockValidationState &state, CBlockIndex *pindex) LOCKS_EXCLUDED(cs_main); /** * Mark a block as finalized. * A finalized block can not be reorged in any way. */ -bool FinalizeBlockAndInvalidate(const Config &config, CValidationState &state, +bool FinalizeBlockAndInvalidate(const Config &config, + BlockValidationState &state, CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** Mark a block as invalid. */ -bool InvalidateBlock(const Config &config, CValidationState &state, +bool InvalidateBlock(const Config &config, BlockValidationState &state, CBlockIndex *pindex); /** Park a block. */ -bool ParkBlock(const Config &config, CValidationState &state, +bool ParkBlock(const Config &config, BlockValidationState &state, CBlockIndex *pindex); /** Remove invalidity status from a block and its descendants. */ diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -293,7 +293,7 @@ // Used to avoid mempool polluting consensus critical paths if CCoinsViewMempool // were somehow broken and returning the wrong scriptPubKeys static bool CheckInputsFromMempoolAndCache( - const CTransaction &tx, CValidationState &state, + const CTransaction &tx, TxValidationState &state, const CCoinsViewCache &view, const CTxMemPool &pool, const uint32_t flags, bool cacheSigStore, PrecomputedTransactionData &txdata, int &nSigChecksOut) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { @@ -339,13 +339,11 @@ * to optionally remove the cache additions if the associated transaction ends * up being rejected by the mempool. */ -static bool -AcceptToMemoryPoolWorker(const Config &config, CTxMemPool &pool, - CValidationState &state, const CTransactionRef &ptx, - bool *pfMissingInputs, int64_t nAcceptTime, - bool bypass_limits, const Amount nAbsurdFee, - std::vector &coins_to_uncache, - bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { +static bool AcceptToMemoryPoolWorker( + const Config &config, CTxMemPool &pool, TxValidationState &state, + const CTransactionRef &ptx, int64_t nAcceptTime, bool bypass_limits, + const Amount nAbsurdFee, std::vector &coins_to_uncache, + bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); const Consensus::Params &consensusParams = @@ -357,9 +355,6 @@ // mempool "read lock" (held through // GetMainSignals().TransactionAddedToMempool()) LOCK(pool.cs); - if (pfMissingInputs) { - *pfMissingInputs = false; - } // Coinbase is only valid in a block, not as a loose transaction. if (!CheckRegularTransaction(tx, state)) { @@ -370,27 +365,27 @@ // Rather not work on nonstandard transactions (unless -testnet) std::string reason; if (fRequireStandard && !IsStandardTx(tx, reason)) { - return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, + return state.Invalid(TxValidationResult::TX_NOT_STANDARD, REJECT_NONSTANDARD, reason); } // Only accept nLockTime-using transactions that can be mined in the next // block; we don't want our mempool filled up with transactions that can't // be mined yet. - CValidationState ctxState; + TxValidationState ctxState; if (!ContextualCheckTransactionForCurrentBlock( consensusParams, tx, ctxState, STANDARD_LOCKTIME_VERIFY_FLAGS)) { // We copy the state from a dummy to ensure we don't increase the // ban score of peer for transaction that could be valid in the future. - return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, + return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, REJECT_NONSTANDARD, ctxState.GetRejectReason(), ctxState.GetDebugMessage()); } // Is it already in the memory pool? if (pool.exists(txid)) { - return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, - REJECT_DUPLICATE, "txn-already-in-mempool"); + return state.Invalid(TxValidationResult::TX_CONFLICT, REJECT_DUPLICATE, + "txn-already-in-mempool"); } // Check for conflicts with in-memory transactions @@ -398,9 +393,8 @@ auto itConflicting = pool.mapNextTx.find(txin.prevout); if (itConflicting != pool.mapNextTx.end()) { // Disable replacement feature for good - return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, - false, REJECT_DUPLICATE, - "txn-mempool-conflict"); + return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, + REJECT_DUPLICATE, "txn-mempool-conflict"); } } @@ -428,29 +422,24 @@ // Optimistically just do efficient check of cache for // outputs. if (pcoinsTip->HaveCoinInCache(COutPoint(txid, out))) { - return state.Invalid( - ValidationInvalidReason::TX_CONFLICT, false, - REJECT_DUPLICATE, "txn-already-known"); + return state.Invalid(TxValidationResult::TX_CONFLICT, + REJECT_DUPLICATE, + "txn-already-known"); } } // Otherwise assume this might be an orphan tx for which we just // haven't seen parents yet. - if (pfMissingInputs) { - *pfMissingInputs = true; - } - - // fMissingInputs and !state.IsInvalid() is used to detect this - // condition, don't set state.Invalid() - return false; + return state.Invalid(TxValidationResult::TX_MISSING_INPUTS, + REJECT_INVALID, + "bad-txns-inputs-missingorspent"); } } // Are the actual inputs available? if (!view.HaveInputs(tx)) { - return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, - false, REJECT_DUPLICATE, - "bad-txns-inputs-spent"); + return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, + REJECT_DUPLICATE, "bad-txns-inputs-spent"); } // Bring the best block into scope. @@ -467,8 +456,8 @@ // own. if (!CheckSequenceLocks(pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp)) { - return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, - false, REJECT_NONSTANDARD, "non-BIP68-final"); + return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, + REJECT_NONSTANDARD, "non-BIP68-final"); } Amount nFees = Amount::zero(); @@ -484,8 +473,8 @@ // Check for non-standard pay-to-script-hash in inputs if (fRequireStandard && !AreInputsStandard(tx, view, nextBlockScriptVerifyFlags)) { - return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, - false, REJECT_NONSTANDARD, + return state.Invalid(TxValidationResult::TX_NOT_STANDARD, + REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs"); } @@ -511,14 +500,14 @@ // Do not change this to use virtualsize without coordinating a network // policy upgrade. if (!bypass_limits && nModifiedFees < minRelayTxFee.GetFee(nSize)) { - return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, - false, REJECT_INSUFFICIENTFEE, + return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, + REJECT_INSUFFICIENTFEE, "min relay fee not met"); } if (nAbsurdFee != Amount::zero() && nFees > nAbsurdFee) { - return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, - false, REJECT_HIGHFEE, "absurdly-high-fee", + return state.Invalid(TxValidationResult::TX_NOT_STANDARD, + REJECT_HIGHFEE, "absurdly-high-fee", strprintf("%d > %d", nFees, nAbsurdFee)); } @@ -530,7 +519,6 @@ if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true, false, txdata, nSigChecksStandard)) { // State filled in by CheckInputs. - assert(IsTransactionReason(state.GetReason())); return false; } @@ -547,8 +535,8 @@ if (!bypass_limits && mempoolRejectFee > Amount::zero() && nModifiedFees < mempoolRejectFee) { return state.Invalid( - ValidationInvalidReason::TX_MEMPOOL_POLICY, false, - REJECT_INSUFFICIENTFEE, "mempool min fee not met", + TxValidationResult::TX_MEMPOOL_POLICY, REJECT_INSUFFICIENTFEE, + "mempool min fee not met", strprintf("%d < %d", nModifiedFees, mempoolRejectFee)); } @@ -569,9 +557,9 @@ if (!pool.CalculateMemPoolAncestors( entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { - return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, - false, REJECT_NONSTANDARD, - "too-long-mempool-chain", errString); + return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, + REJECT_NONSTANDARD, "too-long-mempool-chain", + errString); } // Check again against the next block's script verification flags @@ -622,9 +610,8 @@ std::chrono::hours{ gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)}); if (!pool.exists(txid)) { - return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, - false, REJECT_INSUFFICIENTFEE, - "mempool full"); + return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, + REJECT_INSUFFICIENTFEE, "mempool full"); } } } @@ -638,15 +625,15 @@ */ static bool AcceptToMemoryPoolWithTime(const Config &config, CTxMemPool &pool, - CValidationState &state, const CTransactionRef &tx, - bool *pfMissingInputs, int64_t nAcceptTime, - bool bypass_limits, const Amount nAbsurdFee, - bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { + TxValidationState &state, const CTransactionRef &tx, + int64_t nAcceptTime, bool bypass_limits, + const Amount nAbsurdFee, bool test_accept) + EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); std::vector coins_to_uncache; - bool res = AcceptToMemoryPoolWorker( - config, pool, state, tx, pfMissingInputs, nAcceptTime, bypass_limits, - nAbsurdFee, coins_to_uncache, test_accept); + bool res = AcceptToMemoryPoolWorker(config, pool, state, tx, nAcceptTime, + bypass_limits, nAbsurdFee, + coins_to_uncache, test_accept); if (!res) { // Remove coins that were not present in the coins cache before calling // ATMPW; this is to prevent memory DoS in case we receive a large @@ -661,19 +648,18 @@ // After we've (potentially) uncached entries, ensure our coins cache is // still within its size limits - CValidationState stateDummy; + BlockValidationState stateDummy; ::ChainstateActive().FlushStateToDisk(config.GetChainParams(), stateDummy, FlushStateMode::PERIODIC); return res; } bool AcceptToMemoryPool(const Config &config, CTxMemPool &pool, - CValidationState &state, const CTransactionRef &tx, - bool *pfMissingInputs, bool bypass_limits, - const Amount nAbsurdFee, bool test_accept) { - return AcceptToMemoryPoolWithTime(config, pool, state, tx, pfMissingInputs, - GetTime(), bypass_limits, nAbsurdFee, - test_accept); + TxValidationState &state, const CTransactionRef &tx, + bool bypass_limits, const Amount nAbsurdFee, + bool test_accept) { + return AcceptToMemoryPoolWithTime(config, pool, state, tx, GetTime(), + bypass_limits, nAbsurdFee, test_accept); } /** @@ -965,8 +951,8 @@ } void CChainState::InvalidBlockFound(CBlockIndex *pindex, - const CValidationState &state) { - if (state.GetReason() != ValidationInvalidReason::BLOCK_MUTATED) { + const BlockValidationState &state) { + if (state.GetResult() != BlockValidationResult::BLOCK_MUTATED) { pindex->nStatus = pindex->nStatus.withFailed(); m_failed_blocks.insert(pindex); setDirtyBlockIndex.insert(pindex); @@ -1035,7 +1021,7 @@ return pindexPrev->nHeight + 1; } -bool CheckInputs(const CTransaction &tx, CValidationState &state, +bool CheckInputs(const CTransaction &tx, TxValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, const uint32_t flags, bool sigCacheStore, bool scriptCacheStore, @@ -1068,7 +1054,7 @@ if (!txLimitSigChecks.consume_and_check(nSigChecksOut) || (pBlockLimitSigChecks && !pBlockLimitSigChecks->consume_and_check(nSigChecksOut))) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, + return state.Invalid(TxValidationResult::TX_CONSENSUS, REJECT_INVALID, "too-many-sigchecks"); } return true; @@ -1112,8 +1098,7 @@ sigCacheStore, txdata); if (check2()) { return state.Invalid( - ValidationInvalidReason::TX_NOT_STANDARD, false, - REJECT_NONSTANDARD, + TxValidationResult::TX_NOT_STANDARD, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(scriptError))); } @@ -1122,14 +1107,14 @@ } // MANDATORY flag failures correspond to - // ValidationInvalidReason::CONSENSUS. Because CONSENSUS failures + // TxValidationResult::TX_CONSENSUS. Because CONSENSUS failures // are the most serious case of validation failures, we may need to // consider using RECENT_CONSENSUS_CHANGE for any script failure // that could be due to non-upgraded nodes which we may want to // support, to avoid splitting the network (but this depends on the // details of how net_processing handles such errors). return state.Invalid( - ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, + TxValidationResult::TX_CONSENSUS, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(scriptError))); } @@ -1225,7 +1210,8 @@ return false; } -static bool AbortNode(CValidationState &state, const std::string &strMessage, +static bool AbortNode(BlockValidationState &state, + const std::string &strMessage, const std::string &userMessage = "") { AbortNode(strMessage, userMessage); return state.Error(strMessage); @@ -1362,11 +1348,12 @@ } } -static bool FindUndoPos(CValidationState &state, int nFile, FlatFilePos &pos, - unsigned int nAddSize); +static bool FindUndoPos(BlockValidationState &state, int nFile, + FlatFilePos &pos, unsigned int nAddSize); static bool WriteUndoDataForBlock(const CBlockUndo &blockundo, - CValidationState &state, CBlockIndex *pindex, + BlockValidationState &state, + CBlockIndex *pindex, const CChainParams &chainparams) { // Write undo information to disk if (pindex->GetUndoPos().IsNull()) { @@ -1502,7 +1489,7 @@ * done; ConnectBlock() can fail if those validity checks fail (among other * reasons). */ -bool CChainState::ConnectBlock(const CBlock &block, CValidationState &state, +bool CChainState::ConnectBlock(const CBlock &block, BlockValidationState &state, CBlockIndex *pindex, CCoinsViewCache &view, const CChainParams ¶ms, BlockValidationOptions options, @@ -1529,7 +1516,7 @@ if (!CheckBlock(block, state, consensusParams, options.withCheckPoW(!fJustCheck) .withCheckMerkleRoot(!fJustCheck))) { - if (state.GetReason() == ValidationInvalidReason::BLOCK_MUTATED) { + if (state.GetResult() == BlockValidationResult::BLOCK_MUTATED) { // We don't write down blocks to disk if they may have been // corrupted, so this should be impossible unless we're having // hardware problems. @@ -1643,10 +1630,10 @@ for (const auto &tx : block.vtx) { for (size_t o = 0; o < tx->vout.size(); o++) { if (view.HaveCoin(COutPoint(tx->GetId(), o))) { - return state.Invalid( - ValidationInvalidReason::CONSENSUS, - error("ConnectBlock(): tried to overwrite transaction"), - REJECT_INVALID, "bad-txns-BIP30"); + LogPrintf("ERROR: ConnectBlock(): tried to overwrite " + "transaction\n"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, + REJECT_INVALID, "bad-txns-BIP30"); } } } @@ -1702,10 +1689,9 @@ // disk, and older versions may have saved a weird block. // - its checks are not applied to pre-CTOR chains, which we might visit // with checkpointing off. - return state.Invalid( - ValidationInvalidReason::CONSENSUS, - error("ConnectBlock(): tried to overwrite transaction"), - REJECT_INVALID, "tx-duplicate"); + LogPrintf("ERROR: ConnectBlock(): tried to overwrite transaction\n"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, + REJECT_INVALID, "tx-duplicate"); } size_t txIndex = 0; @@ -1714,28 +1700,31 @@ const bool isCoinBase = tx.IsCoinBase(); nInputs += tx.vin.size(); - Amount txfee = Amount::zero(); - if (!isCoinBase && !Consensus::CheckTxInputs(tx, state, view, - pindex->nHeight, txfee)) { - if (!IsBlockReason(state.GetReason())) { - // CheckTxInputs may return MISSING_INPUTS or PREMATURE_SPEND - // but we can't return that, as it's not defined for a block, so - // we reset the reason flag to CONSENSUS here. - state.Invalid(ValidationInvalidReason::CONSENSUS, false, - state.GetRejectCode(), state.GetRejectReason(), - state.GetDebugMessage()); + { + Amount txfee = Amount::zero(); + TxValidationState tx_state; + if (!isCoinBase && + !Consensus::CheckTxInputs(tx, tx_state, view, pindex->nHeight, + txfee)) { + // Any transaction validation failure in ConnectBlock is a block + // consensus failure. + state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, + tx_state.GetRejectCode(), + tx_state.GetRejectReason(), + tx_state.GetDebugMessage()); + + return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, + tx.GetId().ToString(), FormatStateMessage(state)); } - - return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, - tx.GetId().ToString(), FormatStateMessage(state)); + nFees += txfee; } - nFees += txfee; + if (!MoneyRange(nFees)) { - return state.Invalid( - ValidationInvalidReason::CONSENSUS, - error("%s: accumulated fee in the block out of range.", - __func__), - REJECT_INVALID, "bad-txns-accumulated-fee-outofrange"); + LogPrintf("ERROR: %s: accumulated fee in the block out of range.\n", + __func__); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, + REJECT_INVALID, + "bad-txns-accumulated-fee-outofrange"); } // The following checks do not apply to the coinbase. @@ -1752,10 +1741,10 @@ } if (!SequenceLocks(tx, nLockTimeFlags, &prevheights, *pindex)) { - return state.Invalid( - ValidationInvalidReason::CONSENSUS, - error("%s: contains a non-BIP68-final transaction", __func__), - REJECT_INVALID, "bad-txns-nonfinal"); + LogPrintf("ERROR: %s: contains a non-BIP68-final transaction\n", + __func__); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, + REJECT_INVALID, "bad-txns-nonfinal"); } // Don't cache results if we're actually connecting blocks (still @@ -1774,21 +1763,17 @@ // nSigChecksRet may be accurate (found in cache) or 0 (checks were // deferred into vChecks). int nSigChecksRet; - if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, - fCacheResults, PrecomputedTransactionData(tx), - nSigChecksRet, nSigChecksTxLimiters[txIndex], - &nSigChecksBlockLimiter, &vChecks)) { - if (state.GetReason() == ValidationInvalidReason::TX_NOT_STANDARD) { - // CheckInputs may return NOT_STANDARD for extra flags we - // passed, but we can't return that, as it's not defined for a - // block, so we reset the reason flag to CONSENSUS here. In the - // event of a future soft-fork, we may need to consider whether - // rewriting to CONSENSUS or RECENT_CONSENSUS_CHANGE would be - // more appropriate. - state.Invalid(ValidationInvalidReason::CONSENSUS, false, - state.GetRejectCode(), state.GetRejectReason(), - state.GetDebugMessage()); - } + TxValidationState tx_state; + if (!CheckInputs(tx, tx_state, view, fScriptChecks, flags, + fCacheResults, fCacheResults, + PrecomputedTransactionData(tx), nSigChecksRet, + nSigChecksTxLimiters[txIndex], &nSigChecksBlockLimiter, + &vChecks)) { + // Any transaction validation failure in ConnectBlock is a block + // consensus failure + state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, + state.GetRejectCode(), state.GetRejectReason(), + state.GetDebugMessage()); return error("ConnectBlock(): CheckInputs on %s failed with %s", tx.GetId().ToString(), FormatStateMessage(state)); } @@ -1817,10 +1802,10 @@ Amount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, consensusParams); if (block.vtx[0]->GetValueOut() > blockReward) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, - error("ConnectBlock(): coinbase pays too much " - "(actual=%d vs limit=%d)", - block.vtx[0]->GetValueOut(), blockReward), + LogPrintf("ERROR: ConnectBlock(): coinbase pays too much (actual=%d vs " + "limit=%d)\n", + block.vtx[0]->GetValueOut(), blockReward); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, REJECT_INVALID, "bad-cb-amount"); } @@ -1848,14 +1833,14 @@ } // We did not find an output that match the miner fund requirements. - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, REJECT_INVALID, "bad-cb-minerfund"); } MinerFundSuccess: if (!control.Wait()) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, REJECT_INVALID, "blk-bad-inputs", "parallel script check failed"); } @@ -1902,7 +1887,8 @@ } bool CChainState::FlushStateToDisk(const CChainParams &chainparams, - CValidationState &state, FlushStateMode mode, + BlockValidationState &state, + FlushStateMode mode, int nManualPruneHeight) { int64_t nMempoolUsage = g_mempool.DynamicMemoryUsage(); LOCK(cs_main); @@ -2048,7 +2034,7 @@ } void CChainState::ForceFlushStateToDisk() { - CValidationState state; + BlockValidationState state; const CChainParams &chainparams = Params(); if (!this->FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) { LogPrintf("%s: failed to flush state (%s)\n", __func__, @@ -2057,7 +2043,7 @@ } void CChainState::PruneAndFlush() { - CValidationState state; + BlockValidationState state; fCheckForPruning = true; const CChainParams &chainparams = Params(); if (!this->FlushStateToDisk(chainparams, state, FlushStateMode::NONE)) { @@ -2101,7 +2087,7 @@ * in any case). */ bool CChainState::DisconnectTip(const CChainParams ¶ms, - CValidationState &state, + BlockValidationState &state, DisconnectedBlockTransactions *disconnectpool) { AssertLockHeld(cs_main); CBlockIndex *pindexDelete = m_chain.Tip(); @@ -2247,26 +2233,27 @@ } }; -static bool FinalizeBlockInternal(const Config &config, CValidationState &state, +static bool FinalizeBlockInternal(const Config &config, + BlockValidationState &state, const CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); if (pindex->nStatus.isInvalid()) { // We try to finalize an invalid block. - return state.Invalid(ValidationInvalidReason::CACHED_INVALID, - error("%s: Trying to finalize invalid block %s", - __func__, pindex->GetBlockHash().ToString()), + LogPrintf("ERROR: %s: Trying to finalize invalid block %s\n", __func__, + pindex->GetBlockHash().ToString()); + return state.Invalid(BlockValidationResult::BLOCK_CACHED_INVALID, REJECT_INVALID, "finalize-invalid-block"); } // Check that the request is consistent with current finalization. if (pindexFinalized && !AreOnTheSameFork(pindex, pindexFinalized)) { - return state.Invalid( - ValidationInvalidReason::BLOCK_FINALIZATION, - error("%s: Trying to finalize block %s which conflicts " - "with already finalized block", - __func__, pindex->GetBlockHash().ToString()), - REJECT_AGAINST_FINALIZED, "bad-fork-prior-finalized"); + LogPrintf("ERROR: %s: Trying to finalize block %s which conflicts with " + "already finalized block\n", + __func__, pindex->GetBlockHash().ToString()); + return state.Invalid(BlockValidationResult::BLOCK_FINALIZATION, + REJECT_AGAINST_FINALIZED, + "bad-fork-prior-finalized"); } if (IsBlockFinalized(pindex)) { @@ -2335,7 +2322,7 @@ * by copying pblock) - if that is not intended, care must be taken to remove * the last entry in blocksConnected in case of failure. */ -bool CChainState::ConnectTip(const Config &config, CValidationState &state, +bool CChainState::ConnectTip(const Config &config, BlockValidationState &state, CBlockIndex *pindexNew, const std::shared_ptr &pblock, ConnectTrace &connectTrace, @@ -2641,9 +2628,9 @@ * pindexMostWork. */ bool CChainState::ActivateBestChainStep( - const Config &config, CValidationState &state, CBlockIndex *pindexMostWork, - const std::shared_ptr &pblock, bool &fInvalidFound, - ConnectTrace &connectTrace) { + const Config &config, BlockValidationState &state, + CBlockIndex *pindexMostWork, const std::shared_ptr &pblock, + bool &fInvalidFound, ConnectTrace &connectTrace) { AssertLockHeld(cs_main); const CBlockIndex *pindexOldTip = m_chain.Tip(); @@ -2696,12 +2683,11 @@ connectTrace, disconnectpool)) { if (state.IsInvalid()) { // The block violates a consensus rule. - if (state.GetReason() != - ValidationInvalidReason::BLOCK_MUTATED) { + if (state.GetResult() != + BlockValidationResult::BLOCK_MUTATED) { InvalidChainFound(vpindexToConnect.back()); } - - state = CValidationState(); + state = BlockValidationState(); fInvalidFound = true; fContinue = false; break; @@ -2780,7 +2766,7 @@ * call may be quite long during reindexing or a substantial reorg. */ bool CChainState::ActivateBestChain(const Config &config, - CValidationState &state, + BlockValidationState &state, std::shared_ptr pblock) { // Note that while we're often called here from ProcessNewBlock, this is // far from a guarantee. Things in the P2P/RPC will often end up calling @@ -2916,13 +2902,14 @@ return true; } -bool ActivateBestChain(const Config &config, CValidationState &state, +bool ActivateBestChain(const Config &config, BlockValidationState &state, std::shared_ptr pblock) { return ::ChainstateActive().ActivateBestChain(config, state, std::move(pblock)); } -bool CChainState::PreciousBlock(const Config &config, CValidationState &state, +bool CChainState::PreciousBlock(const Config &config, + BlockValidationState &state, CBlockIndex *pindex) { { LOCK(cs_main); @@ -2960,12 +2947,12 @@ return ActivateBestChain(config, state); } -bool PreciousBlock(const Config &config, CValidationState &state, +bool PreciousBlock(const Config &config, BlockValidationState &state, CBlockIndex *pindex) { return ::ChainstateActive().PreciousBlock(config, state, pindex); } -bool CChainState::UnwindBlock(const Config &config, CValidationState &state, +bool CChainState::UnwindBlock(const Config &config, BlockValidationState &state, CBlockIndex *pindex, bool invalidate) { CBlockIndex *to_mark_failed_or_parked = pindex; bool pindex_was_in_chain = false; @@ -3086,7 +3073,8 @@ return true; } -bool FinalizeBlockAndInvalidate(const Config &config, CValidationState &state, +bool FinalizeBlockAndInvalidate(const Config &config, + BlockValidationState &state, CBlockIndex *pindex) { AssertLockHeld(cs_main); if (!FinalizeBlockInternal(config, state, pindex)) { @@ -3111,12 +3099,12 @@ return true; } -bool InvalidateBlock(const Config &config, CValidationState &state, +bool InvalidateBlock(const Config &config, BlockValidationState &state, CBlockIndex *pindex) { return ::ChainstateActive().UnwindBlock(config, state, pindex, true); } -bool ParkBlock(const Config &config, CValidationState &state, +bool ParkBlock(const Config &config, BlockValidationState &state, CBlockIndex *pindex) { return ::ChainstateActive().UnwindBlock(config, state, pindex, false); } @@ -3395,8 +3383,8 @@ return true; } -static bool FindUndoPos(CValidationState &state, int nFile, FlatFilePos &pos, - unsigned int nAddSize) { +static bool FindUndoPos(BlockValidationState &state, int nFile, + FlatFilePos &pos, unsigned int nAddSize) { pos.nFile = nFile; LOCK(cs_LastBlockFile); @@ -3427,21 +3415,22 @@ * Do not call this for any check that depends on the context. * For context-dependent calls, see ContextualCheckBlockHeader. */ -static bool CheckBlockHeader(const CBlockHeader &block, CValidationState &state, +static bool CheckBlockHeader(const CBlockHeader &block, + BlockValidationState &state, const Consensus::Params ¶ms, BlockValidationOptions validationOptions) { // Check proof of work matches claimed amount if (validationOptions.shouldValidatePoW() && !CheckProofOfWork(block.GetHash(), block.nBits, params)) { - return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_HEADER, - false, REJECT_INVALID, "high-hash", + return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, + REJECT_INVALID, "high-hash", "proof of work failed"); } return true; } -bool CheckBlock(const CBlock &block, CValidationState &state, +bool CheckBlock(const CBlock &block, BlockValidationState &state, const Consensus::Params ¶ms, BlockValidationOptions validationOptions) { // These are checks that are independent of context. @@ -3460,7 +3449,7 @@ bool mutated; uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated); if (block.hashMerkleRoot != hashMerkleRoot2) { - return state.Invalid(ValidationInvalidReason::BLOCK_MUTATED, false, + return state.Invalid(BlockValidationResult::BLOCK_MUTATED, REJECT_INVALID, "bad-txnmrklroot", "hashMerkleRoot mismatch"); } @@ -3469,7 +3458,7 @@ // sequences of transactions in a block without affecting the merkle // root of a block, while still invalidating it. if (mutated) { - return state.Invalid(ValidationInvalidReason::BLOCK_MUTATED, false, + return state.Invalid(BlockValidationResult::BLOCK_MUTATED, REJECT_INVALID, "bad-txns-duplicate", "duplicate transaction"); } @@ -3481,7 +3470,7 @@ // First transaction must be coinbase. if (block.vtx.empty()) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, REJECT_INVALID, "bad-cb-missing", "first tx is not coinbase"); } @@ -3491,37 +3480,39 @@ // Bail early if there is no way this block is of reasonable size. if ((block.vtx.size() * MIN_TRANSACTION_SIZE) > nMaxBlockSize) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, REJECT_INVALID, "bad-blk-length", "size limits failed"); } auto currentBlockSize = ::GetSerializeSize(block, PROTOCOL_VERSION); if (currentBlockSize > nMaxBlockSize) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, REJECT_INVALID, "bad-blk-length", "size limits failed"); } // And a valid coinbase. - if (!CheckCoinbase(*block.vtx[0], state)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, - state.GetRejectCode(), state.GetRejectReason(), + TxValidationState tx_state; + if (!CheckCoinbase(*block.vtx[0], tx_state)) { + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, + tx_state.GetRejectCode(), + tx_state.GetRejectReason(), strprintf("Coinbase check failed (txid %s) %s", block.vtx[0]->GetId().ToString(), - state.GetDebugMessage())); + tx_state.GetDebugMessage())); } // Check transactions for regularity, skipping the first. Note that this // is the first time we check that all after the first are !IsCoinBase. for (size_t i = 1; i < block.vtx.size(); i++) { auto *tx = block.vtx[i].get(); - if (!CheckRegularTransaction(*tx, state)) { + if (!CheckRegularTransaction(*tx, tx_state)) { return state.Invalid( - ValidationInvalidReason::CONSENSUS, false, - state.GetRejectCode(), state.GetRejectReason(), + BlockValidationResult::BLOCK_CONSENSUS, + tx_state.GetRejectCode(), tx_state.GetRejectReason(), strprintf("Transaction check failed (txid %s) %s", - tx->GetId().ToString(), state.GetDebugMessage())); + tx->GetId().ToString(), tx_state.GetDebugMessage())); } } @@ -3543,10 +3534,11 @@ * in ConnectBlock(). * Note that -reindex-chainstate skips the validation that happens here! */ -static bool -ContextualCheckBlockHeader(const CChainParams ¶ms, - const CBlockHeader &block, CValidationState &state, - const CBlockIndex *pindexPrev, int64_t nAdjustedTime) +static bool ContextualCheckBlockHeader(const CChainParams ¶ms, + const CBlockHeader &block, + BlockValidationState &state, + const CBlockIndex *pindexPrev, + int64_t nAdjustedTime) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { assert(pindexPrev != nullptr); const int nHeight = pindexPrev->nHeight + 1; @@ -3556,8 +3548,8 @@ if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) { LogPrintf("bad bits after height: %d\n", pindexPrev->nHeight); - return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_HEADER, - false, REJECT_INVALID, "bad-diffbits", + return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, + REJECT_INVALID, "bad-diffbits", "incorrect proof of work"); } @@ -3568,11 +3560,10 @@ // Check that the block chain matches the known block chain up to a // checkpoint. if (!Checkpoints::CheckBlock(checkpoints, nHeight, block.GetHash())) { - return state.Invalid( - ValidationInvalidReason::BLOCK_CHECKPOINT, - error("%s: rejected by checkpoint lock-in at %d", __func__, - nHeight), - REJECT_CHECKPOINT, "checkpoint mismatch"); + LogPrintf("ERROR: %s: rejected by checkpoint lock-in at %d\n", + __func__, nHeight); + return state.Invalid(BlockValidationResult::BLOCK_CHECKPOINT, + REJECT_CHECKPOINT, "checkpoint mismatch"); } // Don't accept any forks from the main chain prior to last checkpoint. @@ -3580,24 +3571,25 @@ // in our MapBlockIndex. CBlockIndex *pcheckpoint = Checkpoints::GetLastCheckpoint(checkpoints); if (pcheckpoint && nHeight < pcheckpoint->nHeight) { - return state.Invalid( - ValidationInvalidReason::BLOCK_CHECKPOINT, - error("%s: forked chain older than last checkpoint (height %d)", - __func__, nHeight), - REJECT_CHECKPOINT, "bad-fork-prior-to-checkpoint"); + LogPrintf("ERROR: %s: forked chain older than last checkpoint " + "(height %d)\n", + __func__, nHeight); + return state.Invalid(BlockValidationResult::BLOCK_CHECKPOINT, + REJECT_CHECKPOINT, + "bad-fork-prior-to-checkpoint"); } } // Check timestamp against prev if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) { - return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_HEADER, - false, REJECT_INVALID, "time-too-old", + return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, + REJECT_INVALID, "time-too-old", "block's timestamp is too early"); } // Check timestamp if (block.GetBlockTime() > nAdjustedTime + MAX_FUTURE_BLOCK_TIME) { - return state.Invalid(ValidationInvalidReason::BLOCK_TIME_FUTURE, false, + return state.Invalid(BlockValidationResult::BLOCK_TIME_FUTURE, REJECT_INVALID, "time-too-new", "block timestamp too far in the future"); } @@ -3609,8 +3601,8 @@ (block.nVersion < 3 && nHeight >= consensusParams.BIP66Height) || (block.nVersion < 4 && nHeight >= consensusParams.BIP65Height)) { return state.Invalid( - ValidationInvalidReason::BLOCK_INVALID_HEADER, false, - REJECT_OBSOLETE, strprintf("bad-version(0x%08x)", block.nVersion), + BlockValidationResult::BLOCK_INVALID_HEADER, REJECT_OBSOLETE, + strprintf("bad-version(0x%08x)", block.nVersion), strprintf("rejected nVersion=0x%08x block", block.nVersion)); } @@ -3619,7 +3611,7 @@ bool ContextualCheckTransactionForCurrentBlock(const Consensus::Params ¶ms, const CTransaction &tx, - CValidationState &state, + TxValidationState &state, int flags) { AssertLockHeld(cs_main); @@ -3662,7 +3654,8 @@ * in ConnectBlock(). * Note that -reindex-chainstate skips the validation that happens here! */ -static bool ContextualCheckBlock(const CBlock &block, CValidationState &state, +static bool ContextualCheckBlock(const CBlock &block, + BlockValidationState &state, const Consensus::Params ¶ms, const CBlockIndex *pindexPrev) { const int nHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1; @@ -3697,14 +3690,14 @@ if (fIsMagneticAnomalyEnabled) { if (prevTx && (tx.GetId() <= prevTx->GetId())) { if (tx.GetId() == prevTx->GetId()) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, - false, REJECT_INVALID, "tx-duplicate", + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, + REJECT_INVALID, "tx-duplicate", strprintf("Duplicated transaction %s", tx.GetId().ToString())); } return state.Invalid( - ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, + BlockValidationResult::BLOCK_CONSENSUS, REJECT_INVALID, "tx-ordering", strprintf("Transaction order is invalid (%s < %s)", tx.GetId().ToString(), @@ -3716,10 +3709,12 @@ } } - if (!ContextualCheckTransaction(params, tx, state, nHeight, + TxValidationState tx_state; + if (!ContextualCheckTransaction(params, tx, tx_state, nHeight, nLockTimeCutoff, nMedianTimePast)) { - // state set by ContextualCheckTransaction. - return false; + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, + REJECT_INVALID, tx_state.GetRejectReason(), + tx_state.GetDebugMessage()); } } @@ -3729,7 +3724,7 @@ if (block.vtx[0]->vin[0].scriptSig.size() < expect.size() || !std::equal(expect.begin(), expect.end(), block.vtx[0]->vin[0].scriptSig.begin())) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, REJECT_INVALID, "bad-cb-height", "block height mismatch in coinbase"); } @@ -3745,7 +3740,7 @@ */ bool CChainState::AcceptBlockHeader(const Config &config, const CBlockHeader &block, - CValidationState &state, + BlockValidationState &state, CBlockIndex **ppindex) { AssertLockHeld(cs_main); const CChainParams &chainparams = config.GetChainParams(); @@ -3763,10 +3758,11 @@ } if (pindex->nStatus.isInvalid()) { - return state.Invalid(ValidationInvalidReason::CACHED_INVALID, - error("%s: block %s is marked invalid", - __func__, hash.ToString()), - 0, "duplicate"); + LogPrintf("ERROR: %s: block %s is marked invalid\n", __func__, + hash.ToString()); + return state.Invalid( + BlockValidationResult::BLOCK_CACHED_INVALID, 0, + "duplicate"); } return true; @@ -3781,16 +3777,16 @@ // Get prev block index BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); if (mi == mapBlockIndex.end()) { - return state.Invalid(ValidationInvalidReason::BLOCK_MISSING_PREV, - error("%s: prev block not found", __func__), 0, + LogPrintf("ERROR: %s: prev block not found\n", __func__); + return state.Invalid(BlockValidationResult::BLOCK_MISSING_PREV, 0, "prev-blk-not-found"); } CBlockIndex *pindexPrev = (*mi).second; assert(pindexPrev); if (pindexPrev->nStatus.isInvalid()) { - return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_PREV, - error("%s: prev block invalid", __func__), + LogPrintf("ERROR: %s: prev block invalid\n", __func__); + return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, REJECT_INVALID, "bad-prevblk"); } @@ -3835,9 +3831,9 @@ setDirtyBlockIndex.insert(invalid_walk); invalid_walk = invalid_walk->pprev; } + LogPrintf("ERROR: %s: prev block invalid\n", __func__); return state.Invalid( - ValidationInvalidReason::BLOCK_INVALID_PREV, - error("%s: prev block invalid", __func__), + BlockValidationResult::BLOCK_INVALID_PREV, REJECT_INVALID, "bad-prevblk"); } } @@ -3859,13 +3855,8 @@ // Exposed wrapper for AcceptBlockHeader bool ProcessNewBlockHeaders(const Config &config, const std::vector &headers, - CValidationState &state, - const CBlockIndex **ppindex, - CBlockHeader *first_invalid) { - if (first_invalid != nullptr) { - first_invalid->SetNull(); - } - + BlockValidationState &state, + const CBlockIndex **ppindex) { { LOCK(cs_main); for (const CBlockHeader &header : headers) { @@ -3873,9 +3864,6 @@ CBlockIndex *pindex = nullptr; if (!::ChainstateActive().AcceptBlockHeader(config, header, state, &pindex)) { - if (first_invalid) { - *first_invalid = header; - } return false; } @@ -3928,7 +3916,7 @@ */ bool CChainState::AcceptBlock(const Config &config, const std::shared_ptr &pblock, - CValidationState &state, bool fRequested, + BlockValidationState &state, bool fRequested, const FlatFilePos *dbp, bool *fNewBlock) { AssertLockHeld(cs_main); @@ -4021,9 +4009,8 @@ if (!CheckBlock(block, state, consensusParams, BlockValidationOptions(config)) || !ContextualCheckBlock(block, state, consensusParams, pindex->pprev)) { - assert(IsBlockReason(state.GetReason())); if (state.IsInvalid() && - state.GetReason() != ValidationInvalidReason::BLOCK_MUTATED) { + state.GetResult() != BlockValidationResult::BLOCK_MUTATED) { pindex->nStatus = pindex->nStatus.withFailed(); setDirtyBlockIndex.insert(pindex); } @@ -4090,7 +4077,7 @@ *fNewBlock = false; } - CValidationState state; + BlockValidationState state; // CheckBlock() does not support multi-threaded block validation // because CBlock::fChecked can cause data race. @@ -4119,7 +4106,7 @@ NotifyHeaderTip(); // Only used to report errors, not invalidity - ignore it - CValidationState state; + BlockValidationState state; if (!::ChainstateActive().ActivateBestChain(config, state, pblock)) { return error("%s: ActivateBestChain failed (%s)", __func__, FormatStateMessage(state)); @@ -4128,7 +4115,7 @@ return true; } -bool TestBlockValidity(CValidationState &state, const CChainParams ¶ms, +bool TestBlockValidity(BlockValidationState &state, const CChainParams ¶ms, const CBlock &block, CBlockIndex *pindexPrev, BlockValidationOptions validationOptions) { AssertLockHeld(cs_main); @@ -4265,7 +4252,7 @@ /* This function is called from the RPC code for pruneblockchain */ void PruneBlockFilesManual(int nManualPruneHeight) { - CValidationState state; + BlockValidationState state; const CChainParams &chainparams = Params(); if (!::ChainstateActive().FlushStateToDisk( chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) { @@ -4598,7 +4585,7 @@ CBlockIndex *pindex; CBlockIndex *pindexFailure = nullptr; int nGoodTransactions = 0; - CValidationState state; + BlockValidationState state; int reportDone = 0; LogPrintfToBeContinued("[0%%]..."); for (pindex = ::ChainActive().Tip(); pindex && pindex->pprev; @@ -5044,7 +5031,7 @@ // process in case the block isn't known yet CBlockIndex *pindex = LookupBlockIndex(hash); if (!pindex || !pindex->nStatus.hasData()) { - CValidationState state; + BlockValidationState state; if (::ChainstateActive().AcceptBlock( config, pblock, state, true, dbp, nullptr)) { nLoaded++; @@ -5065,7 +5052,7 @@ // Activate the genesis block so normal node progress can // continue if (hash == chainparams.GetConsensus().hashGenesisBlock) { - CValidationState state; + BlockValidationState state; if (!ActivateBestChain(config, state)) { break; } @@ -5096,7 +5083,7 @@ __func__, pblockrecursive->GetHash().ToString(), head.ToString()); LOCK(cs_main); - CValidationState dummy; + BlockValidationState dummy; if (::ChainstateActive().AcceptBlock( config, pblockrecursive, dummy, true, &it->second, nullptr)) { @@ -5530,12 +5517,11 @@ if (amountdelta != Amount::zero()) { pool.PrioritiseTransaction(tx->GetId(), amountdelta); } - CValidationState state; + TxValidationState state; if (nTime + nExpiryTimeout > nNow) { LOCK(cs_main); AcceptToMemoryPoolWithTime( - config, pool, state, tx, nullptr /* pfMissingInputs */, - nTime, false /* bypass_limits */, + config, pool, state, tx, nTime, false /* bypass_limits */, Amount::zero() /* nAbsurdFee */, false /* test_accept */); if (state.IsValid()) { ++count; diff --git a/src/validationinterface.h b/src/validationinterface.h --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -13,13 +13,13 @@ #include extern RecursiveMutex cs_main; +class BlockValidationState; class CBlock; class CBlockIndex; struct CBlockLocator; class CBlockIndex; class CConnman; class CValidationInterface; -class CValidationState; class uint256; class CScheduler; @@ -150,11 +150,11 @@ virtual void ChainStateFlushed(const CBlockLocator &locator) {} /** * Notifies listeners of a block validation result. - * If the provided CValidationState IsValid, the provided block + * If the provided BlockValidationState IsValid, the provided block * is guaranteed to be the current best block at the time the * callback was generated (not necessarily now) */ - virtual void BlockChecked(const CBlock &, const CValidationState &) {} + virtual void BlockChecked(const CBlock &, const BlockValidationState &) {} /** * Notifies listeners that a block which builds directly on our current tip * has been received and connected to the headers tree, though not validated @@ -203,7 +203,7 @@ const std::shared_ptr> &); void BlockDisconnected(const std::shared_ptr &); void ChainStateFlushed(const CBlockLocator &); - void BlockChecked(const CBlock &, const CValidationState &); + void BlockChecked(const CBlock &, const BlockValidationState &); void NewPoWValidBlock(const CBlockIndex *, const std::shared_ptr &); }; diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -259,7 +259,7 @@ } void CMainSignals::BlockChecked(const CBlock &block, - const CValidationState &state) { + const BlockValidationState &state) { LOG_EVENT("%s: block hash=%s state=%s", __func__, block.GetHash().ToString(), FormatStateMessage(state)); m_internals->Iterate([&](CValidationInterface &callbacks) { diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -373,7 +373,7 @@ } throw JSONRPCError(RPC_WALLET_ERROR, strError); } - CValidationState state; + TxValidationState state; if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, state)) { strError = @@ -695,7 +695,7 @@ for (const std::pair &pairWtx : pwallet->mapWallet) { const CWalletTx &wtx = pairWtx.second; - CValidationState state; + TxValidationState state; if (wtx.IsCoinBase() || !locked_chain->contextualCheckTransactionForCurrentBlock( config.GetChainParams().GetConsensus(), *wtx.tx, state)) { @@ -769,7 +769,7 @@ Amount nAmount = Amount::zero(); for (const std::pair &pairWtx : pwallet->mapWallet) { const CWalletTx &wtx = pairWtx.second; - CValidationState state; + TxValidationState state; if (wtx.IsCoinBase() || !locked_chain->contextualCheckTransactionForCurrentBlock( config.GetChainParams().GetConsensus(), *wtx.tx, state)) { @@ -1062,7 +1062,7 @@ if (!fCreated) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); } - CValidationState state; + TxValidationState state; if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, state)) { strFailReason = strprintf("Transaction commit failed:: %s", @@ -1210,7 +1210,7 @@ for (const std::pair &pairWtx : pwallet->mapWallet) { const CWalletTx &wtx = pairWtx.second; - CValidationState state; + TxValidationState state; if (wtx.IsCoinBase() || !locked_chain.contextualCheckTransactionForCurrentBlock( config.GetChainParams().GetConsensus(), *wtx.tx, state)) { diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -401,7 +401,7 @@ BOOST_CHECK(wallet->CreateTransaction( *locked_chain, {recipient}, tx, fee, changePos, error, dummy)); } - CValidationState state; + TxValidationState state; BOOST_CHECK(wallet->CommitTransaction(tx, {}, {}, state)); CMutableTransaction blocktx; { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1305,7 +1305,7 @@ bool CommitTransaction( CTransactionRef tx, mapValue_t mapValue, std::vector> orderForm, - CValidationState &state); + TxValidationState &state); bool DummySignTx(CMutableTransaction &txNew, const std::set &txouts, bool use_max_sig = false) const { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2510,7 +2510,7 @@ bool CWalletTx::IsTrusted(interfaces::Chain::Lock &locked_chain) const { // Quick answer in most cases - CValidationState state; + TxValidationState state; if (!locked_chain.contextualCheckTransactionForCurrentBlock( Params().GetConsensus(), *tx, state)) { return false; @@ -2714,7 +2714,7 @@ const TxId &wtxid = entry.first; const CWalletTx &wtx = entry.second; - CValidationState state; + TxValidationState state; if (!locked_chain.contextualCheckTransactionForCurrentBlock( params, *wtx.tx, state)) { continue; @@ -3659,7 +3659,7 @@ bool CWallet::CommitTransaction( CTransactionRef tx, mapValue_t mapValue, std::vector> orderForm, - CValidationState &state) { + TxValidationState &state) { auto locked_chain = chain().lock(); LOCK(cs_wallet); diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -215,7 +215,7 @@ ssKey >> txid; CWalletTx wtx(nullptr /* pwallet */, MakeTransactionRef()); ssValue >> wtx; - CValidationState state; + TxValidationState state; bool isValid = wtx.IsCoinBase() ? CheckCoinbase(*wtx.tx, state) : CheckRegularTransaction(*wtx.tx, state); diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py --- a/test/functional/rpc_rawtransaction.py +++ b/test/functional/rpc_rawtransaction.py @@ -210,8 +210,10 @@ rawtx = self.nodes[2].signrawtransactionwithwallet(rawtx) # This will raise an exception since there are missing inputs - assert_raises_rpc_error( - -25, "Missing inputs", self.nodes[2].sendrawtransaction, rawtx['hex']) + assert_raises_rpc_error(-25, + "bad-txns-inputs-missingorspent", + self.nodes[2].sendrawtransaction, + rawtx['hex']) ##################################### # getrawtransaction with block hash #