From 307f7d48d4733da016bf73676f6ebff9144365c1 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 29 Nov 2014 16:01:37 +0100 Subject: [PATCH] Report script evaluation failures in log and reject messages --- src/main.cpp | 19 ++++++++++--------- src/main.h | 12 +++++++++--- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 0515eeb156..d0a4db62bc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1327,7 +1327,7 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state if (state.IsInvalid(nDoS)) { std::map::iterator it = mapBlockSource.find(pindex->GetBlockHash()); if (it != mapBlockSource.end() && State(it->second)) { - CBlockReject reject = {state.GetRejectCode(), state.GetRejectReason(), pindex->GetBlockHash()}; + CBlockReject reject = {state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), pindex->GetBlockHash()}; State(it->second)->rejects.push_back(reject); if (nDoS > 0) Misbehaving(it->second, nDoS); @@ -1357,10 +1357,11 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach inputs.ModifyCoins(tx.GetHash())->FromTx(tx, nHeight); } -bool CScriptCheck::operator()() const { +bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; - if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingSignatureChecker(*ptxTo, nIn, cacheStore))) - return error("CScriptCheck() : %s:%d VerifySignature failed", ptxTo->GetHash().ToString(), nIn); + if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingSignatureChecker(*ptxTo, nIn, cacheStore), &error)) { + return ::error("CScriptCheck() : %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error)); + } return true; } @@ -1448,7 +1449,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi CScriptCheck check(*coins, tx, i, flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore); if (check()) - return state.Invalid(false, REJECT_NONSTANDARD, "non-mandatory-script-verify-flag"); + return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); } // Failures of other flags indicate a transaction that is // invalid in new blocks, e.g. a invalid P2SH. We DoS ban @@ -1457,7 +1458,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi // as to the correct behavior - we may want to continue // peering with non-upgraded nodes even after a soft-fork // super-majority vote has passed. - return state.DoS(100,false, REJECT_INVALID, "mandatory-script-verify-flag-failed"); + return state.DoS(100,false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError()))); } } } @@ -3975,7 +3976,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, pfrom->id, pfrom->cleanSubVer, state.GetRejectReason()); pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), - state.GetRejectReason(), inv.hash); + state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); if (nDoS > 0) Misbehaving(pfrom->GetId(), nDoS); } @@ -4049,7 +4050,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int nDoS; if (state.IsInvalid(nDoS)) { pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), - state.GetRejectReason(), inv.hash); + state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash); if (nDoS > 0) { LOCK(cs_main); Misbehaving(pfrom->GetId(), nDoS); @@ -4256,7 +4257,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (fDebug) { try { string strMsg; unsigned char ccode; string strReason; - vRecv >> LIMITED_STRING(strMsg, CMessageHeader::COMMAND_SIZE) >> ccode >> LIMITED_STRING(strReason, 111); + vRecv >> LIMITED_STRING(strMsg, CMessageHeader::COMMAND_SIZE) >> ccode >> LIMITED_STRING(strReason, MAX_REJECT_MESSAGE_LENGTH); ostringstream ss; ss << strMsg << " code " << itostr(ccode) << ": " << strReason; diff --git a/src/main.h b/src/main.h index caf8331ee1..b04692bb54 100644 --- a/src/main.h +++ b/src/main.h @@ -96,6 +96,8 @@ static const unsigned int MAX_HEADERS_RESULTS = 2000; static const unsigned int BLOCK_DOWNLOAD_WINDOW = 1024; /** Time to wait (in seconds) between writing blockchain state to disk. */ static const unsigned int DATABASE_WRITE_INTERVAL = 3600; +/** Maximum length of reject messages. */ +static const unsigned int MAX_REJECT_MESSAGE_LENGTH = 111; /** "reject" message codes **/ static const unsigned char REJECT_MALFORMED = 0x01; @@ -325,14 +327,15 @@ private: unsigned int nIn; unsigned int nFlags; bool cacheStore; + ScriptError error; public: - CScriptCheck(): ptxTo(0), nIn(0), nFlags(0), cacheStore(false) {} + CScriptCheck(): ptxTo(0), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {} CScriptCheck(const CCoins& txFromIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn) : scriptPubKey(txFromIn.vout[txToIn.vin[nInIn].prevout.n].scriptPubKey), - ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn) { } + ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR) { } - bool operator()() const; + bool operator()(); void swap(CScriptCheck &check) { scriptPubKey.swap(check.scriptPubKey); @@ -340,7 +343,10 @@ public: std::swap(nIn, check.nIn); std::swap(nFlags, check.nFlags); std::swap(cacheStore, check.cacheStore); + std::swap(error, check.error); } + + ScriptError GetScriptError() const { return error; } }; /** Data structure that represents a partial merkle tree.