Skip to content

Commit b688b85

Browse files
committed
Merge bitcoin#17004: validation: Remove REJECT code from CValidationState
9075d13 [docs] Add release notes for removal of REJECT reasons (John Newbery) 04a2f32 [validation] Fix REJECT message comments (John Newbery) e9d5a59 [validation] Remove REJECT code from CValidationState (John Newbery) 0053e16 [logging] Don't log REJECT code when transaction is rejected (John Newbery) a1a07cf [validation] Fix peer punishment for bad blocks (John Newbery) Pull request description: We no longer send BIP 61 REJECT messages, so there's no need to set a REJECT code in the CValidationState object. Note that there is a minor bug fix in p2p behaviour here. Because the call to `MaybePunishNode()` in `PeerLogicValidation::BlockChecked()` only previously happened if the REJECT code was > 0 and < `REJECT_INTERNAL`, then there are cases were `MaybePunishNode()` can get called where it wasn't previously: - when `AcceptBlockHeader()` fails with `CACHED_INVALID`. - when `AcceptBlockHeader()` fails with `BLOCK_MISSING_PREV`. Note that `BlockChecked()` cannot fail with an 'internal' reject code. The only internal reject code was `REJECT_HIGHFEE`, which was only set in ATMP. This reverts a minor bug introduced in 5d08c9c. ACKs for top commit: ariard: ACK 9075d13, changes since last reviewed are splitting them in separate commits to ease understanding and fix nits fjahr: ACK 9075d13, confirmed diff to last review was fixing nits in docs/comments. ryanofsky: Code review ACK 9075d13. Only changes since last review are splitting the main commit and updating comments Tree-SHA512: 58e8a1a4d4e6f156da5d29fb6ad6a62fc9c594bbfc6432b3252e962d0e9e10149bf3035185dc5320c46c09f3e49662bc2973ec759679c0f3412232087cb8a3a7
2 parents 8a19114 + 9075d13 commit b688b85

17 files changed

+137
-140
lines changed

doc/release-notes-15437.md

+19
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,22 @@ Please use the recommended alternatives if you rely on this deprecated feature:
3232
could wait until the transaction has confirmed (taking into account the fee
3333
target they set (compare the RPC `estimatesmartfee`)) or listen for the
3434
transaction announcement by other network peers to check for propagation.
35+
36+
The removal of BIP61 REJECT message support also has the following minor RPC
37+
and logging implications:
38+
39+
* `testmempoolaccept` and `sendrawtransaction` no longer return the P2P REJECT
40+
code when a transaction is not accepted to the mempool. They still return the
41+
verbal reject reason.
42+
43+
* Log messages that previously reported the REJECT code when a transaction was
44+
not accepted to the mempool now no longer report the REJECT code. The reason
45+
for rejection is still reported.
46+
47+
Updated RPCs
48+
------------
49+
50+
- `testmempoolaccept` and `sendrawtransaction` no longer return the P2P REJECT
51+
code when a transaction is not accepted to the mempool. See the Section
52+
_Removal of reject network messages from Bitcoin Core (BIP61)_ for details on
53+
the removal of BIP61 REJECT message support.

src/consensus/tx_check.cpp

+9-9
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,24 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe
1111
{
1212
// Basic checks that don't depend on any context
1313
if (tx.vin.empty())
14-
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-vin-empty");
14+
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-vin-empty");
1515
if (tx.vout.empty())
16-
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-vout-empty");
16+
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-vout-empty");
1717
// Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability)
1818
if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT)
19-
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-oversize");
19+
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-oversize");
2020

2121
// Check for negative or overflow output values (see CVE-2010-5139)
2222
CAmount nValueOut = 0;
2323
for (const auto& txout : tx.vout)
2424
{
2525
if (txout.nValue < 0)
26-
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-vout-negative");
26+
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-vout-negative");
2727
if (txout.nValue > MAX_MONEY)
28-
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-vout-toolarge");
28+
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-vout-toolarge");
2929
nValueOut += txout.nValue;
3030
if (!MoneyRange(nValueOut))
31-
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge");
31+
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-txouttotal-toolarge");
3232
}
3333

3434
// Check for duplicate inputs - note that this check is slow so we skip it in CheckBlock
@@ -37,20 +37,20 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe
3737
for (const auto& txin : tx.vin)
3838
{
3939
if (!vInOutPoints.insert(txin.prevout).second)
40-
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-inputs-duplicate");
40+
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-inputs-duplicate");
4141
}
4242
}
4343

