p2p: refactor AlreadyHave(), CInv::type, INV/TX processing #19610

pull jonatack wants to merge 10 commits into bitcoin:master from jonatack:CInv-block-message-helpers changing 4 files +71 −71
  1. jonatack commented at 11:01 AM on July 28, 2020: contributor

    Building on #19590 and the recent wtxid and GenTxid changes, this is a refactoring and cleanup PR to simplify and improve some of the net processing code.

    Some of the diffs are best reviewed with -w to ignore spacing.

    Co-authored by John Newbery.

  2. jonatack cross-referenced this on Jul 28, 2020 from issue p2p: change `CInv::type` from `int` to `uint32_t`, fix UBSan warning by jonatack
  3. jonatack cross-referenced this on Jul 28, 2020 from issue p2p, refactor: add `CInv` transaction message helpers; use in net processing by jonatack
  4. DrahtBot added the label P2P on Jul 28, 2020
  5. DrahtBot commented at 5:03 PM on July 28, 2020: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

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

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #19753 (p2p: don't add AlreadyHave transactions to recentRejects by troygiorshev)
    • #19339 (validation: re-delegate absurd fee checking from mempool to clients by gzhao408)
    • #18261 (Erlay: bandwidth-efficient transaction relay protocol by naumenkogs)

    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.

  6. DrahtBot cross-referenced this on Jul 28, 2020 from issue Enable fetching of orphan parents from wtxid peers by sipa
  7. DrahtBot cross-referenced this on Jul 28, 2020 from issue Remove mempool global by MarcoFalke
  8. DrahtBot cross-referenced this on Jul 29, 2020 from issue [RFC] Package-relay: sender-initiated by ariard
  9. DrahtBot cross-referenced this on Jul 30, 2020 from issue Disable fee estimation in blocksonly mode (by removing the fee estimates global) by darosior
  10. laanwj referenced this in commit 17de75b028 on Jul 30, 2020
  11. jonatack force-pushed on Jul 30, 2020
  12. sidhujag referenced this in commit fc7030c8fe on Jul 31, 2020
  13. DrahtBot added the label Needs rebase on Jul 31, 2020
  14. in src/protocol.h:426 in 517152034a outdated
     443 |      bool IsMsgWitnessTx() const { return type == MSG_WITNESS_TX; }
     444 | +    bool IsMsgFilteredWitnessBlk() const { return type == MSG_FILTERED_WITNESS_BLOCK; }
     445 |  
     446 |      // Combined-message helper methods
     447 | -    bool IsGenTxMsg()     const { return type == MSG_TX || type == MSG_WTX || type == MSG_WITNESS_TX; }
     448 | +    bool IsGenTxMsg() const { return type == MSG_TX || type == MSG_WTX || type == MSG_WITNESS_TX; }
    


    fjahr commented at 7:52 PM on August 1, 2020:

    Could re-use the helper methods from above here in the combined helpers


    jonatack commented at 1:41 PM on August 3, 2020:

    Thanks Fabian. I mulled on doing this, but I think I'd rather keep the method definitions independent.

  15. fjahr commented at 8:02 PM on August 1, 2020: contributor

    Code review ACK 82c92976792f9c9c11b38cb0092f18728c97e64e

  16. jnewbery commented at 4:09 PM on August 3, 2020: member

    I think a better approach than the changes within AlreadyHave() is to split that function up into AlreadyHaveTx() and AlreadyHaveBlock() since those are two totally separate concepts. @jonatack would you be interested in taking the commits from here: https://github.com/jnewbery/bitcoin/commits/2020-07-split-already-have? (see also #19569 (review))

  17. in src/protocol.h:447 in 82c9297679 outdated
     445 |  
     446 |      // Combined-message helper methods
     447 | -    bool IsGenTxMsg()     const { return type == MSG_TX || type == MSG_WTX || type == MSG_WITNESS_TX; }
     448 | +    bool IsGenTxMsg() const { return type == MSG_TX || type == MSG_WTX || type == MSG_WITNESS_TX; }
     449 | +    bool IsMsgBlkOrMsgWitnessBlk() const { return type == MSG_BLOCK || type == MSG_WITNESS_BLOCK; }
     450 | +    bool IsGenBlkMsg() const { return type == MSG_BLOCK || type == MSG_FILTERED_BLOCK || type == MSG_CMPCT_BLOCK || type == MSG_WITNESS_BLOCK; }
    


    jnewbery commented at 4:25 PM on August 4, 2020:

    Should this contain MSG_FILTERED_WITNESS_BLOCK?


    jonatack commented at 2:02 PM on August 8, 2020:

    Well-spotted; added it in the rebase for now. The one place this helper is currently used (https://github.com/bitcoin/bitcoin/pull/19610/files#diff-eff7adeaec73a769788bb78858815c91L1763) did not include MSG_FILTERED_WITNESS_BLOCK, so we'd need to be sure including it doesn't change behavior (or was an oversight).


    jnewbery commented at 11:28 AM on August 11, 2020:

    Oh, it looks like MSG_FILTERED_WITNESS_BLOCK isn't actually used and is 'reserved for future use': https://github.com/bitcoin/bips/blob/master/bip-0144.mediawiki#relay. I recommend removing it from here, removing IsMsgFilteredWitnessBlk() (since it's not used anywhere) and adding a comment to MSG_FILTERED_WITNESS_BLOCK saying it's unused (I think we could even comment out the value since it's unused in the code).

  18. jnewbery commented at 4:25 PM on August 4, 2020: member

    Concept ACK. Needs rebase.

  19. jonatack renamed this:
    p2p, refactor: add `CInv` block message helpers; use in net processing
    p2p: refactor AlreadyHave(), CInv::type and inv/tx processing
    on Aug 8, 2020
  20. jonatack renamed this:
    p2p: refactor AlreadyHave(), CInv::type and inv/tx processing
    p2p: refactor AlreadyHave(), CInv::type, INV/TX processing
    on Aug 8, 2020
  21. jonatack force-pushed on Aug 8, 2020
  22. jonatack force-pushed on Aug 8, 2020
  23. jonatack commented at 2:12 PM on August 8, 2020: contributor

    Rebased with four commits pulled in from @jnewbery's https://github.com/bitcoin/bitcoin/compare/master...jnewbery:2020-07-split-already-have branch as well as the changes in #19611.

  24. DrahtBot removed the label Needs rebase on Aug 8, 2020
  25. DrahtBot cross-referenced this on Aug 8, 2020 from issue Deduplicate parent txid loop of requested transactions and missing parents of orphan transactions by sdaftuar
  26. DrahtBot cross-referenced this on Aug 8, 2020 from issue validation: re-delegate absurd fee checking from mempool to clients by glozow
  27. DrahtBot cross-referenced this on Aug 8, 2020 from issue refactor: Replace RecursiveMutex with Mutex in CTxMemPool by hebasto
  28. laanwj commented at 6:49 PM on August 9, 2020: member

    I think a better approach than the changes within AlreadyHave() is to split that function up into AlreadyHaveTx() and AlreadyHaveBlock() since those are two totally separate concepts.

    Great idea. The block and transaction paths have drifted apart so much that it no longer makes sense to have a common function there.

  29. in src/protocol.h:403 in 4789cff6f6 outdated
     399 | @@ -400,9 +400,11 @@ enum GetDataMsg : uint32_t {
     400 |  /** inv message data */
     401 |  class CInv
     402 |  {
     403 | +private:
    


    laanwj commented at 6:52 PM on August 9, 2020:

    Personally I don't really know if it makes sense for "plain-data" network message types like CInv to have private fields. I see it more as a struct with helper methods than really an encapsulated object with functionality.


    jonatack commented at 3:55 AM on August 10, 2020:

    Oh ok, in that case we may want to make CInv a struct to avoid confusion. As a class, it looked like encapsulation was desirable, and only one place in the code was still accessing CInv::type from the outside so it was easy to make it private.


    jonatack commented at 10:59 AM on August 11, 2020:

    Er, renaming CInv to Inv to make it a struct would be too much noise. Done, dropped the last commit "p2p: refactor CInv::type from public to private class member".

  30. bitcoin deleted a comment on Aug 10, 2020
  31. DrahtBot added the label Needs rebase on Aug 10, 2020
  32. jonatack force-pushed on Aug 10, 2020
  33. DrahtBot removed the label Needs rebase on Aug 10, 2020
  34. jonatack force-pushed on Aug 11, 2020
  35. in src/protocol.h:422 in 806f12427d outdated
     411 | @@ -412,14 +412,22 @@ class CInv
     412 |      std::string ToString() const;
     413 |  
     414 |      // Single-message helper methods
     415 | -    bool IsMsgTx()        const { return type == MSG_TX; }
     416 | -    bool IsMsgWtx()       const { return type == MSG_WTX; }
     417 | +    bool IsUndefined() const { return type == UNDEFINED; }
     418 | +    bool IsMsgTx() const { return type == MSG_TX; }
     419 | +    bool IsMsgBlk() const { return type == MSG_BLOCK; }
    


    vasild commented at 11:00 AM on August 11, 2020:

    [continuing conversation from another PR]

    nit: s/Blk/Block/

    $ git grep -i blk src |wc -l
         108
    $ git grep -i block src |wc -l
       11014
    

    jonatack commented at 2:40 PM on August 19, 2020:

    I prefer to pass on this for these reasons: (a) to maintain a consistent naming style in CInv (tx, blk) (b) block would make the methods names longer (c) blk is an established abbreviation: in the codebase, block data files called blk*.dat, etc. (d) the shorter blk seems more consistent with the abbreviated naming style in use in recent PRs like #19569

  36. in src/protocol.h:426 in 806f12427d outdated
     424 |      bool IsMsgWitnessTx() const { return type == MSG_WITNESS_TX; }
     425 | +    bool IsMsgFilteredWitnessBlk() const { return type == MSG_FILTERED_WITNESS_BLOCK; }
     426 |  
     427 |      // Combined-message helper methods
     428 | -    bool IsGenTxMsg()     const { return type == MSG_TX || type == MSG_WTX || type == MSG_WITNESS_TX; }
     429 | +    bool IsGenTxMsg() const { return type == MSG_TX || type == MSG_WTX || type == MSG_WITNESS_TX; }
    


    jnewbery commented at 11:11 AM on August 11, 2020:

    Did you consider making these switch statements without defaults, so that the compiler warns if a new type is added to the enum but not here?


    jonatack commented at 3:57 PM on August 19, 2020:

    Did you consider making these switch statements without defaults, so that the compiler warns if a new type is added to the enum but not here?

    I think that requires turning enum GetDataMsg into an enum class, and changing the enum type from int to GetDataMsg would break the remaining five GetDataMsg::MSG_BLOCK/MSG_WTX | GetFetchFlags(pfrom) operations.

  37. jnewbery commented at 11:30 AM on August 11, 2020: member

    One inline comment. A couple more suggestions:

    • squash the p2p: remove unused nFetchFlags from NetMsgType::INV processing and p2p: remove unused nFetchFlags from NetMsgType::TX processing commits
    • remove the p2p: change CInv::type from int to uint32_t commit. I don't think it's useful here.
  38. in src/net_processing.cpp:1759 in 806f12427d outdated
    1760 | @@ -1771,7 +1761,7 @@ void static ProcessGetData(CNode& pfrom, const CChainParams& chainparams, CConnm
    1761 |      // expensive to process.
    1762 |      if (it != pfrom.vRecvGetData.end() && !pfrom.fPauseSend) {
    1763 |          const CInv &inv = *it++;
    1764 | -        if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK) {
    1765 | +        if (inv.IsGenBlkMsg()) {
    


    vasild commented at 11:44 AM on August 11, 2020:

    Could this result in unintended behavioral change?

    Previously if inv.type == MSG_FILTERED_WITNESS_BLOCK we would not execute the code inside this if block. With this patch we would execute it.

    Related to #19610 (review)


    jonatack commented at 2:20 PM on August 19, 2020:

    That message type is unused (see #19610 (review)) and has since been commented out by #19721 (merged).


    jonatack commented at 2:21 PM on August 19, 2020:

    Removing it in the rebase.

  39. vasild commented at 11:52 AM on August 11, 2020: contributor

    In commits p2p: remove unused nFetchFlags from NetMsgType::(TX|INV) processing - why would it be unused? It looks used to me. Maybe I am missing something obscure? If that is the case, then maybe the commit messages warrant some more elaborate explanations?

    The change

    -            } else {
    +            } else if (inv.IsGenTxMsg()) {
    

    does not belong to commit [net processing] Remove mempool argument from AlreadyHaveBlock()

  40. jnewbery commented at 1:49 PM on August 11, 2020: member

    In commits p2p: remove unused nFetchFlags from NetMsgType::(TX|INV) processing - why would it be unused? It looks used to me. Maybe I am missing something obscure? If that is the case, then maybe the commit messages warrant some more elaborate explanations?

    Because GetFetchFlags() can only add the MSG_WITNESS_FLAG, which is added to the CInv.type field. That CInv only passed to AlreadyHave() or ToGenTxid() and neither of those functions do anything different depending on whether the CInv type is MSG_TX MSG_WITNESS_TX`. I agree that the git commit log could be expanded to explain this.

    The change [...] does not belong to commit [net processing] Remove mempool argument from AlreadyHaveBlock()

    It's an important change. See #19569 (review) for context. Perhaps it should be split into a separate commit or have a commit log explaining why it's needed.

  41. jonatack cross-referenced this on Aug 15, 2020 from issue p2p: comment out unused MSG_FILTERED_WITNESS_BLOCK by adamjonas
  42. jnewbery commented at 9:26 AM on August 18, 2020: member

    Needs rebase now that #19721 is merged.

  43. jnewbery commented at 7:47 AM on August 20, 2020: member

    Still awaiting rebase

  44. jnewbery added the label Needs rebase on Aug 20, 2020
  45. DrahtBot removed the label Needs rebase on Aug 20, 2020
  46. jonatack force-pushed on Aug 20, 2020
  47. jonatack commented at 11:47 AM on August 20, 2020: contributor
    • rebased and updated
    • dropped the change CInv::type from int to uint32_t commit
    • squashed the 2 commits remove unused nFetchFlags from NetMsgType::INV processing and remove unused nFetchFlags from NetMsgType::TX processing and added explanatory notes to the commit message
    • split the IsGenMsgTx() conditional check to a separate commit, ensure inv is GenMsgTx before ToGenTxid in inv processing, and added explanatory notes in the commit message
  48. DrahtBot cross-referenced this on Aug 20, 2020 from issue p2p: don't add AlreadyHave transactions to recentRejects by troygiorshev
  49. DrahtBot cross-referenced this on Aug 20, 2020 from issue Net processing: move ProcessMessage() to PeerLogicValidation by jnewbery
  50. jnewbery commented at 11:02 AM on August 21, 2020: member

    utACK 1723d0025b744f12b853935aead9acd9f8f32b42

    The last commit is tagged as authored by and co-authored by me. I don't think we need both tags!

  51. jonatack force-pushed on Aug 21, 2020
  52. jonatack commented at 2:23 PM on August 21, 2020: contributor

    utACK 1723d00

    The last commit is tagged as authored by and co-authored by me. I don't think we need both tags!

    Fixed

  53. in src/net_processing.cpp:2675 in 2a2ee28a2a outdated
    2680 | @@ -2697,14 +2681,18 @@ void ProcessMessage(
    2681 |                      // then fetch the blocks we need to catch up.
    2682 |                      best_block = &inv.hash;
    2683 |                  }
    2684 | -            } else {
    2685 | +            } else if (inv.IsGenTxMsg()) {
    


    jnewbery commented at 3:51 PM on August 21, 2020:

    Perhaps we should add an else branch after this that logs that an unknown INV type was received. Previously we'd log it as AlreadyHave.


    jonatack commented at 8:58 AM on August 22, 2020:

    Done in the same commit and updated the commit message, and proposed a commit to move the code immediately after, which sends and logs a GETHEADERS message, into the inv.IsMsgBlk() section where it's used.


    jnewbery commented at 9:36 AM on August 22, 2020:

    proposed a commit to move the code immediately after, which sends and logs a GETHEADERS message, into the inv.IsMsgBlk() section where it's used.

    This is a (bad) behaviour change. It means if a peer sends us an inv message with multiple MSG_BLOCK items, we'll send a getheaders for each of those items instead of just the last one. Each getheaders contains a locator, so this could result in a lot of bandwidth usage.


    jonatack commented at 2:22 PM on August 22, 2020:

    Oops, I misread the nesting level and the tests didn't mind. Thank you! Dropped the commit, and fixed up the bracket as follows in 3b79249dbd45b43

    -        for (CInv &inv : vInv)
    -        {
    +        for (CInv& inv : vInv) {
    
  54. jnewbery commented at 3:53 PM on August 21, 2020: member

    utACK 2a2ee28a2ac2d2cc11ad8a44b036bb4218c13d9a

    One suggestion for improving logging inline.

  55. jonatack force-pushed on Aug 22, 2020
  56. jonatack force-pushed on Aug 22, 2020
  57. jonatack commented at 2:23 PM on August 22, 2020: contributor

    Thanks @jnewbery! Updated per git diff 2a2ee28 25176e6.

  58. jnewbery commented at 9:28 AM on August 24, 2020: member

    utACK 25176e62d0e60a3bca92aa758f935ee782531f75

  59. DrahtBot added the label Needs rebase on Aug 24, 2020
  60. jonatack force-pushed on Aug 24, 2020
  61. jonatack commented at 4:34 PM on August 24, 2020: contributor

    Rebased.

    git range-diff 7f609f6 25176e6 ac0617d

  62. DrahtBot removed the label Needs rebase on Aug 24, 2020
  63. jnewbery commented at 10:51 AM on August 25, 2020: member

    utACK ac0617d3718ac3e330a70b30ef1edd8a46faaf9a

    Verified the range-diff locally

  64. in src/protocol.h:421 in ac0617d371 outdated
     417 | @@ -418,12 +418,24 @@ class CInv
     418 |      std::string ToString() const;
     419 |  
     420 |      // Single-message helper methods
     421 | -    bool IsMsgTx()        const { return type == MSG_TX; }
     422 | -    bool IsMsgWtx()       const { return type == MSG_WTX; }
     423 | +    bool IsUndefined() const { return type == UNDEFINED; }
    


    vasild commented at 12:39 PM on August 25, 2020:

    nit: the methods IsUndefined() and IsMsgWitnessTx() are unused.


    jonatack commented at 9:54 AM on August 26, 2020:

    Good point; probably worth removing them for now.


    jonatack commented at 10:06 AM on August 26, 2020:

    Done

  65. vasild approved
  66. vasild commented at 12:41 PM on August 25, 2020: contributor

    ACK ac0617d37

    Looks good, compiled locally and ran tests.

  67. Crypt-iQ commented at 3:44 PM on August 25, 2020: contributor

    I think the CInv::type change from int to uint32_t change should be added back (not necessarily the private qualifier). While fuzzing it brings up the following complaint https://github.com/bitcoin/bitcoin/issues/19678

  68. jonatack commented at 8:35 PM on August 25, 2020: contributor

    Thank you @Crypt-iQ, if you have the chance, would you please let me know if 533a9a0d2c307330497e61d5af4229c924db8372 resolves the issue on your side?

  69. Crypt-iQ commented at 6:29 AM on August 26, 2020: contributor

    @jonatack That UBSan complaint goes away with this patch 👍 Tested on macOS v10.15.4, brew-installed clang.

  70. vasild approved
  71. vasild commented at 7:53 AM on August 26, 2020: contributor

    ACK 533a9a0d2

    I can't make sense of the CI failure (and tell whether it is related to this PR).

  72. jonatack commented at 9:51 AM on August 26, 2020: contributor

    Thanks @Crypt-iQ and @vasild.

    The CI failed on the wallet_import_rescan functional test and should be unrelated. Restarted; it's now green.

  73. p2p: add CInv block message helper methods 471714e1f0
  74. p2p: remove nFetchFlags from NetMsgType TX and INV processing
    The nFetchFlags code can be removed here because GetFetchFlags() can only add
    the MSG_WITNESS_FLAG, which is added to the CInv::type field. That CInv is only
    passed to AlreadyHave() or ToGenTxid(), and neither of those functions do
    anything different depending on whether the CInv type is MSG_TX or
    MSG_WITNESS_TX.
    
    Co-authored by: John Newbery <john@johnnewbery.com>
    39f1dc9445
  75. [net processing] Split AlreadyHave() into separate block and tx functions 42ca5618ca
  76. [net processing] Remove mempool argument from AlreadyHaveBlock() 430e183b89
  77. [net processing] Change AlreadyHaveBlock() to take block_hash argument 5fdfb80b86
  78. [net processing] Change AlreadyHaveTx() to take a GenTxid acd6642167
  79. p2p: use CInv block message helpers in net_processing.cpp b1c855453b
  80. p2p: make gtxid(.hash) and fAlreadyHave localvars const 24ee4f01ea
  81. test: use CInv::MSG_WITNESS_TX flag in p2p_segwit aa3621385e
  82. p2p: ensure inv is GenMsgTx before ToGenTxid in inv processing
    and otherwise log that an unknown INV type was received.
    
    In INV processing, when handling transaction type inv messages,
    ToGenTxid() expects that we constructed the CInv ourselves or
    that we verified that it is for a transaction type CInv.
    
    Therefore, change this `else` branch into an `else if (inv.GenMsgTx())`
    to make this safer and log any INVs that fall through.
    fb56d37612
  83. jonatack force-pushed on Aug 26, 2020
  84. jonatack commented at 10:07 AM on August 26, 2020: contributor

    Removed the unused helper methods from the first commit per git diff 533a9a0 125468a

  85. vasild approved
  86. vasild commented at 1:14 PM on August 26, 2020: contributor

    ACK 125468af9

  87. jnewbery commented at 3:32 PM on August 26, 2020: member

    I think the CInv::type change from int to uint32_t change should be added back (not necessarily the private qualifier). While fuzzing it brings up the following complaint #19678

    Why? This seems unrelated to this PR (the UBSan warning happens before this PR). Changing the type of CInv.type (which is serialized/deserialized for network messages) is a very different change from the internal refactors in this PR. Can you save it for a different PR?

  88. Crypt-iQ commented at 4:18 PM on August 26, 2020: contributor

    @jnewbery I thought since it was already added previously, it would fit into the scope of the PR. No problem to be added into another PR, either I or somebody else could do that.

  89. jonatack force-pushed on Aug 27, 2020
  90. jonatack commented at 9:14 AM on August 27, 2020: contributor

    Dropped the CInv::type type change commit in favor of #19818. Thanks everyone.

  91. jonatack cross-referenced this on Aug 27, 2020 from issue p2p: change `CInv::type` from `int` to `uint32_t`, fix UBSan warning by jonatack
  92. laanwj commented at 1:10 PM on August 27, 2020: member

    Code review ACK fb56d37612dea6666e7da73d671311a697570dae Agree it's better to split out the P2P change.

  93. DrahtBot cross-referenced this on Aug 27, 2020 from issue Erlay: bandwidth-efficient transaction relay protocol by naumenkogs
  94. jnewbery commented at 8:50 AM on September 1, 2020: member

    utACK fb56d37612dea6666e7da73d671311a697570dae

  95. vasild approved
  96. vasild commented at 9:04 AM on September 2, 2020: contributor

    ACK fb56d3761

  97. laanwj merged this on Sep 2, 2020
  98. laanwj closed this on Sep 2, 2020

  99. jonatack deleted the branch on Sep 2, 2020
  100. laanwj referenced this in commit bd60a9a8ed on Sep 3, 2020
  101. sidhujag referenced this in commit 98e01510c0 on Sep 3, 2020
  102. sidhujag referenced this in commit addddc0053 on Sep 3, 2020
  103. jnewbery cross-referenced this on Nov 5, 2020 from issue Backport wtxid orphan fetch to v0.20 by jnewbery
  104. Fabcien referenced this in commit 8f921089e9 on May 12, 2021
  105. Fabcien referenced this in commit 1a19225299 on May 12, 2021
  106. Fabcien referenced this in commit 63d5b8764b on May 12, 2021
  107. Fabcien referenced this in commit 216399a4b4 on May 12, 2021
  108. Fabcien referenced this in commit 2960f55f44 on May 12, 2021
  109. Fabcien referenced this in commit 0f5b42a26d on May 12, 2021
  110. Fabcien referenced this in commit 0f0d105ce2 on May 12, 2021
  111. Fabcien referenced this in commit 25996e7e23 on May 12, 2021
  112. bitcoin locked this on Feb 15, 2022

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-05-19 06:53 UTC