[validation] Add CValidationState subclasses

Split CValidationState into TxValidationState and BlockValidationState
to store validation results for transactions and blocks respectively.
This commit is contained in:
John Newbery
2019-10-24 11:35:42 -04:00
parent 48cb468ce3
commit a27a2957ed
33 changed files with 363 additions and 323 deletions

View File

@@ -982,14 +982,12 @@ void Misbehaving(NodeId pnode, int howmuch, const std::string& message) EXCLUSIV
* banning/disconnecting us. We use this to determine which unaccepted
* transactions from a whitelisted peer that we can safely relay.
*/
static bool TxRelayMayResultInDisconnect(const CValidationState& state)
{
assert(IsTransactionReason(state.GetReason()));
return state.GetReason() == ValidationInvalidReason::CONSENSUS;
static bool TxRelayMayResultInDisconnect(const TxValidationState& state) {
return state.GetResult() == TxValidationResult::TX_CONSENSUS;
}
/**
* Potentially ban a node based on the contents of a CValidationState object
* Potentially ban a node based on the contents of a BlockValidationState object
*
* @param[in] via_compact_block: this bool is passed in because net_processing should
* punish peers differently depending on whether the data was provided in a compact
@@ -997,23 +995,21 @@ static bool TxRelayMayResultInDisconnect(const CValidationState& state)
* txs, the peer should not be punished. See BIP 152.
*
* @return Returns true if the peer was punished (probably disconnected)
*
* Changes here may need to be reflected in TxRelayMayResultInDisconnect().
*/
static bool MaybePunishNode(NodeId nodeid, const CValidationState& state, bool via_compact_block, const std::string& message = "") {
switch (state.GetReason()) {
case ValidationInvalidReason::NONE:
static bool MaybePunishNodeForBlock(NodeId nodeid, const BlockValidationState& state, bool via_compact_block, const std::string& message = "") {
switch (state.GetResult()) {
case BlockValidationResult::BLOCK_RESULT_UNSET:
break;
// The node is providing invalid data:
case ValidationInvalidReason::CONSENSUS:
case ValidationInvalidReason::BLOCK_MUTATED:
case BlockValidationResult::BLOCK_CONSENSUS:
case BlockValidationResult::BLOCK_MUTATED:
if (!via_compact_block) {
LOCK(cs_main);
Misbehaving(nodeid, 100, message);
return true;
}
break;
case ValidationInvalidReason::CACHED_INVALID:
case BlockValidationResult::BLOCK_CACHED_INVALID:
{
LOCK(cs_main);
CNodeState *node_state = State(nodeid);
@@ -1029,30 +1025,24 @@ 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:
{
LOCK(cs_main);
Misbehaving(nodeid, 100, message);
}
return true;
// Conflicting (but not necessarily invalid) data or different policy:
case ValidationInvalidReason::BLOCK_MISSING_PREV:
case BlockValidationResult::BLOCK_MISSING_PREV:
{
// TODO: Handle this much more gracefully (10 DoS points is super arbitrary)
LOCK(cs_main);
Misbehaving(nodeid, 10, message);
}
return true;
case ValidationInvalidReason::RECENT_CONSENSUS_CHANGE:
case ValidationInvalidReason::BLOCK_TIME_FUTURE:
case ValidationInvalidReason::TX_NOT_STANDARD:
case ValidationInvalidReason::TX_MISSING_INPUTS:
case ValidationInvalidReason::TX_PREMATURE_SPEND:
case ValidationInvalidReason::TX_WITNESS_MUTATED:
case ValidationInvalidReason::TX_CONFLICT:
case ValidationInvalidReason::TX_MEMPOOL_POLICY:
case BlockValidationResult::BLOCK_RECENT_CONSENSUS_CHANGE:
case BlockValidationResult::BLOCK_TIME_FUTURE:
break;
}
if (message != "") {
@@ -1061,6 +1051,39 @@ 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_WITNESS_MUTATED:
case TxValidationResult::TX_CONFLICT:
case TxValidationResult::TX_MEMPOOL_POLICY:
break;
}
if (message != "") {
LogPrint(BCLog::NET, "peer=%d: %s\n", nodeid, message);
}
return false;
}
@@ -1229,7 +1252,7 @@ void PeerLogicValidation::UpdatedBlockTip(const CBlockIndex *pindexNew, const CB
* Handle invalid block rejection and consequent peer banning, 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());
@@ -1240,7 +1263,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
@@ -1378,7 +1401,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 (!ActivateBestChain(state, Params(), a_recent_block)) {
LogPrint(BCLog::NET, "failed to activate chain (%s)\n", FormatStateMessage(state));
}
@@ -1674,11 +1697,11 @@ bool static ProcessHeadersMessage(CNode *pfrom, CConnman *connman, const std::ve
}
}
CValidationState state;
BlockValidationState state;
CBlockHeader first_invalid_header;
if (!ProcessNewBlockHeaders(headers, state, chainparams, &pindexLast, &first_invalid_header)) {
if (state.IsInvalid()) {
MaybePunishNode(pfrom->GetId(), state, via_compact_block, "invalid header received");
MaybePunishNodeForBlock(pfrom->GetId(), state, via_compact_block, "invalid header received");
return false;
}
}
@@ -1815,10 +1838,10 @@ void static ProcessOrphanTx(CConnman* connman, std::set<uint256>& orphan_work_se
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(mempool, orphan_state, porphanTx, &fMissingInputs2, &removed_txn, false /* bypass_limits */, 0 /* nAbsurdFee */)) {
@@ -1837,7 +1860,7 @@ void static ProcessOrphanTx(CConnman* connman, std::set<uint256>& orphan_work_se
} else if (!fMissingInputs2) {
if (orphan_state.IsInvalid()) {
// Punish peer that gave us an invalid orphan tx
if (MaybePunishNode(fromPeer, orphan_state, /*via_compact_block*/ false)) {
if (MaybePunishNodeForTx(fromPeer, orphan_state)) {
setMisbehaving.insert(fromPeer);
}
LogPrint(BCLog::MEMPOOL, " invalid orphan tx %s\n", orphanHash.ToString());
@@ -1845,8 +1868,7 @@ void static ProcessOrphanTx(CConnman* connman, std::set<uint256>& orphan_work_se
// Has inputs but not accepted to mempool
// Probably non-standard or insufficient fee
LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", orphanHash.ToString());
assert(IsTransactionReason(orphan_state.GetReason()));
if (!orphanTx.HasWitness() && orphan_state.GetReason() != ValidationInvalidReason::TX_WITNESS_MUTATED) {
if (!orphanTx.HasWitness() && orphan_state.GetResult() != TxValidationResult::TX_WITNESS_MUTATED) {
// Do not use rejection cache for witness transactions or
// witness-stripped transactions, as they can have been malleated.
// See https://github.com/bitcoin/bitcoin/issues/8279 for details.
@@ -2291,7 +2313,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LOCK(cs_most_recent_block);
a_recent_block = most_recent_block;
}
CValidationState state;
BlockValidationState state;
if (!ActivateBestChain(state, Params(), a_recent_block)) {
LogPrint(BCLog::NET, "failed to activate chain (%s)\n", FormatStateMessage(state));
}
@@ -2472,7 +2494,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
LOCK2(cs_main, g_cs_orphans);
bool fMissingInputs = false;
CValidationState state;
TxValidationState state;
CNodeState* nodestate = State(pfrom->GetId());
nodestate->m_tx_download.m_tx_announced.erase(inv.hash);
@@ -2537,8 +2559,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
recentRejects->insert(tx.GetHash());
}
} else {
assert(IsTransactionReason(state.GetReason()));
if (!tx.HasWitness() && state.GetReason() != ValidationInvalidReason::TX_WITNESS_MUTATED) {
if (!tx.HasWitness() && state.GetResult() != TxValidationResult::TX_WITNESS_MUTATED) {
// Do not use rejection cache for witness transactions or
// witness-stripped transactions, as they can have been malleated.
// See https://github.com/bitcoin/bitcoin/issues/8279 for details.
@@ -2593,7 +2614,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
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);
}
return true;
}
@@ -2627,10 +2648,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
}
const CBlockIndex *pindex = nullptr;
CValidationState state;
BlockValidationState state;
if (!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 true;
}
}