4444
if (tx.IsCoinBase())
4545
{
4646
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
47-
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cb-length");
47+
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-cb-length");
4848
}
4949
else
5050
{
5151
for (const auto& txin : tx.vin)
5252
if (txin.prevout.IsNull())
53-
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-prevout-null");
53+
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-prevout-null");
5454
}
5555

5656
return true;

src/consensus/tx_verify.cpp

+5-5
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c
160160
{
161161
// are the actual inputs available?
162162
if (!inputs.HaveInputs(tx)) {
163-
return state.Invalid(ValidationInvalidReason::TX_MISSING_INPUTS, false, REJECT_INVALID, "bad-txns-inputs-missingorspent",
163+
return state.Invalid(ValidationInvalidReason::TX_MISSING_INPUTS, false, "bad-txns-inputs-missingorspent",
164164
strprintf("%s: inputs missing/spent", __func__));
165165
}
166166

@@ -172,27 +172,27 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c
172172

173173
// If prev is coinbase, check that it's matured
174174
if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) {
175-
return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, REJECT_INVALID, "bad-txns-premature-spend-of-coinbase",
175+
return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, "bad-txns-premature-spend-of-coinbase",
176176
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
177177
}
178178

179179
// Check for negative or overflow input values
180180
nValueIn += coin.out.nValue;
181181
if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn)) {
182-
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange");
182+
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-inputvalues-outofrange");
183183
}
184184
}
185185

186186
const CAmount value_out = tx.GetValueOut();
187187
if (nValueIn < value_out) {
188-
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-in-belowout",
188+
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-in-belowout",
189189
strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out)));
190190
}
191191

192192
// Tally transaction fees
193193
const CAmount txfee_aux = nValueIn - value_out;
194194
if (!MoneyRange(txfee_aux)) {
195-
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-fee-outofrange");
195+
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, "bad-txns-fee-outofrange");
196196
}
197197

198198
txfee = txfee_aux;

src/consensus/validation.h

+2-17
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,8 @@
1212
#include <primitives/transaction.h>
1313
#include <primitives/block.h>
1414

