diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -222,11 +222,6 @@ } } - if (GetSigOpCountWithoutP2SH(tx, SCRIPT_VERIFY_CHECKDATASIG_SIGOPS) > - MAX_TX_SIGOPS_COUNT) { - return state.DoS(100, false, REJECT_INVALID, "bad-txn-sigops"); - } - return true; } diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp --- a/src/test/sigopcount_tests.cpp +++ b/src/test/sigopcount_tests.cpp @@ -251,39 +251,4 @@ 4295 * MAX_BLOCK_SIGOPS_PER_MB); } -BOOST_AUTO_TEST_CASE(test_max_sigops_per_tx) { - CMutableTransaction tx; - tx.nVersion = 1; - tx.vin.resize(1); - tx.vin[0].prevout = COutPoint(TxId(InsecureRand256()), 0); - tx.vin[0].scriptSig = CScript(); - tx.vout.resize(1); - tx.vout[0].nValue = SATOSHI; - tx.vout[0].scriptPubKey = CScript(); - - { - CValidationState state; - BOOST_CHECK(CheckRegularTransaction(CTransaction(tx), state)); - } - - // Get just before the limit. - for (size_t i = 0; i < MAX_TX_SIGOPS_COUNT; i++) { - tx.vout[0].scriptPubKey << OP_CHECKSIG; - } - - { - CValidationState state; - BOOST_CHECK(CheckRegularTransaction(CTransaction(tx), state)); - } - - // And go over. - tx.vout[0].scriptPubKey << OP_CHECKSIG; - - { - CValidationState state; - BOOST_CHECK(!CheckRegularTransaction(CTransaction(tx), state)); - BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-txn-sigops"); - } -} - BOOST_AUTO_TEST_SUITE_END() diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -685,13 +685,13 @@ const uint32_t nextBlockScriptVerifyFlags = GetNextBlockScriptFlags(consensusParams, chainActive.Tip()); - int64_t nSigOpsCount = + auto nSigOpsCount = GetTransactionSigOpCount(tx, view, nextBlockScriptVerifyFlags); // Check that the transaction doesn't have an excessive number of - // sigops. This is more strict than the consensus limit of - // MAX_TX_SIGOPS_COUNT per transaction enforced in - // CheckRegularTransaction above. + // sigops. + static_assert(MAX_STANDARD_TX_SIGOPS <= MAX_TX_SIGOPS_COUNT, + "we don't want transactions we can't even mine"); if (nSigOpsCount > MAX_STANDARD_TX_SIGOPS) { return state.DoS(0, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false, @@ -1821,8 +1821,22 @@ nInputs += tx.vin.size(); if (tx.IsCoinBase()) { - // We've already checked for sigops count before P2SH in CheckBlock. - nSigOpsCount += GetSigOpCountWithoutP2SH(tx, flags); + // Coinbase sigops count towards the block limit. + auto txSigOpsCount = GetSigOpCountWithoutP2SH(tx, flags); + nSigOpsCount += txSigOpsCount; + // We should have already checked the coinbase sigops count limit in + // ContextualCheckBlock at some earlier time, however we don't + // currently re-invoke ContextualCheckBlock in this function. So, + // just in case of any upgrade/downgrade issues, re-check it here. + if (txSigOpsCount > MAX_TX_SIGOPS_COUNT) { + return state.DoS(100, false, REJECT_INVALID, "bad-txn-sigops"); + } + // Should be redundant since MAX_TX_SIGOPS_COUNT <= nMaxSigOpsCount + // and there is only one coinbase, but doesn't hurt to check. + if (nSigOpsCount > nMaxSigOpsCount) { + return state.DoS(100, error("ConnectBlock(): too many sigops"), + REJECT_INVALID, "bad-blk-sigops"); + } } // We do not need to throw when a transaction is duplicated. If they are @@ -3734,8 +3748,10 @@ // Check transactions: // - canonical ordering // - ensure they are finalized - // - perform a preliminary sigops count (they will be recounted more - // strictly during ConnectBlock) + // - perform a preliminary block-sigops count (they will be recounted more + // strictly during ConnectBlock). + // - perform a transaction-sigops check (again, a more strict check will + // happen in ConnectBlock). const CTransaction *prevTx = nullptr; for (const auto &ptx : block.vtx) { const CTransaction &tx = *ptx; @@ -3760,9 +3776,14 @@ } } - // Count the sigops for the current transaction. If the total sigops - // count is too high, the the block is invalid. - nSigOps += GetSigOpCountWithoutP2SH(tx, scriptFlags); + // Count the sigops for the current transaction. If the tx or total + // sigops counts are too high, then the block is invalid. + const auto txSigOps = GetSigOpCountWithoutP2SH(tx, scriptFlags); + if (txSigOps > MAX_TX_SIGOPS_COUNT) { + return state.DoS(100, false, REJECT_INVALID, "bad-txn-sigops", + false, "out-of-bounds SigOpCount"); + } + nSigOps += txSigOps; if (nSigOps > nMaxSigOpsCount) { return state.DoS(100, false, REJECT_INVALID, "bad-blk-sigops", false, "out-of-bounds SigOpCount");