@@ -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(); |
@@ -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); | |||
@@ -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)); | |||
} | |||
} | |||
@@ -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() |