15-
/** "reject" message codes */
16-
static const unsigned char REJECT_MALFORMED = 0x01;
17-
static const unsigned char REJECT_INVALID = 0x10;
18-
static const unsigned char REJECT_OBSOLETE = 0x11;
19-
static const unsigned char REJECT_DUPLICATE = 0x12;
20-
static const unsigned char REJECT_NONSTANDARD = 0x40;
21-
// static const unsigned char REJECT_DUST = 0x41; // part of BIP 61
22-
static const unsigned char REJECT_INSUFFICIENTFEE = 0x42;
23-
static const unsigned char REJECT_CHECKPOINT = 0x43;
24-
2515
/** A "reason" why something was invalid, suitable for determining whether the
2616
* provider of the object should be banned/ignored/disconnected/etc.
27-
* These are much more granular than the rejection codes, which may be more
28-
* useful for some other use-cases.
2917
*/
3018
enum class ValidationInvalidReason {
3119
// txn and blocks:
@@ -104,15 +92,13 @@ class CValidationState {
10492
} mode;
10593
ValidationInvalidReason m_reason;
10694
std::string strRejectReason;
107-
unsigned int chRejectCode;
10895
std::string strDebugMessage;
10996
public:
110-
CValidationState() : mode(MODE_VALID), m_reason(ValidationInvalidReason::NONE), chRejectCode(0) {}
97+
CValidationState() : mode(MODE_VALID), m_reason(ValidationInvalidReason::NONE) {}
11198
bool Invalid(ValidationInvalidReason reasonIn, bool ret = false,
112-
unsigned int chRejectCodeIn=0, const std::string &strRejectReasonIn="",
99+
const std::string &strRejectReasonIn="",
113100
const std::string &strDebugMessageIn="") {
114101
m_reason = reasonIn;
115-
chRejectCode = chRejectCodeIn;
116102
strRejectReason = strRejectReasonIn;
117103
strDebugMessage = strDebugMessageIn;
118104
if (mode == MODE_ERROR)
@@ -136,7 +122,6 @@ class CValidationState {
136122
return mode == MODE_ERROR;
137123
}
138124
ValidationInvalidReason GetReason() const { return m_reason; }
139-
unsigned int GetRejectCode() const { return chRejectCode; }
140125
std::string GetRejectReason() const { return strRejectReason; }
141126
std::string GetDebugMessage() const { return strDebugMessage; }
142127
};

src/net_processing.cpp

+14-11
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,8 @@ namespace {
116116
int nSyncStarted GUARDED_BY(cs_main) = 0;
117117

118118
/**
119-
* Sources of received blocks, saved to be able to send them reject
120-
* messages or ban them when processing happens afterwards.
119+
* Sources of received blocks, saved to be able punish them when processing
120+
* happens afterwards.
121121
* Set mapBlockSource[hash].second to false if the node should not be
122122
* punished if the block is invalid.
123123
*/
@@ -1233,11 +1233,12 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationSta
12331233
const uint256 hash(block.GetHash());
12341234
std::map<uint256, std::pair<NodeId, bool>>::iterator it = mapBlockSource.find(hash);
12351235

1236-
if (state.IsInvalid()) {
1237-
// Don't send reject message with code 0 or an internal reject code.
1238-
if (it != mapBlockSource.end() && State(it->second.first) && state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) {
1236+
// If the block failed validation, we know where it came from and we're still connected
1237+
// to that peer, maybe punish.
1238+
if (state.IsInvalid() &&
1239+
it != mapBlockSource.end() &&
1240+
State(it->second.first)) {
12391241
MaybePunishNode(/*nodeid=*/ it->second.first, state, /*via_compact_block=*/ !it->second.second);
1240-
}
12411242
}
12421243
// Check that:
12431244
// 1. The block is valid
@@ -2859,11 +2860,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
28592860
// been run). This is handled below, so just treat this as
28602861
// though the block was successfully read, and rely on the
28612862
// handling in ProcessNewBlock to ensure the block index is
2862-
// updated, reject messages go out, etc.
2863+
// updated, etc.
28632864
MarkBlockAsReceived(resp.blockhash); // it is now an empty pointer
28642865
fBlockRead = true;
2865-
// mapBlockSource is only used for sending reject messages and DoS scores,
2866-
// so the race between here and cs_main in ProcessNewBlock is fine.
2866+
// mapBlockSource is used for potentially punishing peers and
2867+
// updating which peers send us compact blocks, so the race
2868+
// between here and cs_main in ProcessNewBlock is fine.
28672869
// BIP 152 permits peers to relay compact blocks after validating
28682870
// the header only; we should not punish peers if the block turns
28692871
// out to be invalid.
@@ -2935,8 +2937,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
29352937
// Also always process if we requested the block explicitly, as we may
29362938
// need it even though it is not a candidate for a new best tip.
29372939
forceProcessing |= MarkBlockAsReceived(hash);
2938-
// mapBlockSource is only used for sending reject messages and DoS scores,
2939-
// so the race between here and cs_main in ProcessNewBlock is fine.
2940+
// mapBlockSource is only used for punishing peers and setting
2941+
// which peers send us compact blocks, so the race between here and
2942+
// cs_main in ProcessNewBlock is fine.
29402943
mapBlockSource.emplace(hash, std::make_pair(pfrom->GetId(), true));
29412944
}
29422945
bool fNewBlock = false;

src/rpc/rawtransaction.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -904,7 +904,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
904904
result_0.pushKV("allowed", test_accept_res);
905905
if (!test_accept_res) {
906906
if (state.IsInvalid()) {
907-
result_0.pushKV("reject-reason", strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason()));
907+
result_0.pushKV("reject-reason", strprintf("%s", state.GetRejectReason()));
908908
} else if (missing_inputs) {
909909
result_0.pushKV("reject-reason", "missing-inputs");
910910
} else {

src/util/validation.cpp

+2-3
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@
1111
/** Convert CValidationState to a human-readable message for logging */
1212
std::string FormatStateMessage(const CValidationState &state)
1313
{
14-
return strprintf("%s%s (code %i)",
14+
return strprintf("%s%s",
1515
state.GetRejectReason(),
16-
state.GetDebugMessage().empty() ? "" : ", "+state.GetDebugMessage(),
17-
state.GetRejectCode());
16+
state.GetDebugMessage().empty() ? "" : ", "+state.GetDebugMessage());
1817
}
1918

2019
const std::string strMessageMagic = "Bitcoin Signed Message:\n";

0 commit comments

Comments
 (0)