mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-02-03 12:11:56 +00:00
The *_RECENT_CONSENSUS_CHANGE variants in the validation result enumerations were always unused. They seem to have been kept around speculatively for a soft fork after segwit, however they were never used for taproot either. This points at them not having a clear purpose. Based on the original pull requests' comments their usage was never entirely clear: https://github.com/bitcoin/bitcoin/pull/11639#issuecomment-370234133 https://github.com/bitcoin/bitcoin/pull/15141#discussion_r271039747 Since they are part of the validation interface and need to exposed by the kernel library keeping them around may also be confusing to future users of the library.
169 lines
7.2 KiB
C++
169 lines
7.2 KiB
C++
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
// Copyright (c) 2009-2022 The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#ifndef BITCOIN_CONSENSUS_VALIDATION_H
|
|
#define BITCOIN_CONSENSUS_VALIDATION_H
|
|
|
|
#include <string>
|
|
#include <consensus/consensus.h>
|
|
#include <primitives/transaction.h>
|
|
#include <primitives/block.h>
|
|
|
|
/** Index marker for when no witness commitment is present in a coinbase transaction. */
|
|
static constexpr int NO_WITNESS_COMMITMENT{-1};
|
|
|
|
/** Minimum size of a witness commitment structure. Defined in BIP 141. **/
|
|
static constexpr size_t MINIMUM_WITNESS_COMMITMENT{38};
|
|
|
|
/** A "reason" why a transaction was invalid, suitable for determining whether the
|
|
* provider of the transaction should be banned/ignored/disconnected/etc.
|
|
*/
|
|
enum class TxValidationResult {
|
|
TX_RESULT_UNSET = 0, //!< initial value. Tx has not yet been rejected
|
|
TX_CONSENSUS, //!< invalid by consensus rules
|
|
TX_INPUTS_NOT_STANDARD, //!< inputs (covered by txid) failed policy rules
|
|
TX_NOT_STANDARD, //!< otherwise 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
|
|
/**
|
|
* Transaction might have a witness prior to SegWit
|
|
* activation, or witness may have been malleated (which includes
|
|
* non-standard witnesses).
|
|
*/
|
|
TX_WITNESS_MUTATED,
|
|
/**
|
|
* Transaction is missing a witness.
|
|
*/
|
|
TX_WITNESS_STRIPPED,
|
|
/**
|
|
* Tx already in mempool or conflicts with a tx in the chain
|
|
* (if it conflicts with another tx in mempool, we use MEMPOOL_POLICY as it failed to reach the RBF threshold)
|
|
* Currently this is only used if the transaction already exists in the mempool or on chain.
|
|
*/
|
|
TX_CONFLICT,
|
|
TX_MEMPOOL_POLICY, //!< violated mempool's fee/size/descendant/RBF/etc limits
|
|
TX_NO_MEMPOOL, //!< this node does not have a mempool so can't validate the transaction
|
|
TX_RECONSIDERABLE, //!< fails some policy, but might be acceptable if submitted in a (different) package
|
|
TX_UNKNOWN, //!< transaction was not validated because package failed
|
|
};
|
|
|
|
/** 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 = 0, //!< initial value. Block has not yet been rejected
|
|
BLOCK_CONSENSUS, //!< invalid by consensus rules (excluding any below reasons)
|
|
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
|
|
BLOCK_INVALID_PREV, //!< A block this one builds on is invalid
|
|
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_HEADER_LOW_WORK //!< the block header may be on a too-little-work chain
|
|
};
|
|
|
|
|
|
|
|
/** Template for capturing information about block/transaction validation. This is instantiated
|
|
* by TxValidationState and BlockValidationState for validation information on transactions
|
|
* and blocks respectively. */
|
|
template <typename Result>
|
|
class ValidationState
|
|
{
|
|
private:
|
|
enum class ModeState {
|
|
M_VALID, //!< everything ok
|
|
M_INVALID, //!< network rule violation (DoS value may be set)
|
|
M_ERROR, //!< run-time error
|
|
} m_mode{ModeState::M_VALID};
|
|
Result m_result{};
|
|
std::string m_reject_reason;
|
|
std::string m_debug_message;
|
|
|
|
public:
|
|
bool Invalid(Result result,
|
|
const std::string& reject_reason = "",
|
|
const std::string& debug_message = "")
|
|
{
|
|
m_result = result;
|
|
m_reject_reason = reject_reason;
|
|
m_debug_message = debug_message;
|
|
if (m_mode != ModeState::M_ERROR) m_mode = ModeState::M_INVALID;
|
|
return false;
|
|
}
|
|
bool Error(const std::string& reject_reason)
|
|
{
|
|
if (m_mode == ModeState::M_VALID)
|
|
m_reject_reason = reject_reason;
|
|
m_mode = ModeState::M_ERROR;
|
|
return false;
|
|
}
|
|
bool IsValid() const { return m_mode == ModeState::M_VALID; }
|
|
bool IsInvalid() const { return m_mode == ModeState::M_INVALID; }
|
|
bool IsError() const { return m_mode == ModeState::M_ERROR; }
|
|
Result GetResult() const { return m_result; }
|
|
std::string GetRejectReason() const { return m_reject_reason; }
|
|
std::string GetDebugMessage() const { return m_debug_message; }
|
|
std::string ToString() const
|
|
{
|
|
if (IsValid()) {
|
|
return "Valid";
|
|
}
|
|
|
|
if (!m_debug_message.empty()) {
|
|
return m_reject_reason + ", " + m_debug_message;
|
|
}
|
|
|
|
return m_reject_reason;
|
|
}
|
|
};
|
|
|
|
class TxValidationState : public ValidationState<TxValidationResult> {};
|
|
class BlockValidationState : public ValidationState<BlockValidationResult> {};
|
|
|
|
// These implement the weight = (stripped_size * 4) + witness_size formula,
|
|
// using only serialization with and without witness data. As witness_size
|
|
// is equal to total_size - stripped_size, this formula is identical to:
|
|
// weight = (stripped_size * 3) + total_size.
|
|
static inline int32_t GetTransactionWeight(const CTransaction& tx)
|
|
{
|
|
return ::GetSerializeSize(TX_NO_WITNESS(tx)) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(TX_WITH_WITNESS(tx));
|
|
}
|
|
static inline int64_t GetBlockWeight(const CBlock& block)
|
|
{
|
|
return ::GetSerializeSize(TX_NO_WITNESS(block)) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(TX_WITH_WITNESS(block));
|
|
}
|
|
static inline int64_t GetTransactionInputWeight(const CTxIn& txin)
|
|
{
|
|
// scriptWitness size is added here because witnesses and txins are split up in segwit serialization.
|
|
return ::GetSerializeSize(TX_NO_WITNESS(txin)) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(TX_WITH_WITNESS(txin)) + ::GetSerializeSize(txin.scriptWitness.stack);
|
|
}
|
|
|
|
/** Compute at which vout of the block's coinbase transaction the witness commitment occurs, or -1 if not found */
|
|
inline int GetWitnessCommitmentIndex(const CBlock& block)
|
|
{
|
|
int commitpos = NO_WITNESS_COMMITMENT;
|
|
if (!block.vtx.empty()) {
|
|
for (size_t o = 0; o < block.vtx[0]->vout.size(); o++) {
|
|
const CTxOut& vout = block.vtx[0]->vout[o];
|
|
if (vout.scriptPubKey.size() >= MINIMUM_WITNESS_COMMITMENT &&
|
|
vout.scriptPubKey[0] == OP_RETURN &&
|
|
vout.scriptPubKey[1] == 0x24 &&
|
|
vout.scriptPubKey[2] == 0xaa &&
|
|
vout.scriptPubKey[3] == 0x21 &&
|
|
vout.scriptPubKey[4] == 0xa9 &&
|
|
vout.scriptPubKey[5] == 0xed) {
|
|
commitpos = o;
|
|
}
|
|
}
|
|
}
|
|
return commitpos;
|
|
}
|
|
|
|
#endif // BITCOIN_CONSENSUS_VALIDATION_H
|