Merge pull request #6519

7f1f8f5 Move mempool rejections to new debug category (Wladimir J. van der Laan)
66daed5 Add information to errors in ConnectBlock, CheckBlock (Wladimir J. van der Laan)
6cab808 Remove most logging from transaction validation (Wladimir J. van der Laan)
9003c7c Add function to convert CValidationState to a human-readable message (Wladimir J. van der Laan)
dc58258 Introduce REJECT_INTERNAL codes for local AcceptToMempool errors (Wladimir J. van der Laan)
fbf44e6 Add debug message to CValidationState for optional extra information (Wladimir J. van der Laan)
This commit is contained in:
Wladimir J. van der Laan 2015-08-11 17:36:39 +02:00
commit 87f37e259d
No known key found for this signature in database
GPG key ID: 74810B012346C9A6
4 changed files with 80 additions and 77 deletions

View file

@ -30,14 +30,17 @@ private:
std::string strRejectReason; std::string strRejectReason;
unsigned int chRejectCode; unsigned int chRejectCode;
bool corruptionPossible; bool corruptionPossible;
std::string strDebugMessage;
public: public:
CValidationState() : mode(MODE_VALID), nDoS(0), chRejectCode(0), corruptionPossible(false) {} CValidationState() : mode(MODE_VALID), nDoS(0), chRejectCode(0), corruptionPossible(false) {}
bool DoS(int level, bool ret = false, bool DoS(int level, bool ret = false,
unsigned int chRejectCodeIn=0, std::string strRejectReasonIn="", unsigned int chRejectCodeIn=0, const std::string &strRejectReasonIn="",
bool corruptionIn=false) { bool corruptionIn=false,
const std::string &strDebugMessageIn="") {
chRejectCode = chRejectCodeIn; chRejectCode = chRejectCodeIn;
strRejectReason = strRejectReasonIn; strRejectReason = strRejectReasonIn;
corruptionPossible = corruptionIn; corruptionPossible = corruptionIn;
strDebugMessage = strDebugMessageIn;
if (mode == MODE_ERROR) if (mode == MODE_ERROR)
return ret; return ret;
nDoS += level; nDoS += level;
@ -45,8 +48,9 @@ public:
return ret; return ret;
} }
bool Invalid(bool ret = false, bool Invalid(bool ret = false,
unsigned int _chRejectCode=0, std::string _strRejectReason="") { unsigned int _chRejectCode=0, const std::string &_strRejectReason="",
return DoS(0, ret, _chRejectCode, _strRejectReason); const std::string &_strDebugMessage="") {
return DoS(0, ret, _chRejectCode, _strRejectReason, false, _strDebugMessage);
} }
bool Error(const std::string& strRejectReasonIn) { bool Error(const std::string& strRejectReasonIn) {
if (mode == MODE_VALID) if (mode == MODE_VALID)
@ -75,6 +79,7 @@ public:
} }
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; }
}; };
#endif // BITCOIN_CONSENSUS_VALIDATION_H #endif // BITCOIN_CONSENSUS_VALIDATION_H

View file

@ -373,7 +373,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", 1)); strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", 1));
strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", 0)); strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", 0));
} }
string debugCategories = "addrman, alert, bench, coindb, db, lock, rand, rpc, selectcoins, mempool, net, proxy, prune"; // Don't translate these and qt below string debugCategories = "addrman, alert, bench, coindb, db, lock, rand, rpc, selectcoins, mempool, mempoolrej, net, proxy, prune"; // Don't translate these and qt below
if (mode == HMM_BITCOIN_QT) if (mode == HMM_BITCOIN_QT)
debugCategories += ", qt"; debugCategories += ", qt";
strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " + strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " +

View file

