diff --git a/src/init.cpp b/src/init.cpp index 02fbf5f802f..bac50302055 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -492,7 +492,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageGroup(_("Block creation options:")); strUsage += HelpMessageOpt("-blockminsize=", strprintf(_("Set minimum block size in bytes (default: %u)"), DEFAULT_BLOCK_MIN_SIZE)); - strUsage += HelpMessageOpt("-blockmaxsize=", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE)); + strUsage += HelpMessageOpt("-blockmaxsize=", _("Set maximum block size in bytes (default is equal to network max-block-size)")); strUsage += HelpMessageOpt("-blockprioritysize=", strprintf(_("Set maximum size of high-priority/low-fee transactions in bytes (default: %d)"), DEFAULT_BLOCK_PRIORITY_SIZE)); if (showDebug) strUsage += HelpMessageOpt("-blockversion=", strprintf("Override block version to test forking scenarios (default: %d)", (int)CBlock::CURRENT_VERSION)); diff --git a/src/miner.cpp b/src/miner.cpp index 3b17b0157fa..f956a284f04 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -111,6 +111,7 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s unsigned int nBlockSigOps = 100; int lastFewTxs = 0; CAmount nFees = 0; + bool fCreatedValidBlock = false; { LOCK2(cs_main, mempool.cs); @@ -303,11 +304,31 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]); CValidationState state; - if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) { - throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state))); + fCreatedValidBlock = TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false); + if (!fCreatedValidBlock) { + if (pblock->vtx.size() <= 1) { + // This should REALLY never happen! Empty block that is invalid. + throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", + __func__, FormatStateMessage(state))); + } + // This should also never happen... but if an invalid transaction somehow entered + // the mempool due to a bug, remove all the transactions in the block + // and try again (it is not worth trying to figure out which transaction(s) + // are causing the block to be invalid). + LogPrintf("%s: TestBlockValidity failed: %s, retrying with smaller mempool", + __func__, FormatStateMessage(state)); + std::list unused; + BOOST_REVERSE_FOREACH(const CTransaction& tx, pblock->vtx) { + mempool.remove(tx, unused, true); + } } } + if (!fCreatedValidBlock) { + pblocktemplate.reset(); + return CreateNewBlock(chainparams, scriptPubKeyIn); // recurse with smaller mempool + } + return pblocktemplate.release(); } diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 71b52409b33..816e67c4c96 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -124,7 +124,10 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error); + BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(!mempool.exists(hash)); + BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 1); + delete pblocktemplate; mempool.clear(); tx.vin[0].prevout.hash = txFirst[0]->GetHash(); @@ -139,6 +142,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].prevout.hash = hash; } BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK(pblocktemplate->block.vtx.size() > 1); delete pblocktemplate; mempool.clear(); @@ -163,10 +167,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) delete pblocktemplate; mempool.clear(); - // orphan in mempool, template creation fails + // orphan in mempool not mined hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).FromTx(tx)); - BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error); + BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 1); + delete pblocktemplate; mempool.clear(); // child with higher priority than parent @@ -195,10 +201,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) hash = tx.GetHash(); // give it a fee so it'll get mined mempool.addUnchecked(hash, entry.Fee(100000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); - BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error); + BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 1); + delete pblocktemplate; mempool.clear(); - // invalid (pre-p2sh) txn in mempool, template creation fails + // invalid (pre-p2sh) txn in mempool, don't mine tx.vin[0].prevout.hash = txFirst[0]->GetHash(); tx.vin[0].prevout.n = 0; tx.vin[0].scriptSig = CScript() << OP_1; @@ -212,10 +220,11 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue -= 1000000; hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Fee(1000000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); - BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error); + BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 1); // Just coinbase mempool.clear(); - // double spend txn pair in mempool, template creation fails + // double spend txn pair in mempool, don't mine tx.vin[0].prevout.hash = txFirst[0]->GetHash(); tx.vin[0].scriptSig = CScript() << OP_1; tx.vout[0].nValue = 4900000000LL; @@ -225,7 +234,9 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].scriptPubKey = CScript() << OP_2; hash = tx.GetHash(); mempool.addUnchecked(hash, entry.Fee(100000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - BOOST_CHECK_THROW(CreateNewBlock(chainparams, scriptPubKey), std::runtime_error); + BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey)); + BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 1); // Just coinbase + delete pblocktemplate; mempool.clear(); // subsidy changing