Changeset View
Changeset View
Standalone View
Standalone View
src/consensus/validation.h
// Copyright (c) 2009-2010 Satoshi Nakamoto | // Copyright (c) 2009-2010 Satoshi Nakamoto | ||||
// Copyright (c) 2009-2016 The Bitcoin Core developers | // Copyright (c) 2009-2016 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#ifndef BITCOIN_CONSENSUS_VALIDATION_H | #ifndef BITCOIN_CONSENSUS_VALIDATION_H | ||||
#define BITCOIN_CONSENSUS_VALIDATION_H | #define BITCOIN_CONSENSUS_VALIDATION_H | ||||
#include <cassert> | |||||
#include <string> | #include <string> | ||||
/** "reject" message codes */ | /** "reject" message codes */ | ||||
static const uint8_t REJECT_MALFORMED = 0x01; | static const uint8_t REJECT_MALFORMED = 0x01; | ||||
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 | |||||
* provider of the object should be banned/ignored/disconnected/etc. These are | |||||
* much more granular than the rejection codes, which may be more useful for | |||||
* some other use-cases. | |||||
*/ | |||||
enum class ValidationInvalidReason { | |||||
// txn and blocks: | |||||
//! not actually invalid | |||||
NONE, | |||||
//! invalid by consensus rules (excluding any below reasons) | |||||
CONSENSUS, | |||||
/** | |||||
* Invalid by a recent change to consensus rules. | |||||
* Currently unused as there are no such consensus rule changes. | |||||
*/ | |||||
RECENT_CONSENSUS_CHANGE, | |||||
// Only blocks (or headers): | |||||
//! this object was cached as being invalid, but we don't know why | |||||
CACHED_INVALID, | |||||
//! invalid proof of work or time too old | |||||
BLOCK_INVALID_HEADER, | |||||
//! the block's data didn't match the data committed to by the PoW | |||||
BLOCK_MUTATED, | |||||
//! We don't have the previous block the checked one is built on | |||||
BLOCK_MISSING_PREV, | |||||
//! A block this one builds on is invalid | |||||
BLOCK_INVALID_PREV, | |||||
//! block timestamp was > 2 hours in the future (or our clock is bad) | |||||
BLOCK_TIME_FUTURE, | |||||
//! the block failed to meet one of our checkpoints | |||||
BLOCK_CHECKPOINT, | |||||
//! block finalization problems. | |||||
BLOCK_FINALIZATION, | |||||
// Only loose txn: | |||||
//! didn't meet our local policy rules | |||||
TX_NOT_STANDARD, | |||||
//! a transaction was missing some of its inputs (or its inputs were spent | |||||
//! at < coinbase maturity height) | |||||
TX_MISSING_INPUTS, | |||||
/** | |||||
* 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, | |||||
//! violated mempool's fee/size/descendant/etc limits | |||||
TX_MEMPOOL_POLICY, | |||||
}; | |||||
/** Capture information about block/transaction validation */ | /** Capture information about block/transaction validation */ | ||||
class CValidationState { | class CValidationState { | ||||
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; | } mode; | ||||
ValidationInvalidReason m_reason; | |||||
int nDoS; | int nDoS; | ||||
std::string strRejectReason; | std::string strRejectReason; | ||||
unsigned int chRejectCode; | unsigned int chRejectCode; | ||||
bool corruptionPossible; | bool corruptionPossible; | ||||
std::string strDebugMessage; | std::string strDebugMessage; | ||||
public: | public: | ||||
CValidationState() | CValidationState() | ||||
: mode(MODE_VALID), nDoS(0), chRejectCode(0), | : mode(MODE_VALID), m_reason(ValidationInvalidReason::NONE), nDoS(0), | ||||
corruptionPossible(false) {} | chRejectCode(0), corruptionPossible(false) {} | ||||
bool DoS(int level, bool ret = false, unsigned int chRejectCodeIn = 0, | bool DoS(int level, ValidationInvalidReason reasonIn, bool ret = false, | ||||
unsigned int chRejectCodeIn = 0, | |||||
const std::string &strRejectReasonIn = "", | const std::string &strRejectReasonIn = "", | ||||
bool corruptionIn = false, | bool corruptionIn = false, | ||||
const std::string &strDebugMessageIn = "") { | const std::string &strDebugMessageIn = "") { | ||||
m_reason = reasonIn; | |||||
chRejectCode = chRejectCodeIn; | chRejectCode = chRejectCodeIn; | ||||
strRejectReason = strRejectReasonIn; | strRejectReason = strRejectReasonIn; | ||||
corruptionPossible = corruptionIn; | corruptionPossible = corruptionIn; | ||||
strDebugMessage = strDebugMessageIn; | strDebugMessage = strDebugMessageIn; | ||||
nDoS += level; | |||||
assert(nDoS == GetDoSForReason()); | |||||
assert(corruptionPossible == | |||||
(m_reason == ValidationInvalidReason::BLOCK_MUTATED)); | |||||
if (mode == MODE_ERROR) { | if (mode == MODE_ERROR) { | ||||
return ret; | return ret; | ||||
} | } | ||||
nDoS += level; | |||||
mode = MODE_INVALID; | mode = MODE_INVALID; | ||||
return ret; | return ret; | ||||
} | } | ||||
bool Invalid(ValidationInvalidReason _reason, bool ret = false, | |||||
bool Invalid(bool ret = false, unsigned int _chRejectCode = 0, | unsigned int _chRejectCode = 0, | ||||
const std::string &_strRejectReason = "", | const std::string &_strRejectReason = "", | ||||
const std::string &_strDebugMessage = "") { | const std::string &_strDebugMessage = "") { | ||||
return DoS(0, ret, _chRejectCode, _strRejectReason, false, | return DoS(0, _reason, ret, _chRejectCode, _strRejectReason, false, | ||||
_strDebugMessage); | _strDebugMessage); | ||||
} | } | ||||
bool Error(const std::string &strRejectReasonIn) { | bool Error(const std::string &strRejectReasonIn) { | ||||
if (mode == MODE_VALID) { | if (mode == MODE_VALID) { | ||||
strRejectReason = strRejectReasonIn; | strRejectReason = strRejectReasonIn; | ||||
} | } | ||||
mode = MODE_ERROR; | mode = MODE_ERROR; | ||||
return false; | return false; | ||||
} | } | ||||
bool IsValid() const { return mode == MODE_VALID; } | bool IsValid() const { return mode == MODE_VALID; } | ||||
bool IsInvalid() const { return mode == MODE_INVALID; } | bool IsInvalid() const { return mode == MODE_INVALID; } | ||||
bool IsError() const { return mode == MODE_ERROR; } | bool IsError() const { return mode == MODE_ERROR; } | ||||
bool CorruptionPossible() const { | |||||
bool CorruptionPossible() const { return corruptionPossible; } | assert(corruptionPossible == | ||||
void SetCorruptionPossible() { corruptionPossible = true; } | (m_reason == ValidationInvalidReason::BLOCK_MUTATED)); | ||||
return corruptionPossible; | |||||
} | |||||
void SetCorruptionPossible() { | |||||
corruptionPossible = true; | |||||
assert(corruptionPossible == | |||||
(m_reason == ValidationInvalidReason::BLOCK_MUTATED)); | |||||
} | |||||
int GetDoS() const { return nDoS; } | int GetDoS() const { return nDoS; } | ||||
int GetDoSForReason() const { | |||||
switch (m_reason) { | |||||
case ValidationInvalidReason::NONE: | |||||
return 0; | |||||
case ValidationInvalidReason::CONSENSUS: | |||||
case ValidationInvalidReason::BLOCK_MUTATED: | |||||
case ValidationInvalidReason::BLOCK_INVALID_HEADER: | |||||
case ValidationInvalidReason::BLOCK_CHECKPOINT: | |||||
case ValidationInvalidReason::BLOCK_INVALID_PREV: | |||||
return 100; | |||||
case ValidationInvalidReason::BLOCK_FINALIZATION: | |||||
return 20; | |||||
case ValidationInvalidReason::BLOCK_MISSING_PREV: | |||||
return 10; | |||||
case ValidationInvalidReason::CACHED_INVALID: | |||||
case ValidationInvalidReason::RECENT_CONSENSUS_CHANGE: | |||||
case ValidationInvalidReason::BLOCK_TIME_FUTURE: | |||||
case ValidationInvalidReason::TX_NOT_STANDARD: | |||||
case ValidationInvalidReason::TX_MISSING_INPUTS: | |||||
case ValidationInvalidReason::TX_CONFLICT: | |||||
case ValidationInvalidReason::TX_MEMPOOL_POLICY: | |||||
return 0; | |||||
} | |||||
return 0; | |||||
} | |||||
ValidationInvalidReason GetReason() const { return m_reason; } | |||||
unsigned int GetRejectCode() const { return chRejectCode; } | unsigned int GetRejectCode() const { return chRejectCode; } | ||||
std::string GetRejectReason() const { return strRejectReason; } | std::string GetRejectReason() const { return strRejectReason; } | ||||
std::string GetDebugMessage() const { return strDebugMessage; } | std::string GetDebugMessage() const { return strDebugMessage; } | ||||
}; | }; | ||||
#endif // BITCOIN_CONSENSUS_VALIDATION_H | #endif // BITCOIN_CONSENSUS_VALIDATION_H |