p2p: implement sender-initiated package relay #33500

pull ishaanam wants to merge 12 commits into bitcoin:master from ishaanam:sender_init_package_relay_final changing 18 files +917 −2
  1. ishaanam commented at 4:47 pm on September 29, 2025: contributor

    BIP 331 implements receiver-initiated package relay, where receivers can request missing information about packages. However, this requires excessive round trips. If the sender knows that it is giving the receiver a transaction for which it doesn’t have the ancestors, it should be able to just send a package (containing the transaction and the unknown ancestor(s)) instead.

    This PR could potentially reduce some bandwidth, and could also reduce the number of transactions in the orphanage. However, if a node is sending packages that the receiver already knows about, this could increase significant bandwidth. As a result, this PR needs more testing. I did some initial testing between two nodes with sendpackages enabled, and it showed that no redundant packages were sent, which indicates that this may be a useful feature.

    This PR implements the following:

    • signaling support for sendpackages and the corresponding negotiation logic
    • a node will send a pkgtxns message to the receiver node if the ancestor of a transaction that has been requested is not in the receiver node’s m_tx_inventory_known_filter
    • nodes can process pkgtxns messages
    • the maximum number of transactions that can be sent through sender-initialized package relay has been set to 2, although this can be changed in the future
    • adds logging, functional tests, and fuzzing (for ReceivedPackage)

    Thank you to glozow for her help with this PR!

  2. [p2p] sendpackages negotiation logic
    Co-authored-by: glozow <gloriajzhao@gmail.com>
    63cf2b28b0
  3. [p2p] signal support for package relay
    Co-authored-by: glozow <gloriajzhao@gmail.com>
    16b5a586e9
  4. [rpc] expose package relay on rpc b12cc0bc96
  5. [functional test] test package relay messages
    Co-authored-by: glozow <gloriajzhao@gmail.com>
    3cd35ef5d1
  6. [p2p] add constants for PKGTXNS message 6c643bcbab
  7. [p2p] allow using PackageToValidate for sender-init package relay c98bdab3c1
  8. [p2p] add `ReceivedPackage` functions for pkgtxns 2d2335e7b5
  9. [p2p] process PGTXNS message 1d299b2979
  10. [p2p] send a package instead of a tx when needed 27325bf9d3
  11. [functional test] test sender init package relay 8c23f765be
  12. [logging] add logging for pkgtxns c5223f2084
  13. [fuzz] add fuzzing for ReceivedPackage 4ec9d9230e
  14. DrahtBot added the label P2P on Sep 29, 2025
  15. DrahtBot commented at 4:48 pm on September 29, 2025: contributor

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/33500.

    Reviews

    See the guideline for information on the review process. A summary of reviews will appear here.

    LLM Linter (✨ experimental)

    Possible typos and grammar issues:

    • “Whether this node enable package relay and enforces package-related protocol” -> “Whether this node enables package relay and enforces package-related protocol” [subject-verb agreement: “node” -> “enables”]
    • “sender-initalized” -> “sender-initialized” [spelling: “initalized” -> “initialized”] (occurs in comments above MAX_SENDER_INIT_PKG_SIZE and elsewhere)
    • “sender-inital” -> “sender-initial” [spelling: “inital” -> “initial”] (in the comment line that begins “Maximum number of transactions that can be in a sender-inital…”)
    • “We may remove certain transaction from the package that we already” -> “We may remove certain transactions from the package that we already” [number agreement: “transaction” -> “transactions”]
    • “List versions of each sendpackages received” -> “List of versions of each sendpackages message received” [clarity: make it a proper noun phrase and clarify “sendpackages” refers to messages]

    If you want, I can produce a patch with these exact comment/text replacements.

    drahtbot_id_5_m

  16. DrahtBot added the label Needs rebase on Sep 29, 2025
  17. DrahtBot commented at 6:55 pm on September 29, 2025: contributor
    🐙 This pull request conflicts with the target branch and needs rebase.
  18. in src/init.cpp:548 in 4ec9d9230e
    544@@ -544,6 +545,7 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
    545     argsman.AddArg("-i2pacceptincoming", strprintf("Whether to accept inbound I2P connections (default: %i). Ignored if -i2psam is not set. Listening for inbound I2P connections is done through the SAM proxy, not by binding to a local address and port.", DEFAULT_I2P_ACCEPT_INCOMING), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
    546     argsman.AddArg("-onlynet=<net>", "Make automatic outbound connections only to network <net> (" + Join(GetNetworkNames(), ", ") + "). Inbound and manual connections are not affected by this option. It can be specified multiple times to allow multiple networks.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
    547     argsman.AddArg("-v2transport", strprintf("Support v2 transport (default: %u)", DEFAULT_V2_TRANSPORT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
    548+    argsman.AddArg("-packagerelay", strprintf("[EXPERIMENTAL] Support relaying transaction packages (default: %u)", node::DEFAULT_ENABLE_PACKAGE_RELAY), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
    


    starkshade commented at 8:18 am on September 30, 2025:

    💡 Improvement Needed

    Line: 548

    The description for the ‘-packagerelay’ argument mentions it is ‘[EXPERIMENTAL]’. Consider if this flag should be exposed as a user-facing option or if it should be an internal feature controlled by other means, given its experimental nature.

  19. in src/net.cpp:913 in 4ec9d9230e
    909@@ -910,7 +910,7 @@ namespace {
    910  * Only message types that are actually implemented in this codebase need to be listed, as other
    911  * messages get ignored anyway - whether we know how to decode them or not.
    912  */
    913-const std::array<std::string, 33> V2_MESSAGE_IDS = {
    914+const std::array<std::string, 35> V2_MESSAGE_IDS = {
    


    starkshade commented at 8:18 am on September 30, 2025:

    💡 Improvement Needed

    Line: 913

    The array size has been increased from 33 to 35. The comment at line 910 states that only implemented message types need to be listed. If NetMsgType::SENDPACKAGES and NetMsgType::PKGTXNS (added on lines 943-944) are not implemented, they should not be added to this list. If they are implemented, the comment should be updated to reflect this.

  20. in src/net.cpp:945 in 4ec9d9230e
    939@@ -940,6 +940,8 @@ const std::array<std::string, 33> V2_MESSAGE_IDS = {
    940     NetMsgType::GETCFCHECKPT,
    941     NetMsgType::CFCHECKPT,
    942     NetMsgType::ADDRV2,
    943+    NetMsgType::SENDPACKAGES,
    944+    NetMsgType::PKGTXNS,
    945     // Unimplemented message types that are assigned in BIP324:
    


    starkshade commented at 8:18 am on September 30, 2025:

    💡 Improvement Needed

    Lines: 945-947

    The comment “Unimplemented message types that are assigned in BIP324:” is followed by two empty strings. If these empty strings represent specific unimplemented message types, it would be more maintainable to explicitly name them. If they are not intended to be placeholders for future implementation, they should be removed to avoid confusion.

  21. in src/net_processing.cpp:326 in 4ec9d9230e
    317@@ -316,6 +318,14 @@ struct Peer {
    318 
    319         /** Minimum fee rate with which to filter transaction announcements to this node. See BIP133. */
    320         std::atomic<CAmount> m_fee_filter_received{0};
    321+
    322+        /** What package versions we agreed to relay. */
    323+        std::atomic<node::PackageRelayVersions> m_package_versions_supported;
    324+
    325+        /** Whether or not the peer supports this version of package relay. */
    326+        bool SupportsVersion(node::PackageRelayVersions version) {
    


    starkshade commented at 8:18 am on September 30, 2025:

    ⚠️ Code Issue

    Lines: 326-328

    0bool SupportsVersion(node::PackageRelayVersions version) {
    1    return m_package_versions_supported & version;
    2}
    

    The SupportsVersion method’s return statement m_package_versions_supported & version might not be as clear as it could be. It relies on the caller understanding that a non-zero result implies the version is supported. A more explicit check, such as (m_package_versions_supported & version) == version, would make the intent clearer and reduce potential confusion.

  22. in src/net_processing.cpp:2531 in 4ec9d9230e
    2522@@ -2486,6 +2523,37 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic
    2523     }
    2524 }
    2525 
    2526+std::optional<PackageToSend> PeerManagerImpl::GetSenderInitPackage(const Peer::TxRelay* tx_relay, const CTransactionRef tx) {
    2527+    PackageToSend package_to_send;
    2528+
    2529+    if (auto entry = WITH_LOCK(m_mempool.cs, return m_mempool.GetEntry(tx->GetHash()))) {
    2530+        // look for any ancestors this tx has in the mempool
    2531+        auto ancestors{WITH_LOCK(m_mempool.cs, return m_mempool.AssumeCalculateMemPoolAncestors(__func__, *entry, CTxMemPool::Limits::NoLimits(), /*fSearchForParents=*/false))};
    


    starkshade commented at 8:19 am on September 30, 2025:

    ⚠️ Code Issue

    Lines: 2531-2531

    0auto ancestors{WITH_LOCK(m_mempool.cs, return m_mempool.AssumeCalculateMemPoolAncestors(__func__, *entry, CTxMemPool::Limits::NoLimits(), /*fSearchForParents=*/false))};
    

    The AssumeCalculateMemPoolAncestors function is called with fSearchForParents set to false. This parameter controls whether the function should search for parent transactions that are not currently in the mempool. By setting it to false, the function will only consider ancestors that are already present in the mempool. If a transaction’s parent is not in the mempool, it will not be included in the ancestors set, which can lead to incomplete packages being constructed and sent.

  23. in src/net_processing.cpp:4902 in 4ec9d9230e
    4897+        }
    4898+
    4899+        const auto package_to_validate(WITH_LOCK(m_tx_download_mutex, return m_txdownloadman.ReceivedPackage(pfrom.GetId(), package)));
    4900+
    4901+        if (package_to_validate) {
    4902+            const auto package_result{WITH_LOCK(cs_main, return ProcessNewPackage(m_chainman.ActiveChainstate(), m_mempool, package_to_validate.value().m_txns, /*test_accept=*/false, /*client_maxfeerate=*/std::nullopt))};
    


    starkshade commented at 8:19 am on September 30, 2025:

    ⚠️ Code Issue

    Lines: 4902-4902

    0const auto package_result{WITH_LOCK(cs_main, return ProcessNewPackage(m_chainman.ActiveChainstate(), m_mempool, package_to_validate.value().m_txns, /*test_accept=*/false, /*client_maxfeerate=*/std::nullopt))};
    

    The ProcessNewPackage function is invoked to validate the received package. However, the current implementation might not sufficiently validate the dependency chain of transactions within the package. An attacker could potentially craft a PKGTXNS message containing transactions whose parents are not in the mempool and are not themselves part of the package, or whose dependencies are otherwise invalid. This could lead to mempool pollution or other security vulnerabilities. A more robust validation of the transaction dependency graph within the package is recommended before accepting and processing it.

  24. in src/net_processing.h:85 in 4ec9d9230e
    81@@ -81,6 +82,8 @@ class PeerManager : public CValidationInterface, public NetEventsInterface
    82         uint32_t max_extra_txs{DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN};
    83         //! Whether all P2P messages are captured to disk
    84         bool capture_messages{false};
    85+        //! Whether this node enable package relay and enforces package-related protocol
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 85

    The comment //! Whether this node enable package relay and enforces package-related protocol is slightly redundant. The variable name m_enable_package_relay already indicates that it enables package relay. Consider making the comment more concise by focusing on the enforcement aspect, e.g., //! Whether this node enforces package-related protocol.

  25. in src/node/peerman_args.cpp:28 in 4ec9d9230e
    23@@ -23,6 +24,8 @@ void ApplyArgsManOptions(const ArgsManager& argsman, PeerManager::Options& optio
    24     if (auto value{argsman.GetBoolArg("-capturemessages")}) options.capture_messages = *value;
    25 
    26     if (auto value{argsman.GetBoolArg("-blocksonly")}) options.ignore_incoming_txs = *value;
    27+
    28+    if (auto value{argsman.GetBoolArg("-packagerelay", DEFAULT_ENABLE_PACKAGE_RELAY)}) options.m_enable_package_relay = value;
    


    starkshade commented at 8:19 am on September 30, 2025:

    ⚠️ Code Issue

    Lines: 28-28

    0if (auto value{argsman.GetBoolArg("-packagerelay", DEFAULT_ENABLE_PACKAGE_RELAY)}) options.m_enable_package_relay = value;
    

    This code dereferences the std::optional<bool> returned by GetBoolArg without checking if it contains a value. If the argument is not provided, value will be std::nullopt, and dereferencing it (*value) will result in undefined behavior.

  26. in src/node/txdownloadman.h:24 in 4ec9d9230e
    16@@ -17,9 +17,23 @@ class CRollingBloomFilter;
    17 class CTxMemPool;
    18 class GenTxid;
    19 class TxRequestTracker;
    20+
    21 namespace node {
    22 class TxDownloadManagerImpl;
    23 
    24+enum PackageRelayVersions : uint64_t {
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 24-27

    The PackageRelayVersions enum uses uint64_t as its underlying type. If the number of package relay versions is expected to remain small, consider using a smaller integer type (e.g., uint32_t or uint16_t) to potentially save memory.

  27. in src/node/txdownloadman.h:35 in 4ec9d9230e
    30+/** Maximum number of transactions that can be in a sender-initalized
    31+ * package. Sender-initalized packages are packages that were not requested
    32+ * by the receiver, but sent because the sender recognized that we were
    33+ * missing certain transactions required to accept a previously requested
    34+ * transaction into the mempool. */
    35+static constexpr size_t MAX_SENDER_INIT_PKG_SIZE{2};
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 35

    Add a comment explaining the rationale behind the specific value of MAX_SENDER_INIT_PKG_SIZE (currently 2). This will help future developers understand the reasoning for this limit and assist in potential future adjustments.

  28. in src/node/txdownloadman.h:183 in 4ec9d9230e
    179@@ -157,6 +180,8 @@ class TxDownloadManager {
    180      * PackageToValidate. */
    181     std::pair<bool, std::optional<PackageToValidate>> ReceivedTx(NodeId nodeid, const CTransactionRef& ptx);
    182 
    183+    std::optional<PackageToValidate> ReceivedPackage(NodeId nodeid, Package& package);
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 183

    The ReceivedPackage method is declared in the header but its definition is not provided in this snippet. Ensure that a corresponding definition exists in the implementation file or that its absence is intentional and documented.

  29. in src/node/txdownloadman.h:206 in 4ec9d9230e
    201+    PackageRelayVersions GetSupportedVersions() const;
    202+    /**Whether the node supports the given versions*/
    203+    bool NodeSupportsVersion(const NodeId& nodeid, const PackageRelayVersions& versions);
    204+
    205+    // The following are used for package relay negotiation
    206+    void ReceivedVersion(NodeId nodeid);
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 206-208

    The methods related to package relay negotiation are grouped together. If the complexity of package relay negotiation increases significantly in the future, consider organizing these methods into a dedicated nested namespace or a separate class for better maintainability.

  30. in src/node/txdownloadman_impl.cpp:608 in 4ec9d9230e
    603+        return std::nullopt;
    604+    }
    605+
    606+    // We may remove certain transaction from the package that we already
    607+    // know about or that were recently rejected
    608+    for (size_t i = 0; i < mutable_package.size(); i++) {
    


    starkshade commented at 8:19 am on September 30, 2025:

    ⚠️ Issue

    Type: Bug Severity: High

    Line: 620

    The loop iterating through mutable_package and erasing elements modifies the vector while iterating using an index. This can lead to skipping elements or out-of-bounds access if mutable_package.erase(mutable_package.begin()+i) is called and i is not decremented afterwards. Specifically, when an element is erased, the subsequent elements shift to the left, and the loop increment i++ will then skip the element that moved into the current position i.

    Fix: To fix this, either iterate backwards or decrement the index i after erasing an element. A common pattern is to decrement i after erasing:

     0--- a/src/node/txdownloadman_impl.cpp
     1+++ b/src/node/txdownloadman_impl.cpp
     2@@ -617,7 +617,7 @@
     3             LogDebug(BCLog::TXPACKAGES, "removing tx (%s) from the following package because we already have it: %s\n", txid.ToString(), package_string);
     4             mutable_package.erase(mutable_package.begin()+i);
     5         } else if (RecentRejectsFilter().contains(wtxid.ToUint256())) {
     6-            LogDebug(BCLog::TXPACKAGES, "dropping package because tx (%s) has already been rejected and is not eligible for reconsideration: %s\n", txid.ToString(), package_string);
     7+            LogDebug(BCLog::TXPACKAGES, "dropping package because tx (%s) has already been rejected and is not eligible for reconsideration: %s\n", txid.ToString(), package_string);
     8             mutable_package.erase(mutable_package.begin()+i);
     9         }
    10     }
    

    Alternatively, decrement i after erasing:

     0--- a/src/node/txdownloadman_impl.cpp
     1+++ b/src/node/txdownloadman_impl.cpp
     2@@ -617,7 +617,7 @@
     3             LogDebug(BCLog::TXPACKAGES, "removing tx (%s) from the following package because we already have it: %s\n", txid.ToString(), package_string);
     4             mutable_package.erase(mutable_package.begin()+i);
     5         } else if (RecentRejectsFilter().contains(wtxid.ToUint256())) {
     6-            LogDebug(BCLog::TXPACKAGES, "dropping package because tx (%s) has already been rejected and is not eligible for reconsideration: %s\n", txid.ToString(), package_string);
     7+            LogDebug(BCLog::TXPACKAGES, "dropping package because tx (%s) has already been rejected and is not eligible for reconsideration: %s\n", txid.ToString(), package_string);
     8             mutable_package.erase(mutable_package.begin()+i);
     9         }
    10     }
    

    Or, more robustly:

     0--- a/src/node/txdownloadman_impl.cpp
     1+++ b/src/node/txdownloadman_impl.cpp
     2@@ -617,7 +617,7 @@
     3             LogDebug(BCLog::TXPACKAGES, "removing tx (%s) from the following package because we already have it: %s\n", txid.ToString(), package_string);
     4             mutable_package.erase(mutable_package.begin()+i);
     5         } else if (RecentRejectsFilter().contains(wtxid.ToUint256())) {
     6-            LogDebug(BCLog::TXPACKAGES, "dropping package because tx (%s) has already been rejected and is not eligible for reconsideration: %s\n", txid.ToString(), package_string);
     7+            LogDebug(BCLog::TXPACKAGES, "dropping package because tx (%s) has already been rejected and is not eligible for reconsideration: %s\n", txid.ToString(), package_string);
     8             mutable_package.erase(mutable_package.begin()+i);
     9         }
    10     }
    
  31. in src/node/txdownloadman_impl.cpp:681 in 4ec9d9230e
    676+    it->second.m_sendpackages_received = true;
    677+    // Ignore versions we don't understand. Relay packages of versions that we both support.
    678+    it->second.m_versions_in_common = PackageRelayVersions(GetSupportedVersions() & version);
    679+}
    680+
    681+std::optional<PackageRelayVersions> TxDownloadManagerImpl::UpdateRegistrationState(NodeId nodeid, bool txrelay, bool wtxidrelay) {
    


    starkshade commented at 8:19 am on September 30, 2025:

    ⚠️ Issue

    Type: Bug Severity: Medium

    Line: 688

    In UpdateRegistrationState, the m_registration_states.erase(it) call invalidates the iterator it. If final_state is true, the function returns version immediately after the erase. This is generally safe as the iterator is no longer used. However, it’s good practice to be mindful of iterator invalidation. If there were subsequent operations on it after the erase, this would be a critical bug. As it stands, it’s a potential maintainability concern if the logic were to change.

    Fix: No immediate fix is required as the iterator is not used after invalidation. However, for clarity and robustness, consider re-fetching the iterator if it were to be used again, or restructuring the code to perform the erase after all necessary data has been extracted.

  32. in src/node/txdownloadman_impl.cpp:695 in 4ec9d9230e
    690+        m_package_relay_versions.insert(std::make_pair(nodeid, version));
    691+        return version;
    692+    }
    693+    return std::nullopt;
    694+}
    695+bool TxDownloadManagerImpl::NodeSupportsVersion(const NodeId& nodeid, const PackageRelayVersions& versions) {
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 697

    The NodeSupportsVersion function checks for the existence of nodeid in m_package_relay_versions twice. First, it calls m_package_relay_versions.find(nodeid) and assigns the result to node_versions. Then, it calls m_package_relay_versions.find(nodeid) again within the if condition. This is redundant.

    Suggestion: Store the result of find in a variable and use that variable for the check, or perform the check directly without assigning to node_versions if it’s only used in the if block.

     0--- a/src/node/txdownloadman_impl.cpp
     1+++ b/src/node/txdownloadman_impl.cpp
     2@@ -695,9 +695,9 @@
     3 bool TxDownloadManagerImpl::NodeSupportsVersion(const NodeId& nodeid, const PackageRelayVersions& versions) {
     4     auto node_versions = m_package_relay_versions.find(nodeid);
     5-    if (m_package_relay_versions.find(nodeid) != m_package_relay_versions.end()) {
     6+    if (node_versions != m_package_relay_versions.end()) {
     7         return node_versions->second & versions;
     8     }
     9     return false;
    10 }
    11 }
    
  33. in src/node/txdownloadman_impl.h:32 in 4ec9d9230e
    27+        /** Whether this peer says they can do package relay. */
    28+        bool m_sendpackages_received{false};
    29+        /** Versions of package relay supported by this node.
    30+         * This is a subset of PACKAGE_RELAY_SUPPORTED_VERSIONS. */
    31+        PackageRelayVersions m_versions_in_common{PKG_RELAY_NONE};
    32+        bool CanRelayPackages() {
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 32-34

    The CanRelayPackages method within the RegistrationState struct directly uses member variables to determine package relay capability. If the conditions for package relay become more intricate, this method’s logic might benefit from being more explicit or potentially relocated to a more testable context. This could involve breaking down the conditions or ensuring comprehensive unit tests cover all possible return paths.

  34. in src/node/txdownloadman_impl.h:161 in 4ec9d9230e
    155@@ -141,6 +156,13 @@ class TxDownloadManagerImpl {
    156      * all peers we are connected to (no block-relay-only and temporary connections). */
    157     std::map<NodeId, PeerInfo> m_peer_info;
    158 
    159+    /** Stores relevant information about the peer prior to verack. Upon completion of version
    160+     * handshake, we use this information to decide whether we relay packages with this peer. */
    161+    std::map<NodeId, RegistrationState> m_registration_states;
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 161

    The comment for m_registration_states suggests it stores information prior to verack and is used post-handshake. However, the RegistrationState struct has default initializations for m_txrelay and m_wtxid_relay. Clarification is needed on how these states are accurately populated and managed before verack if they are critical for post-handshake decisions. This could involve ensuring that these states are explicitly set or updated during the version handshake process.

  35. in src/node/txdownloadman_impl.h:164 in 4ec9d9230e
    155@@ -141,6 +156,13 @@ class TxDownloadManagerImpl {
    156      * all peers we are connected to (no block-relay-only and temporary connections). */
    157     std::map<NodeId, PeerInfo> m_peer_info;
    158 
    159+    /** Stores relevant information about the peer prior to verack. Upon completion of version
    160+     * handshake, we use this information to decide whether we relay packages with this peer. */
    161+    std::map<NodeId, RegistrationState> m_registration_states;
    162+
    163+    /** Only contains nodes that support some type of package relay. */
    164+    std::map<NodeId, PackageRelayVersions> m_package_relay_versions;
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 164

    The m_package_relay_versions map is intended to store nodes supporting package relay. This appears to overlap in functionality with m_versions_in_common within the RegistrationState struct. It would be beneficial to clearly define the distinct responsibilities of these two data structures to avoid potential redundancy or confusion, and to ensure data consistency.

  36. in src/node/txdownloadman_impl.h:219 in 4ec9d9230e
    213@@ -190,6 +214,16 @@ class TxDownloadManagerImpl {
    214 
    215     std::vector<TxOrphanage::OrphanTxBase> GetOrphanTransactions() const;
    216 
    217+    PackageRelayVersions GetSupportedVersions() const;
    218+
    219+    void ReceivedVersion(NodeId nodeid);
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 219

    The ReceivedVersion function is declared but its implementation is missing from this diff. Understanding how this function populates or updates the m_registration_states and m_peer_info members is crucial for ensuring correct peer state management after the version handshake.

  37. in src/node/txdownloadman_impl.h:221 in 4ec9d9230e
    213@@ -190,6 +214,16 @@ class TxDownloadManagerImpl {
    214 
    215     std::vector<TxOrphanage::OrphanTxBase> GetOrphanTransactions() const;
    216 
    217+    PackageRelayVersions GetSupportedVersions() const;
    218+
    219+    void ReceivedVersion(NodeId nodeid);
    220+
    221+    void ReceivedSendpackages(NodeId nodeid, PackageRelayVersions version);
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 221

    The ReceivedSendpackages function is declared. Its implementation should ensure that the m_registration_states and m_package_relay_versions are updated accurately based on the received PackageRelayVersions from the peer. This is critical for maintaining an up-to-date view of peer capabilities.

  38. in src/node/txdownloadman_impl.h:223 in 4ec9d9230e
    218+
    219+    void ReceivedVersion(NodeId nodeid);
    220+
    221+    void ReceivedSendpackages(NodeId nodeid, PackageRelayVersions version);
    222+
    223+    std::optional<PackageRelayVersions> UpdateRegistrationState(NodeId nodeid, bool txrelay, bool wtxidrelay);
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 223

    The UpdateRegistrationState function is declared. Its implementation needs to ensure that the m_txrelay and m_wtxidrelay members of m_registration_states are updated correctly. Furthermore, the logic for determining and returning std::optional<PackageRelayVersions> should be clearly defined and robust.

  39. in src/node/txdownloadman_impl.h:225 in 4ec9d9230e
    220+
    221+    void ReceivedSendpackages(NodeId nodeid, PackageRelayVersions version);
    222+
    223+    std::optional<PackageRelayVersions> UpdateRegistrationState(NodeId nodeid, bool txrelay, bool wtxidrelay);
    224+
    225+    bool NodeSupportsVersion(const NodeId& nodeid, const PackageRelayVersions& versions);
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 225

    The NodeSupportsVersion function is declared. Its implementation should efficiently and accurately determine if a peer identified by nodeid supports the provided versions. This likely involves querying the m_registration_states or m_package_relay_versions maps.

  40. in src/policy/packages.h:18 in 4ec9d9230e
    14@@ -15,6 +15,8 @@
    15 #include <unordered_set>
    16 #include <vector>
    17 
    18+using TransactionCompression = DefaultFormatter;
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 18

    The TransactionCompression alias is defined but its usage is not immediately clear in this scope. If it’s intended for use within the PackageToSend serialization, consider moving its definition closer to its usage or ensuring its purpose is well-documented if it’s meant for broader use.

  41. in src/policy/packages.h:64 in 4ec9d9230e
    59+
    60+    PackageToSend() = default;
    61+    explicit PackageToSend(const Package& package) :
    62+        txns(package) {}
    63+
    64+    SERIALIZE_METHODS(PackageToSend, obj)
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Lines: 64-67

    The PackageToSend class uses SERIALIZE_METHODS for serialization. For improved maintainability and potentially better performance or features, consider evaluating if a more modern serialization framework or library would be beneficial, especially as the serialization logic evolves.

  42. in src/protocol.h:307 in 4ec9d9230e
    306@@ -303,6 +307,8 @@ inline const std::array ALL_NET_MESSAGE_TYPES{std::to_array<std::string>({
    307     NetMsgType::CFCHECKPT,
    


    starkshade commented at 8:19 am on September 30, 2025:

    ⚠️ Code Issue

    Lines: 307-312

    0inline const std::array ALL_NET_MESSAGE_TYPES{
    1    NetMsgType::CFCHECKPT,
    2    NetMsgType::WTXIDRELAY,
    3    NetMsgType::SENDTXRCNCL,
    4    NetMsgType::SENDPACKAGES,
    5    NetMsgType::PKGTXNS,
    6};
    

    The ALL_NET_MESSAGE_TYPES array references NetMsgType::SENDPACKAGES and NetMsgType::PKGTXNS. However, these identifiers are not defined within the NetMsgType namespace. This will lead to a compilation failure. Ensure that these message types are declared and defined in the NetMsgType namespace before they are used in this array.

  43. in src/rpc/net.cpp:145 in 4ec9d9230e
    141@@ -142,6 +142,7 @@ static RPCHelpMan getpeerinfo()
    142                         {RPCResult::Type::STR, "SERVICE_NAME", "the service name if it is recognised"}
    143                     }},
    144                     {RPCResult::Type::BOOL, "relaytxes", "Whether we relay transactions to this peer"},
    145+                    {RPCResult::Type::BOOL, "relaytxpackages", "Whether we can relay packages with this peer"},
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 145

    The description for relaytxpackages is present in the RPC help text. This same information is also being added as a key-value pair in the JSON output. Consider if both are necessary or if there’s a more streamlined way to present this information to avoid redundancy.

  44. in src/rpc/net.cpp:276 in 4ec9d9230e
    272@@ -272,6 +273,7 @@ static RPCHelpMan getpeerinfo()
    273             heights.push_back(height);
    274         }
    275         obj.pushKV("inflight", std::move(heights));
    276+        obj.pushKV("relaytxpackages", statestats.m_package_relay);
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 276

    The key relaytxpackages is being added to the RPC result. The help text for this key is defined on line 145. Ensure that documentation and code changes are synchronized. If this key is intended to be part of the RPC output, its definition in the help text should be clearly associated with this addition.

  45. in src/test/fuzz/txdownloadman.cpp:72 in 4ec9d9230e
    67@@ -68,6 +68,17 @@ static CTransactionRef MakeTransactionSpending(const std::vector<COutPoint>& out
    68     for (size_t o = 0; o < num_outputs; ++o) tx.vout.emplace_back(CENT, P2WSH_OP_TRUE);
    69     return MakeTransactionRef(tx);
    70 }
    71+
    72+static Package MakePackage(FuzzedDataProvider& fuzzed_data_provider) {
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 72-80

    The MakePackage function is defined and used within the txdownloadman fuzz target. A similar functionality is also present in the txdownloadman_impl fuzz target (lines 339-340). If the logic for creating a package is identical, consider abstracting this into a shared helper function to avoid code duplication and improve maintainability.

  46. in src/test/fuzz/txdownloadman.cpp:178 in 4ec9d9230e
    174@@ -164,6 +175,12 @@ void CheckPackageToValidate(const node::PackageToValidate& package_to_validate,
    175     Assert(package.size() == 2);
    176 }
    177 
    178+void CheckSenderInitPackageToValidate(const node::PackageToValidate& package_to_validate, NodeId peer)
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 178-182

    The CheckSenderInitPackageToValidate function is called within the fuzzing logic. While the assertions within the function are valuable, ensure that the rand_peer variable used when calling txdownloadman.ReceivedPackage and subsequently CheckSenderInitPackageToValidate is sufficiently fuzzed or varied to cover different peer scenarios. Currently, rand_peer is generated using fuzzed_data_provider.ConsumeIntegral<NodeId>() which is appropriate, but it’s worth noting the dependency.

  47. in src/test/fuzz/txdownloadman.cpp:436 in 4ec9d9230e
    431+                if (const auto& package_to_validate = txdownload_impl.ReceivedPackage(rand_peer, rand_package)) {
    432+                    CheckSenderInitPackageToValidate(*package_to_validate, rand_peer);
    433+
    434+                    const auto& package = package_to_validate->m_txns;
    435+
    436+                    for (size_t i = 0; i < package.size(); i++) {
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 436-438

    In the txdownloadman_impl fuzz target, the code iterates through package_to_validate->m_txns and asserts that none of them are present in txdownload_impl.RecentRejectsFilter(). A similar check for the last transaction of the package is performed in a different branch of the CallOneOf (lines 427). This could indicate a potential for code duplication or an opportunity to consolidate the rejection filter check for the entire package into a single, more comprehensive assertion.

  48. in test/functional/p2p_sender_init_package_relay.py:78 in 4ec9d9230e
    73+            self.send_without_ping(sendpackages_message)
    74+        self.send_without_ping(msg_verack())
    75+        self.nServices = message.nServices
    76+
    77+    def on_sendpackages(self, message):
    78+        self._sendpackages_received.append(message.versions)
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 78

    The PackageRelayTest.test_pkg_received method could be more explicit in verifying the sendpackages message reception. While the test focuses on pkgtxns, explicitly checking that _sendpackages_received contains the expected version after the on_version call would strengthen the test’s coverage of the initial handshake and feature negotiation.

    Suggestion: Add an assertion in test_pkg_received to check package_sender.sendpackages_received after the initial connection setup.

  49. in test/functional/p2p_sender_init_package_relay.py:169 in 4ec9d9230e
    164+        # submit package to package_sender's mempool
    165+        assert_equal(package_sender.submitpackage([low_fee_parent["hex"], high_fee_child["hex"]])["package_msg"], "success")
    166+
    167+        self.wait_until(lambda: "pkgtxns" in package_sender.getpeerinfo()[0]["bytessent_per_msg"])
    168+
    169+        assert_equal(package_sender.getpeerinfo()[0]["bytessent_per_msg"]["pkgtxns"], 291)
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 169

    The assertion assert_equal(package_sender.getpeerinfo()[0]["bytessent_per_msg"]["pkgtxns"], 291) checks for an exact byte count. This is fragile and can break with minor changes to message serialization. It’s better to verify the functional behavior.

    Suggestion: Focus on verifying that the pkgtxns message was sent and that the package_receiver correctly processed the transactions within it, rather than asserting a specific byte count.


    starkshade commented at 8:20 am on September 30, 2025:

    ⚠️ Issue

    Type: Maintainability | Severity: Low

    Line: 169

    The test asserts an exact byte count (291) for the pkgtxns message. This is a fragile assertion that could fail due to minor changes in message serialization, even if the core functionality is working correctly. It makes the test less robust to future code modifications.

    Fix: Remove the assertion for the exact byte count. Focus on verifying the functional aspects of the package relay, such as the correct transactions being sent and received, or the expected behavior of the protocol.

  50. in test/functional/p2p_sender_init_package_relay.py:190 in 4ec9d9230e
    185+        high_fee_child = self.wallet.create_self_transfer_multi(utxos_to_spend=[parent1["new_utxo"], low_fee_parent2["new_utxo"]], fee_per_output=4000)
    186+
    187+        # submit package to package_sender's mempool
    188+        package_sender.send_and_ping(msg_pkgtxns(txns=[low_fee_parent2["tx"], high_fee_child["tx"]]))
    189+
    190+        self.wait_until(lambda: (low_fee_parent2["txid"] in package_receiver.getrawmempool()) and (high_fee_child["txid"] in package_receiver.getrawmempool()))
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 190

    The test test_pkg_received_if_one_parent_known relies on the node’s mempool being in a predictable state. However, the run_test method restarts the node and fills the mempool before this test, and the test itself is not decorated with @cleanup. This could lead to the assertion (low_fee_parent2["txid"] in package_receiver.getrawmempool()) and (high_fee_child["txid"] in package_receiver.getrawmempool()) being affected by pre-existing transactions in the mempool.

    Suggestion: Decorate test_pkg_received_if_one_parent_known with @cleanup to ensure a clean mempool state before the test begins, or explicitly clear the mempool and disconnect peers before this test.

  51. in test/functional/p2p_sender_init_package_relay.py:215 in 4ec9d9230e
    210+
    211+        # submit package to package_sender's mempool
    212+        assert_equal(package_sender.submitpackage([low_fee_parent["hex"], high_fee_child["hex"]])["package_msg"], "success")
    213+
    214+        # move time ahead
    215+        self.nodes[0].bumpmocktime(600)
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 215

    The test test_pkg_not_sent_if_known_parent advances mock time, but the @cleanup decorator resets it to 0 afterwards. This means the time advancement is immediately undone, potentially weakening the assertion that no pkgtxns were received after time has passed. The test might be more robust if the time advancement’s effect is preserved or if the assertion is made before the cleanup.

    Suggestion: Consider making the assertion about _pkgtxns_received before the finally block in the cleanup decorator, or ensure that the time manipulation is necessary and its effect is correctly tested within the scope of the decorated function.

  52. in test/functional/p2p_sender_init_package_relay.py:242 in 4ec9d9230e
    237+
    238+        # submit package to package_sender's mempool
    239+        assert_equal(package_sender.submitpackage([low_fee_parent["hex"], high_fee_child["hex"]])["package_msg"], "success")
    240+
    241+        # move time ahead
    242+        self.nodes[0].setmocktime(current_time+600)
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 242

    This test manipulates mock time, but the @cleanup decorator resets it to 0. The time advancement self.nodes[0].setmocktime(current_time+600) is therefore undone. This might make the assertions assert_equal(len(package_receiver._pkgtxns_received), 0) and assert_equal(len(package_receiver._tx_received), 0) less conclusive if the test’s intent was to verify behavior after a specific duration.

    Suggestion: Ensure that time-dependent assertions are made before the cleanup decorator’s time reset, or reconsider the necessity of mock time manipulation if its effect is immediately nullified by the cleanup process.

  53. in test/functional/p2p_sender_init_package_relay.py:256 in 4ec9d9230e
    251+
    252+        low_fee_child = self.wallet.create_self_transfer(fee_rate=FEERATE_1SAT_VB, confirmed_only=True)
    253+        high_fee_child = self.wallet.create_self_transfer(fee_rate=FEERATE_1SAT_VB*20, confirmed_only=True)
    254+
    255+        # submit package to package_sender's mempool
    256+        package_sender.send_and_ping(msg_pkgtxns(txns=[low_fee_child["tx"], high_fee_child["tx"]]))
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 256

    The test test_pkg_dropped_if_not_1p1c sends a pkgtxns message that is not 1p1c and asserts that the mempool remains empty. While this confirms the package was not accepted, it could be more informative to explicitly check for rejection or logging of the invalid package, if such mechanisms are available and testable.

    Suggestion: If possible, add checks for specific rejection messages or log entries that indicate the pkgtxns message was dropped due to not being 1p1c, rather than solely relying on the mempool state.

  54. in test/functional/p2p_sender_init_package_relay.py:264 in 4ec9d9230e
    259+
    260+        assert_equal(len(package_receiver.getrawmempool()), 0)
    261+
    262+    @cleanup
    263+    def test_disconnect_if_no_tx_relay(self):
    264+        self.restart_node(0, extra_args=["-blocksonly", "-packagerelay=1"])
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 264

    The test test_disconnect_if_no_tx_relay restarts the node with -blocksonly. The intention is to test disconnection when transaction relay is effectively disabled. However, the link between -blocksonly and the absence of transaction relay (specifically wtxidrelay or sendpackages capability negotiation) could be more clearly stated or verified. The PackageRelayTest class’s PackageRelayer is initialized with send_wtxidrelay=True by default, which might not align with the -blocksonly node’s capabilities.

    Suggestion: Clarify in comments or by adjusting the PackageRelayer instantiation (e.g., PackageRelayer(send_wtxidrelay=False)) why the peer should disconnect. Ensure that the PackageRelayer’s send_wtxidrelay flag is set appropriately to reflect the expected behavior when the node is in -blocksonly mode.

  55. in test/functional/p2p_sender_init_package_relay.py:299 in 4ec9d9230e
    294+        # submit package to package_sender's mempool
    295+        assert_equal(package_sender.submitpackage([low_fee_parent2["hex"], high_fee_child["hex"]])["package_msg"], "success")
    296+
    297+        self.wait_until(lambda: len(package_receiver._pkgtxns_received) == 2)
    298+
    299+        self.generate(self.nodes[0], 1)
    


    starkshade commented at 8:19 am on September 30, 2025:

    💡 Improvement Needed

    Line: 299-300

    This test manually generates a block and disconnects peers (self.generate(self.nodes[0], 1) and self.nodes[0].disconnect_p2ps()). This cleanup logic is duplicated within the test function itself, rather than relying on the @cleanup decorator. This can lead to inconsistencies and makes the test harder to maintain.

    Suggestion: Apply the @cleanup decorator to test_pkg_sent_if_one_parent_known to handle the cleanup automatically and consistently, removing the manual cleanup steps from within the test function.

  56. in test/functional/p2p_sender_init_package_relay.py:325 in 4ec9d9230e
    320+        self.nodes[0].setmocktime(current_time+600)
    321+
    322+        assert_equal(len(package_receiver._pkgtxns_received), 0)
    323+        assert_equal(len(package_receiver._tx_received), 0)
    324+
    325+        self.nodes[0].disconnect_p2ps()
    


    starkshade commented at 8:20 am on September 30, 2025:

    💡 Improvement Needed

    Line: 325

    The test test_pkg_not_sent_if_multiple_unknown_ancestors includes a manual call to self.nodes[0].disconnect_p2ps(). This cleanup action is performed within the test function itself, rather than being managed by the @cleanup decorator. This duplication of cleanup logic can reduce maintainability and consistency.

    Suggestion: Apply the @cleanup decorator to test_pkg_not_sent_if_multiple_unknown_ancestors to ensure that peer disconnection and other cleanup tasks are handled automatically and consistently, removing the manual call from the test function.

  57. in test/functional/p2p_sender_init_package_relay.py:37 in 4ec9d9230e
    32+from test_framework.util import assert_equal
    33+
    34+# 1sat/vB feerate denominated in BTC/KvB
    35+FEERATE_1SAT_VB = Decimal("0.00001000")
    36+
    37+def cleanup(func):
    


    starkshade commented at 8:20 am on September 30, 2025:

    ⚠️ Issue

    Type: Maintainability | Severity: Low

    Line: 37-50

    The cleanup decorator performs a block generation and disconnects all peers. This might be too aggressive for tests that need to inspect the state after the test function completes but before the test framework fully tears down. For example, if a test needs to check the mempool state after func(self) has run, the subsequent self.generate(self.nodes[0], 1) will clear it.

    Fix: Consider making the cleanup actions more granular or allowing tests to opt-out of specific cleanup steps if they need to preserve state. Alternatively, ensure that all assertions are made within the try block of the decorated function.

  58. in test/functional/p2p_sender_init_package_relay.py:113 in 4ec9d9230e
    108+        self.log.info("Test that we disconnect the peer if fRelay not sent")
    109+        self.test_disconnect_if_no_tx_relay()
    110+
    111+        # We need a different set-up for the following tests, and we don't run @cleanup
    112+        assert_equal(len(self.nodes[0].getrawmempool()), 0)
    113+        self.restart_node(0, extra_args=["-packagerelay=1", "-maxmempool=5"])
    


    starkshade commented at 8:20 am on September 30, 2025:

    ⚠️ Issue

    Type: Maintainability | Severity: Low

    Line: 113

    The run_test method restarts node 0 and fills its mempool, but the subsequent tests (test_pkg_sent_if_one_parent_known and test_pkg_not_sent_if_multiple_unknown_ancestors) are not decorated with @cleanup. This means that the state from fill_mempool and the restarted node might persist, potentially affecting the isolation of these tests.

    Fix: Apply the @cleanup decorator to test_pkg_sent_if_one_parent_known and test_pkg_not_sent_if_multiple_unknown_ancestors, or add explicit state reset logic before these tests.

  59. in test/functional/p2p_sender_init_package_relay.py:197 in 4ec9d9230e
    192+    @cleanup
    193+    def test_pkg_not_sent_if_known_parent(self):
    194+        package_sender = self.nodes[0]
    195+        package_receiver = self.nodes[0].add_p2p_connection(PackageRelayer())
    196+
    197+        self.nodes[0].setmocktime(int(time.time()))
    


    starkshade commented at 8:20 am on September 30, 2025:

    ⚠️ Issue

    Type: Maintainability | Severity: Low

    Line: 197

    These tests manipulate mock time using setmocktime and bumpmocktime. However, the @cleanup decorator resets the mock time to 0 at the end of each decorated test. This means that the time advancements made within these specific tests are effectively undone before the next test begins. If subsequent tests rely on a specific mock time state, this could lead to unexpected behavior.

    Fix: Ensure that time manipulation is handled consistently. If the time advancement is crucial for the test’s assertion, consider if the @cleanup decorator’s time reset is appropriate for these tests, or if the tests should manage their own time state more explicitly.

  60. in test/functional/p2p_sender_init_package_relay.py:307 in 4ec9d9230e
    302+    def test_pkg_not_sent_if_multiple_unknown_ancestors(self):
    303+        package_sender = self.nodes[0]
    304+        package_receiver = self.nodes[0].add_p2p_connection(PackageRelayer())
    305+
    306+        current_time = int(time.time())
    307+        self.nodes[0].setmocktime(current_time)
    


    starkshade commented at 8:20 am on September 30, 2025:

    ⚠️ Issue

    Type: Maintainability | Severity: Low

    Line: 307

    This test uses setmocktime to manipulate the node’s time. As with other tests employing mock time and the @cleanup decorator, the decorator’s action of resetting mock time to 0 at the end of the test might negate the intended effect of the time manipulation for subsequent tests or lead to unexpected behavior if other tests rely on a specific time state.

    Fix: Evaluate the necessity of mock time manipulation in this context. If time advancement is critical, consider how it interacts with the @cleanup decorator’s reset behavior. Ensure that test isolation is maintained regarding time-dependent states.

  61. in test/functional/p2p_sendpackages.py:27 in 4ec9d9230e
    22+from test_framework.test_framework import BitcoinTestFramework
    23+from test_framework.util import (
    24+    assert_equal,
    25+)
    26+
    27+def cleanup(func):
    


    starkshade commented at 8:20 am on September 30, 2025:

    💡 Improvement Needed

    Lines: 27-37

    The cleanup decorator performs a full block generation and disconnects all peers. This might be overly aggressive for certain test scenarios where only specific cleanup is required. Consider making the cleanup process more granular or configurable to allow for more targeted cleanup operations per test case.

  62. in test/functional/p2p_sendpackages.py:40 in 4ec9d9230e
    35+            self.generate(self.nodes[0], 1)
    36+            self.nodes[0].disconnect_p2ps()
    37+    return wrapper
    38+
    39+class PackageRelayer(P2PInterface):
    40+    def __init__(self, send_sendpackages=True, send_wtxidrelay=True):
    


    starkshade commented at 8:20 am on September 30, 2025:

    💡 Improvement Needed

    Line: 40

    The PackageRelayer constructor takes multiple boolean arguments (send_sendpackages, send_wtxidrelay). If additional configuration options are introduced in the future, this could lead to a long and less readable constructor signature. Consider using a configuration dictionary or a dedicated options object to pass these settings, which would be more scalable and maintainable.

  63. in test/functional/p2p_sendpackages.py:75 in 4ec9d9230e
    70+    @cleanup
    71+    def test_sendpackages(self):
    72+        node = self.nodes[0]
    73+
    74+        self.log.info("Test sendpackages during version handshake")
    75+        peer_normal = node.add_p2p_connection(PackageRelayer())
    


    starkshade commented at 8:20 am on September 30, 2025:

    💡 Improvement Needed

    Lines: 75-79

    The test test_sendpackages contains several blocks that follow a similar pattern: adding a P2P connection, performing assertions on sendpackages_received and relaytxpackages, and then disconnecting. This repetition can be reduced by extracting this common logic into a helper method. This would improve the readability and maintainability of the test function.

  64. in test/functional/p2p_sendpackages.py:119 in 4ec9d9230e
    114+        peer_sendpackages_after_verack.send_without_ping(sendpackages_message)
    115+        peer_sendpackages_after_verack.wait_for_disconnect()
    116+
    117+        self.log.info("Test sendpackages on a blocksonly node")
    118+
    119+        self.restart_node(0, extra_args=["-blocksonly"])
    


    starkshade commented at 8:20 am on September 30, 2025:

    💡 Improvement Needed

    Lines: 119-122

    The test restarts the node with the -blocksonly argument. The cleanup decorator, as implemented, does not explicitly revert this change. If this test were to be run in conjunction with other tests, the node might remain in a blocksonly state, affecting subsequent test executions. The cleanup process should ensure that the node is returned to its default configuration or a known clean state after the test.

  65. in test/functional/rpc_net.py:160 in 4ec9d9230e
    156@@ -157,6 +157,7 @@ def test_getpeerinfo(self):
    157                 "id": no_version_peer_id,
    158                 "inbound": True,
    159                 "inflight": [],
    160+                "relaytxpackages" : False,
    


    starkshade commented at 8:20 am on September 30, 2025:

    💡 Improvement Needed

    Line: 160

    The key relaytxpackages is being added to the peer information dictionary. If this represents a new field or a change in the expected output of getpeerinfo, it should be documented in the relevant RPC documentation to inform users of the change.

  66. in test/functional/test_framework/messages.py:1909 in 4ec9d9230e
    1904@@ -1903,6 +1905,42 @@ def __repr__(self):
    1905         return "msg_sendtxrcncl(version=%lu, salt=%lu)" %\
    1906             (self.version, self.salt)
    1907 
    1908+class msg_sendpackages:
    1909+    __slots__ = ("versions")
    


    starkshade commented at 8:20 am on September 30, 2025:

    💡 Improvement Needed

    Line: 1909

    When defining __slots__ with a single element, it should be defined as a tuple with a trailing comma to avoid ambiguity. Currently, __slots__ = ('versions') is interpreted as a string, not a tuple. The correct way to define it is __slots__ = ('versions',).

  67. in test/functional/test_framework/messages.py:1927 in 4ec9d9230e
    1922+
    1923+    def __repr__(self):
    1924+        return "msg_sendpackages(versions={})".format(self.versions)
    1925+
    1926+class msg_pkgtxns:
    1927+    __slots__ = ("txns")
    


    starkshade commented at 8:20 am on September 30, 2025:

    💡 Improvement Needed

    Line: 1927

    When defining __slots__ with a single element, it should be defined as a tuple with a trailing comma to avoid ambiguity. Currently, __slots__ = ('txns') is interpreted as a string, not a tuple. The correct way to define it is __slots__ = ('txns',).

  68. in test/functional/test_framework/p2p.py:558 in 4ec9d9230e
    554@@ -551,10 +555,12 @@ def on_headers(self, message): pass
    555     def on_mempool(self, message): pass
    556     def on_merkleblock(self, message): pass
    557     def on_notfound(self, message): pass
    558+    def on_pkgtxns(self, message): pass
    


    starkshade commented at 8:20 am on September 30, 2025:

    ⚠️ Code Issue

    Lines: 558-558

    0def on_pkgtxns(self, message): pass
    

    This method is defined but does not implement any logic, only pass. This suggests that the pkgtxns message type is not being handled, which might be an oversight if this message is expected to be processed.

  69. in test/functional/test_framework/p2p.py:563 in 4ec9d9230e
    554@@ -551,10 +555,12 @@ def on_headers(self, message): pass
    555     def on_mempool(self, message): pass
    556     def on_merkleblock(self, message): pass
    557     def on_notfound(self, message): pass
    558+    def on_pkgtxns(self, message): pass
    559     def on_pong(self, message): pass
    560     def on_sendaddrv2(self, message): pass
    561     def on_sendcmpct(self, message): pass
    562     def on_sendheaders(self, message): pass
    563+    def on_sendpackages(self, message): pass
    


    starkshade commented at 8:20 am on September 30, 2025:

    ⚠️ Code Issue

    Lines: 563-563

    0def on_sendpackages(self, message): pass
    

    This method is defined but does not implement any logic, only pass. This suggests that the sendpackages message type is not being handled, which might be an oversight if this message is expected to be processed.

  70. in test/functional/test_runner.py:179 in 4ec9d9230e
    175@@ -176,6 +176,8 @@
    176     'wallet_txn_clone.py --segwit',
    177     'rpc_getchaintips.py',
    178     'rpc_misc.py',
    179+    'p2p_sendpackages.py',
    


    starkshade commented at 8:20 am on September 30, 2025:

    💡 Improvement Needed

    Line: 179-180

    Consider grouping related test files for better organization and readability. For example, ‘p2p_sendpackages.py’ and ‘p2p_sender_init_package_relay.py’ could be grouped together.

  71. starkshade commented at 8:20 am on September 30, 2025: none

    This is a significant refactoring and feature addition focused on implementing transaction package relay, a new mechanism for more efficient transaction propagation in Bitcoin. The changes introduce new message types, update network protocol handling, and integrate with existing transaction management systems. Extensive modifications are present in network-related files and test cases, indicating a substantial effort to enable and test this new functionality.

    Key Changes:

    • Introduction of transaction package relay functionality.
    • Addition of new P2P message types: SENDPACKAGES and PKGTXNS.
    • Modification of network message handling to support package relay.
    • Integration of package relay logic into transaction download and relay mechanisms.
    • Updates to peer management to track package relay support.
    • Introduction of a new experimental command-line argument ‘-packagerelay’.
    • Implementation of new test cases for package relay functionality.
    • Refactoring of transaction relay logic to conditionally send packages or individual transactions.

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2025-10-10 15:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me