Changeset View
Changeset View
Standalone View
Standalone View
src/consensus/validation.h
Show All 13 Lines | |||||
static const uint8_t REJECT_INVALID = 0x10; | static const uint8_t REJECT_INVALID = 0x10; | ||||
static const uint8_t REJECT_OBSOLETE = 0x11; | static const uint8_t REJECT_OBSOLETE = 0x11; | ||||
static const uint8_t REJECT_DUPLICATE = 0x12; | static const uint8_t REJECT_DUPLICATE = 0x12; | ||||
static const uint8_t REJECT_NONSTANDARD = 0x40; | static const uint8_t REJECT_NONSTANDARD = 0x40; | ||||
static const uint8_t REJECT_INSUFFICIENTFEE = 0x42; | static const uint8_t REJECT_INSUFFICIENTFEE = 0x42; | ||||
static const uint8_t REJECT_CHECKPOINT = 0x43; | static const uint8_t REJECT_CHECKPOINT = 0x43; | ||||
/** | /** | ||||
* A "reason" why something was invalid, suitable for determining whether the | * A "reason" why a transaction was invalid, suitable for determining whether | ||||
* provider of the object should be banned/ignored/disconnected/etc. These are | * the provider of the transaction should be banned/ignored/disconnected/etc. | ||||
* much more granular than the rejection codes, which may be more useful for | * These are much more granular than the rejection codes, which may be more | ||||
* some other use-cases. | * useful for some other use-cases. | ||||
*/ | */ | ||||
enum class ValidationInvalidReason { | enum class TxValidationResult { | ||||
// txn and blocks: | //! initial value. Tx has not yet been rejected | ||||
//! not actually invalid | TX_RESULT_UNSET, | ||||
NONE, | //! invalid by consensus rules | ||||
//! invalid by consensus rules (excluding any below reasons) | TX_CONSENSUS, | ||||
CONSENSUS, | |||||
/** | /** | ||||
* Invalid by a recent change to consensus rules. | * Invalid by a recent change to consensus rules. | ||||
* Currently unused as there are no such consensus rule changes. | * Currently unused as there are no such consensus rule changes. | ||||
*/ | */ | ||||
RECENT_CONSENSUS_CHANGE, | TX_RECENT_CONSENSUS_CHANGE, | ||||
// Only blocks (or headers): | |||||
//! this object was cached as being invalid, but we don't know why | |||||
CACHED_INVALID, | |||||
//! invalid proof of work or time too old | |||||
BLOCK_INVALID_HEADER, | |||||
//! the block's data didn't match the data committed to by the PoW | |||||
BLOCK_MUTATED, | |||||
//! We don't have the previous block the checked one is built on | |||||
BLOCK_MISSING_PREV, | |||||
//! A block this one builds on is invalid | |||||
BLOCK_INVALID_PREV, | |||||
//! block timestamp was > 2 hours in the future (or our clock is bad) | |||||
BLOCK_TIME_FUTURE, | |||||
//! the block failed to meet one of our checkpoints | |||||
BLOCK_CHECKPOINT, | |||||
//! block finalization problems. | |||||
BLOCK_FINALIZATION, | |||||
// Only loose txn: | |||||
//! didn't meet our local policy rules | //! didn't meet our local policy rules | ||||
TX_NOT_STANDARD, | TX_NOT_STANDARD, | ||||
//! transaction was missing some of its inputs | //! transaction was missing some of its inputs | ||||
TX_MISSING_INPUTS, | TX_MISSING_INPUTS, | ||||
//! transaction spends a coinbase too early, or violates locktime/sequence | //! transaction spends a coinbase too early, or violates locktime/sequence | ||||
//! locks | //! locks | ||||
TX_PREMATURE_SPEND, | TX_PREMATURE_SPEND, | ||||
/** | /** | ||||
* Tx already in mempool or conflicts with a tx in the chain | * Tx already in mempool or conflicts with a tx in the chain | ||||
* TODO: Currently this is only used if the transaction already exists in | * Currently this is only used if the transaction already exists in the | ||||
* the mempool or on chain, | * mempool or on chain. | ||||
*/ | */ | ||||
TX_CONFLICT, | TX_CONFLICT, | ||||
//! violated mempool's fee/size/descendant/etc limits | //! violated mempool's fee/size/descendant/etc limits | ||||
TX_MEMPOOL_POLICY, | TX_MEMPOOL_POLICY, | ||||
}; | }; | ||||
inline bool IsTransactionReason(ValidationInvalidReason r) { | /** | ||||
return r == ValidationInvalidReason::NONE || | * A "reason" why a block was invalid, suitable for determining whether the | ||||
r == ValidationInvalidReason::CONSENSUS || | * provider of the block should be banned/ignored/disconnected/etc. | ||||
r == ValidationInvalidReason::RECENT_CONSENSUS_CHANGE || | * These are much more granular than the rejection codes, which may be more | ||||
r == ValidationInvalidReason::TX_NOT_STANDARD || | * useful for some other use-cases. | ||||
r == ValidationInvalidReason::TX_PREMATURE_SPEND || | */ | ||||
r == ValidationInvalidReason::TX_MISSING_INPUTS || | enum class BlockValidationResult { | ||||
r == ValidationInvalidReason::TX_CONFLICT || | //! initial value. Block has not yet been rejected | ||||
r == ValidationInvalidReason::TX_MEMPOOL_POLICY; | BLOCK_RESULT_UNSET, | ||||
} | //! invalid by consensus rules (excluding any below reasons) | ||||
BLOCK_CONSENSUS, | |||||
inline bool IsBlockReason(ValidationInvalidReason r) { | /** | ||||
return r == ValidationInvalidReason::NONE || | * Invalid by a change to consensus rules more recent than SegWit. | ||||
r == ValidationInvalidReason::CONSENSUS || | * Currently unused as there are no such consensus rule changes, and any | ||||
r == ValidationInvalidReason::RECENT_CONSENSUS_CHANGE || | * download sources realistically need to support SegWit in order to provide | ||||
r == ValidationInvalidReason::CACHED_INVALID || | * useful data, so differentiating between always-invalid and | ||||
r == ValidationInvalidReason::BLOCK_INVALID_HEADER || | * invalid-by-pre-SegWit-soft-fork is uninteresting. | ||||
r == ValidationInvalidReason::BLOCK_MUTATED || | */ | ||||
r == ValidationInvalidReason::BLOCK_MISSING_PREV || | BLOCK_RECENT_CONSENSUS_CHANGE, | ||||
r == ValidationInvalidReason::BLOCK_INVALID_PREV || | //! this block was cached as being invalid and we didn't store the reason | ||||
r == ValidationInvalidReason::BLOCK_TIME_FUTURE || | //! why | ||||
r == ValidationInvalidReason::BLOCK_CHECKPOINT || | BLOCK_CACHED_INVALID, | ||||
r == ValidationInvalidReason::BLOCK_FINALIZATION; | //! invalid proof of work or time too old | ||||
} | BLOCK_INVALID_HEADER, | ||||
//! the block's data didn't match the data committed to by the PoW | |||||
BLOCK_MUTATED, | |||||
//! We don't have the previous block the checked one is built on | |||||
BLOCK_MISSING_PREV, | |||||
//! A block this one builds on is invalid | |||||
BLOCK_INVALID_PREV, | |||||
//! block timestamp was > 2 hours in the future (or our clock is bad) | |||||
BLOCK_TIME_FUTURE, | |||||
//! the block failed to meet one of our checkpoints | |||||
BLOCK_CHECKPOINT, | |||||
//! block finalization problems | |||||
BLOCK_FINALIZATION, | |||||
}; | |||||
/** Capture information about block/transaction validation */ | /** | ||||
class CValidationState { | * Base class for capturing information about block/transaction validation. | ||||
* This is subclassed by TxValidationState and BlockValidationState for | |||||
* validation information on transactions and blocks respectively. | |||||
*/ | |||||
class ValidationState { | |||||
private: | private: | ||||
enum mode_state { | enum mode_state { | ||||
MODE_VALID, //!< everything ok | MODE_VALID, //!< everything ok | ||||
MODE_INVALID, //!< network rule violation (DoS value may be set) | MODE_INVALID, //!< network rule violation (DoS value may be set) | ||||
MODE_ERROR, //!< run-time error | MODE_ERROR, //!< run-time error | ||||
} mode; | } m_mode; | ||||
ValidationInvalidReason m_reason; | std::string m_reject_reason; | ||||
std::string strRejectReason; | |||||
unsigned int chRejectCode; | unsigned int chRejectCode; | ||||
std::string strDebugMessage; | std::string m_debug_message; | ||||
public: | protected: | ||||
CValidationState() | void Invalid(unsigned int chRejectCodeIn = 0, | ||||
: mode(MODE_VALID), m_reason(ValidationInvalidReason::NONE), | const std::string &reject_reason = "", | ||||
chRejectCode(0) {} | const std::string &debug_message = "") { | ||||
bool Invalid(ValidationInvalidReason reasonIn, | |||||
unsigned int chRejectCodeIn = 0, | |||||
const std::string &strRejectReasonIn = "", | |||||
const std::string &strDebugMessageIn = "") { | |||||
m_reason = reasonIn; | |||||
chRejectCode = chRejectCodeIn; | chRejectCode = chRejectCodeIn; | ||||
strRejectReason = strRejectReasonIn; | m_reject_reason = reject_reason; | ||||
strDebugMessage = strDebugMessageIn; | m_debug_message = debug_message; | ||||
if (mode == MODE_ERROR) { | if (m_mode != MODE_ERROR) { | ||||
return false; | m_mode = MODE_INVALID; | ||||
} | |||||
} | |||||
public: | |||||
// ValidationState is abstract. Have a pure virtual destructor. | |||||
virtual ~ValidationState() = 0; | |||||
ValidationState() : m_mode(MODE_VALID), chRejectCode(0) {} | |||||
bool Error(const std::string &reject_reason) { | |||||
if (m_mode == MODE_VALID) { | |||||
m_reject_reason = reject_reason; | |||||
} | } | ||||
mode = MODE_INVALID; | m_mode = MODE_ERROR; | ||||
return false; | return false; | ||||
} | } | ||||
bool Error(const std::string &strRejectReasonIn) { | bool IsValid() const { return m_mode == MODE_VALID; } | ||||
if (mode == MODE_VALID) { | bool IsInvalid() const { return m_mode == MODE_INVALID; } | ||||
strRejectReason = strRejectReasonIn; | bool IsError() const { return m_mode == MODE_ERROR; } | ||||
unsigned int GetRejectCode() const { return chRejectCode; } | |||||
std::string GetRejectReason() const { return m_reject_reason; } | |||||
std::string GetDebugMessage() const { return m_debug_message; } | |||||
}; | |||||
inline ValidationState::~ValidationState(){}; | |||||
class TxValidationState : public ValidationState { | |||||
private: | |||||
TxValidationResult m_result = TxValidationResult::TX_RESULT_UNSET; | |||||
public: | |||||
bool Invalid(TxValidationResult result, unsigned int chRejectCodeIn = 0, | |||||
const std::string &reject_reason = "", | |||||
const std::string &debug_message = "") { | |||||
m_result = result; | |||||
ValidationState::Invalid(chRejectCodeIn, reject_reason, debug_message); | |||||
return false; | |||||
} | } | ||||
TxValidationResult GetResult() const { return m_result; } | |||||
}; | |||||
mode = MODE_ERROR; | class BlockValidationState : public ValidationState { | ||||
private: | |||||
BlockValidationResult m_result = BlockValidationResult::BLOCK_RESULT_UNSET; | |||||
public: | |||||
bool Invalid(BlockValidationResult result, unsigned int chRejectCodeIn = 0, | |||||
const std::string &reject_reason = "", | |||||
const std::string &debug_message = "") { | |||||
m_result = result; | |||||
ValidationState::Invalid(chRejectCodeIn, reject_reason, debug_message); | |||||
return false; | return false; | ||||
} | } | ||||
bool IsValid() const { return mode == MODE_VALID; } | BlockValidationResult GetResult() const { return m_result; } | ||||
bool IsInvalid() const { return mode == MODE_INVALID; } | |||||
bool IsError() const { return mode == MODE_ERROR; } | |||||
ValidationInvalidReason GetReason() const { return m_reason; } | |||||
unsigned int GetRejectCode() const { return chRejectCode; } | |||||
std::string GetRejectReason() const { return strRejectReason; } | |||||
std::string GetDebugMessage() const { return strDebugMessage; } | |||||
}; | }; | ||||
#endif // BITCOIN_CONSENSUS_VALIDATION_H | #endif // BITCOIN_CONSENSUS_VALIDATION_H |