Skip to content

Commit 863748b

Browse files
committed
Refactor CValidationState API
Replace state.DoS with state.Invalid where possible Replace state.DoS with more descriptive calls where straightforward Replace all remaining uses of state.DoS and state.Invalid with more descriptive calls Convert all nDoS usage to an enum class with named levels Remove dead CValidationState::Invalid fn Clean up one use of NonStandardTx to use default args Use a custom enum class for reporting corruption to make it more clear where corruption occurs CValidationState: Remove rejectreason default argument Drop useless ret parameter from CValidationState::DoS return false directly from CValidtationState update call sites
1 parent 02ac8c8 commit 863748b

File tree

7 files changed

+365
-198
lines changed

7 files changed

+365
-198
lines changed

src/consensus/tx_verify.cpp

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -159,47 +159,65 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& i
159159
bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fCheckDuplicateInputs)
160160
{
161161
// Basic checks that don't depend on any context
162-
if (tx.vin.empty())
163-
return state.DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty");
164-
if (tx.vout.empty())
165-
return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty");
162+
if (tx.vin.empty()) {
163+
state.BadTx("bad-txns-vin-empty", "", DoS_SEVERITY::MEDIUM);
164+
return false;
165+
}
166+
if (tx.vout.empty()) {
167+
state.BadTx("bad-txns-vout-empty", "", DoS_SEVERITY::MEDIUM);
168+
return false;
169+
}
166170
// Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability)
167-
if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT)
168-
return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize");
171+
if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT) {
172+
state.BadTx("bad-txns-oversize");
173+
return false;
174+
}
169175

170176
// Check for negative or overflow output values
171177
CAmount nValueOut = 0;
172178
for (const auto& txout : tx.vout)
173179
{
174-
if (txout.nValue < 0)
175-
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative");
176-
if (txout.nValue > MAX_MONEY)
177-
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge");
180+
if (txout.nValue < 0) {
181+
state.BadTx("bad-txns-vout-negative");
182+
return false;
183+
}
184+
if (txout.nValue > MAX_MONEY) {
185+
state.BadTx("bad-txns-vout-toolarge");
186+
return false;
187+
}
178188
nValueOut += txout.nValue;
179-
if (!MoneyRange(nValueOut))
180-
return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge");
189+
if (!MoneyRange(nValueOut)) {
190+
state.BadTx("bad-txns-txouttotal-toolarge");
191+
return false;
192+
}
181193
}
182194

183195
// Check for duplicate inputs - note that this check is slow so we skip it in CheckBlock
184196
if (fCheckDuplicateInputs) {
185197
std::set<COutPoint> vInOutPoints;
186198
for (const auto& txin : tx.vin)
187199
{
188-
if (!vInOutPoints.insert(txin.prevout).second)
189-
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate");
200+
if (!vInOutPoints.insert(txin.prevout).second) {
201+
state.BadTx("bad-txns-inputs-duplicate");
202+
return false;
203+
}
190204
}
191205
}
192206

193207
if (tx.IsCoinBase())
194208
{
195-
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
196-
return state.DoS(100, false, REJECT_INVALID, "bad-cb-length");
209+
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) {
210+
state.BadTx("bad-cb-length");
211+
return false;
212+
}
197213
}
198214
else
199215
{
200216
for (const auto& txin : tx.vin)
201-
if (txin.prevout.IsNull())
202-
return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null");
217+
if (txin.prevout.IsNull()) {
218+
state.BadTx("bad-txns-prevout-null", "", DoS_SEVERITY::MEDIUM);
219+
return false;
220+
}
203221
}
204222

205223
return true;
@@ -209,8 +227,9 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c
209227
{
210228
// are the actual inputs available?
211229
if (!inputs.HaveInputs(tx)) {
212-
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-missingorspent", false,
230+
state.BadTx("bad-txns-inputs-missingorspent",
213231
strprintf("%s: inputs missing/spent", __func__));
232+
return false;
214233
}
215234

216235
CAmount nValueIn = 0;
@@ -221,28 +240,31 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c
221240

222241
// If prev is coinbase, check that it's matured
223242
if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) {
224-
return state.Invalid(false,
225-
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase",
243+
state.BadTx("bad-txns-premature-spend-of-coinbase",
226244
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
245+
return false;
227246
}
228247

229248
// Check for negative or overflow input values
230249
nValueIn += coin.out.nValue;
231250
if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn)) {
232-
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange");
251+
state.BadTx("bad-txns-inputvalues-outofrange");
252+
return false;
233253
}
234254
}
235255

