net: fix premature stale flagging of unpicked private broadcast txs #34873

pull Mccalabrese wants to merge 2 commits into bitcoin:master from Mccalabrese:fix/private-broadcast-epoch changing 5 files +66 −25
  1. Mccalabrese commented at 4:22 am on March 20, 2026: contributor

    Motivation Currently, freshly added transactions in private_broadcast are almost immediately flagged and logged as stale by the resend-stale job.

    The Bug m_transactions maps a transaction to a std::vector<SendStatus>. When try_emplace adds a new transaction, this vector is empty. When GetStale() runs, DerivePriority() evaluates the empty vector and returns a default Priority struct where last_confirmed evaluates to the Unix Epoch (Jan 1, 1970). The stale checker sees a 50-year-old timestamp and flags it on the next resend-stale cycle.

    The Fix Rather than modifying the transient Priority struct or creating a “Zombie Transaction” edge case by ignoring transactions with 0 picks, this PR modifies the state container:

    • Wraps the SendStatus vector in a new TxSendStatus struct inside private_broadcast.h.
    • TxSendStatus automatically captures time_added upon emplace.
    • GetStale() now checks p.num_confirmed == 0 to measure age against time_added using a new 5-minute INITIAL_STALE_DURATION grace period, falling back to last_confirmed and the standard 1-minute STALE_DURATION once network interaction begins.

    Additional Polish

    • Exposed time_added via the getprivatebroadcastinfo RPC endpoint so users can see when a transaction entered the queue.
    • Added a dedicated stale_unpicked_tx test case and updated private_broadcast_tests.cpp to properly mock the passage of time for the new grace period. Closes #34862
  2. DrahtBot added the label P2P on Mar 20, 2026
  3. DrahtBot commented at 4:22 am on March 20, 2026: contributor

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

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK andrewtoth, vasild, achow101
    Concept ACK w0xlt

    If your review is incorrectly listed, please copy-paste <!–meta-tag:bot-skip–> into the comment that the bot should ignore.

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #34707 (net: keep finished private broadcast txs in memory by andrewtoth)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

  4. Mccalabrese marked this as ready for review on Mar 20, 2026
  5. in src/test/private_broadcast_tests.cpp:124 in 6b69f048f2
    120@@ -114,8 +121,9 @@ BOOST_AUTO_TEST_CASE(basic)
    121         BOOST_CHECK(!peers[0].received.has_value());
    122     }
    123 
    124-    BOOST_CHECK_EQUAL(pb.GetStale().size(), 1);
    125-    BOOST_CHECK_EQUAL(pb.GetStale()[0], tx_for_recipient2);
    126+    const auto stale_after_one_confirm{pb.GetStale()};
    


    optout21 commented at 10:44 am on March 20, 2026:
    Nit naming: This name is rather specific, prone to go out of sync when test is changed or code is copied. Suggestion: stale_state. BTW, this change is not necessary.

    vasild commented at 1:22 pm on March 20, 2026:

    Yeah, drop the unnecessary change. It burdens review now and later when people* dig into git blame and git log.

    * or robots


    Mccalabrese commented at 9:15 pm on March 21, 2026:
    Updated the PR
  6. in src/private_broadcast.cpp:100 in 6b69f048f2
     94@@ -95,9 +95,11 @@ std::vector<CTransactionRef> PrivateBroadcast::GetStale() const
     95     LOCK(m_mutex);
     96     const auto stale_time{NodeClock::now() - STALE_DURATION};
     97     std::vector<CTransactionRef> stale;
     98-    for (const auto& [tx, send_status] : m_transactions) {
     99-        const Priority p{DerivePriority(send_status)};
    100-        if (p.last_confirmed < stale_time) {
    101+    for (const auto& [tx, state] : m_transactions) {
    102+        const Priority p{DerivePriority(state.node_attempts)};
    103+        const bool is_unconfirmed{p.num_confirmed == 0};
    


    optout21 commented at 10:46 am on March 20, 2026:
    Putting this in its own variable doesn’t save code, it should be just used directly.
  7. in src/private_broadcast.cpp:101 in 6b69f048f2
     99-        const Priority p{DerivePriority(send_status)};
    100-        if (p.last_confirmed < stale_time) {
    101+    for (const auto& [tx, state] : m_transactions) {
    102+        const Priority p{DerivePriority(state.node_attempts)};
    103+        const bool is_unconfirmed{p.num_confirmed == 0};
    104+        const auto time_to_check{is_unconfirmed ? state.time_added : p.last_confirmed};
    


    optout21 commented at 10:47 am on March 20, 2026:
    I find the time_to_check name wrong/misleading, something like last_action_time would make more sense.

    vasild commented at 1:17 pm on March 20, 2026:

    So, when a transaction is being send we:

    1. pick a random peer from addrman, try to connect to it, if not then find another one and eventually send the transaction to the peer
    2. they send the transaction to their peers and
    3. eventually it comes back to us from somebody else (round-trip)

    Before this PR we consider a transaction as stale if the duration between 2. and 3. is too long - 1 minute. Because that is normally done in a few seconds.

    However with this PR we will also take into account the duration between 1. and 3. and that does not usually complete in seconds. It could take longer - having to timeout to an offline peer candidate, retry a few times.

    I think all this warrants a longer tolerance for transactions with p.num_confirmed == 0.

  8. in src/private_broadcast.h:180 in 6b69f048f2
    176@@ -177,9 +177,12 @@ class PrivateBroadcast
    177      */
    178     std::optional<TxAndSendStatusForNode> GetSendStatusByNode(const NodeId& nodeid)
    179         EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
    180-
    181+    struct TxState {
    


    optout21 commented at 10:49 am on March 20, 2026:
    Naming: TxState is not a descriptive/good name, it’s too generic. I suggest TxSendStatus.
  9. in src/private_broadcast.h:182 in 6b69f048f2
    176@@ -177,9 +177,12 @@ class PrivateBroadcast
    177      */
    178     std::optional<TxAndSendStatusForNode> GetSendStatusByNode(const NodeId& nodeid)
    179         EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
    180-
    181+    struct TxState {
    182+        NodeClock::time_point time_added{NodeClock::now()};
    183+        std::vector<SendStatus> node_attempts;
    


    optout21 commented at 10:51 am on March 20, 2026:
    Naming: please improve naming. E.g. node_status, peer_status.

    vasild commented at 1:18 pm on March 20, 2026:
    I think this should be called send_statuses.
  10. optout21 commented at 10:54 am on March 20, 2026: contributor
    • The change seems to accomplish the goal.
    • The test appears to correctly check for the problem; it fails without the change.
    • I’ve tried without the new time_added field, just extending the condition in GetStale():
    0    // Pick transactions that were confirmed long ago, but were picked long ago or not at all
    1    if (p.last_confirmed < stale_time && (p.num_picked == 0 || p.last_picked < stale_time)) {
    

    This solution also achieves the same result, but it’s simpler. However, the time_added-based is more robust (against error during the initial pick).

    • An extra test case testing staleness of a num_picked = 0 transaction would be helpful.
    • I feel the descriptiveness could be improved, e.g. by better naming.
    • Please take some extra care in reviewing/polishing LLM-generated code.
  11. in src/private_broadcast.h:181 in 6b69f048f2 outdated
    176@@ -177,9 +177,12 @@ class PrivateBroadcast
    177      */
    178     std::optional<TxAndSendStatusForNode> GetSendStatusByNode(const NodeId& nodeid)
    179         EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
    180-
    181+    struct TxState {
    182+        NodeClock::time_point time_added{NodeClock::now()};
    


    andrewtoth commented at 1:10 pm on March 20, 2026:
    Should we return this data in getprivatebroadcastinfo as well? Could be interesting to the user.

    Mccalabrese commented at 11:42 pm on March 20, 2026:
    Added to PR
  12. vasild commented at 1:23 pm on March 20, 2026: contributor
    Approach ACK 6b69f048f2b50a237ae4027ab4cfc878e3c5b282
  13. vasild commented at 1:24 pm on March 20, 2026: contributor
    Could add Fixes: [#34862](/bitcoin-bitcoin/34862/) to the OP and to the commit message.
  14. Mccalabrese force-pushed on Mar 20, 2026
  15. Mccalabrese commented at 3:19 pm on March 20, 2026: contributor

    @vasild Good call, added the Fixes link to both the OP and the commit message

    Also updated the PR with the following changes based on the review feedback:

    • Grace Period: Added INITIAL_STALE_DURATION (5min) to give freshly added, unconfirmed transactions more time to find a peer before being flagged as stale, plus a new stale_unpicked_tx test case to cover it.
    • RPC Output: time_added is now correctly exposed to the user in getprivatebroadcastinfo (and fixed the structured binding in net_processing that tripped when expanding the info struct).
    • Naming: Renamed the wrapper to TxSendStatus and the vector to send_statuses.
    • Tests & Git Blame: Reverted the unnecessary BOOST_REQUIRE changes in the basic test case to keep git blame clean.

    On the test lines, directly indexing the temporary vector returned by GetStale() inside the BOOST macro was causing test instability. I stored it in stale_state first to ensure memory safety.

    I believe this is ready for another look

  16. andrewtoth approved
  17. andrewtoth commented at 3:33 pm on March 20, 2026: contributor
    ACK 50f0731926b035a23f0b0a590fcdf2e0da7dccb7
  18. DrahtBot requested review from vasild on Mar 20, 2026
  19. optout21 commented at 9:45 am on March 21, 2026: contributor

    Some administration issues:

    • Please split the single commit into two: the introduction of TxSendStatus that is easy to review but creates more diff lines, and the logic change in GetStale()
    • Please improve the PR title. “1970 epoch stale tx bug” is hard to understand (1970 is not very relevant). Derive a concise and specific description from the issue title&description. Also, remove the issue number from the title.
    • Add Closes [#34862](/bitcoin-bitcoin/34862/) to the description.
  20. Mccalabrese renamed this:
    net: fix 1970 epoch stale tx bug in private_broadcast - Issue #34862
    net: fix premature stale flagging of unpicked private broadcast txs
    on Mar 21, 2026
  21. Mccalabrese force-pushed on Mar 21, 2026
  22. Mccalabrese commented at 12:07 pm on March 21, 2026: contributor

    @optout21 Thanks for the review!

    Administrative updates are in: -changes are split into two distinct commits -Title updated to be more descriptive -Added Closes to the description

  23. andrewtoth commented at 11:28 pm on March 21, 2026: contributor

    ACK 198a49e24872ce28c9f88e12719850b07cdeda7f

    About the “Additional polish” section of the PR description:

    Applied const and uniform initialization {} to the updated GetStale() loop to align with modern style guidelines.

    I don’t see how you changed anything like this in the GetStale() loop.

    Swapped BOOST_CHECK_EQUAL for BOOST_REQUIRE_EQUAL in the test suite when evaluating vector sizes to prevent a known out-of-bounds 0x0 memory access violation if the size check fails.

    I don’t see this being done for some existing BOOST_CHECK_EQUAL. Only in the new unit test.

  24. Mccalabrese commented at 0:10 am on March 22, 2026: contributor

    @andrewtoth good catch, I forgot to edit the description after updating the commits based on feedback.

    I have edited the OP so it accurately reflects the diff. Thanks for the ACK!

  25. w0xlt commented at 8:05 am on March 22, 2026: contributor
    Concept ACK
  26. DrahtBot added the label CI failed on Mar 23, 2026
  27. DrahtBot commented at 5:46 am on March 23, 2026: contributor

    🚧 At least one of the CI tasks failed. Task test ancestor commits: https://github.com/bitcoin/bitcoin/actions/runs/23379165994/job/68126883006 LLM reason (✨ experimental): CI failed due to a build/compile error while compiling private_broadcast.cpp (CMake/gmake returned non-zero).

    Try to run the tests locally, according to the documentation. However, a CI failure may still happen due to a number of reasons, for example:

    • Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    • A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.

    • An intermittent issue.

    Leave a comment here, if you need help tracking down a confusing failure.

  28. in src/rpc/mempool.cpp:158 in cf44e2419c outdated
    154@@ -155,6 +155,7 @@ static RPCHelpMan getprivatebroadcastinfo()
    155                                 {RPCResult::Type::STR_HEX, "txid", "The transaction hash in hex"},
    156                                 {RPCResult::Type::STR_HEX, "wtxid", "The transaction witness hash in hex"},
    157                                 {RPCResult::Type::STR_HEX, "hex", "The serialized, hex-encoded transaction data"},
    158+                                {RPCResult::Type::NUM_TIME, "time_added", "The time this transaction was added to the private broadcast queue (seconds since epoch)"},
    


    optout21 commented at 10:02 am on March 23, 2026:
    This change does not belong to this commit.
  29. in src/rpc/mempool.cpp:187 in cf44e2419c outdated
    183@@ -183,6 +184,7 @@ static RPCHelpMan getprivatebroadcastinfo()
    184                 o.pushKV("txid", tx_info.tx->GetHash().ToString());
    185                 o.pushKV("wtxid", tx_info.tx->GetWitnessHash().ToString());
    186                 o.pushKV("hex", EncodeHexTx(*tx_info.tx));
    187+                o.pushKV("time_added", TicksSinceEpoch<std::chrono::seconds>(tx_info.time_added));
    


    optout21 commented at 10:02 am on March 23, 2026:
    This change does not belong to this commit.
  30. in src/net_processing.cpp:1866 in cf44e2419c outdated
    1862@@ -1866,7 +1863,8 @@ std::vector<CTransactionRef> PeerManagerImpl::AbortPrivateBroadcast(const uint25
    1863     std::vector<CTransactionRef> removed_txs;
    1864 
    1865     size_t connections_cancelled{0};
    1866-    for (const auto& [tx, _] : snapshot) {
    1867+    for (const auto& tx_info : snapshot) {
    


    optout21 commented at 10:03 am on March 23, 2026:
    This change does not belong to this commit.
  31. in src/private_broadcast.cpp:120 in cf44e2419c outdated
    119+        peers.reserve(state.send_statuses.size());
    120+        for (const auto& status : state.send_statuses) {
    121             peers.emplace_back(PeerSendInfo{.address = status.address, .sent = status.picked, .received = status.confirmed});
    122         }
    123-        entries.emplace_back(TxBroadcastInfo{.tx = tx, .peers = std::move(peers)});
    124+        entries.emplace_back(TxBroadcastInfo{.tx = tx, .time_added = state.time_added, .peers = std::move(peers)});
    


    optout21 commented at 10:03 am on March 23, 2026:
    This change does not belong to this commit.
  32. in src/private_broadcast.h:41 in cf44e2419c outdated
    37@@ -38,6 +38,7 @@ class PrivateBroadcast
    38 
    39     struct TxBroadcastInfo {
    40         CTransactionRef tx;
    41+        NodeClock::time_point time_added;
    


    optout21 commented at 10:03 am on March 23, 2026:
    This change does not belong to this commit.
  33. optout21 commented at 10:09 am on March 23, 2026: contributor

    Splitting of the new internal struct into its own commit was done, but not cleanly. Otherwise, the change set is moving in the right direction.

    For transparency: the code and all the communication in this PR feels like communicating with an LLM. The code changes are of acceptable quality, but the communication with the author feels rather clumsy.

  34. Mccalabrese force-pushed on Mar 23, 2026
  35. andrewtoth commented at 1:30 pm on March 23, 2026: contributor

    the code and all the communication in this PR feels like communicating with an LLM

    Indeed, I believe https://github.com/bitcoin-core/meta/issues/35 is relevant, since this PR appeared soon after vasild created the issue.

    The code seems correct, so IMO should be merged. I think trying to educate the LLM on better commit splitting and other best practices seems like wasted effort.

  36. Mccalabrese commented at 1:31 pm on March 23, 2026: contributor

    @optout21 The specific lines mentioned were moved to the other commit. I also made sure that GetStale() built targeting test_bitcoin on its own with just the first commits changes.

    On communication, I’m a student, and Ive been grepping around the bitcoin codebase for months and waiting for an issue I could tackle. I got little over excited and was over revising my comments. I will keep communication concise.

  37. Mccalabrese commented at 1:50 pm on March 23, 2026: contributor

    @andrewtoth Ouch. Fair

    I am a person, I’m a sophomore computer engineering student. I’ve been trying to learn the bitcoin codebase. When it came time to submit the PR, I was a bit intimidated by the core review process and leaned in too heavily on revising communication with AI to not sound like an amateur.

    I appreciate you reviewing the code and saying it’s correct. Ill take the L on the communication and keep it in my words only. I do really want to learn from my mistakes especially on the nits and splitting commits so that I can get more familiar with how PRs should be packaged.

    Thanks for your patience.

  38. andrewtoth commented at 2:03 pm on March 23, 2026: contributor

    @Mccalabrese apologies if I caused offense. I think I am a little jaded seeing new contributors pop up with clearly LLM generated text.

    If you are interested in learning, I would suggest not using LLMs at all to start.

  39. optout21 commented at 2:03 pm on March 23, 2026: contributor

    In response to #34873 (comment)

    I think trying to educate the LLM on better commit splitting and other best practices seems like wasted effort.

    Fair, but I’m not trying to educate an LLM, I’m just trying (to make my effort) to get a set of commits suitable for merging :) TBH, after the first communication issues it occurred to me to take the changes and re-package and polish them in a new PR, but that would be unfair with the submitter…

  40. Mccalabrese commented at 2:18 pm on March 23, 2026: contributor

    @andrewtoth no offense taken, and I won’t use AI anymore. I was too preoccupied with trying to make everything sound perfect.

    I’m new, but bitcoin was what initially drove me to get seriously interested in programing, I lurked in the PR Reviews and I want to continue to contribute so I am trying to learn naming, splitting changes up, etc. I’ve been keeping notes for future issues I try to tackle.

  41. in src/private_broadcast.cpp:6 in 8554807072
    2@@ -3,13 +3,15 @@
    3 // file COPYING or https://opensource.org/license/mit/.
    4 
    5 #include <private_broadcast.h>
    6+
    


    vasild commented at 11:13 am on March 24, 2026:
    unnecessary change
  42. in src/private_broadcast.h:182 in 8554807072
    177@@ -176,9 +178,12 @@ class PrivateBroadcast
    178      */
    179     std::optional<TxAndSendStatusForNode> GetSendStatusByNode(const NodeId& nodeid)
    180         EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
    181-
    182+    struct TxSendStatus {
    183+        NodeClock::time_point time_added{NodeClock::now()};
    


    vasild commented at 12:40 pm on March 24, 2026:
    TxSendStatus::time_added can be const. That way it hints the reader of the code that it will never be changed.

    andrewtoth commented at 2:22 pm on March 26, 2026:
    @Mccalabrese you didn’t want to take this suggestion?

    Mccalabrese commented at 2:32 pm on March 26, 2026:
    @andrewtoth No I definitely did, it’s const in my working dir I don’t know why it’s not in the commit. Give me a sec Ill get to the bottom of why that didn’t push

    andrewtoth commented at 2:35 pm on March 26, 2026:
    Oops nevermind @Mccalabrese I was looking at TxBroadcastInfo not TxSendStatus. This change is taken :+1:.

    Mccalabrese commented at 2:44 pm on March 26, 2026:
    ok awesome, my fear of making git mistakes is still very real
  43. in src/private_broadcast.cpp:14 in 8554807072
     9 #include <algorithm>
    10 
    11 /// If a transaction is not received back from the network for this duration
    12 /// after it is broadcast, then we consider it stale / for rebroadcasting.
    13 static constexpr auto STALE_DURATION{1min};
    14+static constexpr auto INITIAL_STALE_DURATION{5min};
    


    vasild commented at 12:46 pm on March 24, 2026:

    Swap the order of these since INITIAL_STALE_DURATION is judged first and document it:

    0/// If a transaction is not sent to any peer for this duration,
    1/// then we consider it stale / for rebroadcasting.
    2static constexpr auto INITIAL_STALE_DURATION{5min};
    3
    4/// If a transaction is not received back from the network for this duration
    5/// after it is broadcast, then we consider it stale / for rebroadcasting.
    6static constexpr auto STALE_DURATION{1min};
    
  44. in src/test/private_broadcast_tests.cpp:113 in 8554807072
    108@@ -102,20 +109,23 @@ BOOST_AUTO_TEST_CASE(basic)
    109     const auto infos{pb.GetBroadcastInfo()};
    110     BOOST_CHECK_EQUAL(infos.size(), 2);
    111     {
    112-        const auto& [tx, peers]{find_tx_info(infos, tx_for_recipient1)};
    113+        const auto& tx_info{find_tx_info(infos, tx_for_recipient1)};
    114+        const auto& peers{tx_info.peers};
    


    vasild commented at 12:59 pm on March 24, 2026:

    Here and below, since tx is not used this can be done shorter:

    0const auto& peers{find_tx_info(infos, tx_for_recipient1).peers};
    

    feel free to ignore if you do not like it.


    Mccalabrese commented at 5:48 pm on March 24, 2026:
    Good call. Applied, thanks!
  45. in src/test/private_broadcast_tests.cpp:128 in 8554807072 outdated
    127 
    128-    BOOST_CHECK_EQUAL(pb.GetStale().size(), 1);
    129-    BOOST_CHECK_EQUAL(pb.GetStale()[0], tx_for_recipient2);
    130+    const auto stale_state{pb.GetStale()};
    131+    BOOST_CHECK_EQUAL(stale_state.size(), 1);
    132+    BOOST_CHECK_EQUAL(stale_state[0], tx_for_recipient2);
    


    vasild commented at 1:01 pm on March 24, 2026:
    This change is unnecessary, but I like it since it eliminates the possibility of GetStale() returning different results when called two times.
  46. in src/test/private_broadcast_tests.cpp:156 in 8554807072
    151+
    152+    // Unpicked transactions use the longer initial stale duration.
    153+    BOOST_CHECK_EQUAL(pb.GetStale().size(), 0);
    154+    SetMockTime(Now<NodeSeconds>() + 4min);
    155+    BOOST_CHECK_EQUAL(pb.GetStale().size(), 0);
    156+    SetMockTime(Now<NodeSeconds>() + 2min);
    


    vasild commented at 1:08 pm on March 24, 2026:

    This depends on the value of INITIAL_STALE_DURATION, so this test will break if the constant is adjusted. Put INITIAL_STALE_DURATION in the comment so it will show up when grepping the code. Or if you wish declare INITIAL_STALE_DURATION in private_broadcast.h and use it here like:

    0set mock now + INITIAL_STALE_DURATION - 1min
    1...
    2set mock now + 2min
    

    If you decide to do that, then the added + 6min earlier in this test would become + INITIAL_STALE_DURATION + 1min. And it would be better to also move STALE_DURATION to the header as well, so that both variables are declared next to each other.


    Mccalabrese commented at 5:50 pm on March 24, 2026:
    I did move the INITIAL_STALE_DURATION and STALE_DURATION to the header, I agree is will be clearer for future contributors.
  47. vasild approved
  48. vasild commented at 1:10 pm on March 24, 2026: contributor
    ACK 8554807072515468f7f64a7ab60d903deaff4306
  49. DrahtBot requested review from andrewtoth on Mar 24, 2026
  50. net: introduce TxSendStatus internal state container 999d18ab1c
  51. Mccalabrese force-pushed on Mar 24, 2026
  52. Mccalabrese commented at 5:40 pm on March 24, 2026: contributor
    @vaslid I believe everything is now good to go on this, please let me know if it all looks correct to you, build was successful and private_broadcast_tests ran without errors.
  53. DrahtBot removed the label CI failed on Mar 24, 2026
  54. net: delay stale evaluation and expose time_added in private broadcast 325afe664d
  55. Mccalabrese force-pushed on Mar 25, 2026
  56. andrewtoth approved
  57. andrewtoth commented at 11:58 pm on March 25, 2026: contributor
    ACK 325afe664d103bbdfd0758881f113aaa84f90627
  58. DrahtBot requested review from vasild on Mar 25, 2026
  59. vasild approved
  60. vasild commented at 10:49 am on March 27, 2026: contributor

    ACK 325afe664d103bbdfd0758881f113aaa84f90627

    Thanks!

  61. DrahtBot added the label CI failed on Mar 30, 2026
  62. DrahtBot removed the label CI failed on Mar 30, 2026
  63. achow101 commented at 0:27 am on April 4, 2026: member
    ACK 325afe664d103bbdfd0758881f113aaa84f90627
  64. achow101 merged this on Apr 4, 2026
  65. achow101 closed this on Apr 4, 2026

  66. Mccalabrese deleted the branch on Apr 4, 2026

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: 2026-04-12 15:13 UTC

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