diff --git a/src/miner.h b/src/miner.h --- a/src/miner.h +++ b/src/miner.h @@ -167,7 +167,16 @@ int lastFewTxs; public: + 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 Config &config, 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_GENERATED_BLOCK_SIZE; +} + +BlockAssembler::BlockAssembler(const Config &config, const CTxMemPool &mpool, + const Options &options) : 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() { + BlockAssembler::Options options; + options.nBlockMaxSize = DEFAULT_MAX_GENERATED_BLOCK_SIZE; + if (gArgs.IsArgSet("-blockmaxsize")) { + options.nBlockMaxSize = + gArgs.GetArg("-blockmaxsize", DEFAULT_MAX_GENERATED_BLOCK_SIZE); + } 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()) {} + 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,8 @@ .FromTx(tx)); std::unique_ptr pblocktemplate = - BlockAssembler(config, g_mempool).CreateNewBlock(config, scriptPubKey); + AssemblerForTest(config, g_mempool) + .CreateNewBlock(config, 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); @@ -148,8 +158,8 @@ int64_t(5000000000LL - 1000 - 50000) * SATOSHI - feeToUse; TxId lowFeeTxId = tx.GetId(); g_mempool.addUnchecked(lowFeeTxId, entry.Fee(feeToUse).FromTx(tx)); - pblocktemplate = - BlockAssembler(config, g_mempool).CreateNewBlock(config, scriptPubKey); + pblocktemplate = AssemblerForTest(config, g_mempool) + .CreateNewBlock(config, 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); @@ -165,8 +175,8 @@ lowFeeTxId = tx.GetId(); g_mempool.addUnchecked(lowFeeTxId, entry.Fee(feeToUse + 2 * SATOSHI).FromTx(tx)); - pblocktemplate = - BlockAssembler(config, g_mempool).CreateNewBlock(config, scriptPubKey); + pblocktemplate = AssemblerForTest(config, g_mempool) + .CreateNewBlock(config, scriptPubKey); BOOST_CHECK(pblocktemplate->block.vtx[4]->GetId() == freeTxId); BOOST_CHECK(pblocktemplate->block.vtx[5]->GetId() == lowFeeTxId); @@ -190,8 +200,8 @@ TxId lowFeeTxId2 = tx.GetId(); g_mempool.addUnchecked( lowFeeTxId2, entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx)); - pblocktemplate = - BlockAssembler(config, g_mempool).CreateNewBlock(config, scriptPubKey); + pblocktemplate = AssemblerForTest(config, g_mempool) + .CreateNewBlock(config, scriptPubKey); // Verify that this tx isn't selected. for (const auto &txn : pblocktemplate->block.vtx) { @@ -205,8 +215,8 @@ // 10k satoshi fee. tx.vout[0].nValue = (100000000 - 10000) * SATOSHI; g_mempool.addUnchecked(tx.GetId(), entry.Fee(10000 * SATOSHI).FromTx(tx)); - pblocktemplate = - BlockAssembler(config, g_mempool).CreateNewBlock(config, scriptPubKey); + pblocktemplate = AssemblerForTest(config, g_mempool) + .CreateNewBlock(config, scriptPubKey); BOOST_CHECK(pblocktemplate->block.vtx[8]->GetId() == lowFeeTxId2); } @@ -221,7 +231,8 @@ << OP_CHECKSIG; std::unique_ptr pblocktemplate = - BlockAssembler(config, g_mempool).CreateNewBlock(config, scriptPubKey); + AssemblerForTest(config, g_mempool) + .CreateNewBlock(config, scriptPubKey); CBlock *pblock = &pblocktemplate->block; @@ -265,7 +276,7 @@ GlobalConfig config; // Simple block creation, nothing special yet: - BOOST_CHECK(pblocktemplate = BlockAssembler(config, g_mempool) + BOOST_CHECK(pblocktemplate = AssemblerForTest(config, g_mempool) .CreateNewBlock(config, scriptPubKey)); // We can't make transactions until we have inputs. Therefore, load 100 @@ -305,7 +316,7 @@ LOCK(cs_main); // Just to make sure we can still make simple blocks. - BOOST_CHECK(pblocktemplate = BlockAssembler(config, g_mempool) + BOOST_CHECK(pblocktemplate = AssemblerForTest(config, g_mempool) .CreateNewBlock(config, scriptPubKey)); const Amount BLOCKSUBSIDY = 50 * COIN; @@ -334,9 +345,9 @@ .FromTx(tx)); tx.vin[0].prevout = COutPoint(txid, 0); } - BOOST_CHECK_THROW( - BlockAssembler(config, g_mempool).CreateNewBlock(config, scriptPubKey), - std::runtime_error); + BOOST_CHECK_THROW(AssemblerForTest(config, g_mempool) + .CreateNewBlock(config, scriptPubKey), + std::runtime_error); g_mempool.clear(); tx.vin[0].prevout = COutPoint(txFirst[0]->GetId(), 0); @@ -355,7 +366,7 @@ .FromTx(tx)); tx.vin[0].prevout = COutPoint(txid, 0); } - BOOST_CHECK(pblocktemplate = BlockAssembler(config, g_mempool) + BOOST_CHECK(pblocktemplate = AssemblerForTest(config, g_mempool) .CreateNewBlock(config, scriptPubKey)); g_mempool.clear(); @@ -381,16 +392,16 @@ .FromTx(tx)); tx.vin[0].prevout = COutPoint(txid, 0); } - BOOST_CHECK(pblocktemplate = BlockAssembler(config, g_mempool) + BOOST_CHECK(pblocktemplate = AssemblerForTest(config, g_mempool) .CreateNewBlock(config, scriptPubKey)); g_mempool.clear(); // Orphan in mempool, template creation fails. TxId txid = tx.GetId(); g_mempool.addUnchecked(txid, entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx)); - BOOST_CHECK_THROW( - BlockAssembler(config, g_mempool).CreateNewBlock(config, scriptPubKey), - std::runtime_error); + BOOST_CHECK_THROW(AssemblerForTest(config, g_mempool) + .CreateNewBlock(config, scriptPubKey), + std::runtime_error); g_mempool.clear(); // Child with higher priority than parent. @@ -411,7 +422,7 @@ g_mempool.addUnchecked( txid, entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - BOOST_CHECK(pblocktemplate = BlockAssembler(config, g_mempool) + BOOST_CHECK(pblocktemplate = AssemblerForTest(config, g_mempool) .CreateNewBlock(config, scriptPubKey)); g_mempool.clear(); @@ -425,9 +436,9 @@ g_mempool.addUnchecked( txid, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); - BOOST_CHECK_THROW( - BlockAssembler(config, g_mempool).CreateNewBlock(config, scriptPubKey), - std::runtime_error); + BOOST_CHECK_THROW(AssemblerForTest(config, g_mempool) + .CreateNewBlock(config, scriptPubKey), + std::runtime_error); g_mempool.clear(); // Double spend txn pair in mempool, template creation fails. @@ -444,9 +455,9 @@ g_mempool.addUnchecked( txid, entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - BOOST_CHECK_THROW( - BlockAssembler(config, g_mempool).CreateNewBlock(config, scriptPubKey), - std::runtime_error); + BOOST_CHECK_THROW(AssemblerForTest(config, g_mempool) + .CreateNewBlock(config, scriptPubKey), + std::runtime_error); g_mempool.clear(); // Subsidy changing. @@ -462,7 +473,7 @@ next->BuildSkip(); chainActive.SetTip(next); } - BOOST_CHECK(pblocktemplate = BlockAssembler(config, g_mempool) + BOOST_CHECK(pblocktemplate = AssemblerForTest(config, g_mempool) .CreateNewBlock(config, scriptPubKey)); // Extend to a 210000-long block chain. while (chainActive.Tip()->nHeight < 210000) { @@ -475,7 +486,7 @@ next->BuildSkip(); chainActive.SetTip(next); } - BOOST_CHECK(pblocktemplate = BlockAssembler(config, g_mempool) + BOOST_CHECK(pblocktemplate = AssemblerForTest(config, g_mempool) .CreateNewBlock(config, scriptPubKey)); // Invalid p2sh txn in mempool, template creation fails @@ -496,9 +507,9 @@ g_mempool.addUnchecked( txid, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); - BOOST_CHECK_THROW( - BlockAssembler(config, g_mempool).CreateNewBlock(config, scriptPubKey), - std::runtime_error); + BOOST_CHECK_THROW(AssemblerForTest(config, g_mempool) + .CreateNewBlock(config, scriptPubKey), + std::runtime_error); g_mempool.clear(); // Delete the dummy blocks again. @@ -667,8 +678,8 @@ // Sequence locks fail. BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); - pblocktemplate = - BlockAssembler(config, g_mempool).CreateNewBlock(config, scriptPubKey); + pblocktemplate = AssemblerForTest(config, g_mempool) + .CreateNewBlock(config, scriptPubKey); BOOST_CHECK(pblocktemplate); // None of the of the absolute height/time locked tx should have made it @@ -686,7 +697,7 @@ chainActive.Tip()->nHeight++; SetMockTime(chainActive.Tip()->GetMedianTimePast() + 1); - BOOST_CHECK(pblocktemplate = BlockAssembler(config, g_mempool) + BOOST_CHECK(pblocktemplate = AssemblerForTest(config, g_mempool) .CreateNewBlock(config, scriptPubKey)); BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5UL); @@ -704,6 +715,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) {