Remove most logging from transaction validation

Remove unnecessary direct logging in CheckTransaction,
AcceptToMemoryPool, CheckTxInputs, CScriptCheck::operator()

All status information should be returned in the CValidationState.
Relevant debug information is also added to the CValidationState using
the recently introduced debug message.

Do keep the "BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags"
error as it is meant to appear as bug in the log.

(cherry picked from commit bitcoin/bitcoin@6cab808272)
This commit is contained in:
Wladimir J. van der Laan 2015-08-06 09:59:09 +02:00 committed by Jack Grigg
parent 3a5474e3f3
commit abdef34b33
1 changed files with 35 additions and 56 deletions

View File

@ -1418,8 +1418,7 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio
tx.vShieldedSpend.empty() &&
!orchard_bundle.SpendsEnabled())
{
return state.DoS(10, error("CheckTransaction(): no source of funds"),
REJECT_INVALID, "bad-txns-no-source-of-funds");
return state.DoS(10, false, REJECT_INVALID, "bad-txns-no-source-of-funds");
}
// Transactions must contain some potential useful sink of funds. This
// rejects obviously-invalid transaction constructions early, but cannot
@ -1434,31 +1433,26 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio
tx.vShieldedOutput.empty() &&
!orchard_bundle.OutputsEnabled())
{
return state.DoS(10, error("CheckTransaction(): no sink of funds"),
REJECT_INVALID, "bad-txns-no-sink-of-funds");
return state.DoS(10, false, REJECT_INVALID, "bad-txns-no-sink-of-funds");
}
// Size limits
static_assert(MAX_BLOCK_SIZE >= MAX_TX_SIZE_AFTER_SAPLING); // sanity
static_assert(MAX_TX_SIZE_AFTER_SAPLING > MAX_TX_SIZE_BEFORE_SAPLING); // sanity
if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_TX_SIZE_AFTER_SAPLING)
return state.DoS(100, error("CheckTransaction(): size limits failed"),
REJECT_INVALID, "bad-txns-oversize");
return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize");
// Check for negative or overflow output values
CAmount nValueOut = 0;
for (const CTxOut& txout : tx.vout)
{
if (txout.nValue < 0)
return state.DoS(100, error("CheckTransaction(): txout.nValue negative"),
REJECT_INVALID, "bad-txns-vout-negative");
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative");
if (txout.nValue > MAX_MONEY)
return state.DoS(100, error("CheckTransaction(): txout.nValue too high"),
REJECT_INVALID, "bad-txns-vout-toolarge");
return state.DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge");
nValueOut += txout.nValue;
if (!MoneyRange(nValueOut))
return state.DoS(100, error("CheckTransaction(): txout total out of range"),
REJECT_INVALID, "bad-txns-txouttotal-toolarge");
return state.DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge");
}
// Check for non-zero valueBalanceSapling when there are no Sapling inputs or outputs
@ -1611,8 +1605,7 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio
for (const CTxIn& txin : tx.vin)
{
if (vInOutPoints.count(txin.prevout))
return state.DoS(100, error("CheckTransaction(): duplicate inputs"),
REJECT_INVALID, "bad-txns-inputs-duplicate");
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate");
vInOutPoints.insert(txin.prevout);
}
@ -1675,15 +1668,13 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio
REJECT_INVALID, "bad-cb-has-orchard-spend");
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
return state.DoS(100, error("CheckTransaction(): coinbase script size"),
REJECT_INVALID, "bad-cb-length");
return state.DoS(100, false, REJECT_INVALID, "bad-cb-length");
}
else
{
for (const CTxIn& txin : tx.vin)
if (txin.prevout.IsNull())
return state.DoS(10, error("CheckTransaction(): prevout is null"),
REJECT_INVALID, "bad-txns-prevout-null");
return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null");
}
return true;
@ -1751,11 +1742,11 @@ bool AcceptToMemoryPool(
auto verifier = ProofVerifier::Strict();
if (!CheckTransaction(tx, state, verifier, orchardAuth))
return error("AcceptToMemoryPool: CheckTransaction failed");
return false;
// Check transaction contextually against the set of consensus rules which apply in the next block to be mined.
if (!ContextualCheckTransaction(tx, state, chainparams, nextBlockHeight, false)) {
return error("AcceptToMemoryPool: ContextualCheckTransaction failed");
return false;
}
// DoS mitigation: reject transactions expiring soon
@ -1767,15 +1758,12 @@ bool AcceptToMemoryPool(
// Coinbase is only valid in a block, not as a loose transaction
if (tx.IsCoinBase())
return state.DoS(100, error("AcceptToMemoryPool: coinbase as individual tx"),
REJECT_INVALID, "coinbase");
return state.DoS(100, false, REJECT_INVALID, "coinbase");
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
string reason;
if (chainparams.RequireStandard() && !IsStandardTx(tx, reason, chainparams, nextBlockHeight))
return state.DoS(0,
error("AcceptToMemoryPool: nonstandard transaction: %s", reason),
REJECT_NONSTANDARD, reason);
return state.DoS(0, false, REJECT_NONSTANDARD, reason);
// 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
@ -1836,8 +1824,7 @@ bool AcceptToMemoryPool(
// are the actual inputs available?
if (!view.HaveInputs(tx))
return state.Invalid(error("AcceptToMemoryPool: inputs already spent"),
REJECT_DUPLICATE, "bad-txns-inputs-spent");
return state.Invalid(false, REJECT_DUPLICATE, "bad-txns-inputs-spent");
// Are the shielded spends' requirements met?
auto unmetShieldedReq = view.HaveShieldedRequirements(tx);
@ -1871,10 +1858,8 @@ bool AcceptToMemoryPool(
unsigned int nSigOps = GetLegacySigOpCount(tx);
nSigOps += GetP2SHSigOpCount(tx, view);
if (nSigOps > MAX_STANDARD_TX_SIGOPS)
return state.DoS(0,
error("AcceptToMemoryPool: too many sigops %s, %d > %d",
hash.ToString(), nSigOps, MAX_STANDARD_TX_SIGOPS),
REJECT_NONSTANDARD, "bad-txns-too-many-sigops");
return state.DoS(0, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false,
strprintf("%d > %d", nSigOps, MAX_STANDARD_TX_SIGOPS));
CAmount nValueOut = tx.GetValueOut();
CAmount nFees = nValueIn-nValueOut;
@ -1904,9 +1889,8 @@ bool AcceptToMemoryPool(
CAmount txMinFee = GetMinRelayFee(tx, pool, nSize, true);
if (fLimitFree && nFees < txMinFee) {
return state.DoS(0, error("AcceptToMemoryPool: not enough fees %s, %d < %d",
hash.ToString(), nFees, txMinFee),
REJECT_INSUFFICIENTFEE, "insufficient fee");
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient fee", false,
strprintf("%d < %d", nFees, txMinFee));
}
// Require that free transactions have sufficient priority to be mined in the next block.
@ -1932,17 +1916,15 @@ bool AcceptToMemoryPool(
// -limitfreerelay unit is thousand-bytes-per-minute
// At default rate it would take over a month to fill 1GB
if (dFreeCount >= GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY) * 10 * 1000)
return state.DoS(0, error("AcceptToMemoryPool: free transaction rejected by rate limiter"),
REJECT_INSUFFICIENTFEE, "rate limited free transaction");
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "rate limited free transaction");
LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize);
dFreeCount += nSize;
}
if (fRejectAbsurdFee && nFees > maxTxFee) {
return state.Invalid(error("AcceptToMemoryPool: absurdly high fees %s, %d > %d",
hash.ToString(),
nFees, maxTxFee),
REJECT_HIGHFEE, "absurdly-high-fee");
return state.Invalid(false,
REJECT_HIGHFEE, "absurdly-high-fee",
strprintf("%d > %d", nFees, maxTxFee));
}
// Check Orchard bundle authorizations.
@ -1957,7 +1939,7 @@ bool AcceptToMemoryPool(
PrecomputedTransactionData txdata(tx);
if (!ContextualCheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true, txdata, chainparams.GetConsensus(), consensusBranchId))
{
return error("AcceptToMemoryPool: ConnectInputs failed %s", hash.ToString());
return false;
}
// Check again against just the consensus-critical mandatory script
@ -1971,7 +1953,8 @@ bool AcceptToMemoryPool(
// can be exploited as a DoS attack.
if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, chainparams.GetConsensus(), consensusBranchId))
{
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));
}
{
@ -2477,7 +2460,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight)
bool CScriptCheck::operator()() {
const CScript &scriptSig = ptxTo->vin[nIn].scriptSig;
if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), consensusBranchId, &error)) {
return ::error("CScriptCheck(): %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error));
return false;
}
return true;
}
@ -2495,7 +2478,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
// for an attacker to attempt to split the network.
if (!inputs.HaveInputs(tx))
return state.Invalid(error("CheckInputs(): %s inputs unavailable", tx.GetHash().ToString()));
return state.Invalid(false, 0, "", "Inputs unavailable");
// Are the shielded spends' requirements met?
auto unmetShieldedReq = inputs.HaveShieldedRequirements(tx);
@ -2521,9 +2504,9 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
if (coins->IsCoinBase()) {
// Ensure that coinbases are matured
if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) {
return state.Invalid(
error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight),
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase");
return state.Invalid(false,
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase",
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight));
}
// Ensure that coinbases cannot be spent to transparent outputs
@ -2540,8 +2523,7 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
// Check for negative or overflow input values
nValueIn += coins->vout[prevout.n].nValue;
if (!MoneyRange(coins->vout[prevout.n].nValue) || !MoneyRange(nValueIn))
return state.DoS(100, error("CheckInputs(): txin values out of range"),
REJECT_INVALID, "bad-txns-inputvalues-outofrange");
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange");
}
@ -2551,19 +2533,16 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins
REJECT_INVALID, "bad-txns-inputvalues-outofrange");
if (nValueIn < tx.GetValueOut())
return state.DoS(100, error("CheckInputs(): %s value in (%s) < value out (%s)",
tx.GetHash().ToString(), FormatMoney(nValueIn), FormatMoney(tx.GetValueOut())),
REJECT_INVALID, "bad-txns-in-belowout");
return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false,
strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(tx.GetValueOut())));
// Tally transaction fees
CAmount nTxFee = nValueIn - tx.GetValueOut();
if (nTxFee < 0)
return state.DoS(100, error("CheckInputs(): %s nTxFee < 0", tx.GetHash().ToString()),
REJECT_INVALID, "bad-txns-fee-negative");
return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-negative");
nFees += nTxFee;
if (!MoneyRange(nFees))
return state.DoS(100, error("CheckInputs(): nFees out of range"),
REJECT_INVALID, "bad-txns-fee-outofrange");
return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange");
return true;
}
}// namespace Consensus