@@ -82,6 +82,7 @@ using interfaces::Mining;
8282using interfaces::Node;
8383using interfaces::WalletLoader;
8484using node::BlockAssembler;
85+ using node::BlockWaitOptions;
8586using util::Join;
8687
8788namespace node {
@@ -877,7 +878,11 @@ class ChainImpl : public Chain
877878class BlockTemplateImpl : public BlockTemplate
878879{
879880public:
880- explicit BlockTemplateImpl (std::unique_ptr<CBlockTemplate> block_template, NodeContext& node) : m_block_template(std::move(block_template)), m_node(node)
881+ explicit BlockTemplateImpl (BlockAssembler::Options assemble_options,
882+ std::unique_ptr<CBlockTemplate> block_template,
883+ NodeContext& node) : m_assemble_options(std::move(assemble_options)),
884+ m_block_template(std::move(block_template)),
885+ m_node(node)
881886 {
882887 assert (m_block_template);
883888 }
@@ -942,9 +947,103 @@ class BlockTemplateImpl : public BlockTemplate
942947 return chainman ().ProcessNewBlock (block_ptr, /* force_processing=*/ true , /* min_pow_checked=*/ true , /* new_block=*/ nullptr );
943948 }
944949
950+ std::unique_ptr<BlockTemplate> waitNext (BlockWaitOptions options) override
951+ {
952+ // Delay calculating the current template fees, just in case a new block
953+ // comes in before the next tick.
954+ CAmount current_fees = -1 ;
955+
956+ // Alternate waiting for a new tip and checking if fees have risen.
957+ // The latter check is expensive so we only run it once per second.
958+ auto now{NodeClock::now ()};
959+ const auto deadline = now + options.timeout ;
960+ const MillisecondsDouble tick{1000 };
961+ const bool allow_min_difficulty{chainman ().GetParams ().GetConsensus ().fPowAllowMinDifficultyBlocks };
962+
963+ do {
964+ bool tip_changed{false };
965+ {
966+ WAIT_LOCK (notifications ().m_tip_block_mutex , lock);
967+ // Note that wait_until() checks the predicate before waiting
968+ notifications ().m_tip_block_cv .wait_until (lock, std::min (now + tick, deadline), [&]() EXCLUSIVE_LOCKS_REQUIRED (notifications ().m_tip_block_mutex ) {
969+ AssertLockHeld (notifications ().m_tip_block_mutex );
970+ const auto tip_block{notifications ().TipBlock ()};
971+ // We assume tip_block is set, because this is an instance
972+ // method on BlockTemplate and no template could have been
973+ // generated before a tip exists.
974+ tip_changed = Assume (tip_block) && tip_block != m_block_template->block .hashPrevBlock ;
975+ return tip_changed || chainman ().m_interrupt ;
976+ });
977+ }
978+
979+ if (chainman ().m_interrupt ) return nullptr ;
980+ // At this point the tip changed, a full tick went by or we reached
981+ // the deadline.
982+
983+ // Must release m_tip_block_mutex before locking cs_main, to avoid deadlocks.
984+ LOCK (::cs_main);
985+
986+ // On test networks return a minimum difficulty block after 20 minutes
987+ if (!tip_changed && allow_min_difficulty) {
988+ const NodeClock::time_point tip_time{std::chrono::seconds{chainman ().ActiveChain ().Tip ()->GetBlockTime ()}};
989+ if (now > tip_time + 20min) {
990+ tip_changed = true ;
991+ }
992+ }
993+
994+ /* *
995+ * We determine if fees increased compared to the previous template by generating
996+ * a fresh template. There may be more efficient ways to determine how much
997+ * (approximate) fees for the next block increased, perhaps more so after
998+ * Cluster Mempool.
999+ *
1000+ * We'll also create a new template if the tip changed during this iteration.
1001+ */
1002+ if (options.fee_threshold < MAX_MONEY || tip_changed) {
1003+ auto tmpl{std::make_unique<BlockTemplateImpl>(m_assemble_options,
1004+ BlockAssembler{
1005+ chainman ().ActiveChainstate (),
1006+ context ()->mempool .get (),
1007+ m_assemble_options}
1008+ .CreateNewBlock (),
1009+ m_node)};
1010+
1011+ // If the tip changed, return the new template regardless of its fees.
1012+ if (tip_changed) return tmpl;
1013+
1014+ // Calculate the original template total fees if we haven't already
1015+ if (current_fees == -1 ) {
1016+ current_fees = 0 ;
1017+ for (CAmount fee : m_block_template->vTxFees ) {
1018+ // Skip coinbase
1019+ if (fee < 0 ) continue ;
1020+ current_fees += fee;
1021+ }
1022+ }
1023+
1024+ CAmount new_fees = 0 ;
1025+ for (CAmount fee : tmpl->m_block_template ->vTxFees ) {
1026+ // Skip coinbase
1027+ if (fee < 0 ) continue ;
1028+ new_fees += fee;
1029+ Assume (options.fee_threshold != MAX_MONEY);
1030+ if (new_fees >= current_fees + options.fee_threshold ) return tmpl;
1031+ }
1032+ }
1033+
1034+ now = NodeClock::now ();
1035+ } while (now < deadline);
1036+
1037+ return nullptr ;
1038+ }
1039+
1040+ const BlockAssembler::Options m_assemble_options;
1041+
9451042 const std::unique_ptr<CBlockTemplate> m_block_template;
9461043
1044+ NodeContext* context () { return &m_node; }
9471045 ChainstateManager& chainman () { return *Assert (m_node.chainman ); }
1046+ KernelNotifications& notifications () { return *Assert (m_node.notifications ); }
9481047 NodeContext& m_node;
9491048};
9501049
@@ -991,7 +1090,7 @@ class MinerImpl : public Mining
9911090 {
9921091 BlockAssembler::Options assemble_options{options};
9931092 ApplyArgsManOptions (*Assert (m_node.args ), assemble_options);
994- return std::make_unique<BlockTemplateImpl>(BlockAssembler{chainman ().ActiveChainstate (), context ()->mempool .get (), assemble_options}.CreateNewBlock (), m_node);
1093+ return std::make_unique<BlockTemplateImpl>(assemble_options, BlockAssembler{chainman ().ActiveChainstate (), context ()->mempool .get (), assemble_options}.CreateNewBlock (), m_node);
9951094 }
9961095
9971096 NodeContext* context () override { return &m_node; }
0 commit comments