diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp index 1afedc9963..80a390952a 100644 --- a/src/bench/block_assemble.cpp +++ b/src/bench/block_assemble.cpp @@ -40,8 +40,8 @@ static void AssembleBlock(benchmark::Bench& bench) LOCK(::cs_main); // Required for ::AcceptToMemoryPool. for (const auto& txr : txs) { - CValidationState state; - bool ret{::AcceptToMemoryPool(::ChainstateActive(), *test_setup.m_node.mempool, state, txr, nullptr /* pfMissingInputs */, false /* bypass_limits */, /* nAbsurdFee */ 0)}; + TxValidationState state; + bool ret{::AcceptToMemoryPool(::ChainstateActive(), *test_setup.m_node.mempool, state, txr, false /* bypass_limits */, /* nAbsurdFee */ 0)}; assert(ret); } } diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp index 0916fdd68f..b3da4b0e77 100644 --- a/src/bench/checkblock.cpp +++ b/src/bench/checkblock.cpp @@ -42,7 +42,7 @@ static void DeserializeAndCheckBlockTest(benchmark::Bench& bench) bool rewound = stream.Rewind(benchmark::data::block813851.size()); assert(rewound); - CValidationState validationState; + BlockValidationState validationState; bool checked = CheckBlock(block, validationState, chainParams->GetConsensus(), block.GetBlockTime()); assert(checked); }); diff --git a/src/bench/duplicate_inputs.cpp b/src/bench/duplicate_inputs.cpp index 378861b5df..cd80056ee5 100644 --- a/src/bench/duplicate_inputs.cpp +++ b/src/bench/duplicate_inputs.cpp @@ -56,7 +56,7 @@ static void DuplicateInputs(benchmark::Bench& bench) block.hashMerkleRoot = BlockMerkleRoot(block); bench.minEpochIterations(10).run([&] { - CValidationState cvstate{}; + BlockValidationState cvstate{}; assert(!CheckBlock(block, cvstate, chainparams.GetConsensus(), false, false)); assert(cvstate.GetRejectReason() == "bad-txns-inputs-duplicate"); }); diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp index 5653685f9e..49ddd3ecc6 100644 --- a/src/blockencodings.cpp +++ b/src/blockencodings.cpp @@ -198,13 +198,13 @@ ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector< if (vtx_missing.size() != tx_missing_offset) return READ_STATUS_INVALID; - CValidationState state; + BlockValidationState state; if (!CheckBlock(block, state, Params().GetConsensus())) { // 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) return READ_STATUS_FAILED; // Possible Short ID collision return READ_STATUS_CHECKBLOCK_FAILED; } diff --git a/src/coinjoin/coinjoin.cpp b/src/coinjoin/coinjoin.cpp index c18c72d148..f8bedbaa12 100644 --- a/src/coinjoin/coinjoin.cpp +++ b/src/coinjoin/coinjoin.cpp @@ -351,8 +351,8 @@ bool CCoinJoin::IsCollateralValid(CTxMemPool& mempool, const CTransaction& txCol { LOCK(cs_main); - CValidationState validationState; - if (!AcceptToMemoryPool(::ChainstateActive(), mempool, validationState, MakeTransactionRef(txCollateral), /*pfMissingInputs=*/nullptr, /*bypass_limits=*/false, /*nAbsurdFee=*/DEFAULT_MAX_RAW_TX_FEE, /*test_accept=*/true)) { + TxValidationState validationState; + if (!AcceptToMemoryPool(::ChainstateActive(), mempool, validationState, MakeTransactionRef(txCollateral), /*bypass_limits=*/false, /*nAbsurdFee=*/DEFAULT_MAX_RAW_TX_FEE, /*test_accept=*/true)) { LogPrint(BCLog::COINJOIN, "CCoinJoin::IsCollateralValid -- didn't pass AcceptToMemoryPool()\n"); return false; } diff --git a/src/coinjoin/server.cpp b/src/coinjoin/server.cpp index 1ebcc36b35..fac306d959 100644 --- a/src/coinjoin/server.cpp +++ b/src/coinjoin/server.cpp @@ -320,9 +320,9 @@ void CCoinJoinServer::CommitFinalTransaction() { // See if the transaction is valid TRY_LOCK(cs_main, lockMain); - CValidationState validationState; + TxValidationState validationState; mempool.PrioritiseTransaction(hashTx, 0.1 * COIN); - if (!lockMain || !AcceptToMemoryPool(::ChainstateActive(), mempool, validationState, finalTransaction, nullptr /* pfMissingInputs */, false /* bypass_limits */, DEFAULT_MAX_RAW_TX_FEE /* nAbsurdFee */)) { + if (!lockMain || !AcceptToMemoryPool(::ChainstateActive(), mempool, validationState, finalTransaction, false /* bypass_limits */, DEFAULT_MAX_RAW_TX_FEE /* nAbsurdFee */)) { LogPrint(BCLog::COINJOIN, "CCoinJoinServer::CommitFinalTransaction -- AcceptToMemoryPool() error: Transaction not valid\n"); WITH_LOCK(cs_coinjoin, SetNull()); // not much we can do in this case, just notify clients @@ -454,8 +454,8 @@ void CCoinJoinServer::ChargeRandomFees() const void CCoinJoinServer::ConsumeCollateral(const CTransactionRef& txref) const { LOCK(cs_main); - CValidationState validationState; - if (!AcceptToMemoryPool(::ChainstateActive(), mempool, validationState, txref, nullptr /* pfMissingInputs */, false /* bypass_limits */, 0 /* nAbsurdFee */)) { + TxValidationState validationState; + if (!AcceptToMemoryPool(::ChainstateActive(), mempool, validationState, txref, false /* bypass_limits */, 0 /* nAbsurdFee */)) { LogPrint(BCLog::COINJOIN, "%s -- AcceptToMemoryPool failed\n", __func__); } else { connman.RelayTransaction(*txref); diff --git a/src/consensus/tx_check.cpp b/src/consensus/tx_check.cpp index 36b91dc58a..dff067b685 100644 --- a/src/consensus/tx_check.cpp +++ b/src/consensus/tx_check.cpp @@ -10,7 +10,7 @@ #include #include -bool CheckTransaction(const CTransaction& tx, CValidationState& state) +bool CheckTransaction(const CTransaction& tx, TxValidationState& state) { bool allowEmptyTxInOut = false; if (tx.nType == TRANSACTION_QUORUM_COMMITMENT) { @@ -19,25 +19,25 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state) // Basic checks that don't depend on any context if (!allowEmptyTxInOut && tx.vin.empty()) - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-vin-empty"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-vin-empty"); if (!allowEmptyTxInOut && tx.vout.empty()) - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-vout-empty"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-vout-empty"); // Size limits if (::GetSerializeSize(tx, PROTOCOL_VERSION) > MAX_LEGACY_BLOCK_SIZE) - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-oversize"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-oversize"); if (tx.vExtraPayload.size() > MAX_TX_EXTRA_PAYLOAD) - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-payload-oversize"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-payload-oversize"); // Check for negative or overflow output values (see CVE-2010-5139) CAmount nValueOut = 0; for (const auto& txout : tx.vout) { if (txout.nValue < 0) - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-vout-negative"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-vout-negative"); if (txout.nValue > MAX_MONEY) - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-vout-toolarge"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-vout-toolarge"); nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-txouttotal-toolarge"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-txouttotal-toolarge"); } // Check for duplicate inputs (see CVE-2018-17144) @@ -48,7 +48,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state) std::set vInOutPoints; for (const auto& txin : tx.vin) { if (!vInOutPoints.insert(txin.prevout).second) - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-inputs-duplicate"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-inputs-duplicate"); } if (tx.IsCoinBase()) { @@ -58,11 +58,11 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state) minCbSize = 1; } if (tx.vin[0].scriptSig.size() < minCbSize || tx.vin[0].scriptSig.size() > 100) - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-cb-length"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-cb-length"); } else { for (const auto& txin : tx.vin) if (txin.prevout.IsNull()) - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-prevout-null"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-prevout-null"); } return true; diff --git a/src/consensus/tx_check.h b/src/consensus/tx_check.h index c1edb916bc..223b5e0a21 100644 --- a/src/consensus/tx_check.h +++ b/src/consensus/tx_check.h @@ -13,9 +13,9 @@ */ class CTransaction; -class CValidationState; +class TxValidationState; /** Context-independent validity checks */ -bool CheckTransaction(const CTransaction& tx, CValidationState& state); +bool CheckTransaction(const CTransaction& tx, TxValidationState& state); #endif // BITCOIN_CONSENSUS_TX_CHECK_H diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index bcad06db3d..58f1b2fc0b 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -158,11 +158,11 @@ unsigned int GetTransactionSigOpCount(const CTransaction& tx, const CCoinsViewCa return nSigOps; } -bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee) +bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee) { // are the actual inputs available? if (!inputs.HaveInputs(tx)) { - return state.Invalid(ValidationInvalidReason::TX_MISSING_INPUTS, false, "bad-txns-inputs-missingorspent", + return state.Invalid(TxValidationResult::TX_MISSING_INPUTS, "bad-txns-inputs-missingorspent", strprintf("%s: inputs missing/spent", __func__)); } @@ -174,27 +174,27 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c // If prev is coinbase, check that it's matured if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) { - return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, "bad-txns-premature-spend-of-coinbase", + return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "bad-txns-premature-spend-of-coinbase", strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight)); } // Check for negative or overflow input values nValueIn += coin.out.nValue; if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-inputvalues-outofrange"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-inputvalues-outofrange"); } } const CAmount value_out = tx.GetValueOut(); if (nValueIn < value_out) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-in-belowout", + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-in-belowout", strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out))); } // Tally transaction fees const CAmount txfee_aux = nValueIn - value_out; if (!MoneyRange(txfee_aux)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-fee-outofrange"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-fee-outofrange"); } txfee = txfee_aux; diff --git a/src/consensus/tx_verify.h b/src/consensus/tx_verify.h index 3ab4bd6117..a2dcf362fc 100644 --- a/src/consensus/tx_verify.h +++ b/src/consensus/tx_verify.h @@ -13,7 +13,7 @@ class CBlockIndex; class CCoinsViewCache; class CTransaction; -class CValidationState; +class TxValidationState; /** Transaction validation functions */ @@ -24,7 +24,7 @@ namespace Consensus { * @param[out] txfee Set to the transaction fee if successful. * Preconditions: tx.IsCoinBase() is false. */ -bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee); +bool CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee); } // namespace Consensus /** Auxiliary functions for transaction validation (ideally should not be exposed) */ diff --git a/src/consensus/validation.h b/src/consensus/validation.h index 6628926255..86efba5dfa 100644 --- a/src/consensus/validation.h +++ b/src/consensus/validation.h @@ -8,19 +8,47 @@ #include -/** A "reason" why something was invalid, suitable for determining whether the - * provider of the object should be banned/ignored/disconnected/etc. +/** A "reason" why a transaction was invalid, suitable for determining whether the + * provider of the transaction should be banned/ignored/disconnected/etc. */ -enum class ValidationInvalidReason { - // txn and blocks: - NONE, //!< not actually invalid - CONSENSUS, //!< invalid by consensus rules (excluding any below reasons) +enum class TxValidationResult { + TX_RESULT_UNSET, //!< initial value. Tx has not yet been rejected + TX_CONSENSUS, //!< invalid by consensus rules /** * Invalid by a change to consensus rules more recent than some major deployment. */ - RECENT_CONSENSUS_CHANGE, - // Only blocks (or headers): - CACHED_INVALID, //!< this object was cached as being invalid, but we don't know why + // Only loose txn: + TX_RECENT_CONSENSUS_CHANGE, + TX_NOT_STANDARD, //!< didn't meet our local policy rules + TX_MISSING_INPUTS, //!< transaction was missing some of its inputs + TX_PREMATURE_SPEND, //!< transaction spends a coinbase too early, or violates locktime/sequence locks + TX_BAD_SPECIAL, //!< special transaction violates some rules that are not enough for insta-ban + /** + * Tx already in mempool or conflicts with a tx in the chain + * Currently this is only used if the transaction already exists in the mempool or on chain. + */ + TX_CONFLICT, + TX_CONFLICT_LOCK, //!< conflicts with InstantSend lock + TX_MEMPOOL_POLICY, //!< violated mempool's fee/size/descendant/etc limits +}; + +/** 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 { + BLOCK_RESULT_UNSET, //!< initial value. Block has not yet been rejected + BLOCK_CONSENSUS, //!< invalid by consensus rules (excluding any below reasons) + /** + * 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, + BLOCK_CACHED_INVALID, //!< this block was cached as being invalid and we didn't store the reason why BLOCK_INVALID_HEADER, //!< invalid proof of work or time too old BLOCK_MUTATED, //!< the block's data didn't match the data committed to by the PoW BLOCK_MISSING_PREV, //!< We don't have the previous block the checked one is built on @@ -28,92 +56,78 @@ enum class ValidationInvalidReason { BLOCK_TIME_FUTURE, //!< block timestamp was > 2 hours in the future (or our clock is bad) BLOCK_CHECKPOINT, //!< the block failed to meet one of our checkpoints BLOCK_CHAINLOCK, //!< the block conflicts with the ChainLock - // Only loose txn: - TX_NOT_STANDARD, //!< didn't meet our local policy rules - TX_MISSING_INPUTS, //!< a transaction was missing some of its inputs - TX_PREMATURE_SPEND, //!< transaction spends a coinbase too early, or violates locktime/sequence locks - TX_BAD_SPECIAL, //!< special transaction violates some rules that are not enough for insta-ban - /** - * 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 - */ - TX_CONFLICT, - TX_CONFLICT_LOCK, //!< conflicts with InstantSend lock - TX_MEMPOOL_POLICY, //!< violated mempool's fee/size/descendant/etc limits }; -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_BAD_SPECIAL || - r == ValidationInvalidReason::TX_CONFLICT || - r == ValidationInvalidReason::TX_CONFLICT_LOCK || - 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_CHAINLOCK; -} -/** 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; - std::string strDebugMessage; -public: - CValidationState() : mode(MODE_VALID), m_reason(ValidationInvalidReason::NONE) {} - bool Invalid(ValidationInvalidReason reasonIn, bool ret = false, - const std::string &strRejectReasonIn="", - const std::string &strDebugMessageIn="") { - m_reason = reasonIn; - strRejectReason = strRejectReasonIn; - strDebugMessage = strDebugMessageIn; - if (mode == MODE_ERROR) - return ret; - mode = MODE_INVALID; - return ret; + } m_mode; + std::string m_reject_reason; + std::string m_debug_message; +protected: + void Invalid(const std::string &reject_reason="", + const std::string &debug_message="") + { + m_reject_reason = reject_reason; + m_debug_message = debug_message; + if (m_mode != MODE_ERROR) m_mode = MODE_INVALID; } - 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) {} + 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 IsValid() const { return m_mode == MODE_VALID; } + bool IsInvalid() const { return m_mode == MODE_INVALID; } + bool IsError() const { return m_mode == MODE_ERROR; } + 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; +public: + bool Invalid(TxValidationResult result, + const std::string &reject_reason="", + const std::string &debug_message="") + { + m_result = result; + ValidationState::Invalid(reject_reason, debug_message); + return false; } - bool IsInvalid() const { - return mode == MODE_INVALID; + TxValidationResult GetResult() const { return m_result; } +}; + +class BlockValidationState : public ValidationState { +private: + BlockValidationResult m_result; +public: + bool Invalid(BlockValidationResult result, + const std::string &reject_reason="", + const std::string &debug_message="") { + m_result = result; + ValidationState::Invalid(reject_reason, debug_message); + return false; } - bool IsError() const { - return mode == MODE_ERROR; - } - ValidationInvalidReason GetReason() const { return m_reason; } - std::string GetRejectReason() const { return strRejectReason; } - std::string GetDebugMessage() const { return strDebugMessage; } + BlockValidationResult GetResult() const { return m_result; } }; #endif // BITCOIN_CONSENSUS_VALIDATION_H diff --git a/src/evo/cbtx.cpp b/src/evo/cbtx.cpp index 81313526af..81fefe7853 100644 --- a/src/evo/cbtx.cpp +++ b/src/evo/cbtx.cpp @@ -15,33 +15,33 @@ #include #include -bool CheckCbTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) +bool CheckCbTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state) { if (tx.nType != TRANSACTION_COINBASE) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-cbtx-type"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-cbtx-type"); } if (!tx.IsCoinBase()) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-cbtx-invalid"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-cbtx-invalid"); } CCbTx cbTx; if (!GetTxPayload(tx, cbTx)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-cbtx-payload"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-cbtx-payload"); } if (cbTx.nVersion == 0 || cbTx.nVersion > CCbTx::CURRENT_VERSION) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-cbtx-version"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-cbtx-version"); } if (pindexPrev && pindexPrev->nHeight + 1 != cbTx.nHeight) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-cbtx-height"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-cbtx-height"); } if (pindexPrev) { bool fDIP0008Active = pindexPrev->nHeight >= Params().GetConsensus().DIP0008Height; if (fDIP0008Active && cbTx.nVersion < 2) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-cbtx-version"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-cbtx-version"); } } @@ -49,7 +49,7 @@ bool CheckCbTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidatio } // This can only be done after the block has been fully processed, as otherwise we won't have the finished MN list -bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex, const llmq::CQuorumBlockProcessor& quorum_block_processor, CValidationState& state, const CCoinsViewCache& view) +bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex, const llmq::CQuorumBlockProcessor& quorum_block_processor, BlockValidationState& state, const CCoinsViewCache& view) { if (block.vtx[0]->nType != TRANSACTION_COINBASE) { return true; @@ -61,7 +61,7 @@ bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex, const CCbTx cbTx; if (!GetTxPayload(*block.vtx[0], cbTx)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-cbtx-payload"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-payload"); } int64_t nTime2 = GetTimeMicros(); nTimePayload += nTime2 - nTime1; @@ -77,7 +77,7 @@ bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex, const return false; } if (calculatedMerkleRoot != cbTx.merkleRootMNList) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-cbtx-mnmerkleroot"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-mnmerkleroot"); } int64_t nTime3 = GetTimeMicros(); nTimeMerkleMNL += nTime3 - nTime2; @@ -89,7 +89,7 @@ bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex, const return false; } if (calculatedMerkleRoot != cbTx.merkleRootQuorums) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-cbtx-quorummerkleroot"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-quorummerkleroot"); } } @@ -101,7 +101,7 @@ bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex, const return true; } -bool CalcCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindexPrev, uint256& merkleRootRet, CValidationState& state, const CCoinsViewCache& view) +bool CalcCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindexPrev, uint256& merkleRootRet, BlockValidationState& state, const CCoinsViewCache& view) { LOCK(deterministicMNManager->cs); @@ -134,7 +134,7 @@ bool CalcCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindexPrev if (sml == smlCached) { merkleRootRet = merkleRootCached; if (mutatedCached) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "mutated-cached-calc-cb-mnmerkleroot"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "mutated-cached-calc-cb-mnmerkleroot"); } return true; } @@ -150,13 +150,13 @@ bool CalcCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindexPrev mutatedCached = mutated; if (mutated) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "mutated-calc-cb-mnmerkleroot"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "mutated-calc-cb-mnmerkleroot"); } return true; } catch (const std::exception& e) { LogPrintf("%s -- failed: %s\n", __func__, e.what()); - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "failed-calc-cb-mnmerkleroot"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "failed-calc-cb-mnmerkleroot"); } } @@ -223,7 +223,7 @@ auto CalcHashCountFromQCHashes(const QcHashMap& qcHashes) return hash_count; } -bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPrev, const llmq::CQuorumBlockProcessor& quorum_block_processor, uint256& merkleRootRet, CValidationState& state) +bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPrev, const llmq::CQuorumBlockProcessor& quorum_block_processor, uint256& merkleRootRet, BlockValidationState& state) { static int64_t nTimeMined = 0; static int64_t nTimeLoop = 0; @@ -233,7 +233,7 @@ bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPre auto retVal = CachedGetQcHashesQcIndexedHashes(pindexPrev, quorum_block_processor); if (!retVal) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "commitment-not-found"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "commitment-not-found"); } // The returned quorums are in reversed order, so the most recent one is at index 0 auto [qcHashes, qcIndexedHashes] = retVal.value(); @@ -249,7 +249,7 @@ bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPre if (tx->nVersion == 3 && tx->nType == TRANSACTION_QUORUM_COMMITMENT) { llmq::CFinalCommitmentTxPayload qc; if (!GetTxPayload(*tx, qc)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-payload-calc-cbtx-quorummerkleroot"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-payload-calc-cbtx-quorummerkleroot"); } if (qc.commitment.IsNull()) { // having null commitments is ok but we don't use them here, move to the next tx @@ -257,7 +257,7 @@ bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPre } const auto& llmq_params_opt = llmq::GetLLMQParams(qc.commitment.llmqType); if (!llmq_params_opt.has_value()) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-commitment-type-calc-cbtx-quorummerkleroot"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-commitment-type-calc-cbtx-quorummerkleroot"); } const auto& llmq_params = llmq_params_opt.value(); auto qcHash = ::SerializeHash(qc.commitment); @@ -291,7 +291,7 @@ bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPre const auto& llmq_params_opt = llmq::GetLLMQParams(llmqType); assert(llmq_params_opt.has_value()); if (vec_hashes.size() > size_t(llmq_params_opt->signingActiveQuorumCount)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "excess-quorums-calc-cbtx-quorummerkleroot"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "excess-quorums-calc-cbtx-quorummerkleroot"); } // Copy vec_hashes into vec_hashes_final std::copy(vec_hashes.begin(), vec_hashes.end(), std::back_inserter(vec_hashes_final)); @@ -308,7 +308,7 @@ bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPre LogPrint(BCLog::BENCHMARK, " - ComputeMerkleRoot: %.2fms [%.2fs]\n", 0.001 * (nTime4 - nTime3), nTimeMerkle * 0.000001); if (mutated) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "mutated-calc-cbtx-quorummerkleroot"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "mutated-calc-cbtx-quorummerkleroot"); } return true; diff --git a/src/evo/cbtx.h b/src/evo/cbtx.h index 90f42bf321..1b64ef2036 100644 --- a/src/evo/cbtx.h +++ b/src/evo/cbtx.h @@ -8,10 +8,11 @@ #include #include +class BlockValidationState; class CBlock; class CBlockIndex; class CCoinsViewCache; -class CValidationState; +class TxValidationState; namespace llmq { class CQuorumBlockProcessor; @@ -53,10 +54,10 @@ public: } }; -bool CheckCbTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state); +bool CheckCbTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state); -bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex, const llmq::CQuorumBlockProcessor& quorum_block_processor, CValidationState& state, const CCoinsViewCache& view); -bool CalcCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindexPrev, uint256& merkleRootRet, CValidationState& state, const CCoinsViewCache& view); -bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPrev, const llmq::CQuorumBlockProcessor& quorum_block_processor, uint256& merkleRootRet, CValidationState& state); +bool CheckCbTxMerkleRoots(const CBlock& block, const CBlockIndex* pindex, const llmq::CQuorumBlockProcessor& quorum_block_processor, BlockValidationState& state, const CCoinsViewCache& view); +bool CalcCbTxMerkleRootMNList(const CBlock& block, const CBlockIndex* pindexPrev, uint256& merkleRootRet, BlockValidationState& state, const CCoinsViewCache& view); +bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPrev, const llmq::CQuorumBlockProcessor& quorum_block_processor, uint256& merkleRootRet, BlockValidationState& state); #endif // BITCOIN_EVO_CBTX_H diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 758b086d0e..6f20c5f7cb 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -654,7 +654,7 @@ void CDeterministicMNList::RemoveMN(const uint256& proTxHash) mnInternalIdMap = mnInternalIdMap.erase(dmn->GetInternalId()); } -bool CDeterministicMNManager::ProcessBlock(const CBlock& block, const CBlockIndex* pindex, CValidationState& _state, const CCoinsViewCache& view, bool fJustCheck) +bool CDeterministicMNManager::ProcessBlock(const CBlock& block, const CBlockIndex* pindex, BlockValidationState& state, const CCoinsViewCache& view, bool fJustCheck) { AssertLockHeld(cs_main); @@ -672,7 +672,7 @@ bool CDeterministicMNManager::ProcessBlock(const CBlock& block, const CBlockInde try { LOCK(cs); - if (!BuildNewListFromBlock(block, pindex->pprev, _state, view, newList, true)) { + if (!BuildNewListFromBlock(block, pindex->pprev, state, view, newList, true)) { // pass the state returned by the function above return false; } @@ -710,7 +710,7 @@ bool CDeterministicMNManager::ProcessBlock(const CBlock& block, const CBlockInde mnListDiffsCache.emplace(pindex->GetBlockHash(), diff); } catch (const std::exception& e) { LogPrintf("CDeterministicMNManager::%s -- internal error: %s\n", __func__, e.what()); - return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, "failed-dmn-block"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "failed-dmn-block"); } // Don't hold cs while calling signals @@ -723,7 +723,7 @@ bool CDeterministicMNManager::ProcessBlock(const CBlock& block, const CBlockInde if (!consensusParams.DIP0003EnforcementHash.IsNull() && consensusParams.DIP0003EnforcementHash != pindex->GetBlockHash()) { LogPrintf("CDeterministicMNManager::%s -- DIP3 enforcement block has wrong hash: hash=%s, expected=%s, nHeight=%d\n", __func__, pindex->GetBlockHash().ToString(), consensusParams.DIP0003EnforcementHash.ToString(), nHeight); - return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-dip3-enf-block"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-dip3-enf-block"); } LogPrintf("CDeterministicMNManager::%s -- DIP3 is enforced now. nHeight=%d\n", __func__, nHeight); } @@ -775,7 +775,7 @@ void CDeterministicMNManager::UpdatedBlockTip(const CBlockIndex* pindex) tipIndex = pindex; } -bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const CBlockIndex* pindexPrev, CValidationState& _state, const CCoinsViewCache& view, CDeterministicMNList& mnListRet, bool debugLogs) +bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const CBlockIndex* pindexPrev, BlockValidationState& state, const CCoinsViewCache& view, CDeterministicMNList& mnListRet, bool debugLogs) { AssertLockHeld(cs); @@ -820,11 +820,11 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C if (tx.nType == TRANSACTION_PROVIDER_REGISTER) { CProRegTx proTx; if (!GetTxPayload(tx, proTx)) { - return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-payload"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-payload"); } if (proTx.nType == MnType::HighPerformance && !llmq::utils::IsV19Active(pindexPrev)) { - return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-payload"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-payload"); } auto dmn = std::make_shared(newList.GetTotalRegisteredCount(), proTx.nType); @@ -842,7 +842,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C if (!proTx.collateralOutpoint.hash.IsNull() && (!view.GetCoin(dmn->collateralOutpoint, coin) || coin.IsSpent() || coin.out.nValue != expectedCollateral)) { // should actually never get to this point as CheckProRegTx should have handled this case. // We do this additional check nevertheless to be 100% sure - return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-collateral"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-collateral"); } auto replacedDmn = newList.GetMNByCollateral(dmn->collateralOutpoint); @@ -858,10 +858,10 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C } if (newList.HasUniqueProperty(proTx.addr)) { - return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-dup-addr"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-addr"); } if (newList.HasUniqueProperty(proTx.keyIDOwner) || newList.HasUniqueProperty(proTx.pubKeyOperator)) { - return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-dup-key"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-key"); } dmn->nOperatorReward = proTx.nOperatorReward; @@ -883,26 +883,26 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_SERVICE) { CProUpServTx proTx; if (!GetTxPayload(tx, proTx)) { - return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-payload"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-payload"); } if (proTx.nType == MnType::HighPerformance && !llmq::utils::IsV19Active(pindexPrev)) { - return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-payload"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-payload"); } if (newList.HasUniqueProperty(proTx.addr) && newList.GetUniquePropertyMN(proTx.addr)->proTxHash != proTx.proTxHash) { - return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-dup-addr"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-addr"); } CDeterministicMNCPtr dmn = newList.GetMN(proTx.proTxHash); if (!dmn) { - return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-hash"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-hash"); } if (proTx.nType != dmn->nType) { - return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-type-mismatch"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-type-mismatch"); } if (!IsValidMnType(proTx.nType)) { - return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-type"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-type"); } auto newState = std::make_shared(*dmn->pdmnState); @@ -932,12 +932,12 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) { CProUpRegTx proTx; if (!GetTxPayload(tx, proTx)) { - return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-payload"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-payload"); } CDeterministicMNCPtr dmn = newList.GetMN(proTx.proTxHash); if (!dmn) { - return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-hash"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-hash"); } auto newState = std::make_shared(*dmn->pdmnState); if (newState->pubKeyOperator.Get() != proTx.pubKeyOperator) { @@ -958,12 +958,12 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REVOKE) { CProUpRevTx proTx; if (!GetTxPayload(tx, proTx)) { - return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-payload"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-payload"); } CDeterministicMNCPtr dmn = newList.GetMN(proTx.proTxHash); if (!dmn) { - return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-hash"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-hash"); } auto newState = std::make_shared(*dmn->pdmnState); newState->ResetOperatorFields(); @@ -979,19 +979,19 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C } else if (tx.nType == TRANSACTION_QUORUM_COMMITMENT) { llmq::CFinalCommitmentTxPayload qc; if (!GetTxPayload(tx, qc)) { - return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-payload"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-payload"); } if (!qc.commitment.IsNull()) { const auto& llmq_params_opt = llmq::GetLLMQParams(qc.commitment.llmqType); if (!llmq_params_opt.has_value()) { - return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-commitment-type"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-commitment-type"); } int qcnHeight = int(qc.nHeight); int quorumHeight = qcnHeight - (qcnHeight % llmq_params_opt->dkgInterval) + int(qc.commitment.quorumIndex); auto pQuorumBaseBlockIndex = pindexPrev->GetAncestor(quorumHeight); if (!pQuorumBaseBlockIndex || pQuorumBaseBlockIndex->GetBlockHash() != qc.commitment.quorumHash) { // we should actually never get into this case as validation should have caught it...but let's be sure - return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-quorum-hash"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-quorum-hash"); } HandleQuorumCommitment(qc.commitment, pQuorumBaseBlockIndex, newList, debugLogs); @@ -1348,111 +1348,111 @@ bool CDeterministicMNManager::MigrateDBIfNeeded() } template -static bool CheckService(const ProTx& proTx, CValidationState& state) +static bool CheckService(const ProTx& proTx, TxValidationState& state) { if (!proTx.addr.IsValid()) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-ipaddr"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-ipaddr"); } if (Params().RequireRoutableExternalIP() && !proTx.addr.IsRoutable()) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-ipaddr"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-ipaddr"); } static int mainnetDefaultPort = CreateChainParams(CBaseChainParams::MAIN)->GetDefaultPort(); if (Params().NetworkIDString() == CBaseChainParams::MAIN) { if (proTx.addr.GetPort() != mainnetDefaultPort) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-ipaddr-port"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-ipaddr-port"); } } else if (proTx.addr.GetPort() == mainnetDefaultPort) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-ipaddr-port"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-ipaddr-port"); } if (!proTx.addr.IsIPv4()) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-ipaddr"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-ipaddr"); } return true; } template -static bool CheckPlatformFields(const ProTx& proTx, CValidationState& state) +static bool CheckPlatformFields(const ProTx& proTx, TxValidationState& state) { if (proTx.platformNodeID.IsNull()) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-platform-nodeid"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-nodeid"); } static int mainnetPlatformP2PPort = CreateChainParams(CBaseChainParams::MAIN)->GetDefaultPlatformP2PPort(); if (Params().NetworkIDString() == CBaseChainParams::MAIN) { if (proTx.platformP2PPort != mainnetPlatformP2PPort) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-platform-p2p-port"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-p2p-port"); } } static int mainnetPlatformHTTPPort = CreateChainParams(CBaseChainParams::MAIN)->GetDefaultPlatformHTTPPort(); if (Params().NetworkIDString() == CBaseChainParams::MAIN) { if (proTx.platformHTTPPort != mainnetPlatformHTTPPort) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-platform-http-port"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-http-port"); } } static int mainnetDefaultP2PPort = CreateChainParams(CBaseChainParams::MAIN)->GetDefaultPort(); if (proTx.platformP2PPort == mainnetDefaultP2PPort) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-platform-p2p-port"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-p2p-port"); } if (proTx.platformHTTPPort == mainnetDefaultP2PPort) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-platform-http-port"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-http-port"); } if (proTx.platformP2PPort == proTx.platformHTTPPort || proTx.platformP2PPort == proTx.addr.GetPort() || proTx.platformHTTPPort == proTx.addr.GetPort()) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-platform-dup-ports"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-dup-ports"); } return true; } template -static bool CheckHashSig(const ProTx& proTx, const PKHash& pkhash, CValidationState& state) +static bool CheckHashSig(const ProTx& proTx, const PKHash& pkhash, TxValidationState& state) { std::string strError; if (!CHashSigner::VerifyHash(::SerializeHash(proTx), ToKeyID(pkhash), proTx.vchSig, strError)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-sig"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-sig"); } return true; } template -static bool CheckStringSig(const ProTx& proTx, const PKHash& pkhash, CValidationState& state) +static bool CheckStringSig(const ProTx& proTx, const PKHash& pkhash, TxValidationState& state) { std::string strError; if (!CMessageSigner::VerifyMessage(ToKeyID(pkhash), proTx.vchSig, proTx.MakeSignString(), strError)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-sig"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-sig"); } return true; } template -static bool CheckHashSig(const ProTx& proTx, const CBLSPublicKey& pubKey, CValidationState& state) +static bool CheckHashSig(const ProTx& proTx, const CBLSPublicKey& pubKey, TxValidationState& state) { if (!proTx.sig.VerifyInsecure(pubKey, ::SerializeHash(proTx))) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-sig"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-sig"); } return true; } -bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, const CCoinsViewCache& view, bool check_sigs) +bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state, const CCoinsViewCache& view, bool check_sigs) { if (tx.nType != TRANSACTION_PROVIDER_REGISTER) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-type"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-type"); } CProRegTx ptx; if (!GetTxPayload(tx, ptx)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-payload"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-payload"); } if (auto maybe_err = ptx.IsTriviallyValid(llmq::utils::IsV19Active(pindexPrev)); maybe_err.did_err) { - return state.Invalid(maybe_err.reason, false, std::string(maybe_err.error_str)); + return state.Invalid(maybe_err.reason, std::string(maybe_err.error_str)); } // It's allowed to set addr to 0, which will put the MN into PoSe-banned state and require a ProUpServTx to be issues later @@ -1477,31 +1477,31 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid if (!ptx.collateralOutpoint.hash.IsNull()) { Coin coin; if (!view.GetCoin(ptx.collateralOutpoint, coin) || coin.IsSpent() || coin.out.nValue != expectedCollateral) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-collateral"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral"); } if (!ExtractDestination(coin.out.scriptPubKey, collateralTxDest)) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-collateral-dest"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral-dest"); } // Extract key from collateral. This only works for P2PK and P2PKH collaterals and will fail for P2SH. // Issuer of this ProRegTx must prove ownership with this key by signing the ProRegTx keyForPayloadSig = std::get_if(&collateralTxDest); if (!keyForPayloadSig) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-collateral-pkh"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral-pkh"); } collateralOutpoint = ptx.collateralOutpoint; } else { if (ptx.collateralOutpoint.n >= tx.vout.size()) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-collateral-index"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral-index"); } if (tx.vout[ptx.collateralOutpoint.n].nValue != expectedCollateral) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-collateral"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral"); } if (!ExtractDestination(tx.vout[ptx.collateralOutpoint.n].scriptPubKey, collateralTxDest)) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-collateral-dest"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral-dest"); } collateralOutpoint = COutPoint(tx.GetHash(), ptx.collateralOutpoint.n); @@ -1510,7 +1510,7 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid // don't allow reuse of collateral key for other keys (don't allow people to put the collateral key onto an online server) // this check applies to internal and external collateral, but internal collaterals are not necessarily a P2PKH if (collateralTxDest == CTxDestination(PKHash(ptx.keyIDOwner)) || collateralTxDest == CTxDestination(PKHash(ptx.keyIDVoting))) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-collateral-reuse"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral-reuse"); } if (pindexPrev) { @@ -1518,30 +1518,30 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid // only allow reusing of addresses when it's for the same collateral (which replaces the old MN) if (mnList.HasUniqueProperty(ptx.addr) && mnList.GetUniquePropertyMN(ptx.addr)->collateralOutpoint != collateralOutpoint) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-dup-addr"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-addr"); } // never allow duplicate keys, even if this ProTx would replace an existing MN if (mnList.HasUniqueProperty(ptx.keyIDOwner) || mnList.HasUniqueProperty(ptx.pubKeyOperator)) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-dup-key"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-key"); } // never allow duplicate platformNodeIds for HPMNs if (ptx.nType == MnType::HighPerformance) { if (mnList.HasUniqueProperty(ptx.platformNodeID)) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-dup-platformnodeid"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-platformnodeid"); } } if (!deterministicMNManager->IsDIP3Enforced(pindexPrev->nHeight)) { if (ptx.keyIDOwner != ptx.keyIDVoting) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-key-not-same"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-key-not-same"); } } } if (auto maybe_err = CheckInputsHash(tx, ptx); maybe_err.did_err) { - return state.Invalid(maybe_err.reason, false, std::string(maybe_err.error_str)); + return state.Invalid(maybe_err.reason, std::string(maybe_err.error_str)); } if (keyForPayloadSig) { @@ -1553,26 +1553,26 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid } else { // collateral is part of this ProRegTx, so we know the collateral is owned by the issuer if (!ptx.vchSig.empty()) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-sig"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-sig"); } } return true; } -bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, bool check_sigs) +bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state, bool check_sigs) { if (tx.nType != TRANSACTION_PROVIDER_UPDATE_SERVICE) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-type"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-type"); } CProUpServTx ptx; if (!GetTxPayload(tx, ptx)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-payload"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-payload"); } if (auto maybe_err = ptx.IsTriviallyValid(llmq::utils::IsV19Active(pindexPrev)); maybe_err.did_err) { - return state.Invalid(maybe_err.reason, false, std::string(maybe_err.error_str)); + return state.Invalid(maybe_err.reason, std::string(maybe_err.error_str)); } if (!CheckService(ptx, state)) { @@ -1590,34 +1590,34 @@ bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVa auto mnList = deterministicMNManager->GetListForBlock(pindexPrev); auto mn = mnList.GetMN(ptx.proTxHash); if (!mn) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-hash"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-hash"); } // don't allow updating to addresses already used by other MNs if (mnList.HasUniqueProperty(ptx.addr) && mnList.GetUniquePropertyMN(ptx.addr)->proTxHash != ptx.proTxHash) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-dup-addr"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-addr"); } // don't allow updating to platformNodeIds already used by other HPMNs if (ptx.nType == MnType::HighPerformance) { if (mnList.HasUniqueProperty(ptx.platformNodeID) && mnList.GetUniquePropertyMN(ptx.platformNodeID)->proTxHash != ptx.proTxHash) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-dup-platformnodeid"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-platformnodeid"); } } if (ptx.scriptOperatorPayout != CScript()) { if (mn->nOperatorReward == 0) { // don't allow setting operator reward payee in case no operatorReward was set - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-operator-payee"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-operator-payee"); } if (!ptx.scriptOperatorPayout.IsPayToPublicKeyHash() && !ptx.scriptOperatorPayout.IsPayToScriptHash()) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-operator-payee"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-operator-payee"); } } // we can only check the signature if pindexPrev != nullptr and the MN is known if (auto maybe_err = CheckInputsHash(tx, ptx); maybe_err.did_err) { - return state.Invalid(maybe_err.reason, false, std::string(maybe_err.error_str)); + return state.Invalid(maybe_err.reason, std::string(maybe_err.error_str)); } if (check_sigs && !CheckHashSig(ptx, mn->pdmnState->pubKeyOperator.Get(), state)) { // pass the state returned by the function above @@ -1628,69 +1628,69 @@ bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVa return true; } -bool CheckProUpRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, const CCoinsViewCache& view, bool check_sigs) +bool CheckProUpRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state, const CCoinsViewCache& view, bool check_sigs) { if (tx.nType != TRANSACTION_PROVIDER_UPDATE_REGISTRAR) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-type"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-type"); } CProUpRegTx ptx; if (!GetTxPayload(tx, ptx)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-payload"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-payload"); } if (auto maybe_err = ptx.IsTriviallyValid(llmq::utils::IsV19Active(pindexPrev)); maybe_err.did_err) { - return state.Invalid(maybe_err.reason, false, std::string(maybe_err.error_str)); + return state.Invalid(maybe_err.reason, std::string(maybe_err.error_str)); } CTxDestination payoutDest; if (!ExtractDestination(ptx.scriptPayout, payoutDest)) { // should not happen as we checked script types before - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-payee-dest"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-payee-dest"); } if (pindexPrev) { auto mnList = deterministicMNManager->GetListForBlock(pindexPrev); auto dmn = mnList.GetMN(ptx.proTxHash); if (!dmn) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-hash"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-hash"); } // don't allow reuse of payee key for other keys (don't allow people to put the payee key onto an online server) if (payoutDest == CTxDestination(PKHash(dmn->pdmnState->keyIDOwner)) || payoutDest == CTxDestination(PKHash(ptx.keyIDVoting))) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-payee-reuse"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-payee-reuse"); } Coin coin; if (!view.GetCoin(dmn->collateralOutpoint, coin) || coin.IsSpent()) { // this should never happen (there would be no dmn otherwise) - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-collateral"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-collateral"); } // don't allow reuse of collateral key for other keys (don't allow people to put the collateral key onto an online server) CTxDestination collateralTxDest; if (!ExtractDestination(coin.out.scriptPubKey, collateralTxDest)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-collateral-dest"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-collateral-dest"); } if (collateralTxDest == CTxDestination(PKHash(dmn->pdmnState->keyIDOwner)) || collateralTxDest == CTxDestination(PKHash(ptx.keyIDVoting))) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-collateral-reuse"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral-reuse"); } if (mnList.HasUniqueProperty(ptx.pubKeyOperator)) { auto otherDmn = mnList.GetUniquePropertyMN(ptx.pubKeyOperator); if (ptx.proTxHash != otherDmn->proTxHash) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-dup-key"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-key"); } } if (!deterministicMNManager->IsDIP3Enforced(pindexPrev->nHeight)) { if (dmn->pdmnState->keyIDOwner != ptx.keyIDVoting) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-protx-key-not-same"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-key-not-same"); } } if (auto maybe_err = CheckInputsHash(tx, ptx); maybe_err.did_err) { - return state.Invalid(maybe_err.reason, false, std::string(maybe_err.error_str)); + return state.Invalid(maybe_err.reason, std::string(maybe_err.error_str)); } if (check_sigs && !CheckHashSig(ptx, PKHash(dmn->pdmnState->keyIDOwner), state)) { // pass the state returned by the function above @@ -1701,29 +1701,29 @@ bool CheckProUpRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVal return true; } -bool CheckProUpRevTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, bool check_sigs) +bool CheckProUpRevTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state, bool check_sigs) { if (tx.nType != TRANSACTION_PROVIDER_UPDATE_REVOKE) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-type"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-type"); } CProUpRevTx ptx; if (!GetTxPayload(tx, ptx)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-payload"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-payload"); } if (auto maybe_err = ptx.IsTriviallyValid(llmq::utils::IsV19Active(pindexPrev)); maybe_err.did_err) { - return state.Invalid(maybe_err.reason, false, std::string(maybe_err.error_str)); + return state.Invalid(maybe_err.reason, std::string(maybe_err.error_str)); } if (pindexPrev) { auto mnList = deterministicMNManager->GetListForBlock(pindexPrev); auto dmn = mnList.GetMN(ptx.proTxHash); if (!dmn) - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-protx-hash"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-hash"); if (auto maybe_err = CheckInputsHash(tx, ptx); maybe_err.did_err) { - return state.Invalid(maybe_err.reason, false, std::string(maybe_err.error_str)); + return state.Invalid(maybe_err.reason, std::string(maybe_err.error_str)); } if (check_sigs && !CheckHashSig(ptx, dmn->pdmnState->pubKeyOperator.Get(), state)) { // pass the state returned by the function above diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 103add8d19..a510289821 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -27,7 +27,7 @@ class CConnman; class CBlock; class CBlockIndex; -class CValidationState; +class TxValidationState; class CSimplifiedMNListDiff; extern CCriticalSection cs_main; @@ -563,14 +563,14 @@ public: m_evoDb(evoDb), connman(_connman) {} ~CDeterministicMNManager() = default; - bool ProcessBlock(const CBlock& block, const CBlockIndex* pindex, CValidationState& state, + bool ProcessBlock(const CBlock& block, const CBlockIndex* pindex, BlockValidationState& state, const CCoinsViewCache& view, bool fJustCheck) EXCLUSIVE_LOCKS_REQUIRED(cs_main) LOCKS_EXCLUDED(cs); bool UndoBlock(const CBlock& block, const CBlockIndex* pindex) LOCKS_EXCLUDED(cs); void UpdatedBlockTip(const CBlockIndex* pindex) LOCKS_EXCLUDED(cs); // the returned list will not contain the correct block hash (we can't know it yet as the coinbase TX is not updated yet) - bool BuildNewListFromBlock(const CBlock& block, const CBlockIndex* pindexPrev, CValidationState& state, const CCoinsViewCache& view, + bool BuildNewListFromBlock(const CBlock& block, const CBlockIndex* pindexPrev, BlockValidationState& state, const CCoinsViewCache& view, CDeterministicMNList& mnListRet, bool debugLogs) EXCLUSIVE_LOCKS_REQUIRED(cs); static void HandleQuorumCommitment(const llmq::CFinalCommitment& qc, const CBlockIndex* pQuorumBaseBlockIndex, CDeterministicMNList& mnList, bool debugLogs); static void DecreasePoSePenalties(CDeterministicMNList& mnList); @@ -595,10 +595,10 @@ private: CDeterministicMNList GetListForBlockInternal(const CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs); }; -bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, const CCoinsViewCache& view, bool check_sigs); -bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, bool check_sigs); -bool CheckProUpRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, const CCoinsViewCache& view, bool check_sigs); -bool CheckProUpRevTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, bool check_sigs); +bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state, const CCoinsViewCache& view, bool check_sigs); +bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state, bool check_sigs); +bool CheckProUpRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state, const CCoinsViewCache& view, bool check_sigs); +bool CheckProUpRevTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state, bool check_sigs); extern std::unique_ptr deterministicMNManager; diff --git a/src/evo/mnhftx.cpp b/src/evo/mnhftx.cpp index e12ce03861..55394beaa4 100644 --- a/src/evo/mnhftx.cpp +++ b/src/evo/mnhftx.cpp @@ -34,33 +34,33 @@ bool MNHFTx::Verify(const CBlockIndex* pQuorumIndex) const llmq::CSigningManager::VerifyRecoveredSig(llmqType, *llmq::quorumManager, pQuorumIndex->nHeight, requestId, pQuorumIndex->GetBlockHash(), sig, signOffset); } -bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { MNHFTxPayload mnhfTx; if (!GetTxPayload(tx, mnhfTx)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-mnhf-payload"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-payload"); } if (mnhfTx.nVersion == 0 || mnhfTx.nVersion > MNHFTxPayload::CURRENT_VERSION) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-mnhf-version"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-version"); } const CBlockIndex* pindexQuorum = g_chainman.m_blockman.LookupBlockIndex(mnhfTx.signal.quorumHash); if (!pindexQuorum) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-mnhf-quorum-hash"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-quorum-hash"); } if (pindexQuorum != pindexPrev->GetAncestor(pindexQuorum->nHeight)) { // not part of active chain - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-mnhf-quorum-hash"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-quorum-hash"); } if (!llmq::GetLLMQParams(Params().GetConsensus().llmqTypeMnhf).has_value()) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-mnhf-type"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-type"); } if (!mnhfTx.signal.Verify(pindexQuorum)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-mnhf-invalid"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-invalid"); } return true; diff --git a/src/evo/mnhftx.h b/src/evo/mnhftx.h index 90438e1bfd..61c14f6502 100644 --- a/src/evo/mnhftx.h +++ b/src/evo/mnhftx.h @@ -12,7 +12,7 @@ #include class CBlockIndex; -class CValidationState; +class TxValidationState; extern CCriticalSection cs_main; // mnhf signal special transaction @@ -72,6 +72,6 @@ public: } }; -bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main); +bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main); #endif // BITCOIN_EVO_MNHFTX_H diff --git a/src/evo/providertx.cpp b/src/evo/providertx.cpp index 26123cb63e..a58cbec75c 100644 --- a/src/evo/providertx.cpp +++ b/src/evo/providertx.cpp @@ -14,34 +14,34 @@ maybe_error CProRegTx::IsTriviallyValid(bool is_bls_legacy_scheme) const { if (nVersion == 0 || nVersion > GetVersion(is_bls_legacy_scheme)) { - return {ValidationInvalidReason::CONSENSUS, "bad-protx-version"}; + return {TxValidationResult::TX_CONSENSUS, "bad-protx-version"}; } if (!IsValidMnType(nType)) { - return {ValidationInvalidReason::CONSENSUS, "bad-protx-type"}; + return {TxValidationResult::TX_CONSENSUS, "bad-protx-type"}; } if (nMode != 0) { - return {ValidationInvalidReason::CONSENSUS, "bad-protx-mode"}; + return {TxValidationResult::TX_CONSENSUS, "bad-protx-mode"}; } if (keyIDOwner.IsNull() || !pubKeyOperator.IsValid() || keyIDVoting.IsNull()) { - return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-protx-key-null"}; + return {TxValidationResult::TX_BAD_SPECIAL, "bad-protx-key-null"}; } if (!scriptPayout.IsPayToPublicKeyHash() && !scriptPayout.IsPayToScriptHash()) { - return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-protx-payee"}; + return {TxValidationResult::TX_BAD_SPECIAL, "bad-protx-payee"}; } CTxDestination payoutDest; if (!ExtractDestination(scriptPayout, payoutDest)) { // should not happen as we checked script types before - return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-protx-payee-dest"}; + return {TxValidationResult::TX_BAD_SPECIAL, "bad-protx-payee-dest"}; } // don't allow reuse of payout key for other keys (don't allow people to put the payee key onto an online server) if (payoutDest == CTxDestination(PKHash(keyIDOwner)) || payoutDest == CTxDestination(PKHash(keyIDVoting))) { - return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-protx-payee-reuse"}; + return {TxValidationResult::TX_BAD_SPECIAL, "bad-protx-payee-reuse"}; } if (nOperatorReward > 10000) { - return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-protx-operator-reward"}; + return {TxValidationResult::TX_BAD_SPECIAL, "bad-protx-operator-reward"}; } return {}; @@ -87,7 +87,7 @@ std::string CProRegTx::ToString() const maybe_error CProUpServTx::IsTriviallyValid(bool is_bls_legacy_scheme) const { if (nVersion == 0 || nVersion > GetVersion(is_bls_legacy_scheme)) { - return {ValidationInvalidReason::CONSENSUS, "bad-protx-version"}; + return {TxValidationResult::TX_CONSENSUS, "bad-protx-version"}; } return {}; @@ -108,17 +108,17 @@ std::string CProUpServTx::ToString() const maybe_error CProUpRegTx::IsTriviallyValid(bool is_bls_legacy_scheme) const { if (nVersion == 0 || nVersion > GetVersion(is_bls_legacy_scheme)) { - return {ValidationInvalidReason::CONSENSUS, "bad-protx-version"}; + return {TxValidationResult::TX_CONSENSUS, "bad-protx-version"}; } if (nMode != 0) { - return {ValidationInvalidReason::CONSENSUS, "bad-protx-mode"}; + return {TxValidationResult::TX_CONSENSUS, "bad-protx-mode"}; } if (!pubKeyOperator.IsValid() || keyIDVoting.IsNull()) { - return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-protx-key-null"}; + return {TxValidationResult::TX_BAD_SPECIAL, "bad-protx-key-null"}; } if (!scriptPayout.IsPayToPublicKeyHash() && !scriptPayout.IsPayToScriptHash()) { - return {ValidationInvalidReason::TX_BAD_SPECIAL, "bad-protx-payee"}; + return {TxValidationResult::TX_BAD_SPECIAL, "bad-protx-payee"}; } return {}; } @@ -138,13 +138,13 @@ std::string CProUpRegTx::ToString() const maybe_error CProUpRevTx::IsTriviallyValid(bool is_bls_legacy_scheme) const { if (nVersion == 0 || nVersion > GetVersion(is_bls_legacy_scheme)) { - return {ValidationInvalidReason::CONSENSUS, "bad-protx-version"}; + return {TxValidationResult::TX_CONSENSUS, "bad-protx-version"}; } // nReason < CProUpRevTx::REASON_NOT_SPECIFIED is always `false` since // nReason is unsigned and CProUpRevTx::REASON_NOT_SPECIFIED == 0 if (nReason > CProUpRevTx::REASON_LAST) { - return {ValidationInvalidReason::CONSENSUS, "bad-protx-reason"}; + return {TxValidationResult::TX_CONSENSUS, "bad-protx-reason"}; } return {}; } diff --git a/src/evo/providertx.h b/src/evo/providertx.h index e113911eab..17395bda3f 100644 --- a/src/evo/providertx.h +++ b/src/evo/providertx.h @@ -20,7 +20,7 @@ class CBlockIndex; class CCoinsViewCache; -class CValidationState; +class TxValidationState; class CProRegTx { @@ -329,7 +329,7 @@ template static maybe_error CheckInputsHash(const CTransaction& tx, const ProTx& proTx) { if (uint256 inputsHash = CalcTxInputsHash(tx); inputsHash != proTx.inputsHash) { - return {ValidationInvalidReason::CONSENSUS, "bad-protx-inputs-hash"}; + return {TxValidationResult::TX_CONSENSUS, "bad-protx-inputs-hash"}; } return {}; diff --git a/src/evo/specialtx.h b/src/evo/specialtx.h index 7eb1adaa1d..8eb755370c 100644 --- a/src/evo/specialtx.h +++ b/src/evo/specialtx.h @@ -17,11 +17,11 @@ struct maybe_error { bool did_err{false}; - ValidationInvalidReason reason{ValidationInvalidReason::CONSENSUS}; + TxValidationResult reason{TxValidationResult::TX_CONSENSUS}; std::string_view error_str; constexpr maybe_error() = default; - constexpr maybe_error(ValidationInvalidReason reasonIn, std::string_view err): did_err(true), reason(reasonIn), error_str(err) {}; + constexpr maybe_error(TxValidationResult reasonIn, std::string_view err): did_err(true), reason(reasonIn), error_str(err) {}; }; template diff --git a/src/evo/specialtxman.cpp b/src/evo/specialtxman.cpp index 0c2083d3f9..e3837c50f0 100644 --- a/src/evo/specialtxman.cpp +++ b/src/evo/specialtxman.cpp @@ -16,7 +16,7 @@ #include #include -bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, const CCoinsViewCache& view, bool check_sigs) +bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state, const CCoinsViewCache& view, bool check_sigs) { AssertLockHeld(cs_main); @@ -24,7 +24,7 @@ bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVali return true; if (pindexPrev && pindexPrev->nHeight + 1 < Params().GetConsensus().DIP0003Height) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-tx-type"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-tx-type"); } try { @@ -46,13 +46,13 @@ bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVali } } catch (const std::exception& e) { LogPrintf("%s -- failed: %s\n", __func__, e.what()); - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "failed-check-special-tx"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "failed-check-special-tx"); } - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, "bad-tx-type-check"); + return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-tx-type-check"); } -bool ProcessSpecialTx(const CTransaction& tx, const CBlockIndex* pindex, CValidationState& state) +bool ProcessSpecialTx(const CTransaction& tx, const CBlockIndex* pindex, TxValidationState& state) { if (tx.nVersion != 3 || tx.nType == TRANSACTION_NORMAL) { return true; @@ -72,7 +72,7 @@ bool ProcessSpecialTx(const CTransaction& tx, const CBlockIndex* pindex, CValida return true; // handled per block } - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-tx-type-proc"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-tx-type-proc"); } bool UndoSpecialTx(const CTransaction& tx, const CBlockIndex* pindex) @@ -99,7 +99,7 @@ bool UndoSpecialTx(const CTransaction& tx, const CBlockIndex* pindex) } bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, llmq::CQuorumBlockProcessor& quorum_block_processor, - CValidationState& state, const CCoinsViewCache& view, bool fJustCheck, bool fCheckCbTxMerleRoots) + BlockValidationState& state, const CCoinsViewCache& view, bool fJustCheck, bool fCheckCbTxMerleRoots) { AssertLockHeld(cs_main); @@ -112,13 +112,18 @@ bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, ll int64_t nTime1 = GetTimeMicros(); for (const auto& ptr_tx : block.vtx) { - if (!CheckSpecialTx(*ptr_tx, pindex->pprev, state, view, fCheckCbTxMerleRoots)) { - // pass the state returned by the function above - return false; + TxValidationState tx_state; + // At this moment CheckSpecialTx() and ProcessSpecialTx() may fail by 2 possible ways: + // consensus failures and "TX_BAD_SPECIAL" + if (!CheckSpecialTx(*ptr_tx, pindex->pprev, tx_state, view, fCheckCbTxMerleRoots)) { + assert(tx_state.GetResult() == TxValidationResult::TX_CONSENSUS || tx_state.GetResult() == TxValidationResult::TX_BAD_SPECIAL); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, tx_state.GetRejectReason(), + strprintf("Special Transaction check failed (tx hash %s) %s", ptr_tx->GetHash().ToString(), tx_state.GetDebugMessage())); } - if (!ProcessSpecialTx(*ptr_tx, pindex, state)) { - // pass the state returned by the function above - return false; + if (!ProcessSpecialTx(*ptr_tx, pindex, tx_state)) { + assert(tx_state.GetResult() == TxValidationResult::TX_CONSENSUS || tx_state.GetResult() == TxValidationResult::TX_BAD_SPECIAL); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, tx_state.GetRejectReason(), + strprintf("Process Special Transaction failed (tx hash %s) %s", ptr_tx->GetHash().ToString(), tx_state.GetDebugMessage())); } } @@ -154,7 +159,7 @@ bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, ll LogPrint(BCLog::BENCHMARK, " - CheckCbTxMerkleRoots: %.2fms [%.2fs]\n", 0.001 * (nTime5 - nTime4), nTimeMerkle * 0.000001); } catch (const std::exception& e) { LogPrintf("%s -- failed: %s\n", __func__, e.what()); - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "failed-procspectxsinblock"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "failed-procspectxsinblock"); } return true; diff --git a/src/evo/specialtxman.h b/src/evo/specialtxman.h index e526a20a90..ed8d4b5c31 100644 --- a/src/evo/specialtxman.h +++ b/src/evo/specialtxman.h @@ -9,19 +9,20 @@ #include #include +class BlockValidationState; class CBlock; class CBlockIndex; class CCoinsViewCache; -class CValidationState; +class TxValidationState; namespace llmq { class CQuorumBlockProcessor; } // namespace llmq extern CCriticalSection cs_main; -bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, const CCoinsViewCache& view, bool check_sigs) EXCLUSIVE_LOCKS_REQUIRED(cs_main); +bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state, const CCoinsViewCache& view, bool check_sigs) EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, llmq::CQuorumBlockProcessor& quorum_block_processor, - CValidationState& state, const CCoinsViewCache& view, bool fJustCheck, bool fCheckCbTxMerleRoots) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + BlockValidationState& state, const CCoinsViewCache& view, bool fJustCheck, bool fCheckCbTxMerleRoots) EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool UndoSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, llmq::CQuorumBlockProcessor& quorum_block_processor) EXCLUSIVE_LOCKS_REQUIRED(cs_main); #endif // BITCOIN_EVO_SPECIALTXMAN_H diff --git a/src/init.cpp b/src/init.cpp index fd758a1201..09d8e3d967 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -928,7 +928,7 @@ static void ThreadImport(ChainstateManager& chainman, std::vector vImp // the chainman unique_ptrs since ABC requires us not to be holding cs_main, so retrieve // the relevant pointers before the ABC call. for (CChainState* chainstate : WITH_LOCK(::cs_main, return chainman.GetAll())) { - CValidationState state; + BlockValidationState state; if (!chainstate->ActivateBestChain(state, chainparams, nullptr)) { LogPrintf("Failed to connect best block (%s)\n", FormatStateMessage(state)); StartShutdown(); diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h index 683d6c1955..98471db956 100644 --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -23,7 +23,6 @@ class CFeeRate; class CRPCCommand; class CScheduler; class CTxMemPool; -class CValidationState; class CFeeRate; class CBlockIndex; class Coin; diff --git a/src/llmq/blockprocessor.cpp b/src/llmq/blockprocessor.cpp index 86782e14e4..606ae15cf5 100644 --- a/src/llmq/blockprocessor.cpp +++ b/src/llmq/blockprocessor.cpp @@ -136,7 +136,7 @@ void CQuorumBlockProcessor::ProcessMessage(const CNode& peer, std::string_view m AddMineableCommitment(qc); } -bool CQuorumBlockProcessor::ProcessBlock(const CBlock& block, const CBlockIndex* pindex, CValidationState& state, bool fJustCheck, bool fBLSChecks) +bool CQuorumBlockProcessor::ProcessBlock(const CBlock& block, const CBlockIndex* pindex, BlockValidationState& state, bool fJustCheck, bool fBLSChecks) { AssertLockHeld(cs_main); @@ -172,11 +172,11 @@ bool CQuorumBlockProcessor::ProcessBlock(const CBlock& block, const CBlockIndex* const auto numCommitmentsInNewBlock = qcs.count(params.type); if (numCommitmentsRequired < numCommitmentsInNewBlock) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-not-allowed"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-not-allowed"); } if (numCommitmentsRequired > numCommitmentsInNewBlock) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-missing"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-missing"); } if (llmq::utils::IsQuorumRotationEnabled(params, pindex)) { LogPrintf("[ProcessBlock] h[%d] numCommitmentsRequired[%d] numCommitmentsInNewBlock[%d]\n", pindex->nHeight, numCommitmentsRequired, numCommitmentsInNewBlock); @@ -210,7 +210,7 @@ static std::tuple BuildInversed return std::make_tuple(DB_MINED_COMMITMENT_BY_INVERSED_HEIGHT_Q_INDEXED, llmqType, quorumIndex, htobe32(std::numeric_limits::max() - nMinedHeight)); } -bool CQuorumBlockProcessor::ProcessCommitment(int nHeight, const uint256& blockHash, const CFinalCommitment& qc, CValidationState& state, bool fJustCheck, bool fBLSChecks) +bool CQuorumBlockProcessor::ProcessCommitment(int nHeight, const uint256& blockHash, const CFinalCommitment& qc, BlockValidationState& state, bool fJustCheck, bool fBLSChecks) { AssertLockHeld(cs_main); @@ -234,31 +234,31 @@ bool CQuorumBlockProcessor::ProcessCommitment(int nHeight, const uint256& blockH if (quorumHash.IsNull()) { LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s height=%d, type=%d, quorumIndex=%d, quorumHash=%s, signers=%s, validMembers=%d, quorumPublicKey=%s quorumHash is null.\n", __func__, nHeight, ToUnderlying(qc.llmqType), qc.quorumIndex, quorumHash.ToString(), qc.CountSigners(), qc.CountValidMembers(), qc.quorumPublicKey.ToString()); - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-block"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-block"); } if (quorumHash != qc.quorumHash) { LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s height=%d, type=%d, quorumIndex=%d, quorumHash=%s, qc.quorumHash=%s signers=%s, validMembers=%d, quorumPublicKey=%s non equal quorumHash.\n", __func__, nHeight, ToUnderlying(qc.llmqType), qc.quorumIndex, quorumHash.ToString(), qc.quorumHash.ToString(), qc.CountSigners(), qc.CountValidMembers(), qc.quorumPublicKey.ToString()); - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-block"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-block"); } if (qc.IsNull()) { if (!qc.VerifyNull()) { LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s height=%d, type=%d, quorumIndex=%d, quorumHash=%s, signers=%s, validMembers=%dqc verifynull failed.\n", __func__, nHeight, ToUnderlying(qc.llmqType), qc.quorumIndex, quorumHash.ToString(), qc.CountSigners(), qc.CountValidMembers()); - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-invalid-null"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-invalid-null"); } return true; } if (HasMinedCommitment(llmq_params.type, quorumHash)) { // should not happen as it's already handled in ProcessBlock - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-dup"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-dup"); } if (!IsMiningPhase(llmq_params, nHeight)) { // should not happen as it's already handled in ProcessBlock - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-height"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-height"); } const auto* pQuorumBaseBlockIndex = g_chainman.m_blockman.LookupBlockIndex(qc.quorumHash); @@ -266,7 +266,7 @@ bool CQuorumBlockProcessor::ProcessCommitment(int nHeight, const uint256& blockH if (!qc.Verify(pQuorumBaseBlockIndex, fBLSChecks)) { LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s height=%d, type=%d, quorumIndex=%d, quorumHash=%s, signers=%s, validMembers=%d, quorumPublicKey=%s qc verify failed.\n", __func__, nHeight, ToUnderlying(qc.llmqType), qc.quorumIndex, quorumHash.ToString(), qc.CountSigners(), qc.CountValidMembers(), qc.quorumPublicKey.ToString()); - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-invalid"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-invalid"); } if (fJustCheck) { @@ -313,7 +313,7 @@ bool CQuorumBlockProcessor::UndoBlock(const CBlock& block, const CBlockIndex* pi llmq::utils::PreComputeQuorumMembers(pindex, true); std::multimap qcs; - CValidationState dummy; + BlockValidationState dummy; if (!GetCommitmentsFromBlock(block, pindex, qcs, dummy)) { return false; } @@ -378,7 +378,7 @@ bool CQuorumBlockProcessor::UpgradeDB() assert(r); std::multimap qcs; - CValidationState dummyState; + BlockValidationState dummyState; GetCommitmentsFromBlock(block, pindex, qcs, dummyState); for (const auto& p : qcs) { @@ -407,7 +407,7 @@ bool CQuorumBlockProcessor::UpgradeDB() return true; } -bool CQuorumBlockProcessor::GetCommitmentsFromBlock(const CBlock& block, const CBlockIndex* pindex, std::multimap& ret, CValidationState& state) +bool CQuorumBlockProcessor::GetCommitmentsFromBlock(const CBlock& block, const CBlockIndex* pindex, std::multimap& ret, BlockValidationState& state) { AssertLockHeld(cs_main); @@ -421,19 +421,19 @@ bool CQuorumBlockProcessor::GetCommitmentsFromBlock(const CBlock& block, const C if (!GetTxPayload(*tx, qc)) { // should not happen as it was verified before processing the block LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s height=%d GetTxPayload fails\n", __func__, pindex->nHeight); - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-payload"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-payload"); } const auto& llmq_params_opt = GetLLMQParams(qc.commitment.llmqType); if (!llmq_params_opt.has_value()) { // should not happen as it was verified before processing the block - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-commitment-type"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-commitment-type"); } // only allow one commitment per type and per block (This was changed with rotation) if (!utils::IsQuorumRotationEnabled(llmq_params_opt.value(), pindex)) { if (ret.count(qc.commitment.llmqType) != 0) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-dup"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-dup"); } } @@ -442,7 +442,7 @@ bool CQuorumBlockProcessor::GetCommitmentsFromBlock(const CBlock& block, const C } if (pindex->nHeight < consensus.DIP0003Height && !ret.empty()) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-premature"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-premature"); } return true; diff --git a/src/llmq/blockprocessor.h b/src/llmq/blockprocessor.h index 529d24e009..a5a28fc535 100644 --- a/src/llmq/blockprocessor.h +++ b/src/llmq/blockprocessor.h @@ -16,9 +16,9 @@ #include +class BlockValidationState; class CNode; class CConnman; -class CValidationState; class CEvoDB; extern CCriticalSection cs_main; @@ -49,7 +49,7 @@ public: void ProcessMessage(const CNode& peer, std::string_view msg_type, CDataStream& vRecv); - bool ProcessBlock(const CBlock& block, const CBlockIndex* pindex, CValidationState& state, bool fJustCheck, bool fBLSChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + bool ProcessBlock(const CBlock& block, const CBlockIndex* pindex, BlockValidationState& state, bool fJustCheck, bool fBLSChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool UndoBlock(const CBlock& block, const CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main); void AddMineableCommitment(const CFinalCommitment& fqc); @@ -67,8 +67,8 @@ public: std::vector> GetLastMinedCommitmentsPerQuorumIndexUntilBlock(Consensus::LLMQType llmqType, const CBlockIndex* pindex, size_t cycle) const; std::optional GetLastMinedCommitmentsByQuorumIndexUntilBlock(Consensus::LLMQType llmqType, const CBlockIndex* pindex, int quorumIndex, size_t cycle) const; private: - static bool GetCommitmentsFromBlock(const CBlock& block, const CBlockIndex* pindex, std::multimap& ret, CValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - bool ProcessCommitment(int nHeight, const uint256& blockHash, const CFinalCommitment& qc, CValidationState& state, bool fJustCheck, bool fBLSChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + static bool GetCommitmentsFromBlock(const CBlock& block, const CBlockIndex* pindex, std::multimap& ret, BlockValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + bool ProcessCommitment(int nHeight, const uint256& blockHash, const CFinalCommitment& qc, BlockValidationState& state, bool fJustCheck, bool fBLSChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main); static bool IsMiningPhase(const Consensus::LLMQParams& llmqParams, int nHeight) EXCLUSIVE_LOCKS_REQUIRED(cs_main); size_t GetNumCommitmentsRequired(const Consensus::LLMQParams& llmqParams, int nHeight) const EXCLUSIVE_LOCKS_REQUIRED(cs_main); static uint256 GetQuorumBlockHash(const Consensus::LLMQParams& llmqParams, int nHeight, int quorumIndex) EXCLUSIVE_LOCKS_REQUIRED(cs_main); diff --git a/src/llmq/chainlocks.cpp b/src/llmq/chainlocks.cpp index 746a36de4f..7a73939daf 100644 --- a/src/llmq/chainlocks.cpp +++ b/src/llmq/chainlocks.cpp @@ -494,20 +494,20 @@ void CChainLocksHandler::EnforceBestChainLock() } } - CValidationState state; + BlockValidationState dummy_state; const auto ¶ms = Params(); // Go backwards through the chain referenced by clsig until we find a block that is part of the main chain. // For each of these blocks, check if there are children that are NOT part of the chain referenced by clsig // and mark all of them as conflicting. LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- enforcing block %s via CLSIG (%s)\n", __func__, pindex->GetBlockHash().ToString(), clsig->ToString()); - ::ChainstateActive().EnforceBlock(state, params, pindex); + ::ChainstateActive().EnforceBlock(dummy_state, params, pindex); bool activateNeeded = WITH_LOCK(::cs_main, return ::ChainActive().Tip()->GetAncestor(currentBestChainLockBlockIndex->nHeight)) != currentBestChainLockBlockIndex; if (activateNeeded) { - if (!::ChainstateActive().ActivateBestChain(state, params)) { - LogPrintf("CChainLocksHandler::%s -- ActivateBestChain failed: %s\n", __func__, FormatStateMessage(state)); + if (!::ChainstateActive().ActivateBestChain(dummy_state, params)) { + LogPrintf("CChainLocksHandler::%s -- ActivateBestChain failed: %s\n", __func__, FormatStateMessage(dummy_state)); return; } LOCK(cs_main); diff --git a/src/llmq/commitment.cpp b/src/llmq/commitment.cpp index 730d1814ea..78ff897e10 100644 --- a/src/llmq/commitment.cpp +++ b/src/llmq/commitment.cpp @@ -176,18 +176,18 @@ bool CFinalCommitment::VerifySizes(const Consensus::LLMQParams& params) const return true; } -bool CheckLLMQCommitment(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) +bool CheckLLMQCommitment(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state) { CFinalCommitmentTxPayload qcTx; if (!GetTxPayload(tx, qcTx)) { LogPrintfFinalCommitment("h[%d] GetTxPayload failed\n", pindexPrev->nHeight); - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-payload"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-payload"); } const auto& llmq_params_opt = GetLLMQParams(qcTx.commitment.llmqType); if (!llmq_params_opt.has_value()) { LogPrintfFinalCommitment("h[%d] GetLLMQParams failed for llmqType[%d]\n", pindexPrev->nHeight, ToUnderlying(qcTx.commitment.llmqType)); - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-commitment-type"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-commitment-type"); } if (LogAcceptCategory(BCLog::LLMQ)) { @@ -201,36 +201,36 @@ bool CheckLLMQCommitment(const CTransaction& tx, const CBlockIndex* pindexPrev, if (qcTx.nVersion == 0 || qcTx.nVersion > CFinalCommitmentTxPayload::CURRENT_VERSION) { LogPrintfFinalCommitment("h[%d] invalid qcTx.nVersion[%d]\n", pindexPrev->nHeight, qcTx.nVersion); - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-version"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-version"); } if (qcTx.nHeight != uint32_t(pindexPrev->nHeight + 1)) { LogPrintfFinalCommitment("h[%d] invalid qcTx.nHeight[%d]\n", pindexPrev->nHeight, qcTx.nHeight); - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-height"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-height"); } const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(qcTx.commitment.quorumHash)); if (pQuorumBaseBlockIndex == nullptr) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-quorum-hash"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-quorum-hash"); } if (pQuorumBaseBlockIndex != pindexPrev->GetAncestor(pQuorumBaseBlockIndex->nHeight)) { // not part of active chain - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-quorum-hash"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-quorum-hash"); } if (qcTx.commitment.IsNull()) { if (!qcTx.commitment.VerifyNull()) { LogPrintfFinalCommitment("h[%d] invalid qcTx.commitment[%s] VerifyNull failed\n", pindexPrev->nHeight, qcTx.commitment.quorumHash.ToString()); - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-invalid-null"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-invalid-null"); } return true; } if (!qcTx.commitment.Verify(pQuorumBaseBlockIndex, false)) { LogPrintfFinalCommitment("h[%d] invalid qcTx.commitment[%s] Verify failed\n", pindexPrev->nHeight, qcTx.commitment.quorumHash.ToString()); - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-qc-invalid"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-invalid"); } LogPrintfFinalCommitment("h[%d] CheckLLMQCommitment VALID\n", pindexPrev->nHeight); diff --git a/src/llmq/commitment.h b/src/llmq/commitment.h index 8021ae550e..1a3cd84950 100644 --- a/src/llmq/commitment.h +++ b/src/llmq/commitment.h @@ -15,7 +15,7 @@ #include class CBlockIndex; -class CValidationState; +class TxValidationState; namespace llmq { @@ -168,7 +168,7 @@ public: } }; -bool CheckLLMQCommitment(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state); +bool CheckLLMQCommitment(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state); } // namespace llmq diff --git a/src/llmq/instantsend.cpp b/src/llmq/instantsend.cpp index d39fb1db88..8e453ead4b 100644 --- a/src/llmq/instantsend.cpp +++ b/src/llmq/instantsend.cpp @@ -1477,7 +1477,7 @@ void CInstantSendManager::ResolveBlockConflicts(const uint256& islockHash, const LogPrintf("CInstantSendManager::%s -- invalidating block %s\n", __func__, pindex->GetBlockHash().ToString()); - CValidationState state; + BlockValidationState state; // need non-const pointer auto pindex2 = WITH_LOCK(::cs_main, return g_chainman.m_blockman.LookupBlockIndex(pindex->GetBlockHash())); if (!::ChainstateActive().InvalidateBlock(state, Params(), pindex2)) { @@ -1495,7 +1495,7 @@ void CInstantSendManager::ResolveBlockConflicts(const uint256& islockHash, const } if (activateBestChain) { - CValidationState state; + BlockValidationState state; if (!::ChainstateActive().ActivateBestChain(state, Params())) { LogPrintf("CChainLocksHandler::%s -- ActivateBestChain failed: %s\n", __func__, FormatStateMessage(state)); // This should not have happened and we are in a state were it's not safe to continue anymore diff --git a/src/miner.cpp b/src/miner.cpp index 3e4b4171ec..9673c8a2bc 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -205,7 +205,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(CChainState& chai cbTx.nHeight = nHeight; - CValidationState state; + BlockValidationState state; if (!CalcCbTxMerkleRootMNList(*pblock, pindexPrev, cbTx.merkleRootMNList, state, ::ChainstateActive().CoinsTip())) { throw std::runtime_error(strprintf("%s: CalcCbTxMerkleRootMNList failed: %s", __func__, FormatStateMessage(state))); } @@ -233,8 +233,8 @@ std::unique_ptr BlockAssembler::CreateNewBlock(CChainState& chai pblocktemplate->nPrevBits = pindexPrev->nBits; pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(*pblock->vtx[0]); - CValidationState state; assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate)); + BlockValidationState state; if (!TestBlockValidity(state, m_clhandler, m_evoDb, chainparams, chainstate, *pblock, pindexPrev, false, false)) { throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state))); } diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 152bd572e4..dfe8442226 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1222,7 +1222,7 @@ bool IsBanned(NodeId pnode) } /** - * Potentially mark a node discouraged based on the contents of a CValidationState object + * Potentially mark a node discouraged 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 a compact @@ -1231,20 +1231,20 @@ bool IsBanned(NodeId pnode) * * @return Returns true if the peer was punished (probably disconnected) */ -static bool MaybePunishNode(NodeId nodeid, const CValidationState& state, bool via_compact_block, const std::string& message = "") +static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state, bool via_compact_block, const std::string& message = "") { - switch (state.GetReason()) { - case ValidationInvalidReason::NONE: + 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) { Misbehaving(nodeid, 100, message); return true; } break; - case ValidationInvalidReason::CACHED_INVALID: + case BlockValidationResult::BLOCK_CACHED_INVALID: { LOCK(cs_main); CNodeState *node_state = State(nodeid); @@ -1260,25 +1260,18 @@ static bool MaybePunishNode(NodeId nodeid, const CValidationState& state, bool v } 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: Misbehaving(nodeid, 100, message); return true; // Conflicting (but not necessarily invalid) data or different policy: - case ValidationInvalidReason::BLOCK_MISSING_PREV: - case ValidationInvalidReason::BLOCK_CHAINLOCK: - case ValidationInvalidReason::TX_BAD_SPECIAL: - case ValidationInvalidReason::TX_CONFLICT_LOCK: + case BlockValidationResult::BLOCK_MISSING_PREV: + case BlockValidationResult::BLOCK_CHAINLOCK: 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 != "") { @@ -1287,6 +1280,41 @@ static bool MaybePunishNode(NodeId nodeid, const CValidationState& state, bool v 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: + // moved from BLOCK + case TxValidationResult::TX_BAD_SPECIAL: + case TxValidationResult::TX_CONFLICT_LOCK: + break; + } + if (message != "") { + LogPrint(BCLog::NET, "peer=%d: %s\n", nodeid, message); + } + return false; +} ////////////////////////////////////////////////////////////////////////////// // @@ -1507,7 +1535,7 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB * Handle invalid block rejection and consequent peer discouragement, maintain which * peers announce compact blocks. */ -void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationState& state) { +void PeerLogicValidation::BlockChecked(const CBlock& block, const BlockValidationState& state) { LOCK(cs_main); const uint256 hash(block.GetHash()); @@ -1518,7 +1546,7 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationSta if (state.IsInvalid() && it != mapBlockSource.end() && State(it->second.first)) { - 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: // 1. The block is valid @@ -1711,7 +1739,7 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c } } // release cs_main before calling ActivateBestChain if (need_activate_chain) { - CValidationState state; + BlockValidationState state; if (!::ChainstateActive().ActivateBestChain(state, Params(), a_recent_block)) { LogPrint(BCLog::NET, "failed to activate chain (%s)\n", FormatStateMessage(state)); } @@ -2127,11 +2155,10 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan } } - CValidationState state; - CBlockHeader first_invalid_header; - if (!chainman.ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast, &first_invalid_header)) { + BlockValidationState state; + if (!chainman.ProcessNewBlockHeaders(headers, state, chainparams, &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; } } @@ -2266,14 +2293,13 @@ void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::setsecond.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 + // 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). - CValidationState orphan_state; + TxValidationState orphan_state; if (setMisbehaving.count(fromPeer)) continue; - if (AcceptToMemoryPool(::ChainstateActive(), mempool, orphan_state, porphanTx, &fMissingInputs2 /* pfMissingInputs */, + if (AcceptToMemoryPool(::ChainstateActive(), mempool, orphan_state, porphanTx, false /* bypass_limits */, 0 /* nAbsurdFee */)) { LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString()); RelayTransaction(orphanTx.GetHash(), connman); @@ -2287,10 +2313,10 @@ void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::setinsert(orphanHash); EraseOrphanTx(orphanHash); @@ -3130,7 +3155,7 @@ void PeerLogicValidation::ProcessMessage( LOCK(cs_most_recent_block); a_recent_block = most_recent_block; } - CValidationState state; + BlockValidationState state; if (!::ChainstateActive().ActivateBestChain(state, Params(), a_recent_block)) { LogPrint(BCLog::NET, "failed to activate chain (%s)\n", FormatStateMessage(state)); } @@ -3346,10 +3371,9 @@ void PeerLogicValidation::ProcessMessage( LOCK2(cs_main, g_cs_orphans); - bool fMissingInputs = false; - CValidationState state; + TxValidationState state; - if (!AlreadyHave(inv, m_mempool, *m_llmq_ctx) && AcceptToMemoryPool(::ChainstateActive(), m_mempool, state, ptx, &fMissingInputs /* pfMissingInputs */, + if (!AlreadyHave(inv, m_mempool, *m_llmq_ctx) && AcceptToMemoryPool(::ChainstateActive(), m_mempool, state, ptx, false /* bypass_limits */, 0 /* nAbsurdFee */)) { // Process custom txes, this changes AlreadyHave to "true" if (nInvType == MSG_DSTX) { @@ -3380,7 +3404,7 @@ void PeerLogicValidation::ProcessMessage( // Recursively process any orphan transactions that depended on this one ProcessOrphanTx(m_connman, m_mempool, pfrom.orphan_work_set); } - else if (fMissingInputs) + else if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) { bool fRejectedParents = false; // It may be the case that the orphans parents have all been rejected for (const CTxIn& txin : tx.vin) { @@ -3417,7 +3441,6 @@ void PeerLogicValidation::ProcessMessage( m_llmq_ctx->isman->TransactionRemovedFromMempool(ptx); } } else { - assert(IsTransactionReason(state.GetReason())); assert(recentRejects); recentRejects->insert(tx.GetHash()); if (RecursiveDynamicUsage(*ptx) < 100000) { @@ -3460,7 +3483,7 @@ void PeerLogicValidation::ProcessMessage( LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(), pfrom.GetId(), FormatStateMessage(state)); - MaybePunishNode(pfrom.GetId(), state, /*via_compact_block*/ false); + MaybePunishNodeForTx(pfrom.GetId(), state); m_llmq_ctx->isman->TransactionRemovedFromMempool(ptx); } return; @@ -3495,10 +3518,10 @@ void PeerLogicValidation::ProcessMessage( } const CBlockIndex *pindex = nullptr; - CValidationState state; + BlockValidationState state; if (!m_chainman.ProcessNewBlockHeaders({cmpctblock.header}, state, chainparams, &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; } } diff --git a/src/net_processing.h b/src/net_processing.h index 2f94dae0b2..4932f9267d 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -52,7 +52,7 @@ public: /** * Overridden from CValidationInterface. */ - void BlockChecked(const CBlock& block, const CValidationState& state) override; + void BlockChecked(const CBlock& block, const BlockValidationState& state) override; /** * Overridden from CValidationInterface. */ diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp index 21d762c51d..82f42ccc44 100644 --- a/src/node/transaction.cpp +++ b/src/node/transaction.cpp @@ -39,18 +39,16 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t } if (!node.mempool->exists(hashTx)) { // Transaction is not already in the mempool. Submit it. - CValidationState state; - bool fMissingInputs; - if (!AcceptToMemoryPool(::ChainstateActive(), *node.mempool, state, std::move(tx), &fMissingInputs, + TxValidationState state; + if (!AcceptToMemoryPool(::ChainstateActive(), *node.mempool, state, std::move(tx), bypass_limits, max_tx_fee)) { + err_string = FormatStateMessage(state); if (state.IsInvalid()) { - err_string = FormatStateMessage(state); - return TransactionError::MEMPOOL_REJECTED; - } else { - if (fMissingInputs) { + if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS) { return TransactionError::MISSING_INPUTS; } - err_string = FormatStateMessage(state); + return TransactionError::MEMPOOL_REJECTED; + } else { return TransactionError::MEMPOOL_ERROR; } } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 3a9101bbc5..732e89066e 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1811,7 +1811,7 @@ static UniValue preciousblock(const JSONRPCRequest& request) } } - CValidationState state; + BlockValidationState state; ::ChainstateActive().PreciousBlock(state, Params(), pblockindex); if (!state.IsValid()) { @@ -1836,7 +1836,7 @@ static UniValue invalidateblock(const JSONRPCRequest& request) }.Check(request); uint256 hash(ParseHashV(request.params[0], "blockhash")); - CValidationState state; + BlockValidationState state; CBlockIndex* pblockindex; { @@ -1886,7 +1886,7 @@ static UniValue reconsiderblock(const JSONRPCRequest& request) ::ChainstateActive().ResetBlockFailureFlags(pblockindex); } - CValidationState state; + BlockValidationState state; ::ChainstateActive().ActivateBestChain(state, Params()); if (!state.IsValid()) { diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index 58dff88ebc..66420c8708 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -326,7 +326,7 @@ static std::string SignAndSendSpecialTx(const JSONRPCRequest& request, const CMu { LOCK(cs_main); - CValidationState state; + TxValidationState state; if (!CheckSpecialTx(CTransaction(tx), ::ChainActive().Tip(), state, ::ChainstateActive().CoinsTip(), true)) { throw std::runtime_error(FormatStateMessage(state)); } diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 6e540ee417..f7463c1a32 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -380,7 +380,7 @@ static UniValue generateblock(const JSONRPCRequest& request) { LOCK(cs_main); - CValidationState state; + BlockValidationState state; if (!TestBlockValidity(state, *llmq_ctx.clhandler, *node_context.evodb, chainparams, ::ChainstateActive(), block, g_chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock), false, false)) { throw JSONRPCError(RPC_VERIFY_ERROR, strprintf("TestBlockValidity failed: %s", state.GetRejectReason())); } @@ -483,7 +483,7 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request) // NOTE: Assumes a conclusive result; if result is inconclusive, it must be handled by caller -static UniValue BIP22ValidationResult(const CValidationState& state) +static UniValue BIP22ValidationResult(const BlockValidationState& state) { if (state.IsValid()) return NullUniValue; @@ -670,7 +670,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) // TestBlockValidity only supports blocks built on the current Tip if (block.hashPrevBlock != pindexPrev->GetBlockHash()) return "inconclusive-not-best-prevblk"; - CValidationState state; + BlockValidationState state; TestBlockValidity(state, *llmq_ctx.clhandler, *node_context.evodb, Params(), ::ChainstateActive(), block, pindexPrev, false, true); return BIP22ValidationResult(state); } @@ -951,12 +951,12 @@ class submitblock_StateCatcher : public CValidationInterface 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 { + void BlockChecked(const CBlock& block, const BlockValidationState& stateIn) override { if (block.GetHash() != hash) return; found = true; @@ -1046,8 +1046,8 @@ static UniValue submitheader(const JSONRPCRequest& request) } } - CValidationState state; - EnsureChainman(request.context).ProcessNewBlockHeaders({h}, state, Params(), /* ppindex */ nullptr, /* first_invalid */ nullptr); + BlockValidationState state; + EnsureChainman(request.context).ProcessNewBlockHeaders({h}, state, Params()); if (state.IsValid()) return NullUniValue; if (state.IsError()) { throw JSONRPCError(RPC_VERIFY_ERROR, FormatStateMessage(state)); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 0392695503..31f43579f6 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -918,20 +918,21 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request) UniValue result_0(UniValue::VOBJ); result_0.pushKV("txid", tx_hash.GetHex()); - CValidationState state; - bool missing_inputs; + TxValidationState state; bool test_accept_res; { LOCK(cs_main); - test_accept_res = AcceptToMemoryPool(::ChainstateActive(), mempool, state, std::move(tx), &missing_inputs, + test_accept_res = AcceptToMemoryPool(::ChainstateActive(), mempool, state, std::move(tx), false /* bypass_limits */, max_raw_tx_fee, /* test_accept */ true); } result_0.pushKV("allowed", test_accept_res); if (!test_accept_res) { if (state.IsInvalid()) { - result_0.pushKV("reject-reason", strprintf("%s", 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("%s", state.GetRejectReason())); + } } else { result_0.pushKV("reject-reason", state.GetRejectReason()); } diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp index 63fb2e39a2..a208a0e6a0 100644 --- a/src/test/blockfilter_index_tests.cpp +++ b/src/test/blockfilter_index_tests.cpp @@ -100,8 +100,8 @@ bool BuildChainTestingSetup::BuildChain(const CBlockIndex* pindex, block = std::make_shared(CreateBlock(pindex, no_txns, coinbase_script_pub_key)); CBlockHeader header = block->GetBlockHeader(); - CValidationState state; - if (!Assert(m_node.chainman)->ProcessNewBlockHeaders({header}, state, Params(), &pindex, nullptr)) { + BlockValidationState state; + if (!Assert(m_node.chainman)->ProcessNewBlockHeaders({header}, state, Params(), &pindex)) { return false; } } diff --git a/src/test/evo_deterministicmns_tests.cpp b/src/test/evo_deterministicmns_tests.cpp index ba1a293db4..95f7f11550 100644 --- a/src/test/evo_deterministicmns_tests.cpp +++ b/src/test/evo_deterministicmns_tests.cpp @@ -294,12 +294,12 @@ void FuncDIP3Protx(TestChainSetup& setup) // payload itself. This means, we need to rely on script verification, which takes the hash of the extra payload // into account auto tx2 = MalleateProTxPayout(tx); - CValidationState dummyState; + TxValidationState dummy_state; // Technically, the payload is still valid... { LOCK(cs_main); - BOOST_ASSERT(CheckProRegTx(CTransaction(tx), ::ChainActive().Tip(), dummyState, ::ChainstateActive().CoinsTip(), true)); - BOOST_ASSERT(CheckProRegTx(CTransaction(tx2), ::ChainActive().Tip(), dummyState, ::ChainstateActive().CoinsTip(), true)); + BOOST_ASSERT(CheckProRegTx(CTransaction(tx), ::ChainActive().Tip(), dummy_state, ::ChainstateActive().CoinsTip(), true)); + BOOST_ASSERT(CheckProRegTx(CTransaction(tx2), ::ChainActive().Tip(), dummy_state, ::ChainstateActive().CoinsTip(), true)); } // But the signature should not verify anymore BOOST_ASSERT(CheckTransactionSignature(*(setup.m_node.mempool), tx)); @@ -401,11 +401,11 @@ void FuncDIP3Protx(TestChainSetup& setup) tx = CreateProUpRegTx(*(setup.m_node.mempool), utxos, dmnHashes[0], ownerKeys[dmnHashes[0]], newOperatorKey.GetPublicKey(), ownerKeys[dmnHashes[0]].GetPubKey().GetID(), dmn->pdmnState->scriptPayout, setup.coinbaseKey); // check malleability protection again, but this time by also relying on the signature inside the ProUpRegTx auto tx2 = MalleateProTxPayout(tx); - CValidationState dummyState; + TxValidationState dummy_state; { LOCK(cs_main); - BOOST_ASSERT(CheckProUpRegTx(CTransaction(tx), ::ChainActive().Tip(), dummyState, ::ChainstateActive().CoinsTip(), true)); - BOOST_ASSERT(!CheckProUpRegTx(CTransaction(tx2), ::ChainActive().Tip(), dummyState, ::ChainstateActive().CoinsTip(), true)); + BOOST_ASSERT(CheckProUpRegTx(CTransaction(tx), ::ChainActive().Tip(), dummy_state, ::ChainstateActive().CoinsTip(), true)); + BOOST_ASSERT(!CheckProUpRegTx(CTransaction(tx2), ::ChainActive().Tip(), dummy_state, ::ChainstateActive().CoinsTip(), true)); } BOOST_ASSERT(CheckTransactionSignature(*(setup.m_node.mempool), tx)); BOOST_ASSERT(!CheckTransactionSignature(*(setup.m_node.mempool), tx2)); diff --git a/src/test/fuzz/block.cpp b/src/test/fuzz/block.cpp index 218d6c5e2b..3304ef2090 100644 --- a/src/test/fuzz/block.cpp +++ b/src/test/fuzz/block.cpp @@ -36,17 +36,17 @@ FUZZ_TARGET_INIT(block, initialize_block) return; } const Consensus::Params& consensus_params = Params().GetConsensus(); - CValidationState validation_state_pow_and_merkle; + BlockValidationState validation_state_pow_and_merkle; const bool valid_incl_pow_and_merkle = CheckBlock(block, validation_state_pow_and_merkle, consensus_params, /* fCheckPOW= */ true, /* fCheckMerkleRoot= */ true); assert(validation_state_pow_and_merkle.IsValid() || validation_state_pow_and_merkle.IsInvalid() || validation_state_pow_and_merkle.IsError()); (void)validation_state_pow_and_merkle.Error(""); - CValidationState validation_state_pow; + BlockValidationState validation_state_pow; const bool valid_incl_pow = CheckBlock(block, validation_state_pow, consensus_params, /* fCheckPOW= */ true, /* fCheckMerkleRoot= */ false); assert(validation_state_pow.IsValid() || validation_state_pow.IsInvalid() || validation_state_pow.IsError()); - CValidationState validation_state_merkle; + BlockValidationState validation_state_merkle; const bool valid_incl_merkle = CheckBlock(block, validation_state_merkle, consensus_params, /* fCheckPOW= */ false, /* fCheckMerkleRoot= */ true); assert(validation_state_merkle.IsValid() || validation_state_merkle.IsInvalid() || validation_state_merkle.IsError()); - CValidationState validation_state_none; + BlockValidationState validation_state_none; const bool valid_incl_none = CheckBlock(block, validation_state_none, consensus_params, /* fCheckPOW= */ false, /* fCheckMerkleRoot= */ false); assert(validation_state_none.IsValid() || validation_state_none.IsInvalid() || validation_state_none.IsError()); if (valid_incl_pow_and_merkle) { diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp index 748c4c29a7..69bcfb97c9 100644 --- a/src/test/fuzz/coins_view.cpp +++ b/src/test/fuzz/coins_view.cpp @@ -223,12 +223,12 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view) (void)AreInputsStandard(CTransaction{random_mutable_transaction}, coins_view_cache); }, [&] { - CValidationState state; + TxValidationState state; CAmount tx_fee_out; const CTransaction transaction{random_mutable_transaction}; if (ContainsSpentInput(transaction, coins_view_cache)) { // Avoid: - // consensus/tx_verify.cpp:171: bool Consensus::CheckTxInputs(const CTransaction &, CValidationState &, const CCoinsViewCache &, int, CAmount &): Assertion `!coin.IsSpent()' failed. + // consensus/tx_verify.cpp:171: bool Consensus::CheckTxInputs(const CTransaction &, TxValidationState&, const CCoinsViewCache &, int, CAmount &): Assertion `!coin.IsSpent()' failed. return; } try { diff --git a/src/test/fuzz/transaction.cpp b/src/test/fuzz/transaction.cpp index 1366df9313..160a5369f2 100644 --- a/src/test/fuzz/transaction.cpp +++ b/src/test/fuzz/transaction.cpp @@ -60,7 +60,7 @@ FUZZ_TARGET_INIT(transaction, initialize_transaction) return; } - CValidationState state_with_dupe_check; + TxValidationState state_with_dupe_check; (void)CheckTransaction(tx, state_with_dupe_check); std::string reason; diff --git a/src/test/interfaces_tests.cpp b/src/test/interfaces_tests.cpp index 97231003bb..a60f4c4629 100644 --- a/src/test/interfaces_tests.cpp +++ b/src/test/interfaces_tests.cpp @@ -97,7 +97,7 @@ BOOST_AUTO_TEST_CASE(findCommonAncestor) const CChain& active = Assert(m_node.chainman)->ActiveChain(); auto* orig_tip = active.Tip(); for (int i = 0; i < 10; ++i) { - CValidationState state; + BlockValidationState state; ChainstateActive().InvalidateBlock(state, Params(), active.Tip()); } BOOST_CHECK_EQUAL(active.Height(), orig_tip->nHeight - 10); diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 91f3a10613..dacf12688d 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -536,7 +536,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5U); } // unlock cs_main while calling InvalidateBlock - CValidationState state; + BlockValidationState state; ::ChainstateActive().InvalidateBlock(state, chainparams, WITH_LOCK(cs_main, return ::ChainActive().Tip())); SetMockTime(0); diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index 5c4b0f4433..3ecf337964 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -187,7 +187,7 @@ BOOST_AUTO_TEST_CASE(sighash_from_data) CDataStream stream(ParseHex(raw_tx), SER_NETWORK, PROTOCOL_VERSION); stream >> tx; - CValidationState state; + TxValidationState state; BOOST_CHECK_MESSAGE(CheckTransaction(*tx, state), strTest); BOOST_CHECK(state.IsValid()); diff --git a/src/test/specialtx_tests.cpp b/src/test/specialtx_tests.cpp index 773d1a47cd..04dc3329af 100644 --- a/src/test/specialtx_tests.cpp +++ b/src/test/specialtx_tests.cpp @@ -18,15 +18,15 @@ #include -bool VerifyMNHFTx(const CTransaction& tx, CValidationState& state) +bool VerifyMNHFTx(const CTransaction& tx, TxValidationState& state) { MNHFTxPayload mnhfTx; if (!GetTxPayload(tx, mnhfTx)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-mnhf-payload"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-payload"); } if (mnhfTx.nVersion == 0 || mnhfTx.nVersion > MNHFTxPayload::CURRENT_VERSION) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-mnhf-version"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-version"); } return true; @@ -78,7 +78,7 @@ BOOST_AUTO_TEST_CASE(verify_mnhf_specialtx_tests) BOOST_CHECK(sig.VerifyInsecure(ag_pk, verHash)); const CMutableTransaction tx = CreateMNHFTx(hash, sig, ver); - CValidationState state; + TxValidationState state; BOOST_CHECK(VerifyMNHFTx(CTransaction(tx), state)); } diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index ee54b792da..d130f5cc37 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -137,7 +137,7 @@ BOOST_AUTO_TEST_CASE(tx_valid) CDataStream stream(ParseHex(transaction), SER_NETWORK, PROTOCOL_VERSION); CTransaction tx(deserialize, stream); - CValidationState state; + TxValidationState state; BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest); BOOST_CHECK(state.IsValid()); @@ -214,7 +214,7 @@ BOOST_AUTO_TEST_CASE(tx_invalid) CDataStream stream(ParseHex(transaction), SER_NETWORK, PROTOCOL_VERSION); CTransaction tx(deserialize, stream); - CValidationState state; + TxValidationState state; fValid = CheckTransaction(tx, state) && state.IsValid(); PrecomputedTransactionData txdata(tx); @@ -245,7 +245,7 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests) CDataStream stream(vch, SER_DISK, CLIENT_VERSION); CMutableTransaction tx; stream >> tx; - CValidationState state; + TxValidationState state; BOOST_CHECK_MESSAGE(CheckTransaction(CTransaction(tx), state) && state.IsValid(), "Simple deserialized transaction should be valid."); // Check that duplicate txins fail diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp index fdb2b4a8c0..58fdbfd134 100644 --- a/src/test/txvalidation_tests.cpp +++ b/src/test/txvalidation_tests.cpp @@ -30,7 +30,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup) BOOST_CHECK(CTransaction(coinbaseTx).IsCoinBase()); - CValidationState state; + TxValidationState state; LOCK(cs_main); @@ -39,7 +39,6 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup) BOOST_CHECK_EQUAL( false, AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool, state, MakeTransactionRef(coinbaseTx), - nullptr /* pfMissingInputs */, true /* bypass_limits */, 0 /* nAbsurdFee */)); @@ -49,7 +48,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup) // Check that the validation state reflects the unsuccessful attempt. BOOST_CHECK(state.IsInvalid()); BOOST_CHECK_EQUAL(state.GetRejectReason(), "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 index 39f74e4acf..f794180876 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -12,7 +12,7 @@ #include -bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector *pvChecks); +bool CheckInputs(const CTransaction& tx, TxValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector *pvChecks); BOOST_AUTO_TEST_SUITE(txvalidationcache_tests) @@ -27,8 +27,8 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) const auto ToMemPool = [this](const CMutableTransaction& tx) { LOCK(cs_main); - CValidationState state; - return AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool, state, MakeTransactionRef(tx), nullptr /* pfMissingInputs */, + TxValidationState validationState; + return AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool, validationState, MakeTransactionRef(tx), true /* bypass_limits */, 0 /* nAbsurdFee */); }; @@ -111,7 +111,7 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail // If we add many more flags, this loop can get too expensive, but we can // rewrite in the future to randomly pick a set of flags to evaluate. for (uint32_t test_flags=0; test_flags < (1U << 16); test_flags += 1) { - CValidationState state; + TxValidationState state; // Filter out incompatible flag choices if ((test_flags & SCRIPT_VERIFY_CLEANSTACK)) { // CLEANSTACK requires P2SH, see VerifyScript() in @@ -193,7 +193,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) { LOCK(cs_main); - CValidationState state; + TxValidationState state; PrecomputedTransactionData ptd_spend_tx; BOOST_CHECK(!CheckInputs(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr)); @@ -262,7 +262,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) // Make it valid, and check again invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100; - CValidationState state; + TxValidationState state; PrecomputedTransactionData txdata; BOOST_CHECK(CheckInputs(CTransaction(invalid_with_cltv_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr)); } @@ -290,7 +290,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) // Make it valid, and check again invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100; - CValidationState state; + TxValidationState state; PrecomputedTransactionData txdata; BOOST_CHECK(CheckInputs(CTransaction(invalid_with_csv_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr)); } diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 36f7156f12..ecda0359c0 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -253,7 +253,7 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vectorInit(options); } - CValidationState state; + BlockValidationState state; if (!::ChainstateActive().ActivateBestChain(state, chainparams)) { throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", FormatStateMessage(state))); } @@ -338,7 +338,7 @@ CBlock TestChainSetup::CreateBlock(const std::vector& txns, if (!GetTxPayload(*block.vtx[0], cbTx)) { BOOST_ASSERT(false); } - CValidationState state; + BlockValidationState state; if (!CalcCbTxMerkleRootMNList(block, ::ChainActive().Tip(), cbTx.merkleRootMNList, state, ::ChainstateActive().CoinsTip())) { BOOST_ASSERT(false); } diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp index becce21a2b..d8949833e2 100644 --- a/src/test/validation_block_tests.cpp +++ b/src/test/validation_block_tests.cpp @@ -162,7 +162,7 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering) } bool ignored; - CValidationState state; + BlockValidationState state; std::vector headers; std::transform(blocks.begin(), blocks.end(), std::back_inserter(headers), [](std::shared_ptr b) { return b->GetBlockHeader(); }); @@ -296,14 +296,13 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg) // Add the txs to the tx pool { LOCK(cs_main); - CValidationState state; + TxValidationState state; for (const auto& tx : txs) { BOOST_REQUIRE(AcceptToMemoryPool( ::ChainstateActive(), *m_node.mempool, state, tx, - /* pfMissingInputs */ &ignored, /* bypass_limits */ false, /* nAbsurdFee */ 0)); } diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp index 069fdaf229..22b97d9e5e 100644 --- a/src/test/validation_chainstatemanager_tests.cpp +++ b/src/test/validation_chainstatemanager_tests.cpp @@ -67,8 +67,8 @@ BOOST_AUTO_TEST_CASE(chainstatemanager) WITH_LOCK(::cs_main, c2.InitCoinsCache(1 << 23)); // Unlike c1, which doesn't have any blocks. Gets us different tip, height. c2.LoadGenesisBlock(chainparams); - CValidationState _; - BOOST_CHECK(c2.ActivateBestChain(_, chainparams, nullptr)); + BlockValidationState dummy_state; + BOOST_CHECK(c2.ActivateBestChain(dummy_state, chainparams, nullptr)); BOOST_CHECK(manager.IsSnapshotActive()); BOOST_CHECK(!manager.IsSnapshotValidated()); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index d8a1e21d01..cf106b6b2a 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -1023,9 +1023,9 @@ void CTxMemPool::clear() static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& mempoolDuplicate, const int64_t spendheight) { - CValidationState state; + TxValidationState dummy_state; // Not used. CheckTxInputs() should always pass CAmount txfee = 0; - bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, state, mempoolDuplicate, spendheight, txfee); + bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee); assert(fCheckResult); UpdateCoins(tx, mempoolDuplicate, std::numeric_limits::max()); } diff --git a/src/util/validation.cpp b/src/util/validation.cpp index 0992ca658f..8c0bf9bb88 100644 --- 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", state.GetRejectReason(), diff --git a/src/util/validation.h b/src/util/validation.h index 986d582d1f..5ee260a055 100644 --- a/src/util/validation.h +++ b/src/util/validation.h @@ -8,9 +8,9 @@ #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); #endif // BITCOIN_UTIL_VALIDATION_H diff --git a/src/validation.cpp b/src/validation.cpp index 79c555efd0..a29d59b286 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -200,7 +200,7 @@ CBlockIndex* BlockManager::FindForkInGlobalIndex(const CChain& chain, const CBlo std::unique_ptr pblocktree; -bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector *pvChecks = nullptr); +bool CheckInputs(const CTransaction& tx, TxValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector *pvChecks = nullptr); static FILE* OpenUndoFile(const FlatFilePos &pos, bool fReadOnly = false); static FlatFileSeq BlockFileSeq(); static FlatFileSeq UndoFileSeq(); @@ -361,8 +361,7 @@ int GetUTXOConfirmations(const COutPoint& outpoint) return (nPrevoutHeight > -1 && ::ChainActive().Tip()) ? ::ChainActive().Height() - nPrevoutHeight + 1 : -1; } - -bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) +static bool ContextualCheckTransaction(const CTransaction& tx, TxValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) { int nHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1; bool fDIP0001Active_context = nHeight >= consensusParams.DIP0001Height; @@ -379,18 +378,18 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, tx.nType != TRANSACTION_COINBASE && tx.nType != TRANSACTION_QUORUM_COMMITMENT && tx.nType != TRANSACTION_MNHF_SIGNAL) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-type"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-type"); } if (tx.IsCoinBase() && tx.nType != TRANSACTION_COINBASE) - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-cb-type"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-cb-type"); } else if (tx.nType != TRANSACTION_NORMAL) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-type"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-type"); } } // Size limits if (fDIP0001Active_context && ::GetSerializeSize(tx, PROTOCOL_VERSION) > MAX_STANDARD_TX_SIZE) - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-oversize"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-oversize"); return true; } @@ -454,9 +453,9 @@ static void UpdateMempoolForReorg(CChainState& active_chainstate, CTxMemPool& me auto it = disconnectpool.queuedTx.get().rbegin(); while (it != disconnectpool.queuedTx.get().rend()) { // ignore validation errors in resurrected transactions - CValidationState stateDummy; + TxValidationState stateDummy; if (!fAddToMempool || (*it)->IsCoinBase() || - !AcceptToMemoryPool(active_chainstate, mempool, stateDummy, *it, nullptr /* pfMissingInputs */, + !AcceptToMemoryPool(active_chainstate, mempool, stateDummy, *it, true /* bypass_limits */, 0 /* nAbsurdFee */)) { // If the transaction doesn't make it in to the mempool, remove any // transactions that depend on it (which would now be orphans). @@ -482,7 +481,7 @@ static void UpdateMempoolForReorg(CChainState& active_chainstate, CTxMemPool& me // 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, +static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& view, const CTxMemPool& pool, unsigned int flags, PrecomputedTransactionData& txdata, CCoinsViewCache& coins_tip) EXCLUSIVE_LOCKS_REQUIRED(cs_main) @@ -538,8 +537,7 @@ public: // around easier. struct ATMPArgs { const CChainParams& m_chainparams; - CValidationState &m_state; - bool* m_missing_inputs; + TxValidationState &m_state; const int64_t m_accept_time; const bool m_bypass_limits; const CAmount& m_absurd_fee; @@ -593,15 +591,15 @@ private: bool Finalize(ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs); // Compare a package's feerate against minimum allowed. - bool CheckFeeRate(size_t package_size, CAmount package_fee, CValidationState& state) + bool CheckFeeRate(size_t package_size, CAmount package_fee, TxValidationState& state) { CAmount mempoolRejectFee = m_pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(package_size); if (mempoolRejectFee > 0 && package_fee < mempoolRejectFee) { - return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, "mempool min fee not met", strprintf("%d < %d", package_fee, mempoolRejectFee)); + return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool min fee not met", strprintf("%d < %d", package_fee, mempoolRejectFee)); } if (package_fee < ::minRelayTxFee.GetFee(package_size)) { - return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, "min relay fee not met", strprintf("%d < %d", package_fee, ::minRelayTxFee.GetFee(package_size))); + return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "min relay fee not met", strprintf("%d < %d", package_fee, ::minRelayTxFee.GetFee(package_size))); } return true; } @@ -630,8 +628,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) // Copy/alias what we need out of args const CChainParams& chainparams = args.m_chainparams; - CValidationState &state = args.m_state; - bool* pfMissingInputs = args.m_missing_inputs; + TxValidationState &state = args.m_state; const int64_t nAcceptTime = args.m_accept_time; const bool bypass_limits = args.m_bypass_limits; const CAmount& nAbsurdFee = args.m_absurd_fee; @@ -642,10 +639,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) std::unique_ptr& entry = ws.m_entry; CAmount& nModifiedFees = ws.m_modified_fees; - if (pfMissingInputs) { - *pfMissingInputs = false; - } - if (!CheckTransaction(tx, state)) return false; // state filled in by CheckTransaction @@ -655,35 +648,35 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) if (tx.nVersion == 3 && tx.nType == TRANSACTION_QUORUM_COMMITMENT) { // quorum commitment is not allowed outside of blocks - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "qc-not-allowed"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "qc-not-allowed"); } // Coinbase is only valid in a block, not as a loose transaction if (tx.IsCoinBase()) - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "coinbase"); + return state.Invalid(TxValidationResult::TX_CONSENSUS, "coinbase"); // Rather not work on nonstandard transactions (unless -testnet/-regtest) std::string reason; if (fRequireStandard && !IsStandardTx(tx, reason)) - return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, reason); + return state.Invalid(TxValidationResult::TX_NOT_STANDARD, reason); // Do not work on transactions that are too small. // A transaction with 1 empty scriptSig input and 1 P2SH output has size of 83 bytes. // Transactions smaller than this are not relayed to mitigate CVE-2017-12842 by not relaying // 64-byte transactions. if (::GetSerializeSize(tx, PROTOCOL_VERSION) < MIN_STANDARD_TX_SIZE) - return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, "tx-size-small"); + return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "tx-size-small"); // 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. if (!CheckFinalTx(m_active_chainstate.m_chain.Tip(), tx, STANDARD_LOCKTIME_VERIFY_FLAGS)) - return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, "non-final"); + return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-final"); // is it already in the memory pool? if (m_pool.exists(hash)) { statsClient.inc("transactions.duplicate", 1.0f); - return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, "txn-already-in-mempool"); + return state.Invalid(TxValidationResult::TX_CONFLICT, "txn-already-in-mempool"); } llmq::CInstantSendLockPtr conflictLock = llmq::quorumInstantSendManager->GetConflictingLock(tx); @@ -693,7 +686,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) if (txConflict) { GetMainSignals().NotifyInstantSendDoubleSpendAttempt(ptx, txConflict); } - return state.Invalid(ValidationInvalidReason::TX_CONFLICT_LOCK, error("AcceptToMemoryPool : Transaction %s conflicts with locked TX %s", hash.ToString(), conflictLock->txid.ToString()), "tx-txlock-conflict"); + LogPrintf("ERROR: AcceptToMemoryPool : Transaction %s conflicts with locked TX %s\n", hash.ToString(), conflictLock->txid.ToString()); + return state.Invalid(TxValidationResult::TX_CONFLICT_LOCK, "tx-txlock-conflict"); } if (llmq::quorumInstantSendManager->IsWaitingForTx(hash)) { @@ -707,7 +701,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) if (ptxConflicting) { // Transaction conflicts with mempool and RBF doesn't exist in Dash - return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, "txn-mempool-conflict"); + return state.Invalid(TxValidationResult::TX_CONFLICT, "txn-mempool-conflict"); } } } @@ -731,14 +725,11 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) for (size_t out = 0; out < tx.vout.size(); out++) { // Optimistically just do efficient check of cache for outputs if (coins_cache.HaveCoinInCache(COutPoint(hash, out))) { - return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, "txn-already-known"); + return state.Invalid(TxValidationResult::TX_CONFLICT, "txn-already-known"); } } // Otherwise assume this might be an orphan tx for which we just haven't seen parents yet - if (pfMissingInputs) { - *pfMissingInputs = true; - } - return false; // fMissingInputs and !state.IsInvalid() is used to detect this condition, don't set state.Invalid() + return state.Invalid(TxValidationResult::TX_MISSING_INPUTS, "bad-txns-inputs-missingorspent"); } } @@ -757,7 +748,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) // CoinsViewCache instead of create its own assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate)); if (!CheckSequenceLocks(m_active_chainstate, m_pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp)) - return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, "non-BIP68-final"); + return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-BIP68-final"); assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_active_chainstate.m_blockman)); CAmount nFees = 0; @@ -767,7 +758,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) // Check for non-standard pay-to-script-hash in inputs if (fRequireStandard && !AreInputsStandard(tx, m_view)) - return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, "bad-txns-nonstandard-inputs"); + return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "bad-txns-nonstandard-inputs"); unsigned int nSigOps = GetTransactionSigOpCount(tx, m_view, STANDARD_SCRIPT_VERIFY_FLAGS); @@ -792,7 +783,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) unsigned int nSize = entry->GetTxSize(); if (nSigOps > MAX_STANDARD_TX_SIGOPS) - return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, "bad-txns-too-many-sigops", + return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "bad-txns-too-many-sigops", strprintf("%d", nSigOps)); // No transactions are allowed below minRelayTxFee except from disconnected @@ -800,7 +791,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) if (!bypass_limits && !CheckFeeRate(nSize, nModifiedFees, state)) return false; if (nAbsurdFee && nFees > nAbsurdFee) - return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, + return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "absurdly-high-fee", strprintf("%d > %d", nFees, nAbsurdFee)); // Calculate in-mempool ancestors, up to a limit. @@ -821,7 +812,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) // this, see https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-November/016518.html if (nSize > EXTRA_DESCENDANT_TX_SIZE_LIMIT || !m_pool.CalculateMemPoolAncestors(*entry, setAncestors, 2, m_limit_ancestor_size, m_limit_descendants + 1, m_limit_descendant_size + EXTRA_DESCENDANT_TX_SIZE_LIMIT, dummy_err_string)) { - return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, "too-long-mempool-chain", errString); + return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "too-long-mempool-chain", errString); } } @@ -833,7 +824,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) return false; if (m_pool.existsProviderTxConflict(tx)) { - return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, "protx-dup"); + return state.Invalid(TxValidationResult::TX_CONFLICT, "protx-dup"); } return true; @@ -843,14 +834,13 @@ bool MemPoolAccept::PolicyScriptChecks(ATMPArgs& args, Workspace& ws, Precompute { const CTransaction& tx = *ws.m_ptx; - CValidationState &state = args.m_state; + TxValidationState &state = args.m_state; constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS; // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. if (!CheckInputs(tx, state, m_view, scriptVerifyFlags, true, false, txdata)) { - assert(IsTransactionReason(state.GetReason())); return false; // state filled in by CheckInputs } @@ -862,7 +852,7 @@ bool MemPoolAccept::ConsensusScriptChecks(ATMPArgs& args, Workspace& ws, Precomp const CTransaction& tx = *ws.m_ptx; const uint256& hash = ws.m_hash; - CValidationState &state = args.m_state; + TxValidationState &state = args.m_state; const CChainParams& chainparams = args.m_chainparams; // Check again against the current block tip's script verification @@ -894,7 +884,7 @@ bool MemPoolAccept::Finalize(ATMPArgs& args, Workspace& ws) { const CTransaction& tx = *ws.m_ptx; const uint256& hash = ws.m_hash; - CValidationState &state = args.m_state; + TxValidationState &state = args.m_state; const bool bypass_limits = args.m_bypass_limits; CTxMemPool::setEntries& setAncestors = ws.m_ancestors; @@ -934,7 +924,7 @@ bool MemPoolAccept::Finalize(ATMPArgs& args, Workspace& ws) assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(m_active_chainstate.CoinsTip())); LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip(), gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)}); if (!m_pool.exists(hash)) - return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, "mempool full"); + return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool full"); } return true; } @@ -981,12 +971,12 @@ bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs } // anon namespace /** (try to) add transaction to memory pool with a specified acceptance time **/ -static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CChainState& active_chainstate, CValidationState &state, const CTransactionRef &tx, - bool* pfMissingInputs, int64_t nAcceptTime, bool bypass_limits, +static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CChainState& active_chainstate, TxValidationState &state, const CTransactionRef &tx, + int64_t nAcceptTime, bool bypass_limits, const CAmount nAbsurdFee, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { std::vector coins_to_uncache; - MemPoolAccept::ATMPArgs args { chainparams, state, pfMissingInputs, nAcceptTime, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept }; + MemPoolAccept::ATMPArgs args { chainparams, state, nAcceptTime, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept }; assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate)); bool res = MemPoolAccept(pool, active_chainstate).AcceptSingleTransaction(tx, args); @@ -1002,17 +992,17 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo active_chainstate.CoinsTip().Uncache(hashTx); } // After we've (potentially) uncached entries, ensure our coins cache is still within its size limits - CValidationState stateDummy; - active_chainstate.FlushStateToDisk(chainparams, stateDummy, FlushStateMode::PERIODIC); + BlockValidationState state_dummy; + active_chainstate.FlushStateToDisk(chainparams, state_dummy, FlushStateMode::PERIODIC); return res; } -bool AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, - bool* pfMissingInputs, bool bypass_limits, const CAmount nAbsurdFee, bool test_accept) +bool AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx, + bool bypass_limits, const CAmount nAbsurdFee, bool test_accept) { const CChainParams& chainparams = Params(); assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate)); - return AcceptToMemoryPoolWithTime(chainparams, pool, active_chainstate, state, tx, pfMissingInputs, GetTime(), bypass_limits, nAbsurdFee, test_accept); + return AcceptToMemoryPoolWithTime(chainparams, pool, active_chainstate, state, tx, GetTime(), bypass_limits, nAbsurdFee, test_accept); } bool GetTimestampIndex(const unsigned int &high, const unsigned int &low, std::vector &hashes) @@ -1451,9 +1441,9 @@ void CChainState::ConflictingChainFound(CBlockIndex* pindexNew) // Same as InvalidChainFound, above, except not called directly from InvalidateBlock, // which does its own setBlockIndexCandidates manageent. -void CChainState::InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) { +void CChainState::InvalidBlockFound(CBlockIndex *pindex, const BlockValidationState &state) { statsClient.inc("warnings.InvalidBlockFound", 1.0f); - if (state.GetReason() != ValidationInvalidReason::BLOCK_MUTATED) { + if (state.GetResult() != BlockValidationResult::BLOCK_MUTATED) { pindex->nStatus |= BLOCK_FAILED_VALID; m_blockman.m_failed_blocks.insert(pindex); setDirtyBlockIndex.insert(pindex); @@ -1534,7 +1524,7 @@ void InitScriptExecutionCache() { * * Non-static (and re-declared) in src/test/txvalidationcache_tests.cpp */ -bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector *pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +bool CheckInputs(const CTransaction& tx, TxValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector *pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { boost::posix_time::ptime start = boost::posix_time::microsec_clock::local_time(); if (tx.IsCoinBase()) return true; @@ -1592,10 +1582,10 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi CScriptCheck check2(coin.out, tx, i, (flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS) | SCRIPT_ENABLE_DIP0020_OPCODES, cacheSigStore, &txdata); if (check2()) - return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); + return state.Invalid(TxValidationResult::TX_NOT_STANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); } // MANDATORY flag failures correspond to - // ValidationInvalidReason::CONSENSUS. Because CONSENSUS + // 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 @@ -1603,7 +1593,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi // 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, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError()))); + return state.Invalid(TxValidationResult::TX_CONSENSUS, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError()))); } } @@ -1690,7 +1680,7 @@ static bool AbortNode(const std::string& strMessage, bilingual_str user_message return false; } -static bool AbortNode(CValidationState& state, const std::string& strMessage, const bilingual_str& userMessage = bilingual_str()) +static bool AbortNode(BlockValidationState& state, const std::string& strMessage, const bilingual_str& userMessage = bilingual_str()) { AbortNode(strMessage, userMessage); return state.Error(strMessage); @@ -1929,9 +1919,9 @@ static void FlushBlockFile(bool fFinalize = false, bool finalize_undo = false) if (!fFinalize || finalize_undo) FlushUndoFile(nLastBlockFile, finalize_undo); } -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, const CChainParams& chainparams) +static bool WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValidationState& state, CBlockIndex* pindex, const CChainParams& chainparams) { // Write undo information to disk if (pindex->GetUndoPos().IsNull()) { @@ -2085,7 +2075,7 @@ static int64_t nBlocksTotal = 0; /** Apply the effects of this block (with given index) on the UTXO set represented by coins. * Validity checks that depend on the UTXO set are also done; ConnectBlock() * can fail if those validity checks fail (among other reasons). */ -bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, +bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck) { boost::posix_time::ptime start = boost::posix_time::microsec_clock::local_time(); @@ -2115,7 +2105,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl // re-enforce that rule here (at least until we make it impossible for // GetAdjustedTime() to go backward). if (!CheckBlock(block, state, chainparams.GetConsensus(), !fJustCheck, !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. @@ -2125,7 +2115,8 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl } if (pindex->pprev && pindex->phashBlock && m_clhandler->HasConflictingChainLock(pindex->nHeight, pindex->GetBlockHash())) { - return state.Invalid(ValidationInvalidReason::BLOCK_CHAINLOCK, error("%s: conflicting with chainlock", __func__), "bad-chainlock"); + LogPrintf("ERROR: %s: conflicting with chainlock\n", __func__); + return state.Invalid(BlockValidationResult::BLOCK_CHAINLOCK, "bad-chainlock"); } // verify that the view's current state corresponds to the previous block @@ -2213,7 +2204,8 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl for (const auto& tx : block.vtx) { for (size_t o = 0; o < tx->vout.size(); o++) { if (view.HaveCoin(COutPoint(tx->GetHash(), o))) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, error("ConnectBlock(): tried to overwrite transaction"), "bad-txns-BIP30"); + LogPrintf("ERROR: ConnectBlock(): tried to overwrite transaction\n"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-txns-BIP30"); } } } @@ -2225,7 +2217,8 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl if (pindex->nHeight == chainparams.GetConsensus().nSuperblockStartBlock && chainparams.GetConsensus().nSuperblockStartHash != uint256() && block_hash != chainparams.GetConsensus().nSuperblockStartHash) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, error("ConnectBlock(): invalid superblock start"), "bad-sb-start"); + LogPrintf("ERROR: ConnectBlock(): invalid superblock start\n"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-sb-start"); } /// END DASH @@ -2281,21 +2274,17 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl if (!tx.IsCoinBase()) { CAmount txfee = 0; - if (!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.GetRejectReason(), state.GetDebugMessage()); - } - return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state)); + TxValidationState tx_state; + if (!Consensus::CheckTxInputs(tx, tx_state, view, pindex->nHeight, txfee)) { + // Any transaction validation failure in ConnectBlock is a block consensus failure + LogPrintf("ERROR: %s: Consensus::CheckTxInputs: %s, %s\n", __func__, tx.GetHash().ToString(), FormatStateMessage(state)); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, + tx_state.GetRejectReason(), tx_state.GetDebugMessage()); } nFees += txfee; if (!MoneyRange(nFees)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, error("%s: accumulated fee in the block out of range.", __func__), - "bad-txns-accumulated-fee-outofrange"); + LogPrintf("ERROR: %s: accumulated fee in the block out of range.\n", __func__); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-txns-accumulated-fee-outofrange"); } // Check that transaction is BIP68 final @@ -2307,8 +2296,8 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl } if (!SequenceLocks(tx, nLockTimeFlags, prevheights, *pindex)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, error("%s: contains a non-BIP68-final transaction", __func__), - "bad-txns-nonfinal"); + LogPrintf("ERROR: %s: contains a non-BIP68-final transaction\n", __func__); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-txns-nonfinal"); } if (fAddressIndex || fSpentIndex) @@ -2358,8 +2347,8 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl // * p2sh (when P2SH enabled in flags and excludes coinbase) nSigOps += GetTransactionSigOpCount(tx, view, flags); if (nSigOps > MaxBlockSigOps(fDIP0001Active_context)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, error("ConnectBlock(): too many sigops"), - "bad-blk-sigops"); + LogPrintf("ERROR: ConnectBlock(): too many sigops\n"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-blk-sigops"); } if (!tx.IsCoinBase()) @@ -2367,19 +2356,14 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl std::vector vChecks; bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */ - if (fScriptChecks && !CheckInputs(tx, state, view, flags, fCacheResults, fCacheResults, txsdata[i], g_parallel_script_checks ? &vChecks : nullptr)) { - 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.GetRejectReason(), state.GetDebugMessage()); - } - return error("ConnectBlock(): CheckInputs on %s failed with %s", + TxValidationState tx_state; + if (fScriptChecks && !CheckInputs(tx, tx_state, view, flags, fCacheResults, fCacheResults, txsdata[i], g_parallel_script_checks ? &vChecks : nullptr)) { + // Any transaction validation failure in ConnectBlock is a block consensus failure + state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, + tx_state.GetRejectReason(), tx_state.GetDebugMessage()); + LogPrintf("ERROR: ConnectBlock(): CheckInputs on %s failed with %s\n", tx.GetHash().ToString(), FormatStateMessage(state)); + return false; } control.Add(vChecks); } @@ -2426,8 +2410,11 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl int64_t nTime3 = GetTimeMicros(); nTimeConnect += nTime3 - nTime2; LogPrint(BCLog::BENCHMARK, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs (%.2fms/blk)]\n", (unsigned)block.vtx.size(), MILLI * (nTime3 - nTime2), MILLI * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : MILLI * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * MICRO, nTimeConnect * MILLI / nBlocksTotal); - if (!control.Wait()) - return state.Invalid(ValidationInvalidReason::CONSENSUS, error("%s: CheckQueue failed", __func__), "block-validation-failed"); + + if (!control.Wait()) { + LogPrintf("ERROR: %s: CheckQueue failed\n", __func__); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "block-validation-failed"); + } int64_t nTime4 = GetTimeMicros(); nTimeVerify += nTime4 - nTime2; LogPrint(BCLog::BENCHMARK, " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs (%.2fms/blk)]\n", nInputs - 1, MILLI * (nTime4 - nTime2), nInputs <= 1 ? 0 : MILLI * (nTime4 - nTime2) / (nInputs-1), nTimeVerify * MICRO, nTimeVerify * MILLI / nBlocksTotal); @@ -2455,7 +2442,8 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl } else { // The node which relayed this should switch to correct chain. // TODO: relay instantsend data/proof. - return state.Invalid(ValidationInvalidReason::TX_CONFLICT_LOCK, error("ConnectBlock(DASH): transaction %s conflicts with transaction lock %s", tx->GetHash().ToString(), conflictLock->txid.ToString()), "conflict-tx-lock"); + LogPrintf("ERROR: ConnectBlock(DASH): transaction %s conflicts with transaction lock %s\n", tx->GetHash().ToString(), conflictLock->txid.ToString()); + return state.Invalid(BlockValidationResult::BLOCK_CHAINLOCK, "conflict-tx-lock"); } } } @@ -2475,7 +2463,8 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl if (!IsBlockValueValid(*sporkManager, *governance, *::masternodeSync, block, pindex->nHeight, blockReward, strError)) { // NOTE: Do not punish, the node might be missing governance data - return state.Invalid(ValidationInvalidReason::NONE, error("ConnectBlock(DASH): %s", strError), "bad-cb-amount"); + LogPrintf("ERROR: ConnectBlock(DASH): %s\n", strError); + return state.Invalid(BlockValidationResult::BLOCK_RESULT_UNSET, "bad-cb-amount"); } int64_t nTime5_3 = GetTimeMicros(); nTimeValueValid += nTime5_3 - nTime5_2; @@ -2483,7 +2472,8 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl if (!IsBlockPayeeValid(*sporkManager, *governance, *block.vtx[0], pindex->nHeight, blockReward)) { // NOTE: Do not punish, the node might be missing governance data - return state.Invalid(ValidationInvalidReason::NONE, error("ConnectBlock(DASH): couldn't find masternode or superblock payments"), "bad-cb-payee"); + LogPrintf("ERROR: ConnectBlock(DASH): couldn't find masternode or superblock payments\n"); + return state.Invalid(BlockValidationResult::BLOCK_RESULT_UNSET, "bad-cb-payee"); } int64_t nTime5_4 = GetTimeMicros(); nTimePayeeValid += nTime5_4 - nTime5_3; @@ -2581,7 +2571,7 @@ CoinsCacheSizeState CChainState::GetCoinsCacheSizeState( bool CChainState::FlushStateToDisk( const CChainParams& chainparams, - CValidationState &state, + BlockValidationState &state, FlushStateMode mode, int nManualPruneHeight) { @@ -2716,7 +2706,7 @@ bool CChainState::FlushStateToDisk( } 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__, FormatStateMessage(state)); @@ -2724,7 +2714,7 @@ void CChainState::ForceFlushStateToDisk() { } void CChainState::PruneAndFlush() { - CValidationState state; + BlockValidationState state; fCheckForPruning = true; const CChainParams& chainparams = Params(); @@ -2812,7 +2802,7 @@ static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const C * disconnectpool (note that the caller is responsible for mempool consistency * in any case). */ -bool CChainState::DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) +bool CChainState::DisconnectTip(BlockValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) { AssertLockHeld(cs_main); AssertLockHeld(m_mempool.cs); @@ -2916,7 +2906,7 @@ public: * * The block is added to connectTrace if connection succeeds. */ -bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool) +bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool) { boost::posix_time::ptime start = boost::posix_time::microsec_clock::local_time(); AssertLockHeld(cs_main); @@ -3064,7 +3054,7 @@ void CChainState::PruneBlockIndexCandidates() { * * @returns true unless a system error occurred */ -bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) +bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) { AssertLockHeld(cs_main); AssertLockHeld(m_mempool.cs); @@ -3113,10 +3103,10 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr(), 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.front()); } - state = CValidationState(); + state = BlockValidationState(); fInvalidFound = true; fContinue = false; break; @@ -3182,7 +3172,7 @@ static void LimitValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main) { } } -bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr pblock) { +bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainParams& chainparams, 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 // us in the middle of ProcessNewBlock - do not assume pblock is set @@ -3287,7 +3277,7 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams& return true; } -bool CChainState::PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex) +bool CChainState::PreciousBlock(BlockValidationState& state, const CChainParams& params, CBlockIndex *pindex) { { LOCK(cs_main); @@ -3316,7 +3306,7 @@ bool CChainState::PreciousBlock(CValidationState& state, const CChainParams& par return ActivateBestChain(state, params, std::shared_ptr()); } -bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) +bool CChainState::InvalidateBlock(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) { // Genesis block can't be invalidated assert(pindex); @@ -3470,7 +3460,7 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c return true; } -void CChainState::EnforceBlock(CValidationState& state, const CChainParams& chainparams, const CBlockIndex *pindex) +void CChainState::EnforceBlock(BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex *pindex) { AssertLockNotHeld(::cs_main); @@ -3502,7 +3492,7 @@ void CChainState::EnforceBlock(CValidationState& state, const CChainParams& chai } } -bool CChainState::MarkConflictingBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) +bool CChainState::MarkConflictingBlock(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) { AssertLockHeld(cs_main); assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); @@ -3782,7 +3772,7 @@ static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int n 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; @@ -3804,23 +3794,24 @@ static bool FindUndoPos(CValidationState &state, int nFile, FlatFilePos &pos, un return true; } -static bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true) +static bool CheckBlockHeader(const CBlockHeader& block, BlockValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true) { // Check proof of work matches claimed amount if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, consensusParams)) - return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_HEADER, false, "high-hash", "proof of work failed"); + return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "high-hash", "proof of work failed"); // Check DevNet if (!consensusParams.hashDevnetGenesisBlock.IsNull() && block.hashPrevBlock == consensusParams.hashGenesisBlock && block.GetHash() != consensusParams.hashDevnetGenesisBlock) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, error("CheckBlockHeader(): wrong devnet genesis"), "devnet-genesis"); + LogPrintf("ERROR: CheckBlockHeader(): wrong devnet genesis\n"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "devnet-genesis"); } return true; } -bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW, bool fCheckMerkleRoot) +bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW, bool fCheckMerkleRoot) { // These are checks that are independent of context. @@ -3839,13 +3830,13 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P bool mutated; uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated); if (block.hashMerkleRoot != hashMerkleRoot2) - return state.Invalid(ValidationInvalidReason::BLOCK_MUTATED, false, "bad-txnmrklroot", "hashMerkleRoot mismatch"); + return state.Invalid(BlockValidationResult::BLOCK_MUTATED, "bad-txnmrklroot", "hashMerkleRoot mismatch"); // Check for merkle tree malleability (CVE-2012-2459): repeating 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, "bad-txns-duplicate", "duplicate transaction"); + return state.Invalid(BlockValidationResult::BLOCK_MUTATED, "bad-txns-duplicate", "duplicate transaction"); } // All potential-corruption validation must be done before we do any @@ -3854,21 +3845,27 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P // Size limits (relaxed) if (block.vtx.empty() || block.vtx.size() > MaxBlockSize() || ::GetSerializeSize(block, PROTOCOL_VERSION) > MaxBlockSize()) - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-blk-length", "size limits failed"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-blk-length", "size limits failed"); // First transaction must be coinbase, the rest must not be if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-cb-missing", "first tx is not coinbase"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cb-missing", "first tx is not coinbase"); for (unsigned int i = 1; i < block.vtx.size(); i++) if (block.vtx[i]->IsCoinBase()) - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-cb-multiple", "more than one coinbase"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cb-multiple", "more than one coinbase"); // Check transactions - for (const auto& tx : block.vtx) - if (!CheckTransaction(*tx, state)) - return state.Invalid(state.GetReason(), false, state.GetRejectReason(), - strprintf("Transaction check failed (tx hash %s) %s", tx->GetHash().ToString(), state.GetDebugMessage())); - + // Must check for duplicate inputs (see CVE-2018-17144) + for (const auto& tx : block.vtx) { + TxValidationState tx_state; + if (!CheckTransaction(*tx, tx_state)) { + // CheckBlock() does context-free validation checks. The only + // possible failures are consensus failures. + assert(tx_state.GetResult() == TxValidationResult::TX_CONSENSUS); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, tx_state.GetRejectReason(), + strprintf("Transaction check failed (tx hash %s) %s", tx->GetHash().ToString(), tx_state.GetDebugMessage())); + } + } unsigned int nSigOps = 0; for (const auto& tx : block.vtx) { @@ -3876,7 +3873,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P } // sigops limits (relaxed) if (nSigOps > MaxBlockSigOps()) - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-blk-sigops", "out-of-bounds SigOpCount"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-blk-sigops", "out-of-bounds SigOpCount"); if (fCheckPOW && fCheckMerkleRoot) block.fChecked = true; @@ -3913,7 +3910,7 @@ CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data) * in ConnectBlock(). * Note that -reindex-chainstate skips the validation that happens here! */ -static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, BlockManager& blockman, const CChainParams& params, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidationState& state, BlockManager& blockman, const CChainParams& params, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { assert(pindexPrev != nullptr); const int nHeight = pindexPrev->nHeight + 1; @@ -3926,12 +3923,13 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta double n1 = ConvertBitsToDouble(block.nBits); double n2 = ConvertBitsToDouble(nBitsNext); - if (abs(n1-n2) > n1*0.5) - return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_HEADER, error("%s : incorrect proof of work (DGW pre-fork) - %f %f %f at %d", __func__, abs(n1-n2), n1, n2, nHeight), - "bad-diffbits"); + if (abs(n1-n2) > n1*0.5) { + LogPrintf("ERROR: %s : incorrect proof of work (DGW pre-fork) - %f %f %f at %d\n", __func__, abs(n1-n2), n1, n2, nHeight); + return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "bad-diffbits"); + } } else { if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) - return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_HEADER, false, "bad-diffbits", strprintf("incorrect proof of work at %d", nHeight)); + return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "bad-diffbits", strprintf("incorrect proof of work at %d", nHeight)); } // Check against checkpoints @@ -3941,23 +3939,25 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta // BlockIndex(). assert(std::addressof(g_chainman.m_blockman) == std::addressof(blockman)); CBlockIndex* pcheckpoint = blockman.GetLastCheckpoint(params.Checkpoints()); - if (pcheckpoint && nHeight < pcheckpoint->nHeight) - return state.Invalid(ValidationInvalidReason::BLOCK_CHECKPOINT, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight), "bad-fork-prior-to-checkpoint"); + if (pcheckpoint && nHeight < pcheckpoint->nHeight) { + LogPrintf("ERROR: %s: forked chain older than last checkpoint (height %d)\n", __func__, nHeight); + return state.Invalid(BlockValidationResult::BLOCK_CHECKPOINT, "bad-fork-prior-to-checkpoint"); + } } // Check timestamp against prev if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) - return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_HEADER, false, "time-too-old", strprintf("block's timestamp is too early %d %d", block.GetBlockTime(), pindexPrev->GetMedianTimePast())); + return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "time-too-old", strprintf("block's timestamp is too early %d %d", block.GetBlockTime(), pindexPrev->GetMedianTimePast())); // Check timestamp if (block.GetBlockTime() > nAdjustedTime + MAX_FUTURE_BLOCK_TIME) - return state.Invalid(ValidationInvalidReason::BLOCK_TIME_FUTURE, false, "time-too-new", strprintf("block timestamp too far in the future %d %d", block.GetBlockTime(), nAdjustedTime + 2 * 60 * 60)); + return state.Invalid(BlockValidationResult::BLOCK_TIME_FUTURE, "time-too-new", strprintf("block timestamp too far in the future %d %d", block.GetBlockTime(), nAdjustedTime + 2 * 60 * 60)); // check for version 2, 3 and 4 upgrades if((block.nVersion < 2 && nHeight >= consensusParams.BIP34Height) || (block.nVersion < 3 && nHeight >= consensusParams.BIP66Height) || (block.nVersion < 4 && nHeight >= consensusParams.BIP65Height)) - return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_HEADER, false, strprintf("bad-version(0x%08x)", block.nVersion), + return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, strprintf("bad-version(0x%08x)", block.nVersion), strprintf("rejected nVersion=0x%08x block", block.nVersion)); return true; @@ -3969,7 +3969,7 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta * in ConnectBlock(). * Note that -reindex-chainstate skips the validation that happens here! */ -static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) +static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) { AssertLockHeld(cs_main); const int nHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1; @@ -3991,24 +3991,29 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c // Size limits unsigned int nMaxBlockSize = MaxBlockSize(fDIP0001Active_context); if (block.vtx.empty() || block.vtx.size() > nMaxBlockSize || ::GetSerializeSize(block, PROTOCOL_VERSION) > nMaxBlockSize) - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-blk-length", "size limits failed"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-blk-length", "size limits failed"); // Check that all transactions are finalized and not over-sized // Also count sigops unsigned int nSigOps = 0; for (const auto& tx : block.vtx) { if (!IsFinalTx(*tx, nHeight, nLockTimeCutoff)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-nonfinal", "non-final transaction"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-txns-nonfinal", "non-final transaction"); } - if (!ContextualCheckTransaction(*tx, state, consensusParams, pindexPrev)) { - return false; + TxValidationState tx_state; + if (!ContextualCheckTransaction(*tx, tx_state, consensusParams, pindexPrev)) { + // ContextCheckTransaction() does validation checks than only should + // fails as consensus failures. + assert(tx_state.GetResult() == TxValidationResult::TX_CONSENSUS); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, tx_state.GetRejectReason(), + strprintf("Transaction check failed (tx hash %s) %s", tx->GetHash().ToString(), tx_state.GetDebugMessage())); } nSigOps += GetLegacySigOpCount(*tx); } // Check sigops if (nSigOps > MaxBlockSigOps(fDIP0001Active_context)) - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-blk-sigops", "out-of-bounds SigOpCount"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-blk-sigops", "out-of-bounds SigOpCount"); // Enforce rule that the coinbase starts with serialized block height // After DIP3/DIP4 activation, we don't enforce the height in the input script anymore. @@ -4018,20 +4023,20 @@ static bool ContextualCheckBlock(const CBlock& block, CValidationState& state, c CScript expect = CScript() << nHeight; 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, "bad-cb-height", "block height mismatch in coinbase"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cb-height", "block height mismatch in coinbase"); } } if (fDIP0003Active_context) { if (block.vtx[0]->nType != TRANSACTION_COINBASE) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-cb-type", "coinbase is not a CbTx"); + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cb-type", "coinbase is not a CbTx"); } } return true; } -bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex) +bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, BlockValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex) { AssertLockHeld(cs_main); // Check for duplicate @@ -4046,10 +4051,14 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, CValidationState pindex = miSelf->second; if (ppindex) *ppindex = pindex; - if (pindex->nStatus & BLOCK_FAILED_MASK) - return state.Invalid(ValidationInvalidReason::CACHED_INVALID, error("%s: block %s is marked invalid", __func__, hash.ToString()), "duplicate"); - if (pindex->nStatus & BLOCK_CONFLICT_CHAINLOCK) - return state.Invalid(ValidationInvalidReason::BLOCK_CHAINLOCK, error("%s: block %s is marked conflicting", __func__, hash.ToString()), "duplicate"); + if (pindex->nStatus & BLOCK_FAILED_MASK) { + LogPrintf("ERROR: %s: block %s is marked invalid\n", __func__, hash.ToString()); + return state.Invalid(BlockValidationResult::BLOCK_CACHED_INVALID, "duplicate"); + } + if (pindex->nStatus & BLOCK_CONFLICT_CHAINLOCK) { + LogPrintf("ERROR: %s: block %s is marked conflicting\n", __func__, hash.ToString()); + return state.Invalid(BlockValidationResult::BLOCK_CHAINLOCK, "duplicate"); + } return true; } @@ -4059,17 +4068,23 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, CValidationState // Get prev block index CBlockIndex* pindexPrev = nullptr; BlockMap::iterator mi = m_block_index.find(block.hashPrevBlock); - if (mi == m_block_index.end()) - return state.Invalid(ValidationInvalidReason::BLOCK_MISSING_PREV, error("%s: prev block not found", __func__), "prev-blk-not-found"); + if (mi == m_block_index.end()) { + LogPrintf("ERROR: %s: prev block not found\n", __func__); + return state.Invalid(BlockValidationResult::BLOCK_MISSING_PREV, "prev-blk-not-found"); + } pindexPrev = (*mi).second; assert(pindexPrev); - if (pindexPrev->nStatus & BLOCK_FAILED_MASK) - return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_PREV, error("%s: prev block invalid", __func__), "bad-prevblk"); + if (pindexPrev->nStatus & BLOCK_FAILED_MASK) { + LogPrintf("ERROR: %s: prev block invalid\n", __func__); + return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk"); + } - if (pindexPrev->nStatus & BLOCK_CONFLICT_CHAINLOCK) + if (pindexPrev->nStatus & BLOCK_CONFLICT_CHAINLOCK) { // it's ok-ish, the other node is probably missing the latest chainlock - return state.Invalid(ValidationInvalidReason::BLOCK_CHAINLOCK, error("%s: prev block %s conflicts with chainlock", __func__, block.hashPrevBlock.ToString()), "bad-prevblk-chainlock"); + LogPrintf("ERROR: %s: prev block %s conflicts with chainlock\n", __func__, block.hashPrevBlock.ToString()); + return state.Invalid(BlockValidationResult::BLOCK_CHAINLOCK, "bad-prevblk-chainlock"); + } if (!ContextualCheckBlockHeader(block, state, *this, chainparams, pindexPrev, GetAdjustedTime())) return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); @@ -4107,7 +4122,8 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, CValidationState setDirtyBlockIndex.insert(invalid_walk); invalid_walk = invalid_walk->pprev; } - return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_PREV, error("%s: prev block invalid", __func__), "bad-prevblk"); + LogPrintf("ERROR: %s: prev block invalid\n", __func__); + return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk"); } } } @@ -4116,7 +4132,8 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, CValidationState if (pindex == nullptr) { AddToBlockIndex(block, BLOCK_CONFLICT_CHAINLOCK); } - return state.Invalid(ValidationInvalidReason::BLOCK_CHAINLOCK, error("%s: header %s conflicts with chainlock", __func__, hash.ToString()), "bad-chainlock"); + LogPrintf("ERROR: %s: header %s conflicts with chainlock\n", __func__, hash.ToString()); + return state.Invalid(BlockValidationResult::BLOCK_CHAINLOCK, "bad-chainlock"); } } if (pindex == nullptr) @@ -4132,11 +4149,10 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, CValidationState } // Exposed wrapper for AcceptBlockHeader -bool ChainstateManager::ProcessNewBlockHeaders(const std::vector& headers, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex, CBlockHeader *first_invalid) +bool ChainstateManager::ProcessNewBlockHeaders(const std::vector& headers, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex) { assert(std::addressof(::ChainstateActive()) == std::addressof(ActiveChainstate())); AssertLockNotHeld(cs_main); - if (first_invalid != nullptr) first_invalid->SetNull(); { LOCK(cs_main); for (const CBlockHeader& header : headers) { @@ -4146,7 +4162,6 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector& ActiveChainstate().CheckBlockIndex(chainparams.GetConsensus()); if (!accepted) { - if (first_invalid) *first_invalid = header; return false; } if (ppindex) { @@ -4182,7 +4197,7 @@ static FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, CChain& act } /** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */ -bool CChainState::AcceptBlock(const std::shared_ptr& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) +bool CChainState::AcceptBlock(const std::shared_ptr& pblock, BlockValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) { boost::posix_time::ptime start = boost::posix_time::microsec_clock::local_time(); @@ -4234,8 +4249,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr& pblock, CVali if (!CheckBlock(block, state, chainparams.GetConsensus()) || !ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindex->pprev)) { - assert(IsBlockReason(state.GetReason())); - if (state.IsInvalid() && state.GetReason() != ValidationInvalidReason::BLOCK_MUTATED) { + if (state.IsInvalid() && state.GetResult() != BlockValidationResult::BLOCK_MUTATED) { pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); } @@ -4282,7 +4296,7 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s { CBlockIndex *pindex = nullptr; if (fNewBlock) *fNewBlock = false; - CValidationState state; + BlockValidationState state; // CheckBlock() does not support multi-threaded block validation because CBlock::fChecked can cause data race. // Therefore, the following critical section must include the CheckBlock() call as well. @@ -4303,7 +4317,7 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s NotifyHeaderTip(ActiveChainstate()); - CValidationState state; // Only used to report errors, not invalidity - ignore it + BlockValidationState state; // Only used to report errors, not invalidity - ignore it if (!ActiveChainstate().ActivateBestChain(state, chainparams, pblock)) return error("%s: ActivateBestChain failed: %s", __func__, FormatStateMessage(state)); @@ -4311,7 +4325,7 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s return true; } -bool TestBlockValidity(CValidationState& state, +bool TestBlockValidity(BlockValidationState& state, llmq::CChainLocksHandler& clhandler, CEvoDB& evoDb, const CChainParams& chainparams, @@ -4327,7 +4341,8 @@ bool TestBlockValidity(CValidationState& state, uint256 hash = block.GetHash(); if (clhandler.HasConflictingChainLock(pindexPrev->nHeight + 1, hash)) { - return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_PREV, error("%s: conflicting with chainlock", __func__), "bad-chainlock"); + LogPrintf("ERROR: %s: conflicting with chainlock\n", __func__); + return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-chainlock"); } CCoinsViewCache viewNew(&chainstate.CoinsTip()); @@ -4442,7 +4457,7 @@ void BlockManager::FindFilesToPruneManual(std::set& setFilesToPrune, int nM /* This function is called from the RPC code for pruneblockchain */ void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeight) { - CValidationState state; + BlockValidationState state; const CChainParams& chainparams = Params(); assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate)); if (!active_chainstate.FlushStateToDisk( @@ -4766,7 +4781,7 @@ bool CVerifyDB::VerifyDB( CBlockIndex* pindex; CBlockIndex* pindexFailure = nullptr; int nGoodTransactions = 0; - CValidationState state; + BlockValidationState state; int reportDone = 0; LogPrintf("[0%%]..."); /* Continued */ @@ -4867,7 +4882,7 @@ bool CChainState::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& i } // MUST process special txes before updating UTXO to ensure consistency between mempool and block processing - CValidationState state; + BlockValidationState state; if (!ProcessSpecialTxsInBlock(block, pindex, *m_quorum_block_processor, state, inputs, false /*fJustCheck*/, false /*fScriptChecks*/)) { return error("RollforwardBlock(DASH): ProcessSpecialTxsInBlock for block %s failed with %s", pindex->GetBlockHash().ToString(), FormatStateMessage(state)); @@ -5022,7 +5037,7 @@ bool ChainstateManager::LoadBlockIndex(const CChainParams& chainparams) return true; } -bool CChainState::AddGenesisBlock(const CChainParams& chainparams, const CBlock& block, CValidationState& state) +bool CChainState::AddGenesisBlock(const CChainParams& chainparams, const CBlock& block, BlockValidationState& state) { assert(std::addressof(::ChainActive()) == std::addressof(m_chain)); FlatFilePos blockPos = SaveBlockToDisk(block, 0, m_chain, chainparams, nullptr); @@ -5045,7 +5060,7 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams) return true; try { - CValidationState state; + BlockValidationState state; if (!AddGenesisBlock(chainparams, chainparams.GenesisBlock(), state)) return false; @@ -5129,7 +5144,7 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_blockman)); CBlockIndex* pindex = m_blockman.LookupBlockIndex(hash); if (!pindex || (pindex->nStatus & BLOCK_HAVE_DATA) == 0) { - CValidationState state; + BlockValidationState state; assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); if (AcceptBlock(pblock, state, chainparams, nullptr, true, dbp, nullptr)) { nLoaded++; @@ -5144,8 +5159,8 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f // Activate the genesis block so normal node progress can continue if (hash == chainparams.GetConsensus().hashGenesisBlock) { - CValidationState state; assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); + BlockValidationState state; if (!ActivateBestChain(state, chainparams, nullptr)) { break; } @@ -5169,8 +5184,8 @@ void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* f LogPrint(BCLog::REINDEX, "%s: Processing out of order child %s of %s\n", __func__, pblockrecursive->GetHash().ToString(), head.ToString()); LOCK(cs_main); - CValidationState dummy; assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); + BlockValidationState dummy; if (AcceptBlock(pblockrecursive, dummy, chainparams, nullptr, true, &it->second, nullptr)) { nLoaded++; @@ -5409,7 +5424,7 @@ bool CChainState::ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size) LogPrintf("[%s] resized coinstip cache to %.1f MiB\n", this->ToString(), coinstip_size * (1.0 / 1024 / 1024)); - CValidationState state; + BlockValidationState state; const CChainParams& chainparams = Params(); bool ret; @@ -5477,11 +5492,11 @@ bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate) if (amountdelta) { pool.PrioritiseTransaction(tx->GetHash(), amountdelta); } - CValidationState state; + TxValidationState state; if (nTime + nExpiryTimeout > nNow) { LOCK(cs_main); assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate)); - AcceptToMemoryPoolWithTime(chainparams, pool, active_chainstate, state, tx, nullptr /* pfMissingInputs */, nTime, + AcceptToMemoryPoolWithTime(chainparams, pool, active_chainstate, state, tx, nTime, false /* bypass_limits */, 0 /* nAbsurdFee */, false /* test_accept */); if (state.IsValid()) { ++count; diff --git a/src/validation.h b/src/validation.h index f2028d7a41..1976bfa773 100644 --- a/src/validation.h +++ b/src/validation.h @@ -43,6 +43,7 @@ class CQuorumBlockProcessor; class CEvoDB; class CChainState; +class BlockValidationState; class CBlockIndex; class CBlockTreeDB; class CBlockUndo; @@ -52,7 +53,7 @@ class CInv; class CConnman; class CScriptCheck; class CTxMemPool; -class CValidationState; +class TxValidationState; class ChainstateManager; struct PrecomputedTransactionData; struct ChainTxData; @@ -215,8 +216,8 @@ void UnlinkPrunedFiles(const std::set& setFilesToPrune); void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeight); /** (try to) add transaction to memory pool */ -bool AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, - bool* pfMissingInputs, bool bypass_limits, +bool AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, TxValidationState &state, const CTransactionRef &tx, + bool bypass_limits, const CAmount nAbsurdFee, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool GetUTXOCoin(const COutPoint& outpoint, Coin& coin); @@ -315,10 +316,10 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex); /** Functions for validating blocks and updating the block tree */ /** Context-independent validity checks */ -bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true, bool fCheckMerkleRoot = true); +bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true, bool fCheckMerkleRoot = true); /** Check a block is completely valid from start to finish (only works on top of our current best block) */ -bool TestBlockValidity(CValidationState& state, +bool TestBlockValidity(BlockValidationState& state, llmq::CChainLocksHandler& clhandler, CEvoDB& evoDb, const CChainParams& chainparams, @@ -454,7 +455,7 @@ public: */ bool AcceptBlockHeader( const CBlockHeader& block, - CValidationState& state, + BlockValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main); @@ -681,7 +682,7 @@ public: */ bool FlushStateToDisk( const CChainParams& chainparams, - CValidationState &state, + BlockValidationState &state, FlushStateMode mode, int nManualPruneHeight = 0); @@ -708,29 +709,29 @@ public: * @returns true unless a system error occurred */ bool ActivateBestChain( - CValidationState& state, + BlockValidationState& state, const CChainParams& chainparams, std::shared_ptr pblock = nullptr) LOCKS_EXCLUDED(cs_main); - bool AcceptBlock(const std::shared_ptr& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + bool AcceptBlock(const std::shared_ptr& pblock, BlockValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main); // Block (dis)connection on a given view: DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + bool ConnectBlock(const CBlock& block, BlockValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main); // Apply the effects of a block disconnection on the UTXO set. - bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs); + bool DisconnectTip(BlockValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs); // Manual block validity manipulation: /** Mark a block as precious and reorganize. * * May not be called in a validationinterface callback. */ - bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main); + bool PreciousBlock(BlockValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main); /** Mark a block as invalid. */ - bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main); + bool InvalidateBlock(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main); /** Enforce a block marking all the other chains as conflicting. */ - void EnforceBlock(CValidationState& state, const CChainParams& chainparams, const CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main); + void EnforceBlock(BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main); /** Remove invalidity status from a block and its descendants. */ void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main); @@ -738,7 +739,7 @@ public: bool ReplayBlocks(const CChainParams& params); /** Ensures we have a genesis block in the block tree, possibly writing one to disk. */ bool LoadGenesisBlock(const CChainParams& chainparams); - bool AddGenesisBlock(const CChainParams& chainparams, const CBlock& block, CValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + bool AddGenesisBlock(const CChainParams& chainparams, const CBlock& block, BlockValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main); void PruneBlockIndexCandidates(); @@ -774,10 +775,10 @@ public: std::string ToString() EXCLUSIVE_LOCKS_REQUIRED(::cs_main); private: - bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs); - bool ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs); + bool ActivateBestChainStep(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs); + bool ConnectTip(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions& disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs); - void InvalidBlockFound(CBlockIndex* pindex, const CValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + 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, const FlatFilePos& pos) EXCLUSIVE_LOCKS_REQUIRED(cs_main); @@ -785,7 +786,7 @@ private: bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params) EXCLUSIVE_LOCKS_REQUIRED(cs_main); //! Mark a block as conflicting - bool MarkConflictingBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + bool MarkConflictingBlock(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main); //! Mark a block as not having block data void EraseBlockData(CBlockIndex* index) EXCLUSIVE_LOCKS_REQUIRED(cs_main); @@ -990,7 +991,7 @@ public: * @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 */ - bool ProcessNewBlockHeaders(const std::vector& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr, CBlockHeader* first_invalid = nullptr) LOCKS_EXCLUDED(cs_main); + bool ProcessNewBlockHeaders(const std::vector& block, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr) LOCKS_EXCLUDED(cs_main); //! Load the block tree and coins database from disk, initializing state if we're running with -reindex bool LoadBlockIndex(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main); diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index 8a33f76469..585c81961b 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -44,7 +44,7 @@ struct MainSignalsInstance { boost::signals2::signal&, const CBlockIndex* pindex)> BlockDisconnected; boost::signals2::signal TransactionRemovedFromMempool; boost::signals2::signal ChainStateFlushed; - boost::signals2::signal BlockChecked; + boost::signals2::signal BlockChecked; boost::signals2::signal&)> NewPoWValidBlock; boost::signals2::signalAcceptedBlockHeader; boost::signals2::signalNotifyHeaderTip; @@ -198,7 +198,7 @@ void CMainSignals::ChainStateFlushed(const CBlockLocator &locator) { }); } -void CMainSignals::BlockChecked(const CBlock& block, const CValidationState& state) { +void CMainSignals::BlockChecked(const CBlock& block, const BlockValidationState& state) { m_internals->BlockChecked(block, state); } diff --git a/src/validationinterface.h b/src/validationinterface.h index 0790d846f1..cc96d6a04c 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -13,12 +13,12 @@ #include extern CCriticalSection cs_main; +class BlockValidationState; class CBlock; class CBlockIndex; struct CBlockLocator; class CConnman; class CValidationInterface; -class CValidationState; class CGovernanceVote; class CGovernanceObject; class CDeterministicMNList; @@ -185,11 +185,11 @@ protected: 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 yet */ @@ -236,7 +236,7 @@ public: void NotifyRecoveredSig(const std::shared_ptr &sig); void NotifyMasternodeListChanged(bool undo, const CDeterministicMNList& oldMNList, const CDeterministicMNListDiff& diff, CConnman& connman); 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/test/functional/feature_llmq_is_cl_conflicts.py b/test/functional/feature_llmq_is_cl_conflicts.py index 415b4f57be..9616cd32a2 100755 --- a/test/functional/feature_llmq_is_cl_conflicts.py +++ b/test/functional/feature_llmq_is_cl_conflicts.py @@ -232,7 +232,7 @@ class LLMQ_IS_CL_Conflicts(DashTestFramework): # Assert that the conflicting tx got mined and the locked TX is not valid assert self.nodes[0].getrawtransaction(rawtx1_txid, True)['confirmations'] > 0 - assert_raises_rpc_error(-25, "Missing inputs", self.nodes[0].sendrawtransaction, rawtx2) + assert_raises_rpc_error(-25, "bad-txns-inputs-missingorspent", self.nodes[0].sendrawtransaction, rawtx2) # Create the block and the corresponding clsig but do not relay clsig yet cl_block = self.create_block(self.nodes[0]) diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py index df59bee924..a14678125e 100755 --- a/test/functional/rpc_rawtransaction.py +++ b/test/functional/rpc_rawtransaction.py @@ -151,7 +151,7 @@ class RawTransactionsTest(BitcoinTestFramework): 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 #