@ -696,30 +696,24 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
{ {
// Basic checks that don't depend on any context // Basic checks that don't depend on any context
if (tx.vin.empty()) if (tx.vin.empty())
return state.DoS(10, error("CheckTransaction(): vin empty"), return state.DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty");
REJECT_INVALID, "bad-txns-vin-empty");
if (tx.vout.empty()) if (tx.vout.empty())
return state.DoS(10, error("CheckTransaction(): vout empty"), return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty");
REJECT_INVALID, "bad-txns-vout-empty");
// Size limits // Size limits
if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
return state.DoS(100, error("CheckTransaction(): size limits failed"), return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize");
REJECT_INVALID, "bad-txns-oversize");
// Check for negative or overflow output values // Check for negative or overflow output values
CAmount nValueOut = 0; CAmount nValueOut = 0;
BOOST_FOREACH(const CTxOut& txout, tx.vout) BOOST_FOREACH(const CTxOut& txout, tx.vout)
{ {
if (txout.nValue < 0) if (txout.nValue < 0)
return state.DoS(100, error("CheckTransaction(): txout.nValue negative"), return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative");
REJECT_INVALID, "bad-txns-vout-negative");
if (txout.nValue > MAX_MONEY) if (txout.nValue > MAX_MONEY)
return state.DoS(100, error("CheckTransaction(): txout.nValue too high"), return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge");
REJECT_INVALID, "bad-txns-vout-toolarge");
nValueOut += txout.nValue; nValueOut += txout.nValue;
if (!MoneyRange(nValueOut)) if (!MoneyRange(nValueOut))
return state.DoS(100, error("CheckTransaction(): txout total out of range"), return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge");
REJECT_INVALID, "bad-txns-txouttotal-toolarge");
} }
// Check for duplicate inputs // Check for duplicate inputs
@ -727,23 +721,20 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
BOOST_FOREACH(const CTxIn& txin, tx.vin) BOOST_FOREACH(const CTxIn& txin, tx.vin)
{ {
if (vInOutPoints.count(txin.prevout)) if (vInOutPoints.count(txin.prevout))
return state.DoS(100, error("CheckTransaction(): duplicate inputs"), return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate");
REJECT_INVALID, "bad-txns-inputs-duplicate");
vInOutPoints.insert(txin.prevout); vInOutPoints.insert(txin.prevout);
} }
if (tx.IsCoinBase()) if (tx.IsCoinBase())
{ {
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100) if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
return state.DoS(100, error("CheckTransaction(): coinbase script size"), return state.DoS(100, false, REJECT_INVALID, "bad-cb-length");
REJECT_INVALID, "bad-cb-length");
} }
else else
{ {
BOOST_FOREACH(const CTxIn& txin, tx.vin) BOOST_FOREACH(const CTxIn& txin, tx.vin)
if (txin.prevout.IsNull()) if (txin.prevout.IsNull())
return state.DoS(10, error("CheckTransaction(): prevout is null"), return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null");
REJECT_INVALID, "bad-txns-prevout-null");
} }
return true; return true;
@ -778,6 +769,14 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF
return nMinFee; return nMinFee;
} }
/** Convert CValidationState to a human-readable message for logging */
static std::string FormatStateMessage(const CValidationState &state)
{
return strprintf("%s%s (code %i)",
state.GetRejectReason(),
state.GetDebugMessage().empty() ? "" : ", "+state.GetDebugMessage(),
state.GetRejectCode());
}
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
bool* pfMissingInputs, bool fRejectAbsurdFee) bool* pfMissingInputs, bool fRejectAbsurdFee)
@ -787,31 +786,27 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
*pfMissingInputs = false; *pfMissingInputs = false;
if (!CheckTransaction(tx, state)) if (!CheckTransaction(tx, state))
return error("AcceptToMemoryPool: CheckTransaction failed"); return false;
// Coinbase is only valid in a block, not as a loose transaction // Coinbase is only valid in a block, not as a loose transaction
if (tx.IsCoinBase()) if (tx.IsCoinBase())
return state.DoS(100, error("AcceptToMemoryPool: coinbase as individual tx"), return state.DoS(100, false, REJECT_INVALID, "coinbase");
REJECT_INVALID, "coinbase");
// Rather not work on nonstandard transactions (unless -testnet/-regtest) // Rather not work on nonstandard transactions (unless -testnet/-regtest)
string reason; string reason;
if (fRequireStandard && !IsStandardTx(tx, reason)) if (fRequireStandard && !IsStandardTx(tx, reason))
return state.DoS(0, return state.DoS(0, false, REJECT_NONSTANDARD, reason);
error("AcceptToMemoryPool: nonstandard transaction: %s", reason),
REJECT_NONSTANDARD, reason);
// Only accept nLockTime-using transactions that can be mined in the next // Only accept nLockTime-using transactions that can be mined in the next
// block; we don't want our mempool filled up with transactions that can't // block; we don't want our mempool filled up with transactions that can't
// be mined yet. // be mined yet.
if (!CheckFinalTx(tx)) if (!CheckFinalTx(tx))
return state.DoS(0, error("AcceptToMemoryPool: non-final"), return state.DoS(0, false, REJECT_NONSTANDARD, "non-final");
REJECT_NONSTANDARD, "non-final");
// is it already in the memory pool? // is it already in the memory pool?
uint256 hash = tx.GetHash(); uint256 hash = tx.GetHash();
if (pool.exists(hash)) if (pool.exists(hash))
return false; return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-in-mempool");
// Check for conflicts with in-memory transactions // Check for conflicts with in-memory transactions
{ {
@ -822,7 +817,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
if (pool.mapNextTx.count(outpoint)) if (pool.mapNextTx.count(outpoint))
{ {
// Disable replacement feature for now // Disable replacement feature for now
return false; return state.Invalid(false, REJECT_CONFLICT, "txn-mempool-conflict");
} }
} }
} }
@ -839,7 +834,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// do we already have it? // do we already have it?
if (view.HaveCoins(hash)) if (view.HaveCoins(hash))
return false; return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-known");
// do all inputs exist? // do all inputs exist?
// Note that this does not check for the presence of actual outputs (see the next check for that), // Note that this does not check for the presence of actual outputs (see the next check for that),
@ -848,14 +843,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
if (!view.HaveCoins(txin.prevout.hash)) { if (!view.HaveCoins(txin.prevout.hash)) {
if (pfMissingInputs) if (pfMissingInputs)
*pfMissingInputs = true; *pfMissingInputs = true;
return false; return false; // fMissingInputs and !state.IsInvalid() is used to detect this condition, don't set state.Invalid()
} }
} }
// are the actual inputs available? // are the actual inputs available?
if (!view.HaveInputs(tx)) if (!view.HaveInputs(tx))
return state.Invalid(error("AcceptToMemoryPool: inputs already spent"), return state.Invalid(false, REJECT_DUPLICATE, "bad-txns-inputs-spent");
REJECT_DUPLICATE, "bad-txns-inputs-spent");
// Bring the best block into scope // Bring the best block into scope
view.GetBestBlock(); view.GetBestBlock();
@ -868,7 +862,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// Check for non-standard pay-to-script-hash in inputs // Check for non-standard pay-to-script-hash in inputs
if (fRequireStandard && !AreInputsStandard(tx, view)) if (fRequireStandard && !AreInputsStandard(tx, view))
return error("AcceptToMemoryPool: nonstandard transaction input"); return state.Invalid(false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs");
// Check that the transaction doesn't have an excessive number of // Check that the transaction doesn't have an excessive number of
// sigops, making it impossible to mine. Since the coinbase transaction // sigops, making it impossible to mine. Since the coinbase transaction
@ -878,10 +872,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
unsigned int nSigOps = GetLegacySigOpCount(tx); unsigned int nSigOps = GetLegacySigOpCount(tx);
nSigOps += GetP2SHSigOpCount(tx, view); nSigOps += GetP2SHSigOpCount(tx, view);
if (nSigOps > MAX_STANDARD_TX_SIGOPS) if (nSigOps > MAX_STANDARD_TX_SIGOPS)
return state.DoS(0, return state.DoS(0, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false,
error("AcceptToMemoryPool: too many sigops %s, %d > %d", strprintf("%d > %d", nSigOps, MAX_STANDARD_TX_SIGOPS));
hash.ToString(), nSigOps, MAX_STANDARD_TX_SIGOPS),
REJECT_NONSTANDARD, "bad-txns-too-many-sigops");
CAmount nValueOut = tx.GetValueOut(); CAmount nValueOut = tx.GetValueOut();
CAmount nFees = nValueIn-nValueOut; CAmount nFees = nValueIn-nValueOut;
@ -893,9 +885,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// Don't accept it if it can't get into a block // Don't accept it if it can't get into a block
CAmount txMinFee = GetMinRelayFee(tx, nSize, true); CAmount txMinFee = GetMinRelayFee(tx, nSize, true);
if (fLimitFree && nFees < txMinFee) if (fLimitFree && nFees < txMinFee)
return state.DoS(0, error("AcceptToMemoryPool: not enough fees %s, %d < %d", return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient fee", false,
hash.ToString(), nFees, txMinFee), strprintf("%d < %d", nFees, txMinFee));
REJECT_INSUFFICIENTFEE, "insufficient fee");
// Require that free transactions have sufficient priority to be mined in the next block. // Require that free transactions have sufficient priority to be mined in the next block.
if (GetBoolArg("-relaypriority", true) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) { if (GetBoolArg("-relaypriority", true) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) {
@ -920,24 +911,20 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// -limitfreerelay unit is thousand-bytes-per-minute // -limitfreerelay unit is thousand-bytes-per-minute
// At default rate it would take over a month to fill 1GB // At default rate it would take over a month to fill 1GB
if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000) if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000)
return state.DoS(0, error("AcceptToMemoryPool: free transaction rejected by rate limiter"), return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "rate limited free transaction");
REJECT_INSUFFICIENTFEE, "rate limited free transaction");
LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize);
dFreeCount += nSize; dFreeCount += nSize;
} }
if (fRejectAbsurdFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000) if (fRejectAbsurdFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000)
return state.Invalid(error("AcceptToMemoryPool: absurdly high fees %s, %d > %d", return state.Invalid(false,
hash.ToString(), REJECT_HIGHFEE, "absurdly-high-fee",
nFees, ::minRelayTxFee.GetFee(nSize) * 10000), strprintf("%d > %d", nFees, ::minRelayTxFee.GetFee(nSize) * 10000));
REJECT_HIGHFEE, "absurdly-high-fee");
// Check against previous transactions // Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks. // This is done last to help prevent CPU exhaustion denial-of-service attacks.
if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true)) if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true))
{ return false;
return error("AcceptToMemoryPool: ConnectInputs failed %s", hash.ToString());
}
// Check again against just the consensus-critical mandatory script // Check again against just the consensus-critical mandatory script
// verification flags, in case of bugs in the standard flags that cause // verification flags, in case of bugs in the standard flags that cause
@ -950,7 +937,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// can be exploited as a DoS attack. // can be exploited as a DoS attack.
if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true)) if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true))
{ {
return error("AcceptToMemoryPool: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s", hash.ToString()); return error("%s: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s, %s",
__func__, hash.ToString(), FormatStateMessage(state));
} }
// Store transaction in memory // Store transaction in memory
@ -1241,7 +1229,7 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state
if (state.IsInvalid(nDoS)) { if (state.IsInvalid(nDoS)) {
std::map<uint256, NodeId>::iterator it = mapBlockSource.find(pindex->GetBlockHash()); std::map<uint256, NodeId>::iterator it = mapBlockSource.find(pindex->GetBlockHash());
if (it != mapBlockSource.end() && State(it->second)) { if (it != mapBlockSource.end() && State(it->second)) {
assert(state.GetRejectCode() < 0x100); assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), pindex->GetBlockHash()}; CBlockReject reject = {(unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), pindex->GetBlockHash()};
State(it->second)->rejects.push_back(reject); State(it->second)->rejects.push_back(reject);
if (nDoS > 0) if (nDoS > 0)
@ -1292,7 +1280,7 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach
bool CScriptCheck::operator()() { bool CScriptCheck::operator()() {
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, cacheStore), &error)) { if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, cacheStore), &error)) {
return ::error("CScriptCheck(): %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error)); return false;
} }
return true; return true;
} }
@ -1310,7 +1298,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
// This doesn't trigger the DoS code on purpose; if it did, it would make it easier // This doesn't trigger the DoS code on purpose; if it did, it would make it easier
// for an attacker to attempt to split the network. // for an attacker to attempt to split the network.
if (!inputs.HaveInputs(tx)) if (!inputs.HaveInputs(tx))
return state.Invalid(error("CheckInputs(): %s inputs unavailable", tx.GetHash().ToString())); return state.Invalid(false, 0, "", "Inputs unavailable");
CAmount nValueIn = 0; CAmount nValueIn = 0;
CAmount nFees = 0; CAmount nFees = 0;
@ -1323,33 +1311,29 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
// If prev is coinbase, check that it's matured // If prev is coinbase, check that it's matured
if (coins->IsCoinBase()) { if (coins->IsCoinBase()) {
if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) if (nSpendHeight - coins->nHeight < COINBASE_MATURITY)
return state.Invalid( return state.Invalid(false,
error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight), REJECT_INVALID, "bad-txns-premature-spend-of-coinbase",
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); strprintf("tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight));
} }
// Check for negative or overflow input values // Check for negative or overflow input values
nValueIn += coins->vout[prevout.n].nValue; nValueIn += coins->vout[prevout.n].nValue;
if (!MoneyRange(coins->vout[prevout.n].nValue) || !MoneyRange(nValueIn)) if (!MoneyRange(coins->vout[prevout.n].nValue) || !MoneyRange(nValueIn))
return state.DoS(100, error("CheckInputs(): txin values out of range"), return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange");
REJECT_INVALID, "bad-txns-inputvalues-outofrange");
} }
if (nValueIn < tx.GetValueOut()) if (nValueIn < tx.GetValueOut())
return state.DoS(100, error("CheckInputs(): %s value in (%s) < value out (%s)", return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false,
tx.GetHash().ToString(), FormatMoney(nValueIn), FormatMoney(tx.GetValueOut())), strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(tx.GetValueOut())));
REJECT_INVALID, "bad-txns-in-belowout");
// Tally transaction fees // Tally transaction fees
CAmount nTxFee = nValueIn - tx.GetValueOut(); CAmount nTxFee = nValueIn - tx.GetValueOut();
if (nTxFee < 0) if (nTxFee < 0)
return state.DoS(100, error("CheckInputs(): %s nTxFee < 0", tx.GetHash().ToString()), return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-negative");
REJECT_INVALID, "bad-txns-fee-negative");
nFees += nTxFee; nFees += nTxFee;
if (!MoneyRange(nFees)) if (!MoneyRange(nFees))
return state.DoS(100, error("CheckInputs(): nFees out of range"), return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange");
REJECT_INVALID, "bad-txns-fee-outofrange");
return true; return true;
} }
}// namespace Consensus }// namespace Consensus
@ -1795,7 +1779,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
std::vector<CScriptCheck> vChecks; std::vector<CScriptCheck> vChecks;
if (!CheckInputs(tx, state, view, fScriptChecks, flags, false, nScriptCheckThreads ? &vChecks : NULL)) if (!CheckInputs(tx, state, view, fScriptChecks, flags, false, nScriptCheckThreads ? &vChecks : NULL))
return false; return error("ConnectBlock(): CheckInputs on %s failed with %s",
tx.GetHash().ToString(), FormatStateMessage(state));
control.Add(vChecks); control.Add(vChecks);
} }
@ -2622,7 +2607,9 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo
// Check transactions // Check transactions
BOOST_FOREACH(const CTransaction& tx, block.vtx) BOOST_FOREACH(const CTransaction& tx, block.vtx)
if (!CheckTransaction(tx, state)) if (!CheckTransaction(tx, state))
return error("CheckBlock(): CheckTransaction failed"); return error("CheckBlock(): CheckTransaction of %s failed with %s",
tx.GetHash().ToString(),
FormatStateMessage(state));
unsigned int nSigOps = 0; unsigned int nSigOps = 0;
BOOST_FOREACH(const CTransaction& tx, block.vtx) BOOST_FOREACH(const CTransaction& tx, block.vtx)
@ -4357,11 +4344,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
int nDoS = 0; int nDoS = 0;
if (state.IsInvalid(nDoS)) if (state.IsInvalid(nDoS))
{ {
LogPrint("mempool", "%s from peer=%d %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(), LogPrint("mempoolrej", "%s from peer=%d %s was not accepted into the memory pool: %s\n", tx.GetHash().ToString(),
pfrom->id, pfrom->cleanSubVer, pfrom->id, pfrom->cleanSubVer,
state.GetRejectReason()); FormatStateMessage(state));
pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), if (state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P
state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); pfrom->PushMessage("reject", strCommand, state.GetRejectCode(),
state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash);
if (nDoS > 0) if (nDoS > 0)
Misbehaving(pfrom->GetId(), nDoS); Misbehaving(pfrom->GetId(), nDoS);
} }
@ -4441,6 +4429,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
ProcessNewBlock(state, pfrom, &block, forceProcessing, NULL); ProcessNewBlock(state, pfrom, &block, forceProcessing, NULL);
int nDoS; int nDoS;
if (state.IsInvalid(nDoS)) { if (state.IsInvalid(nDoS)) {
assert (state.GetRejectCode() < REJECT_INTERNAL); // Blocks are never rejected with internal reject codes
pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), pfrom->PushMessage("reject", strCommand, state.GetRejectCode(),
state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash);
if (nDoS > 0) { if (nDoS > 0) {

View file

@ -455,7 +455,16 @@ extern CBlockTreeDB *pblocktree;
*/ */
int GetSpendHeight(const CCoinsViewCache& inputs); int GetSpendHeight(const CCoinsViewCache& inputs);
/** local "reject" message codes for RPC which can not be triggered by p2p trasactions */ /** Reject codes greater or equal to this can be returned by AcceptToMemPool
* for transactions, to signal internal conditions. They cannot and should not
* be sent over the P2P network.
*/
static const unsigned int REJECT_INTERNAL = 0x100;
/** Too high fee. Can not be triggered by P2P transactions */
static const unsigned int REJECT_HIGHFEE = 0x100; static const unsigned int REJECT_HIGHFEE = 0x100;
/** Transaction is already known (either in mempool or blockchain) */
static const unsigned int REJECT_ALREADY_KNOWN = 0x101;
/** Transaction conflicts with a transaction already known */
static const unsigned int REJECT_CONFLICT = 0x102;
#endif // BITCOIN_MAIN_H #endif // BITCOIN_MAIN_H