236256
const CAmount value_out = tx.GetValueOut();
237257
if (nValueIn < value_out) {
238-
return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false,
258+
state.BadTx("bad-txns-in-belowout",
239259
strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out)));
260+
return false;
240261
}
241262

242263
// Tally transaction fees
243264
const CAmount txfee_aux = nValueIn - value_out;
244265
if (!MoneyRange(txfee_aux)) {
245-
return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange");
266+
state.BadTx("bad-txns-fee-outofrange");
267+
return false;
246268
}
247269

248270
txfee = txfee_aux;

src/consensus/validation.h

Lines changed: 81 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,35 @@ static const unsigned char REJECT_NONSTANDARD = 0x40;
2222
static const unsigned char REJECT_INSUFFICIENTFEE = 0x42;
2323
static const unsigned char REJECT_CHECKPOINT = 0x43;
2424

25+
/** Reject codes greater or equal to this can be returned by AcceptToMemPool
26+
* for transactions, to signal internal conditions. They cannot and should not
27+
* be sent over the P2P network.
28+
*
29+
* These error codes are not consensus, but consensus changes should avoid using them
30+
* unnecessarily so as not to cause needless churn in core-based clients.
31+
*/
32+
static const unsigned int REJECT_INTERNAL = 0x100;
33+
/** Too high fee. Can not be triggered by P2P transactions */
34+
static const unsigned int REJECT_HIGHFEE = 0x100;
35+
36+
enum class DoS_SEVERITY : int {
37+
NONE = 0,
38+
LOW = 1,
39+
MEDIUM = 10,
40+
ELEVATED = 20,
41+
HIGH = 50,
42+
CRITICAL = 100,
43+
};
44+
inline int ToBanScore(const DoS_SEVERITY x) {
45+
return (int) x;
46+
}
47+
inline bool operator>(const DoS_SEVERITY lhs, const DoS_SEVERITY rhs) {
48+
return ((int) lhs) > ((int) rhs);
49+
}
50+
enum class CORRUPTION_POSSIBLE : bool {
51+
False = 0,
52+
True = 1
53+
};
2554
/** Capture information about block/transaction validation */
2655
class CValidationState {
2756
private:
@@ -30,37 +59,71 @@ class CValidationState {
3059
MODE_INVALID, //!< network rule violation (DoS value may be set)
3160
MODE_ERROR, //!< run-time error
3261
} mode;
33-
int nDoS;
62+
DoS_SEVERITY nDoS;
3463
std::string strRejectReason;
3564
unsigned int chRejectCode;
36-
bool corruptionPossible;
65+
CORRUPTION_POSSIBLE corruptionPossible;
3766
std::string strDebugMessage;
38-
public:
39-
CValidationState() : mode(MODE_VALID), nDoS(0), chRejectCode(0), corruptionPossible(false) {}
40-
bool DoS(int level, bool ret = false,
67+
void DoS(DoS_SEVERITY level,
4168
unsigned int chRejectCodeIn=0, const std::string &strRejectReasonIn="",
42-
bool corruptionIn=false,
69+
CORRUPTION_POSSIBLE corruptionIn=CORRUPTION_POSSIBLE::False,
4370
const std::string &strDebugMessageIn="") {
4471
chRejectCode = chRejectCodeIn;
4572
strRejectReason = strRejectReasonIn;
4673
corruptionPossible = corruptionIn;
4774
strDebugMessage = strDebugMessageIn;
4875
if (mode == MODE_ERROR)
49-
return ret;
50-
nDoS += level;
76+
return;
77+
nDoS = (DoS_SEVERITY) (((unsigned int) nDoS) + ((unsigned int) level));
5178
mode = MODE_INVALID;
52-
return ret;
5379
}
54-
bool Invalid(bool ret = false,
55-
unsigned int _chRejectCode=0, const std::string &_strRejectReason="",
80+
public:
81+
CValidationState() : mode(MODE_VALID), nDoS(DoS_SEVERITY::NONE), chRejectCode(0), corruptionPossible(CORRUPTION_POSSIBLE::False) {}
82+
void BadBlockHeader(const std::string &_strRejectReason,
83+
const std::string &_strDebugMessage="", DoS_SEVERITY level=DoS_SEVERITY::CRITICAL, unsigned int _chRejectCode=REJECT_INVALID) {
84+
DoS(level, _chRejectCode, _strRejectReason, CORRUPTION_POSSIBLE::False, _strDebugMessage);
85+
}
86+
void CorruptBlockHeader(const std::string &_strRejectReason,
87+
const std::string &_strDebugMessage="", DoS_SEVERITY level=DoS_SEVERITY::CRITICAL) {
88+
DoS(level, REJECT_INVALID, _strRejectReason, CORRUPTION_POSSIBLE::True, _strDebugMessage);
89+
}
90+
void ForkingBlockHeaderDisallowed() {
91+
DoS(DoS_SEVERITY::CRITICAL, REJECT_CHECKPOINT, "bad-fork-prior-to-checkpoint");
92+
}
93+
void BadBlock(const std::string &_strRejectReason,
94+
const std::string &_strDebugMessage="", DoS_SEVERITY level=DoS_SEVERITY::CRITICAL) {
95+
DoS(level, REJECT_INVALID, _strRejectReason, CORRUPTION_POSSIBLE::False, _strDebugMessage);
96+
}
97+
void CorruptBlock(const std::string &_strRejectReason,
98+
const std::string &_strDebugMessage="", DoS_SEVERITY level=DoS_SEVERITY::CRITICAL) {
99+
DoS(level, REJECT_INVALID, _strRejectReason, CORRUPTION_POSSIBLE::True, _strDebugMessage);
100+
}
101+
void BadTx(const std::string &_strRejectReason,
102+
const std::string &_strDebugMessage="", DoS_SEVERITY level=DoS_SEVERITY::CRITICAL, unsigned int _chRejectCode=REJECT_INVALID) {
103+
DoS(level, _chRejectCode, _strRejectReason, CORRUPTION_POSSIBLE::False, _strDebugMessage);
104+
}
105+
void CorruptTx(const std::string &_strRejectReason,
106+
const std::string &_strDebugMessage="", DoS_SEVERITY level=DoS_SEVERITY::CRITICAL) {
107+
DoS(level, REJECT_INVALID, _strRejectReason, CORRUPTION_POSSIBLE::True, _strDebugMessage);
108+
}
109+
void NonStandardTx(const std::string &_strRejectReason,
110+
const std::string &_strDebugMessage="", CORRUPTION_POSSIBLE corrupted=CORRUPTION_POSSIBLE::False,
111+
DoS_SEVERITY level=DoS_SEVERITY::NONE) {
112+
DoS(level, REJECT_NONSTANDARD, _strRejectReason, corrupted, _strDebugMessage);
113+
}
114+
void DuplicateData(const std::string &_strRejectReason,
115+
const std::string &_strDebugMessage="") {
116+
DoS(DoS_SEVERITY::NONE, REJECT_DUPLICATE, _strRejectReason, CORRUPTION_POSSIBLE::False, _strDebugMessage);
117+
}
118+
void RejectFee(unsigned int _chRejectCode, const std::string &_strRejectReason,
56119
const std::string &_strDebugMessage="") {
57-
return DoS(0, ret, _chRejectCode, _strRejectReason, false, _strDebugMessage);
120+
assert(_chRejectCode == REJECT_INSUFFICIENTFEE || _chRejectCode == REJECT_HIGHFEE);
121+
DoS(DoS_SEVERITY::NONE, _chRejectCode, _strRejectReason, CORRUPTION_POSSIBLE::False, _strDebugMessage);
58122
}
59-
bool Error(const std::string& strRejectReasonIn) {
123+
void Error(const std::string& strRejectReasonIn) {
60124
if (mode == MODE_VALID)
61125
strRejectReason = strRejectReasonIn;
62126
mode = MODE_ERROR;
63-
return false;
64127
}
65128
bool IsValid() const {
66129
return mode == MODE_VALID;
@@ -71,22 +134,23 @@ class CValidationState {
71134
bool IsError() const {
72135
return mode == MODE_ERROR;
73136
}
74-
bool IsInvalid(int &nDoSOut) const {
137+
bool IsInvalid(DoS_SEVERITY &nDoSOut) const {
75138
if (IsInvalid()) {
76139
nDoSOut = nDoS;
77140
return true;
78141
}
79142
return false;
80143
}
81144
bool CorruptionPossible() const {
82-
return corruptionPossible;
145+
return corruptionPossible != CORRUPTION_POSSIBLE::False;
83146
}
84147
void SetCorruptionPossible() {
85-
corruptionPossible = true;
148+
corruptionPossible = CORRUPTION_POSSIBLE::True;
86149
}
87150
unsigned int GetRejectCode() const { return chRejectCode; }
88151
std::string GetRejectReason() const { return strRejectReason; }
89152
std::string GetDebugMessage() const { return strDebugMessage; }
153+
void SetDebugMessage(const std::string& msg){ strDebugMessage = msg; }
90154
};
91155

92156
// These implement the weight = (stripped_size * 4) + witness_size formula,

0 commit comments

Comments
 (0)