2323#include < netbase.h>
2424#include < netmessagemaker.h>
2525#include < node/blockstorage.h>
26+ #include < node/miner.h>
2627#include < node/timeoffsets.h>
2728#include < node/txdownloadman.h>
2829#include < node/txreconciliation.h>
@@ -165,6 +166,11 @@ static constexpr double MAX_ADDR_RATE_PER_SECOND{0.1};
165166static constexpr size_t MAX_ADDR_PROCESSING_TOKEN_BUCKET{MAX_ADDR_TO_SEND};
166167/* * The compactblocks version we support. See BIP 152. */
167168static constexpr uint64_t CMPCTBLOCKS_VERSION{2 };
169+ /* * How frequently to update templates for sharing */
170+ static constexpr std::chrono::microseconds TEMPLATE_UPDATE_INTERVAL{30s};
171+ /* * Template weight limit */
172+ static constexpr unsigned int MAX_TEMPLATE_WEIGHT{8000000 };
173+ static_assert (MAX_TEMPLATE_WEIGHT == 2 * MAX_BLOCK_WEIGHT);
168174
169175// Internal stuff
170176namespace {
@@ -176,6 +182,57 @@ struct QueuedBlock {
176182 std::unique_ptr<PartiallyDownloadedBlock> partialBlock;
177183};
178184
185+ struct TemplateTx
186+ {
187+ CTransactionRef tx;
188+ uint32_t num_templates{0 };
189+
190+ explicit TemplateTx (CTransactionRef tx) : tx{std::move (tx)} { }
191+ };
192+ using TemplateTxSet = std::map<Wtxid, TemplateTx>;
193+ using TemplateTxRefVec = std::vector<TemplateTxSet::iterator>;
194+
195+ struct MyTemplate {
196+ uint256 hash;
197+ TemplateTxRefVec txs;
198+ CBlockHeaderAndShortTxIDs compact;
199+
200+ // don't relay this template to peers whose m_last_sequence isn't at least this value
201+ uint64_t inv_sequence;
202+ uint32_t weight;
203+ };
204+
205+ class TemplateManager
206+ {
207+ public:
208+ TemplateTxSet template_txs;
209+
210+ std::deque<MyTemplate> my_templates;
211+
212+ void DiscardTxs (TemplateTxRefVec& txrv)
213+ {
214+ for (auto & it : txrv) {
215+ if (--it->second .num_templates == 0 ) {
216+ template_txs.erase (it);
217+ }
218+ }
219+ txrv.clear ();
220+ }
221+
222+ TemplateTxRefVec AddTxs (const std::vector<CTransactionRef>& txs)
223+ {
224+ TemplateTxRefVec result;
225+ result.reserve (txs.size ());
226+ for (auto & tx : txs) {
227+ const auto & wtxid = tx->GetWitnessHash ();
228+ auto [it, inserted] = template_txs.try_emplace (wtxid, tx);
229+ ++it->second .num_templates ;
230+ result.emplace_back (it);
231+ }
232+ return result;
233+ }
234+ };
235+
179236/* *
180237 * Data structure for an individual peer. This struct is not protected by
181238 * cs_main since it does not contain validation-critical data.
@@ -286,6 +343,9 @@ struct Peer {
286343
287344 /* * Minimum fee rate with which to filter transaction announcements to this node. See BIP133. */
288345 std::atomic<CAmount> m_fee_filter_received{0 };
346+
347+ /* * Whether this peer negotiated SENDTEMPLATE */
348+ std::atomic<bool > m_support_sendtemplate{false };
289349 };
290350
291351 /* Initializes a TxRelay struct for this peer. Can be called at most once for a peer. */
@@ -493,7 +553,7 @@ class PeerManagerImpl final : public PeerManager
493553 void FinalizeNode (const CNode& node) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_headers_presync_mutex, !m_tx_download_mutex);
494554 bool HasAllDesirableServiceFlags (ServiceFlags services) const override ;
495555 bool ProcessMessages (CNode* pfrom, std::atomic<bool >& interrupt) override
496- EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex, g_msgproc_mutex, !m_tx_download_mutex);
556+ EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex, g_msgproc_mutex, !m_tx_download_mutex, !m_templatestats_mutex );
497557 bool SendMessages (CNode* pto) override
498558 EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_most_recent_block_mutex, g_msgproc_mutex, !m_tx_download_mutex);
499559
@@ -504,6 +564,7 @@ class PeerManagerImpl final : public PeerManager
504564 EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
505565 bool GetNodeStateStats (NodeId nodeid, CNodeStateStats& stats) const override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
506566 std::vector<TxOrphanage::OrphanTxBase> GetOrphanTransactions () override EXCLUSIVE_LOCKS_REQUIRED(!m_tx_download_mutex);
567+ TemplateStats GetTemplateStats () const override EXCLUSIVE_LOCKS_REQUIRED(!m_templatestats_mutex);
507568 PeerManagerInfo GetInfo () const override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
508569 void SendPings () override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
509570 void RelayTransaction (const uint256& txid, const uint256& wtxid) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
@@ -1041,6 +1102,15 @@ class PeerManagerImpl final : public PeerManager
10411102
10421103 void AddAddressKnown (Peer& peer, const CAddress& addr) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
10431104 void PushAddress (Peer& peer, const CAddress& addr) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
1105+
1106+ TemplateManager m_templateman GUARDED_BY (g_msgproc_mutex);
1107+ NodeClock::time_point m_next_template_update GUARDED_BY (g_msgproc_mutex){NodeClock::time_point::min ()};
1108+
1109+ mutable Mutex m_templatestats_mutex;
1110+ TemplateStats m_templatestats GUARDED_BY (m_templatestats_mutex);
1111+
1112+ void MaybeGenerateNewTemplate () EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex, !m_templatestats_mutex);
1113+ void SendTemplateTransactions (CNode& pfrom, Peer& peer, const MyTemplate& mytmp, const BlockTransactionsRequest& req) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
10441114};
10451115
10461116const CNodeState* PeerManagerImpl::State (NodeId pnode) const
@@ -3473,6 +3543,12 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
34733543 MakeAndPushMessage (pfrom, NetMsgType::WTXIDRELAY);
34743544 }
34753545
3546+ if (greatest_common_version >= SENDTEMPLATE_VERSION) {
3547+ if (m_opts.share_template_count != 0 ) {
3548+ MakeAndPushMessage (pfrom, NetMsgType::SENDTEMPLATE);
3549+ }
3550+ }
3551+
34763552 // Signal ADDRv2 support (BIP155).
34773553 if (greatest_common_version >= 70016 ) {
34783554 // BIP155 defines addrv2 and sendaddrv2 for all protocol versions, but some
@@ -3694,6 +3770,20 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
36943770 return ;
36953771 }
36963772
3773+ if (msg_type == NetMsgType::SENDTEMPLATE) {
3774+ if (pfrom.fSuccessfullyConnected ) {
3775+ LogDebug (BCLog::NET, " sendtemplate received after verack, %s\n " , pfrom.DisconnectMsg (fLogIPs ));
3776+ pfrom.fDisconnect = true ;
3777+ return ;
3778+ }
3779+ if (m_opts.share_template_count == 0 ) {
3780+ return ;
3781+ }
3782+ auto * tx_relay = peer->GetTxRelay ();
3783+ if (tx_relay) tx_relay->m_support_sendtemplate = true ;
3784+ return ;
3785+ }
3786+
36973787 // BIP339 defines feature negotiation of wtxidrelay, which must happen between
36983788 // VERSION and VERACK to avoid relay problems from switching after a connection is up.
36993789 if (msg_type == NetMsgType::WTXIDRELAY) {
@@ -4084,6 +4174,16 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
40844174 return ;
40854175 }
40864176
4177+ if (auto * tx_relay = peer->GetTxRelay (); tx_relay) {
4178+ for (const auto & mytmp : m_templateman.my_templates ) {
4179+ if (mytmp.hash == req.blockhash && tx_relay->m_last_inv_sequence >= mytmp.inv_sequence ) {
4180+ LogDebug (BCLog::SHARETMPL, " Sending requested txns for template %s peer=%d\n " , mytmp.hash .ToString (), peer->m_id );
4181+ SendTemplateTransactions (pfrom, *peer, mytmp, req);
4182+ return ;
4183+ }
4184+ }
4185+ }
4186+
40874187 FlatFilePos block_pos{};
40884188 {
40894189 LOCK (cs_main);
@@ -4279,6 +4379,23 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
42794379 return ;
42804380 }
42814381
4382+ if (msg_type == NetMsgType::GETTEMPLATE) {
4383+ auto tx_relay = peer->GetTxRelay ();
4384+ if (tx_relay == nullptr || !tx_relay->m_support_sendtemplate ) return ;
4385+
4386+ for (const auto & mytmp : m_templateman.my_templates ) {
4387+ if (mytmp.inv_sequence <= tx_relay->m_last_inv_sequence ) {
4388+ MakeAndPushMessage (pfrom, NetMsgType::TEMPLATE, mytmp.compact );
4389+ break ;
4390+ }
4391+ }
4392+ return ;
4393+ }
4394+
4395+ if (msg_type == NetMsgType::TEMPLATE) {
4396+ return ; // ignore these for now
4397+ }
4398+
42824399 if (msg_type == NetMsgType::CMPCTBLOCK)
42834400 {
42844401 // Ignore cmpctblock received while importing
@@ -4928,6 +5045,94 @@ bool PeerManagerImpl::MaybeDiscourageAndDisconnect(CNode& pnode, Peer& peer)
49285045 return true ;
49295046}
49305047
5048+ void PeerManagerImpl::SendTemplateTransactions (CNode& pfrom, Peer& peer, const MyTemplate& mytmp, const BlockTransactionsRequest& req)
5049+ {
5050+ BlockTransactions resp (req);
5051+ unsigned int tx_requested_size = 0 ;
5052+ for (size_t i = 0 ; i < req.indexes .size (); i++) {
5053+ if (req.indexes [i] >= mytmp.txs .size ()) {
5054+ Misbehaving (peer, " getblocktxn with out-of-bounds tx indices" );
5055+ return ;
5056+ }
5057+ resp.txn [i] = mytmp.txs [req.indexes [i]]->second .tx ;
5058+ tx_requested_size += resp.txn [i]->GetTotalSize ();
5059+ }
5060+
5061+ LogDebug (BCLog::SHARETMPL, " Peer %d sent us a GETBLOCKTXN for template %s, sending a BLOCKTXN with %u txns. (%u bytes)\n " , pfrom.GetId (), mytmp.hash .ToString (), resp.txn .size (), tx_requested_size);
5062+ MakeAndPushMessage (pfrom, NetMsgType::BLOCKTXN, resp);
5063+ }
5064+
5065+ TemplateStats PeerManagerImpl::GetTemplateStats () const
5066+ {
5067+ LOCK (m_templatestats_mutex);
5068+ return m_templatestats;
5069+ }
5070+
5071+ void PeerManagerImpl::MaybeGenerateNewTemplate ()
5072+ {
5073+ if (m_opts.share_template_count == 0 ) return ;
5074+ if (m_next_template_update == NodeClock::time_point::min ()) {
5075+ if (m_chainman.IsInitialBlockDownload () || !m_mempool.GetLoadTried ()) {
5076+ return ;
5077+ } else if (WITH_LOCK (cs_main, return !CanDirectFetch ())) {
5078+ return ;
5079+ }
5080+ }
5081+
5082+ auto now = NodeClock::now ();
5083+ if (now < m_next_template_update) return ;
5084+ m_next_template_update = now + TEMPLATE_UPDATE_INTERVAL;
5085+
5086+ auto & my_templates = m_templateman.my_templates ;
5087+ while (my_templates.size () >= m_opts.share_template_count ) {
5088+ m_templateman.DiscardTxs (my_templates.back ().txs );
5089+ my_templates.pop_back ();
5090+ }
5091+
5092+ const auto assemble_options = []() {
5093+ node::BlockAssembler::Options opt;
5094+ opt.nBlockMaxWeight =MAX_TEMPLATE_WEIGHT;
5095+ opt.blockMinFeeRate =CFeeRate (0 );
5096+ opt.test_block_validity =false ;
5097+ opt.print_modified_fee =false ;
5098+ return opt;
5099+ }();
5100+ node::BlockAssembler assembler{m_chainman.ActiveChainstate (), &m_mempool, assemble_options, node::BlockAssembler::ALLOW_OVERSIZED_BLOCKS};
5101+ auto & new_template = my_templates.emplace_front ();
5102+
5103+ auto block_template = assembler.CreateNewBlock ();
5104+ auto & block = block_template->block ;
5105+ assert (block.vtx [0 ]->IsCoinBase ());
5106+ block.vtx .erase (block.vtx .begin ());
5107+ block.nNonce = 0 ;
5108+ block.nTime = std::numeric_limits<uint32_t >::max ();
5109+
5110+ new_template.hash = block.GetHash ();
5111+ new_template.compact = CBlockHeaderAndShortTxIDs (block, FastRandomContext ().rand64 ());
5112+ new_template.weight = 0 ;
5113+ for (auto & tx : block.vtx ) {
5114+ new_template.weight += GetTransactionWeight (*tx);
5115+ }
5116+ new_template.txs = m_templateman.AddTxs (block.vtx );
5117+ new_template.inv_sequence = WITH_LOCK (m_mempool.cs , return m_mempool.GetSequence ());
5118+
5119+ LogDebug (BCLog::SHARETMPL, " Generated template for sharing hash=%s (%d txs, %d weight)\n " , new_template.hash .ToString (), new_template.txs .size (), new_template.weight );
5120+
5121+ LOCK (m_templatestats_mutex);
5122+ m_templatestats.num_templates = my_templates.size ();
5123+ m_templatestats.max_templates = m_opts.share_template_count ;
5124+ m_templatestats.num_transactions = m_templateman.template_txs .size ();
5125+ if (!m_templateman.my_templates .empty ()) {
5126+ const auto & tmp = m_templateman.my_templates .front ();
5127+ m_templatestats.latest_template_weight = tmp.weight ;
5128+ m_templatestats.latest_template_tx = tmp.txs .size ();
5129+ } else {
5130+ m_templatestats.latest_template_weight = 0 ;
5131+ m_templatestats.latest_template_tx = 0 ;
5132+ }
5133+ m_templatestats.next_update = m_next_template_update;
5134+ }
5135+
49315136bool PeerManagerImpl::ProcessMessages (CNode* pfrom, std::atomic<bool >& interruptMsgProc)
49325137{
49335138 AssertLockNotHeld (m_tx_download_mutex);
@@ -4936,6 +5141,8 @@ bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt
49365141 PeerRef peer = GetPeerRef (pfrom->GetId ());
49375142 if (peer == nullptr ) return false ;
49385143
5144+ MaybeGenerateNewTemplate ();
5145+
49395146 // For outbound connections, ensure that the initial VERSION message
49405147 // has been sent first before processing any incoming messages
49415148 if (!pfrom->IsInboundConn () && !peer->m_outbound_version_message_sent ) return false ;
0 commit comments