diff --git a/src/miner.h b/src/miner.h --- a/src/miner.h +++ b/src/miner.h @@ -168,7 +168,16 @@ int lastFewTxs; public: - BlockAssembler(const Config &_config, const CTxMemPool &mempool); + struct Options { + Options(); + size_t nBlockMaxSize; + CFeeRate blockMinFeeRate; + }; + + BlockAssembler(const Config &config, const CTxMemPool &mempool); + BlockAssembler(const Config &config, const CTxMemPool &mempool, + const Options &options); + /** Construct a new block template with coinbase to scriptPubKeyIn */ std::unique_ptr CreateNewBlock(const CScript &scriptPubKeyIn); diff --git a/src/miner.cpp b/src/miner.cpp --- a/src/miner.cpp +++ b/src/miner.cpp @@ -63,10 +63,7 @@ static uint64_t ComputeMaxGeneratedBlockSize(const Config &config, const CBlockIndex *pindexPrev) { - // Block resource limits // If -blockmaxsize is not given, limit to DEFAULT_MAX_GENERATED_BLOCK_SIZE - // If only one is given, only restrict the specified resource. - // If both are given, restrict both. uint64_t nMaxGeneratedBlockSize = DEFAULT_MAX_GENERATED_BLOCK_SIZE; if (gArgs.IsArgSet("-blockmaxsize")) { nMaxGeneratedBlockSize = @@ -81,22 +78,41 @@ return nMaxGeneratedBlockSize; } -BlockAssembler::BlockAssembler(const Config &_config, const CTxMemPool &mpool) +BlockAssembler::Options::Options() { + blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE_PER_KB); + nBlockMaxSize = DEFAULT_MAX_BLOCK_SIZE; +} + +BlockAssembler::BlockAssembler(const Config &_config, const CTxMemPool &mpool, + const Options &options) : config(&_config), mempool(&mpool) { + blockMinFeeRate = options.blockMinFeeRate; + // Limit size to between 1K and max block size-1K for sanity: + nMaxGeneratedBlockSize = + std::max(uint64_t(1000), std::min(config->GetMaxBlockSize() - 1000, + options.nBlockMaxSize)); +} +static BlockAssembler::Options DefaultOptions(const Config &config) { + BlockAssembler::Options options; + { + LOCK(cs_main); + options.nBlockMaxSize = + ComputeMaxGeneratedBlockSize(config, chainActive.Tip()); + } if (gArgs.IsArgSet("-blockmintxfee")) { Amount n = Amount::zero(); ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n); - blockMinFeeRate = CFeeRate(n); + options.blockMinFeeRate = CFeeRate(n); } else { - blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE_PER_KB); + options.blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE_PER_KB); } - - LOCK(cs_main); - nMaxGeneratedBlockSize = - ComputeMaxGeneratedBlockSize(*config, chainActive.Tip()); + return options; } +BlockAssembler::BlockAssembler(const Config &_config, const CTxMemPool &mpool) + : BlockAssembler(_config, mpool, DefaultOptions(_config)) {} + void BlockAssembler::resetBlock() { inBlock.clear(); diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -32,6 +32,15 @@ static CFeeRate blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE_PER_KB); +static BlockAssembler AssemblerForTest(const Config &config, + const CTxMemPool &mempool) { + BlockAssembler::Options options; + + options.nBlockMaxSize = DEFAULT_MAX_BLOCK_SIZE; + options.blockMinFeeRate = blockMinFeeRate; + return BlockAssembler(config, mempool, options); +} + static struct { uint8_t extranonce; uint32_t nonce; @@ -126,7 +135,7 @@ .FromTx(tx)); std::unique_ptr pblocktemplate = - BlockAssembler(config, g_mempool).CreateNewBlock(scriptPubKey); + AssemblerForTest(config, g_mempool).CreateNewBlock(scriptPubKey); BOOST_CHECK(pblocktemplate->block.vtx[1]->GetId() == parentTxId); BOOST_CHECK(pblocktemplate->block.vtx[2]->GetId() == highFeeTxId); BOOST_CHECK(pblocktemplate->block.vtx[3]->GetId() == mediumFeeTxId); @@ -149,7 +158,7 @@ TxId lowFeeTxId = tx.GetId(); g_mempool.addUnchecked(lowFeeTxId, entry.Fee(feeToUse).FromTx(tx)); pblocktemplate = - BlockAssembler(config, g_mempool).CreateNewBlock(scriptPubKey); + AssemblerForTest(config, g_mempool).CreateNewBlock(scriptPubKey); // Verify that the free tx and the low fee tx didn't get selected. for (const auto &txn : pblocktemplate->block.vtx) { BOOST_CHECK(txn->GetId() != freeTxId); @@ -166,7 +175,7 @@ g_mempool.addUnchecked(lowFeeTxId, entry.Fee(feeToUse + 2 * SATOSHI).FromTx(tx)); pblocktemplate = - BlockAssembler(config, g_mempool).CreateNewBlock(scriptPubKey); + AssemblerForTest(config, g_mempool).CreateNewBlock(scriptPubKey); BOOST_CHECK(pblocktemplate->block.vtx[4]->GetId() == freeTxId); BOOST_CHECK(pblocktemplate->block.vtx[5]->GetId() == lowFeeTxId); @@ -191,7 +200,7 @@ g_mempool.addUnchecked( lowFeeTxId2, entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx)); pblocktemplate = - BlockAssembler(config, g_mempool).CreateNewBlock(scriptPubKey); + AssemblerForTest(config, g_mempool).CreateNewBlock(scriptPubKey); // Verify that this tx isn't selected. for (const auto &txn : pblocktemplate->block.vtx) { @@ -206,7 +215,7 @@ tx.vout[0].nValue = (100000000 - 10000) * SATOSHI; g_mempool.addUnchecked(tx.GetId(), entry.Fee(10000 * SATOSHI).FromTx(tx)); pblocktemplate = - BlockAssembler(config, g_mempool).CreateNewBlock(scriptPubKey); + AssemblerForTest(config, g_mempool).CreateNewBlock(scriptPubKey); BOOST_CHECK(pblocktemplate->block.vtx[8]->GetId() == lowFeeTxId2); } @@ -221,7 +230,7 @@ << OP_CHECKSIG; std::unique_ptr pblocktemplate = - BlockAssembler(config, g_mempool).CreateNewBlock(scriptPubKey); + AssemblerForTest(config, g_mempool).CreateNewBlock(scriptPubKey); CBlock *pblock = &pblocktemplate->block; @@ -268,7 +277,7 @@ // Simple block creation, nothing special yet: BOOST_CHECK( pblocktemplate = - BlockAssembler(config, g_mempool).CreateNewBlock(scriptPubKey)); + AssemblerForTest(config, g_mempool).CreateNewBlock(scriptPubKey)); // We can't make transactions until we have inputs. Therefore, load 100 // blocks :) @@ -309,7 +318,7 @@ // Just to make sure we can still make simple blocks. BOOST_CHECK( pblocktemplate = - BlockAssembler(config, g_mempool).CreateNewBlock(scriptPubKey)); + AssemblerForTest(config, g_mempool).CreateNewBlock(scriptPubKey)); const Amount BLOCKSUBSIDY = 50 * COIN; const Amount LOWFEE = CENT; @@ -338,7 +347,7 @@ tx.vin[0].prevout = COutPoint(hash, 0); } BOOST_CHECK_THROW( - BlockAssembler(config, g_mempool).CreateNewBlock(scriptPubKey), + AssemblerForTest(config, g_mempool).CreateNewBlock(scriptPubKey), std::runtime_error); g_mempool.clear(); @@ -360,7 +369,7 @@ } BOOST_CHECK( pblocktemplate = - BlockAssembler(config, g_mempool).CreateNewBlock(scriptPubKey)); + AssemblerForTest(config, g_mempool).CreateNewBlock(scriptPubKey)); g_mempool.clear(); // block size > limit @@ -387,14 +396,14 @@ } BOOST_CHECK( pblocktemplate = - BlockAssembler(config, g_mempool).CreateNewBlock(scriptPubKey)); + AssemblerForTest(config, g_mempool).CreateNewBlock(scriptPubKey)); g_mempool.clear(); // Orphan in mempool, template creation fails. hash = tx.GetId(); g_mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx)); BOOST_CHECK_THROW( - BlockAssembler(config, g_mempool).CreateNewBlock(scriptPubKey), + AssemblerForTest(config, g_mempool).CreateNewBlock(scriptPubKey), std::runtime_error); g_mempool.clear(); @@ -418,7 +427,7 @@ entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK( pblocktemplate = - BlockAssembler(config, g_mempool).CreateNewBlock(scriptPubKey)); + AssemblerForTest(config, g_mempool).CreateNewBlock(scriptPubKey)); g_mempool.clear(); // Coinbase in mempool, template creation fails. @@ -432,7 +441,7 @@ hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); BOOST_CHECK_THROW( - BlockAssembler(config, g_mempool).CreateNewBlock(scriptPubKey), + AssemblerForTest(config, g_mempool).CreateNewBlock(scriptPubKey), std::runtime_error); g_mempool.clear(); @@ -451,7 +460,7 @@ hash, entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK_THROW( - BlockAssembler(config, g_mempool).CreateNewBlock(scriptPubKey), + AssemblerForTest(config, g_mempool).CreateNewBlock(scriptPubKey), std::runtime_error); g_mempool.clear(); @@ -470,7 +479,7 @@ } BOOST_CHECK( pblocktemplate = - BlockAssembler(config, g_mempool).CreateNewBlock(scriptPubKey)); + AssemblerForTest(config, g_mempool).CreateNewBlock(scriptPubKey)); // Extend to a 210000-long block chain. while (chainActive.Tip()->nHeight < 210000) { CBlockIndex *prev = chainActive.Tip(); @@ -484,7 +493,7 @@ } BOOST_CHECK( pblocktemplate = - BlockAssembler(config, g_mempool).CreateNewBlock(scriptPubKey)); + AssemblerForTest(config, g_mempool).CreateNewBlock(scriptPubKey)); // Invalid p2sh txn in mempool, template creation fails tx.vin[0].prevout = COutPoint(txFirst[0]->GetId(), 0); @@ -505,7 +514,7 @@ hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); BOOST_CHECK_THROW( - BlockAssembler(config, g_mempool).CreateNewBlock(scriptPubKey), + AssemblerForTest(config, g_mempool).CreateNewBlock(scriptPubKey), std::runtime_error); g_mempool.clear(); @@ -676,7 +685,7 @@ BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); pblocktemplate = - BlockAssembler(config, g_mempool).CreateNewBlock(scriptPubKey); + AssemblerForTest(config, g_mempool).CreateNewBlock(scriptPubKey); BOOST_CHECK(pblocktemplate); // None of the of the absolute height/time locked tx should have made it @@ -696,7 +705,7 @@ BOOST_CHECK( pblocktemplate = - BlockAssembler(config, g_mempool).CreateNewBlock(scriptPubKey)); + AssemblerForTest(config, g_mempool).CreateNewBlock(scriptPubKey)); BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5UL); chainActive.Tip()->nHeight--; @@ -713,6 +722,13 @@ BlockAssembler ba(config, g_mempool); BOOST_CHECK_EQUAL(ba.GetMaxGeneratedBlockSize(), expected); + + // BlockAssembler with options gives the same result + BlockAssembler::Options options; + options.nBlockMaxSize = size; + options.blockMinFeeRate = blockMinFeeRate; + BlockAssembler ba2(config, g_mempool, options); + BOOST_CHECK_EQUAL(ba2.GetMaxGeneratedBlockSize(), expected); } BOOST_AUTO_TEST_CASE(BlockAssembler_construction) {