From 980bfe6ef8bf03633d98f4d51e925c1aa4349421 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Sun, 23 Jun 2013 02:05:25 -0400 Subject: [PATCH] Log reason for non-standard transaction rejection --- src/main.cpp | 37 +++++++++++++++++++++++++--------- src/main.h | 2 +- src/test/script_P2SH_tests.cpp | 8 +++++--- src/test/transaction_tests.cpp | 9 +++++---- 4 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 9b5459cae..3879b78ad 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -457,38 +457,53 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) -bool IsStandardTx(const CTransaction& tx) +bool IsStandardTx(const CTransaction& tx, string& reason) { - if (tx.nVersion > CTransaction::CURRENT_VERSION) + if (tx.nVersion > CTransaction::CURRENT_VERSION) { + reason = "version"; return false; + } - if (!IsFinalTx(tx)) + if (!IsFinalTx(tx)) { + reason = "non-final"; return false; + } // Extremely large transactions with lots of inputs can cost the network // almost as much to process as they cost the sender in fees, because // computing signature hashes is O(ninputs*txsize). Limiting transactions // to MAX_STANDARD_TX_SIZE mitigates CPU exhaustion attacks. unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION); - if (sz >= MAX_STANDARD_TX_SIZE) + if (sz >= MAX_STANDARD_TX_SIZE) { + reason = "tx-size"; return false; + } BOOST_FOREACH(const CTxIn& txin, tx.vin) { // Biggest 'standard' txin is a 3-signature 3-of-3 CHECKMULTISIG // pay-to-script-hash, which is 3 ~80-byte signatures, 3 // ~65-byte public keys, plus a few script ops. - if (txin.scriptSig.size() > 500) + if (txin.scriptSig.size() > 500) { + reason = "scriptsig-size"; return false; - if (!txin.scriptSig.IsPushOnly()) + } + if (!txin.scriptSig.IsPushOnly()) { + reason = "scriptsig-not-pushonly"; return false; + } } BOOST_FOREACH(const CTxOut& txout, tx.vout) { - if (!::IsStandard(txout.scriptPubKey)) + if (!::IsStandard(txout.scriptPubKey)) { + reason = "scriptpubkey"; return false; - if (txout.IsDust(CTransaction::nMinRelayTxFee)) + } + if (txout.IsDust(CTransaction::nMinRelayTxFee)) { + reason = "dust"; return false; + } } + return true; } @@ -782,8 +797,10 @@ bool CTxMemPool::accept(CValidationState &state, CTransaction &tx, bool fLimitFr return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet"); // Rather not work on nonstandard transactions (unless -testnet) - if (!TestNet() && !IsStandardTx(tx)) - return error("CTxMemPool::accept() : nonstandard transaction type"); + string reason; + if (!TestNet() && !IsStandardTx(tx, reason)) + return error("CTxMemPool::accept() : nonstandard transaction: %s", + reason.c_str()); // is it already in the memory pool? uint256 hash = tx.GetHash(); diff --git a/src/main.h b/src/main.h index 2fbf3e802..b9879d109 100644 --- a/src/main.h +++ b/src/main.h @@ -324,7 +324,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState& state); /** Check for standard transaction types @return True if all outputs (scriptPubKeys) use only standard transaction forms */ -bool IsStandardTx(const CTransaction& tx); +bool IsStandardTx(const CTransaction& tx, std::string& reason); bool IsFinalTx(const CTransaction &tx, int nBlockHeight = 0, int64 nBlockTime = 0); diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index 23cb3a8e0..3c666d284 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -74,6 +74,7 @@ BOOST_AUTO_TEST_CASE(sign) } CTransaction txFrom; // Funding transaction: + string reason; txFrom.vout.resize(8); for (int i = 0; i < 4; i++) { @@ -82,7 +83,7 @@ BOOST_AUTO_TEST_CASE(sign) txFrom.vout[i+4].scriptPubKey = standardScripts[i]; txFrom.vout[i+4].nValue = COIN; } - BOOST_CHECK(IsStandardTx(txFrom)); + BOOST_CHECK(IsStandardTx(txFrom, reason)); CTransaction txTo[8]; // Spending transactions for (int i = 0; i < 8; i++) @@ -167,13 +168,14 @@ BOOST_AUTO_TEST_CASE(set) } CTransaction txFrom; // Funding transaction: + string reason; txFrom.vout.resize(4); for (int i = 0; i < 4; i++) { txFrom.vout[i].scriptPubKey = outer[i]; txFrom.vout[i].nValue = CENT; } - BOOST_CHECK(IsStandardTx(txFrom)); + BOOST_CHECK(IsStandardTx(txFrom, reason)); CTransaction txTo[4]; // Spending transactions for (int i = 0; i < 4; i++) @@ -189,7 +191,7 @@ BOOST_AUTO_TEST_CASE(set) for (int i = 0; i < 4; i++) { BOOST_CHECK_MESSAGE(SignSignature(keystore, txFrom, txTo[i], 0), strprintf("SignSignature %d", i)); - BOOST_CHECK_MESSAGE(IsStandardTx(txTo[i]), strprintf("txTo[%d].IsStandard", i)); + BOOST_CHECK_MESSAGE(IsStandardTx(txTo[i], reason), strprintf("txTo[%d].IsStandard", i)); } } diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 53d1307b6..0c7475b4f 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -260,16 +260,17 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) key.MakeNewKey(true); t.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID()); - BOOST_CHECK(IsStandardTx(t)); + string reason; + BOOST_CHECK(IsStandardTx(t, reason)); t.vout[0].nValue = 5011; // dust - BOOST_CHECK(!IsStandardTx(t)); + BOOST_CHECK(!IsStandardTx(t, reason)); t.vout[0].nValue = 6011; // not dust - BOOST_CHECK(IsStandardTx(t)); + BOOST_CHECK(IsStandardTx(t, reason)); t.vout[0].scriptPubKey = CScript() << OP_1; - BOOST_CHECK(!IsStandardTx(t)); + BOOST_CHECK(!IsStandardTx(t, reason)); } BOOST_AUTO_TEST_SUITE_END()