banman: save the banlist in a JSON format on disk #20966

pull vasild wants to merge 4 commits into bitcoin:master from vasild:json_bans changing 10 files +193 −49
  1. vasild commented at 7:05 pm on January 19, 2021: member

    Save the banlist in banlist.json instead of banlist.dat.

    This makes it possible to store Tor v3 entries in the banlist on disk (and any other addresses that cannot be serialized in addrv1 format).

    Only read banlist.dat if it exists and banlist.json does not exist (first start after an upgrade).

    Supersedes #20904 Resolves #19748

  2. Dusto-beep changes_requested
  3. Dusto-beep commented at 7:34 pm on January 19, 2021: none
    (off topic reply deleted)
  4. DrahtBot added the label Docs on Jan 19, 2021
  5. DrahtBot added the label Tests on Jan 19, 2021
  6. laanwj added the label P2P on Jan 19, 2021
  7. DrahtBot commented at 5:29 am on January 20, 2021: member

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

    Conflicts

    No conflicts as of last run.

  8. theStack commented at 10:16 am on January 20, 2021: member
    Concept ACK
  9. MarcoFalke removed the label Docs on Jan 20, 2021
  10. MarcoFalke removed the label Tests on Jan 20, 2021
  11. vasild commented at 10:57 am on January 20, 2021: member
    790727eea..fd751850e: add the tests from #20904 (closed without merge), they are precious
  12. vasild commented at 11:30 am on January 20, 2021: member
    fd751850e..2d287f639: add release notes
  13. in doc/release-notes.md:119 in 2d287f639a outdated
    110+
    111+- The list of banned hosts and networks (via `setban` RPC) is now saved on disk
    112+  in JSON format in `banlist.json` instead of `banlist.dat`. Both files are
    113+  read on startup and contents merged. Updateds are only written to the new
    114+  `banlist.json`. A future version of Bitcoin Core may completely ignore
    115+  `banlist.dat`. (#20966)
    


    MarcoFalke commented at 12:10 pm on January 20, 2021:

    Please add the release notes to https://github.com/bitcoin-core/bitcoin-devwiki/wiki/%2320852-Release-notes-snippet

    Otherwise they will have to be removed here after merge, then added to the 0.21 branch. (Release notes are only added to the point releases, not the next major version that also includes those fixes)


    vasild commented at 12:49 pm on January 20, 2021:
    I guess after/if this PR is merged?
  14. MarcoFalke added the label Needs backport (0.21) on Jan 20, 2021
  15. MarcoFalke added this to the milestone 0.21.1 on Jan 20, 2021
  16. laanwj commented at 12:37 pm on January 20, 2021: member
    Concept ACK
  17. vasild commented at 12:53 pm on January 20, 2021: member
    2d287f639…fd751850e: remove edits of doc/release-notes.md
  18. vasild force-pushed on Jan 20, 2021
  19. vasild commented at 1:01 pm on January 20, 2021: member

    Notes:

    • I guess it would be possible to write the ban list to settings.json instead of in banlist.json (as originally proposed in #19748), but the periodic dump may create some complications and also it is unclear what to do if we are running with -nosettings.

    • The current patch would write banlist.json on the first change of the ban list. This means that a node that upgrades from before this PR with only banlist.dat on disk may not create banlist.json for some time. I think this is ok.

  20. promag commented at 12:30 pm on January 27, 2021: member

    Concept ACK.

    Should try to read JSON first, and if it exists then skip old format? Or am I missing something?

    Personally I wouldn’t add from/to JSON in CBanEntry, BanMapFromJson and BanMapToJson seem enough. Also, there should be some validation in BanMapFromJson?

  21. jonatack commented at 12:46 pm on January 27, 2021: member

    Concept ACK

    790727e..fd75185: add the tests from #20904 (closed without merge), they are precious

    Good to see these tests brought in.

  22. vasild force-pushed on Jan 28, 2021
  23. vasild commented at 1:39 pm on January 28, 2021: member

    fd751850e…e74196741: read banlist.dat only if banlist.json is not present and catch all exceptions that could come from an invalid JSON. @promag

    Should try to read JSON first, and if it exists then skip old format?

    I made it to read both if present so that if the user temporary downgrades and makes some changes to banlist.dat those changes would be picked up by the new version. However this has the deficiency that if an entry is deleted from banlist.json it will keep being loaded from banlist.dat. Changed to what you suggest above.

    Personally I wouldn’t add from/to JSON in CBanEntry, BanMapFromJson and BanMapToJson seem enough.

    CBanEntry being able to ser/unser itself to/from JSON improves its encapsulation.

    Also, there should be some validation in BanMapFromJson?

    Right! Added try/catch to handle invalid JSONs.

  24. MarcoFalke commented at 2:20 pm on February 15, 2021: member
    needs rebase after #21167
  25. MarcoFalke added the label Needs rebase on Feb 15, 2021
  26. vasild force-pushed on Feb 15, 2021
  27. vasild commented at 2:54 pm on February 15, 2021: member
    e74196741...7493bbe38: rebase due to conflicts
  28. DrahtBot removed the label Needs rebase on Feb 15, 2021
  29. fanquake deleted a comment on Feb 16, 2021
  30. DrahtBot added the label Needs rebase on Feb 23, 2021
  31. vasild force-pushed on Mar 10, 2021
  32. vasild commented at 4:12 pm on March 10, 2021: member
    7493bbe38...1310b5460: rebase due to conflicts
  33. MarcoFalke commented at 4:20 pm on March 10, 2021: member
    So many rebases. I hope the backport won’t be too nasty
  34. DrahtBot removed the label Needs rebase on Mar 10, 2021
  35. DrahtBot added the label Needs rebase on Mar 12, 2021
  36. vasild force-pushed on Mar 15, 2021
  37. vasild commented at 3:27 pm on March 15, 2021: member
    1310b5460...469da8ca3: rebase due to conflicts
  38. DrahtBot removed the label Needs rebase on Mar 15, 2021
  39. in src/addrdb.cpp:48 in 8372aad9cc outdated
    43+ * passing to `BanMapFromJson()`.
    44+ */
    45+UniValue BanMapToJson(const banmap_t& bans)
    46+{
    47+    UniValue bans_json(UniValue::VARR);
    48+    for (const auto& [address, ban_entry] : bans) {
    


    MarcoFalke commented at 8:45 am on March 20, 2021:

    8372aad9cc01774e981edee1dd8822a31ef6ea18

    is the goal still to backport this? If yes, it could make sense to use C++11


    vasild commented at 3:37 pm on March 24, 2021:

    My understanding is that this deserves a backport because it would allow storing torv3 addresses in the banlist on disk. Not super important though.

    Switched to C++11.

  40. in src/addrdb.cpp:69 in 8372aad9cc outdated
    64+{
    65+    for (const auto& ban_entry_json : bans_json.getValues()) {
    66+        CSubNet subnet;
    67+        const auto& subnet_str = ban_entry_json["address"].get_str();
    68+        if (!LookupSubNet(subnet_str, subnet)) {
    69+            return error("%s: Cannot parse banned address or subnet: %s", __func__, subnet_str);
    


    MarcoFalke commented at 8:50 am on March 20, 2021:

    8372aad9cc01774e981edee1dd8822a31ef6ea18

    • Any reason to prepend the function name? This is already done by -logfunctionnames
    • Any reason to use error here, but std::runtime_error if a field is missing?
    • It would be better to leave the logging to the caller and not mix logging responsibility between a utility function and the caller object

    vasild commented at 3:39 pm on March 24, 2021:

    You probably mean -logsourcelocations instead of -logfunctionnames.

    Removed __func__. Changed this function to throw an exception for all errors and return void. Now it does not do any logging - just bubble up a detailed error message to the callers via the exception.

  41. in src/addrdb.cpp:192 in 8372aad9cc outdated
    189+    if (util::WriteSettings(m_banlist_json, {{JSON_KEY, BanMapToJson(banSet)}}, errors)) {
    190+        return true;
    191+    }
    192+
    193+    for (const auto& err : errors) {
    194+        error("%s: %s", __func__, err);
    


    MarcoFalke commented at 9:00 am on March 20, 2021:

    same commit:

    • It seems the caller ignores the return code, so calling this ERROR seems confusing
    • No longer need __func__

    vasild commented at 3:42 pm on March 24, 2021:

    The caller of CBanDB::Write() does not ignore its return code? If you mean the return code of error() itself - this is because we want to log all of the errors (possibly more than one) before returning false.

    Removed __func__.

  42. in src/addrdb.cpp:208 in 8372aad9cc outdated
    206+    std::map<std::string, util::SettingsValue> settings;
    207+    std::vector<std::string> errors;
    208+
    209+    if (!util::ReadSettings(m_banlist_json, settings, errors)) {
    210+        for (const auto& err : errors) {
    211+            error("%s: %s", __func__, err);
    


    MarcoFalke commented at 9:03 am on March 20, 2021:

    same commit:

    • The caller will be “Recreating the banlist database”, so calling this ERROR seems confusing
    • __func__ ;)

    vasild commented at 3:44 pm on March 24, 2021:

    I think it is ok to see something like the following in the log:

    0ERROR: ... banlist foo ...
    1ERROR: ... banlist bar ...
    2Recreating the banlist database
    

    Removed __func__.


    MarcoFalke commented at 10:03 am on April 17, 2021:
    User will report bugs if they see ERROR. For example, http://www.erisian.com.au/bitcoin-core-dev/log-2021-02-13.html#l-296

    vasild commented at 3:05 pm on April 19, 2021:

    I changed it to avoid the word “error”. I don’t think those errors should be hushed - those are errors from loading the file and we already checked that the file exists. So may include something like “json parse error”, “io error”, “permission denied”.

    In general I don’t think we should avoid using “error” or use it just for cases where we have detected a bug in the program. Messages like “error: permission denied” or “error: connection reset” are to be expected.


    MarcoFalke commented at 7:00 pm on April 19, 2021:
    :facepalm: Oh wait, I might have missed that this is actually an error. Then it is fine to mention the word “error”

    vasild commented at 3:59 pm on April 20, 2021:
    I am leaving it as is, I don’t like error() too much.
  43. in src/addrdb.cpp:216 in 8372aad9cc outdated
    214+    }
    215+
    216+    try {
    217+        return BanMapFromJson(settings[JSON_KEY], banSet);
    218+    } catch (const std::runtime_error& e) {
    219+        return error("%s: Cannot parse %s: %s", __func__, m_banlist_json.string(), e.what());
    


    MarcoFalke commented at 9:03 am on March 20, 2021:
    same ;)

    vasild commented at 3:44 pm on March 24, 2021:
    Removed __func__.
  44. in src/test/fuzz/banman.cpp:38 in 8372aad9cc outdated
    33@@ -34,8 +34,9 @@ FUZZ_TARGET_INIT(banman, initialize_banman)
    34 {
    35     FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
    36     SetMockTime(ConsumeTime(fuzzed_data_provider));
    37-    const fs::path banlist_file = GetDataDir() / "fuzzed_banlist.dat";
    38-    fs::remove(banlist_file);
    39+    const fs::path banlist_file = GetDataDir() / "fuzzed_banlist";
    40+    fs::remove(banlist_file.string() + ".dat");
    


    MarcoFalke commented at 9:11 am on March 20, 2021:

    same commit:

    I don’t understand this line. There shouldn’t be any reason this instance will ever write a banlist.dat file.

    Also, to make the fuzz test more useful, it should probably create a banlist.dat file. Extra points for creating a corrupted one. With extra bonus points for passing one in without disk access.


    vasild commented at 3:47 pm on March 24, 2021:
    Right, removed the fs::remove calls and replaced them with a creation of a corrupted or inaccessible banlist file.
  45. MarcoFalke approved
  46. MarcoFalke commented at 9:28 am on March 20, 2021: member

    review ACK 469da8ca3f566041972a87e847e7ecf44ef2306b 🎐

    Signature:

     0-----BEGIN PGP SIGNED MESSAGE-----
     1Hash: SHA512
     2
     3review ACK 469da8ca3f566041972a87e847e7ecf44ef2306b 🎐
     4-----BEGIN PGP SIGNATURE-----
     5
     6iQGzBAEBCgAdFiEE+rVPoUahrI9sLGYTzit1aX5ppUgFAlwqrYAACgkQzit1aX5p
     7pUiXDwv9E2HLRr4rmJmOP/mO+47lhG6gEcqx6EAYuIjlBklEnDK9PhDo43/t3AzN
     8suyNHfsJ9mGGD/QXoZxUYge2dfqaH1U1y/BdrlVZw02r/HXqvOGzJ4YMjV4ysw04
     9vVh0U1osD7w3ypYLR0BhcAj4SVR1NDT3SYMKf8qlfPFtXfrn6W9i0kG6siwdV6ZE
    10mw/IqbisqDq92azMw+26z07Mu7uXv4Z1zWiz0qD7KBsJ/pinDdXCNi45gde7G091
    119tMF7KOJYyv/6uY/ibXoq/MYSDvkwye0SgGkNh6kku1MBjscBZG2KF/B6YIZMOn7
    122GTl+vRyUWMmsmANB7ELHyJs0wlh30bNGpjUErWfYwH4fs7KKLiQfvohms3UFOI4
    13oLeAsqpDo8HX8X0SzMmIOib/bhg6+X5yxIJW113pLfuohc35bBKEHATvZlzmYjSZ
    14lXFVLKY38YKsbxCBoXr3ISL7OeKKn6AwFVFZLjA8BlbogASWI9bunmJyHuC/o1n2
    15n2ha9e1V
    16=sn/g
    17-----END PGP SIGNATURE-----
    

    Timestamp of file with hash 4f0238a21835acd2f6c1095be832c4be7733461a6b3f0bf1498f5bab2c98df7d -

  47. vasild force-pushed on Mar 24, 2021
  48. vasild commented at 3:35 pm on March 24, 2021: member
    469da8ca3...4efa5e73b: address review suggestions, remove the release notes that were removed before but sneaked back in one of the updates
  49. DrahtBot added the label Needs rebase on Mar 30, 2021
  50. MarcoFalke commented at 5:41 pm on April 2, 2021: member
    @vasild I’ve split up the unit tests from this pull, since they are unrelated to changes here (They are checking functionality that already works just fine on master). See #21571. The benefit is that this pull can focus on the disk serialization changes only. I hope you don’t mind and maybe can leave a (Concept) (N)ACK.
  51. MarcoFalke commented at 8:27 am on April 6, 2021: member
    Rebase should be easy now git rebase --skip
  52. MarcoFalke referenced this in commit 1a7dec77f6 on Apr 6, 2021
  53. vasild force-pushed on Apr 6, 2021
  54. vasild force-pushed on Apr 6, 2021
  55. vasild commented at 9:00 am on April 6, 2021: member
    4efa5e7...03239a3ab: rebase due to conflicts, remove 3 commits from this PR that were already merged via #21571.
  56. DrahtBot removed the label Needs rebase on Apr 6, 2021
  57. sidhujag referenced this in commit 2be4116d32 on Apr 6, 2021
  58. laanwj removed this from the milestone 0.21.1 on Apr 16, 2021
  59. laanwj added this to the milestone 0.21.2 on Apr 16, 2021
  60. laanwj commented at 5:15 am on April 16, 2021: member
    Moving milestone to 0.21.2. We’d like to do 0.21.1 soon, and even though this may be ready for merge soon, it seems like a risky thing to rush into a point release.
  61. in src/banman.cpp:191 in 03239a3ab2 outdated
    187@@ -188,7 +188,7 @@ void BanMan::SweepBanned()
    188                 m_banned.erase(it++);
    189                 m_is_dirty = true;
    190                 notify_ui = true;
    191-                LogPrint(BCLog::NET, "%s: Removed banned node ip/subnet from banlist.dat: %s\n", __func__, sub_net.ToString());
    192+                LogPrint(BCLog::NET, "%s: Removed banned node address/subnet: %s\n", __func__, sub_net.ToString());
    


    MarcoFalke commented at 10:11 am on April 17, 2021:
    Can remove __func__ while touching this line?

    vasild commented at 2:59 pm on April 19, 2021:
    Removed
  62. vasild force-pushed on Apr 19, 2021
  63. vasild commented at 2:59 pm on April 19, 2021: member
    03239a3ab...aa90a7d46: address suggestions
  64. in test/functional/rpc_setban.py:61 in aa90a7d46a outdated
    50@@ -51,12 +51,22 @@ def run_test(self):
    51         ip_addr = "1.2.3.4"
    52         assert(not self.is_banned(node, tor_addr))
    53         assert(not self.is_banned(node, ip_addr))
    54+
    55         node.setban(tor_addr, "add")
    56         assert(self.is_banned(node, tor_addr))
    57         assert(not self.is_banned(node, ip_addr))
    58+
    59+        self.restart_node(1)
    


    promag commented at 8:47 pm on April 19, 2021:

    aa90a7d46a506b26a6016d1171f7ddae809e9c99

    nit, here and below, add a comment on why the check is repeated after the restart.


    vasild commented at 3:59 pm on April 20, 2021:
    Added a comment.
  65. in src/addrdb.cpp:182 in 51909ded1e outdated
    175@@ -119,18 +176,49 @@ bool DeserializeFileDB(const fs::path& path, Data& data)
    176 }
    177 } // namespace
    178 
    179-CBanDB::CBanDB(fs::path ban_list_path) : m_ban_list_path(std::move(ban_list_path))
    180+CBanDB::CBanDB(fs::path ban_list_path)
    181+    : m_banlist_dat(ban_list_path.string() + ".dat"),
    


    promag commented at 8:58 pm on April 19, 2021:

    51909ded1e8b26158f8d36cfe323ac72feac1d78

    nit, fs::path(ban_list_path).replace_extension("dat").


    vasild commented at 4:01 pm on April 20, 2021:
    I find replace_extension() a bit confusing in this case because we know that there is no extension, so we are not replacing anything (I know it will do the right thing, but still).
  66. in src/addrdb.cpp:203 in aa90a7d46a outdated
    198 }
    199 
    200 bool CBanDB::Read(banmap_t& banSet)
    201 {
    202-    return DeserializeFileDB(m_ban_list_path, banSet);
    203+    if (!fs::exists(m_banlist_json)) {
    


    promag commented at 10:11 pm on April 19, 2021:

    51909ded1e8b26158f8d36cfe323ac72feac1d78

    A comment here would be nice. Or maybe just take the next suggestion so it’s clear what this is doing.


    vasild commented at 4:01 pm on April 20, 2021:
    Added some comments.
  67. in src/addrdb.cpp:206 in aa90a7d46a outdated
    199 
    200 bool CBanDB::Read(banmap_t& banSet)
    201 {
    202-    return DeserializeFileDB(m_ban_list_path, banSet);
    203+    if (!fs::exists(m_banlist_json)) {
    204+        return DeserializeFileDB(m_banlist_dat, banSet);
    


    promag commented at 10:19 pm on April 19, 2021:

    51909ded1e8b26158f8d36cfe323ac72feac1d78

    Maybe migrate on 1st run?

     0 bool CBanDB::Read(banmap_t& banSet)
     1 {
     2     if (!fs::exists(m_banlist_json)) {
     3-        return DeserializeFileDB(m_banlist_dat, banSet);
     4+        if (!DeserializeFileDB(m_banlist_dat, banSet)) {
     5+            return false;
     6+        }
     7+        // log something
     8+        Write(banSet);
     9+        return true;
    10     }
    

    vasild commented at 4:03 pm on April 20, 2021:
    Migrate on first run - yes. But Read() calling Write() seems like some anti-pattern to me. Instead I signal from Read() that the list needs re-flushing, so that the caller takes care of it.

    promag commented at 4:20 pm on April 20, 2021:
    Good point. Maybe add a CBanDB::MigrateAndRead where calling Write() isn’t so bad.

    vasild commented at 8:13 am on April 21, 2021:
    Not necessary with the latest changes.
  68. promag commented at 10:27 pm on April 19, 2021: member
    Tested ACK 03239a3ab204dfc71bb46f85679414b331836763. Left some comments for your consideration.
  69. DrahtBot added the label Needs rebase on Apr 20, 2021
  70. vasild force-pushed on Apr 20, 2021
  71. vasild commented at 3:55 pm on April 20, 2021: member
    aa90a7d46...7e399e2de: rebase due to conflicts
  72. vasild force-pushed on Apr 20, 2021
  73. vasild commented at 3:58 pm on April 20, 2021: member
    7e399e2de...08f4f98ea: address some suggestions; force saving on disk if we loaded just the old banlist.dat so that we create the new banlist.json this way, without waiting for mods to the list in order to flush it to disk.
  74. DrahtBot removed the label Needs rebase on Apr 20, 2021
  75. in doc/files.md:58 in 08f4f98ea2 outdated
    54@@ -55,7 +55,8 @@ Subdirectory       | File(s)               | Description
    55 `indexes/blockfilter/basic/`    | `fltrNNNNN.dat`<sup>[\[2\]](#note2)</sup> | Blockfilter index filters for the basic filtertype; *optional*, used if `-blockfilterindex=basic`
    56 `wallets/`         |                       | [Contains wallets](#multi-wallet-environment); can be specified by `-walletdir` option; if `wallets/` subdirectory does not exist, wallets reside in the [data directory](#data-directory-location)
    57 `./`               | `anchors.dat`         | Anchor IP address database, created on shutdown and deleted at startup. Anchors are last known outgoing block-relay-only peers that are tried to re-connect to on startup
    58-`./`               | `banlist.dat`         | Stores the IPs/subnets of banned nodes
    59+`./`               | `banlist.dat`         | Stores the addresses/subnets of banned nodes (deprecated). Bitcoin Core will not save the banlist to it. It would only read it on startup if `banlist.json` is not present.
    


    jonatack commented at 5:04 pm on April 23, 2021:

    s/would/will/

    Suggested version all in present tense:

    0`./`               | `banlist.dat`         | Stores the addresses/subnets of banned nodes (deprecated); `bitcoind` or `bitcoin-qt` no longer save the banlist to this file, but read it on startup if `banlist.json` is not present.
    

    if you retouch, in the same commit and file would you please touch up:

    0 ## Legacy subdirectories and files
    1 
    2-These subdirectories and files are no longer used by the Bitcoin Core:
    3+These subdirectories and files are no longer used by Bitcoin Core:
    

    vasild commented at 8:01 am on April 26, 2021:
    Done. Put that last change in a separate commit.
  76. in test/functional/rpc_setban.py:71 in 08f4f98ea2 outdated
    66         assert(not self.is_banned(self.nodes[1], tor_addr))
    67         assert(not self.is_banned(node, ip_addr))
    68 
    69+        self.restart_node(1)
    70+        assert(not self.is_banned(self.nodes[1], tor_addr))
    71+        assert(not self.is_banned(node, ip_addr))
    


    jonatack commented at 5:08 pm on April 23, 2021:

    08f4f98ea nit, drop the outer parens on the 4 added asserts and add a second newline at line 72 (per pycodestyle, “E305 expected 2 blank lines after class or function definition, found 1”)

    (the black linter, e.g. apt install black && black test/functional/rpc_setban.py will do both of these for you)


    vasild commented at 8:03 am on April 26, 2021:
    Done. Since the surrounding code uses assert() I added the new code with assert() for consistency and then removed all () from asserts in the entire file in a separate commit.
  77. jonatack commented at 3:58 pm on April 24, 2021: member
    Reviewing and testing this later today.
  78. jonatack commented at 9:27 pm on April 24, 2021: member
    ACK 08f4f98ea2cdc3fbff5c9e21870296106430e468 with the suggestion to perhaps remove any leftover (e.g. “::/0”) addresses from mis-saved addrv2 ban entries when creating the json file and add a test. They may eventually expire–or not, depending on the duration–so it might not be a bad idea to remove any that are identifiable. Tested on master that an existing banlist.dat containing IPv4/6 and torv3 addresses is read on restart (with an entry of “::/0” or an “IPv4”-like address for the torv3 addresses), and that after building this patch, a banlist.json file with the existing entries is auto-created on startup except with incorrect ban entries for the addrv2 addresses, and that subsequent setban add/remove operations correctly update the banlist.json file for torv3 and i2p addresses. As expected, attempting to remove any previously incorrectly saved addrv2 addresses fails to recognize them unless the “new” address is stipulated, but subsequent setban add/remove operations work correctly. This testing was done by simultaneously looking at the banlist files and running setban/listbanned, shutting down, sometimes rm-ing the banlist.json file, restarting on master and with this patch, and repeating the testing.
  79. vasild force-pushed on Apr 26, 2021
  80. vasild commented at 8:00 am on April 26, 2021: member
    08f4f98ea...fe4aafda5: apply doc and style suggestions
  81. vasild commented at 8:06 am on April 26, 2021: member

    … remove any leftover (e.g. “::/0”) addresses from mis-saved addrv2 ban entries when creating the json file …

    Hmm. Could there be real, manually added, no-mistake, ban entries like ::/0? Somebody has banned deliberately the entire IPv6 network?

  82. in test/functional/rpc_setban.py:25 in fe4aafda51 outdated
    21@@ -22,7 +22,7 @@ def run_test(self):
    22         # Node 0 connects to Node 1, check that the noban permission is not granted
    23         self.connect_nodes(0, 1)
    24         peerinfo = self.nodes[1].getpeerinfo()[0]
    25-        assert(not 'noban' in peerinfo['permissions'])
    26+        assert not "noban" in peerinfo["permissions"]
    


    jonatack commented at 8:39 am on April 26, 2021:

    fe4aafda512483e863b23cfad403509b1604555c drive-by nit, while touching lines 25 and 46 in this commit (maybe just make it a PEP8/black linter commit)

    $ pycodestyle test/functional/rpc_setban.py

    test/functional/rpc_setban.py:25:16: E713 test for membership should be 'not in'
    test/functional/rpc_setban.py:46:16: E713 test for membership should be 'not in'
    

    vasild commented at 9:15 am on April 26, 2021:
    Going to leave it as is because none of “black” or “pycodestyle” is mentioned in doc/. Don’t want to bother other reviewers with too big and unrelated changes to this PR.

    jonatack commented at 9:19 am on April 26, 2021:

    Ok. For info, pycodestyle is PEP8 that we follow per doc/test/functional/README.md

    https://pypi.org/project/pycodestyle


    jonatack commented at 9:21 am on April 26, 2021:
    (ah, it was renamed from PEP8 for this reason https://github.com/PyCQA/pycodestyle/issues/466)

    vasild commented at 9:37 am on April 26, 2021:

    lint-python.sh is happy with the current rpc_setban.py:

    0$ ./test/lint/lint-python.sh
    1test/functional/test_runner.py:42: error: Module has no attribute "getwindowsversion"
    2Found 1 error in 1 file (checked 199 source files)
    3$
    

    jonatack commented at 10:09 am on April 26, 2021:
    If adding new code or retouching old code for style reasons, I’d try to make it conform (https://www.python.org/dev/peps/pep-0008/#other-recommendations, https://www.flake8rules.com/rules/E713.html for “not in”) because reviewers are often picky about it. I also wouldn’t add a commit that does a one-word grammar change because otherwise someone will invariably ask me to squash the commit. But everyone has a different experience with this so definitely feel free to ignore.

    dhruv commented at 6:05 pm on May 14, 2021:
    I know this is a resolved comment. Just leaving my +1 for assert x not in y being easier than not assert x in y
  83. jonatack commented at 8:45 am on April 26, 2021: member

    … remove any leftover (e.g. “::/0”) addresses from mis-saved addrv2 ban entries when creating the json file …

    Hmm. Could there be real, manually added, no-mistake, ban entries like ::/0? Somebody has banned deliberately the entire IPv6 network?

    Good question. Would anyone do that. OTOH, have people been banning tor v3 addresses yet. Possible. IDK if it’s an edge case worth worrying about.

  84. DrahtBot commented at 5:25 pm on May 3, 2021: member

    🕵️ @harding @hebasto have been requested to review this pull request as specified in the REVIEWERS file.

  85. in src/addrdb.cpp:190 in 391b4d5e84 outdated
    185 
    186 bool CBanDB::Write(const banmap_t& banSet)
    187 {
    188-    return SerializeFileDB("banlist", m_ban_list_path, banSet);
    189+    std::vector<std::string> errors;
    190+    if (util::WriteSettings(m_banlist_json, {{JSON_KEY, BanMapToJson(banSet)}}, errors)) {
    


    dhruv commented at 8:29 pm on May 12, 2021:
    Would it be worthwhile renaming (Read)WriteSettings() to something like (Read)WriteJsonObjFile()?

    vasild commented at 2:57 pm on May 18, 2021:
    I think either one is fine. Renaming is out of the scope of this PR.
  86. in src/addrdb.cpp:52 in 391b4d5e84 outdated
    47+    UniValue bans_json(UniValue::VARR);
    48+    for (const auto& it : bans) {
    49+        const auto& address = it.first;
    50+        const auto& ban_entry = it.second;
    51+        UniValue j = ban_entry.ToJson();
    52+        j.pushKV("address", address.ToString());
    


    dhruv commented at 8:34 pm on May 12, 2021:
    Can the string “address” be a file-level const and re-used in BanMapFromJson()?

    vasild commented at 2:54 pm on May 18, 2021:
    Yes, good idea! Done.

    jonatack commented at 2:36 pm on May 19, 2021:
    Not a fan of doing this for a short string like this that is used only twice, but it’s not a blocker.
  87. in src/addrdb.cpp:72 in 391b4d5e84 outdated
    65+void BanMapFromJson(const UniValue& bans_json, banmap_t& bans)
    66+{
    67+    for (const auto& ban_entry_json : bans_json.getValues()) {
    68+        CSubNet subnet;
    69+        const auto& subnet_str = ban_entry_json["address"].get_str();
    70+        if (!LookupSubNet(subnet_str, subnet)) {
    


    dhruv commented at 9:06 pm on May 12, 2021:
    Is it useful to sanity check version and banned_until as well?

    vasild commented at 2:29 pm on May 18, 2021:

    I am not sure. version and banned_until are not sanity-checked when reading banlist.dat, so I do the same with banlist.json as the purpose of this PR is to switch the format, not introduce new checks.

    Maybe worth considering separately.

  88. in src/addrdb.h:98 in 391b4d5e84 outdated
     96+
     97+    /**
     98+     * Read the banlist from disk.
     99+     * @param[out] banSet The loaded list, set if `true` is returned, otherwise it is left
    100+     * in an undefined state.
    101+     * @param[out] dirty Indicates whether the loaded list needs flushing to disk, only set
    


    dhruv commented at 10:39 pm on May 13, 2021:

    only set if true is returned.

    I think it’s possible for it to be set and yet the function return false if DeserializeFileDB returns false due to an invalid or missing file.


    vasild commented at 2:53 pm on May 18, 2021:
    Right, reworded the comment.
  89. in src/addrdb.cpp:191 in 391b4d5e84 outdated
    186 bool CBanDB::Write(const banmap_t& banSet)
    187 {
    188-    return SerializeFileDB("banlist", m_ban_list_path, banSet);
    189+    std::vector<std::string> errors;
    190+    if (util::WriteSettings(m_banlist_json, {{JSON_KEY, BanMapToJson(banSet)}}, errors)) {
    191+        return true;
    


    dhruv commented at 10:54 pm on May 13, 2021:
    Is it useful to remove banlist.dat if it does exist at this point? Or a log to inform the user that they can remove it? Might be confusing to have two files both claiming to be banlists.

    vasild commented at 2:43 pm on May 18, 2021:

    I deliberately avoid removing banlist.dat automatically because that would prevent downgrading to an older version (and keeping the ban list).

    Something like this may be a good idea:

     0--- i/src/addrdb.cpp
     1+++ w/src/addrdb.cpp
     2@@ -185,12 +185,18 @@ CBanDB::CBanDB(fs::path ban_list_path)
     3 }
     4
     5 bool CBanDB::Write(const banmap_t& banSet)
     6 {
     7     std::vector<std::string> errors;
     8     if (util::WriteSettings(m_banlist_json, {{JSON_KEY, BanMapToJson(banSet)}}, errors)) {
     9+        if (fs::exists(m_banlist_dat)) {
    10+            LogPrintf("Successfully saved %s to disk. %s can be removed manually now if "
    11+                      "downgrading to an older version is not planned.\n",
    12+                      m_banlist_json.string(),
    13+                      m_banlist_dat.string());
    14+        }
    15         return true;
    16     }
    17
    18     for (const auto& err : errors) {
    19         error("%s", err);
    20     }
    

    but it would be printed every time which would be annoying. There is some doc added to doc/files.md which should clarify things if the user wonders why there are two files. Maybe leave it as is?

    We can add an automatic removal of banlist.dat one major version after this change is released.


    dhruv commented at 3:51 pm on May 18, 2021:
    Removing banlist.dat one major version later seems like a fair choice!
  90. in src/banman.h:21 in 391b4d5e84 outdated
    16@@ -17,7 +17,8 @@
    17 
    18 // NOTE: When adjusting this, update rpcnet:setban's help ("24h")
    19 static constexpr unsigned int DEFAULT_MISBEHAVING_BANTIME = 60 * 60 * 24; // Default 24-hour ban
    20-// How often to dump addresses to banlist.dat
    21+
    22+/// How often to dump banned addresses/subnets to disk.
    


    dhruv commented at 11:48 pm on May 13, 2021:
    Extra “/”

    vasild commented at 2:46 pm on May 18, 2021:
    The 3 slashes make it a doxygen comment for inclusion in https://doxygen.bitcoincore.org/.

    jonatack commented at 3:29 pm on May 18, 2021:
    Yes, and doc/developer-notes.md also provides an example with /// iirc.

    dhruv commented at 3:48 pm on May 18, 2021:
    Oh. Thanks for teaching.
  91. in src/test/fuzz/banman.cpp:39 in 391b4d5e84 outdated
    35@@ -34,8 +36,20 @@ FUZZ_TARGET_INIT(banman, initialize_banman)
    36 {
    37     FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
    38     SetMockTime(ConsumeTime(fuzzed_data_provider));
    39-    const fs::path banlist_file = GetDataDir() / "fuzzed_banlist.dat";
    40-    fs::remove(banlist_file);
    41+    fs::path banlist_file = GetDataDir() / "fuzzed_banlist";
    


    dhruv commented at 4:52 am on May 14, 2021:
    Is there a way to add these fuzz tests and not invalidate the existing seeds?

    vasild commented at 2:51 pm on May 18, 2021:
    I don’t see how - we must first write the file or set banlist_file and only after that execute the actual test.

    dhruv commented at 3:52 pm on May 18, 2021:
    Perhaps with a separate fuzz test? I’m a fuzzing n00b though. We should ask @MarcoFalke and @practicalswift!
  92. dhruv commented at 7:36 pm on May 14, 2021: member

    ACK fe4aafd

    Code reviewed and tested locally. Thank you for the patch @vasild ! I especially like that node operators will have an option to share banlists as a human-readable json file instead of rpc commands 🚀

    To verify things work as intended:

     0$ git pull --rebase master
     1$ make -j 9
     2$ mkdir -p node0 node1
     3
     4# Run node 0
     5$ src/bitcoind -regtest -datadir=`pwd`/node0 -rpccookiefile=.cookie -port=2222 -rpcport=3333
     6
     7# Run node 1
     8$ src/bitcoind -regtest -datadir=`pwd`/node1 -rpccookiefile=.cookie -port=2223 -rpcport=3334
     9
    10# Node 0 adds Node 1 as peer
    11$ src/bitcoin-cli -regtest -datadir=`pwd`/node0 -rpccookiefile=.cookie -rpcport=3333 addnode "127.0.0.1:2223" "onetry"
    12
    13# Node 1 sees the peer
    14$ src/bitcoin-cli -regtest -datadir=`pwd`/node1 -rpccookiefile=.cookie -rpcport=3334 getpeerinfo
    15
    16#Node 1 bans Node 0
    17$ src/bitcoin-cli -regtest -datadir=`pwd`/node1 -rpccookiefile=.cookie -rpcport=3334 setban "127.0.0.1" "add"
    18$ src/bitcoin-cli -regtest -datadir=`pwd`/node1 -rpccookiefile=.cookie -rpcport=3334 listbanned
    19
    20# Node 1 eliminates the peer
    21$ src/bitcoin-cli -regtest -datadir=`pwd`/node1 -rpccookiefile=.cookie -rpcport=3334 getpeerinfo
    22
    23# Stop both nodes manually
    24
    25# Banlist files on each node
    26$ ls -lan node0/regtest | grep banlist
    27-rw-------   1 501  20      37 May 14 11:58 banlist.dat
    28
    29$ ls -lan node1/regtest | grep banlist
    30-rw-------   1 501  20      91 May 14 12:08 banlist.dat
    31
    32$ git checkout -b pr-20966 vasild/json_bans
    33$ make -j 9
    34
    35# Run node 0
    36$ src/bitcoind -regtest -datadir=`pwd`/node0 -rpccookiefile=.cookie -port=2222 -rpcport=3333
    37
    38# Run node 1
    39$ src/bitcoind -regtest -datadir=`pwd`/node1 -rpccookiefile=.cookie -port=2223 -rpcport=3334
    40
    41# Banlist files on each node
    42$ ls -lan node0/regtest | grep banlist
    43-rw-------   1 501  20      37 May 14 11:58 banlist.dat
    44-rw-------   1 501  20      34 May 14 12:21 banlist.json
    45
    46$ ls -lan node1/regtest | grep banlist
    47-rw-------   1 501  20      91 May 14 12:08 banlist.dat
    48-rw-------   1 501  20     167 May 14 12:21 banlist.json
    49
    50$ cat node0/regtest/banlist.json
    51{
    52    "banned_nets": [
    53    ]
    54   }
    55
    56$ cat node1/regtest/banlist.json
    57{
    58    "banned_nets": [
    59     {
    60      "version": 1,
    61      "ban_created": 1621019336,
    62      "banned_until": 1621105736,
    63      "address": "127.0.0.1/32"
    64     }
    65    ]
    66   }
    67 
    68# Unban node 0 from node 1
    69$ src/bitcoin-cli -regtest -datadir=`pwd`/node1 -rpccookiefile=.cookie -rpcport=3334 setban "127.0.0.1" "remove"
    70
    71# Verify banlist.json updated
    72$ cat node1/regtest/banlist.json
    73{
    74    "banned_nets": [
    75    ]
    76   }
    77 
    78# Manually restart node1
    79# Verify unchanged banlist.json
    80$ cat node1/regtest/banlist.json
    81{
    82    "banned_nets": [
    83    ]
    84   }
    

    However, this is the behavior I’d like to point to and make sure it’s ok:

     0# Manually stop node1
     1# Node 1 deletes banlist.json to clear all bans
     2$ rm node1/regtest/banlist.json
     3# Restart node 1
     4
     5# The ban comes back from banlist.dat
     6$ src/bitcoin-cli -regtest -datadir=`pwd`/node1 -rpccookiefile=.cookie -rpcport=3334 listbanned
     7[
     8  {
     9    "address": "127.0.0.1/32",
    10    "ban_created": 1621019336,
    11    "banned_until": 1621105736,
    12    "ban_duration": 86400,
    13    "time_remaining": 85186
    14  }
    15]
    16
    17$ cat node1/regtest/banlist.json
    18{
    19    "banned_nets": [
    20     {
    21      "version": 1,
    22      "ban_created": 1621019336,
    23      "banned_until": 1621105736,
    24      "address": "127.0.0.1/32"
    25     }
    26    ]
    27   }
    

    Some other suggestions below.

  93. vasild force-pushed on May 18, 2021
  94. vasild commented at 2:59 pm on May 18, 2021: member
    fe4aafda51...13c681161c: address review suggestions
  95. vasild commented at 3:05 pm on May 18, 2021: member

    this is the behavior I’d like to point to and make sure it’s ok … Node 1 deletes banlist.json to clear all bans … The ban comes back from banlist.dat

    Yes, this is how it is supposed to work. The operator should have seen that there are two files banlist.dat and banlist.json and deleted both or consulted doc/files.md. We don’t automatically delete banlist.dat after creating banlist.json because that would create issues if the user decides to downgrade to the previous version which only recognizes banlist.dat.

  96. dhruv commented at 3:55 pm on May 18, 2021: member
    re-ACK 13c6811
  97. jonatack commented at 2:41 pm on May 19, 2021: member

    Diff-review re-ACK 13c681161c4a3f2a2acd6d812c1360e0ad94019c per git diff 08f4f98 13c6811 and git range-diff d4c409c 08f4f98 13c6811

    Only non-showstopping mini-regret is the Python “not in” not updated while re-touching to conform to PEP8 per our functional test guidelines.

  98. DrahtBot added the label Needs rebase on May 24, 2021
  99. MarcoFalke commented at 10:00 am on May 24, 2021: member
    Sorry, this had two ACKs and I didn’t see the conflict.
  100. vasild force-pushed on May 24, 2021
  101. vasild commented at 12:13 pm on May 24, 2021: member

    13c681161c...88b843e49c: rebase due to conflicts

     0--- a/doc/release-notes.md
     1+++ b/doc/release-notes.md
     2@@ -182,12 +182,21 @@ RPC
     3   - `verifymessage` now returns RPC_TYPE_ERROR (-3) if the passed signature
     4     is malformed. Previously returned RPC_INVALID_ADDRESS_OR_KEY (-5).
     5
     6 Tests
     7 -----
     8
     9+Files
    10+-----
    11+
    12+- The list of banned hosts and networks (via `setban` RPC) is now saved on disk
    13+  in JSON format in `banlist.json` instead of `banlist.dat`. `banlist.dat` is
    14+  only read on startup if `banlist.json` is not present. Changes are only
    15+  written to the new `banlist.json`. A future version of Bitcoin Core may
    16+  completely ignore `banlist.dat`. (#20966)
    17+
    18 Credits
    19 =======
    
  102. hebasto commented at 1:07 pm on May 24, 2021: member
    Concept ACK.
  103. in test/functional/rpc_setban.py:71 in 88b843e49c outdated
    74+        assert not self.is_banned(self.nodes[1], tor_addr)
    75+        assert not self.is_banned(node, ip_addr)
    76+
    77+        self.restart_node(1)
    78+        assert not self.is_banned(self.nodes[1], tor_addr)
    79+        assert not self.is_banned(node, ip_addr)
    


    jonatack commented at 1:09 pm on May 24, 2021:

    f371273 could be done in a follow-up along with not_in, but if you retouch

     0--- a/test/functional/rpc_setban.py
     1+++ b/test/functional/rpc_setban.py
     2@@ -56,18 +56,17 @@ class SetBanTests(BitcoinTestFramework):
     3         assert self.is_banned(node, tor_addr)
     4         assert not self.is_banned(node, ip_addr)
     5 
     6-        # Test that the ban list is preserved through restart.
     7-
     8+        self.log.info("Test the ban list is preserved through restart")
     9         self.restart_node(1)
    10         assert self.is_banned(node, tor_addr)
    11         assert not self.is_banned(node, ip_addr)
    12 
    13         node.setban(tor_addr, "remove")
    14-        assert not self.is_banned(self.nodes[1], tor_addr)
    15+        assert not self.is_banned(node, tor_addr)
    16         assert not self.is_banned(node, ip_addr)
    17 
    18         self.restart_node(1)
    19-        assert not self.is_banned(self.nodes[1], tor_addr)
    20+        assert not self.is_banned(node, tor_addr)
    21         assert not self.is_banned(node, ip_addr)
    

    vasild commented at 1:35 pm on June 21, 2021:
    Added the log message and changed to node in the second one, but left the first one as that line is not modified by this PR.

    jonatack commented at 2:08 pm on June 21, 2021:
    Seems that line was added by me (hides) in 39b43298d9c, so I don’t mind if you change it :D
  104. jonatack commented at 1:13 pm on May 24, 2021: member
    re-ACK 88b843e49c84e331e40bfbe249269deaf2a6a2ac per git range-diff ce4a852 13c6811 88b843e
  105. in doc/files.md:60 in 88b843e49c outdated
    55@@ -56,7 +56,8 @@ Subdirectory       | File(s)               | Description
    56 `indexes/coinstats/db/` | LevelDB database | Coinstats index; *optional*, used if `-coinstatsindex=1`
    57 `wallets/`         |                       | [Contains wallets](#multi-wallet-environment); can be specified by `-walletdir` option; if `wallets/` subdirectory does not exist, wallets reside in the [data directory](#data-directory-location)
    58 `./`               | `anchors.dat`         | Anchor IP address database, created on shutdown and deleted at startup. Anchors are last known outgoing block-relay-only peers that are tried to re-connect to on startup
    59-`./`               | `banlist.dat`         | Stores the IPs/subnets of banned nodes
    60+`./`               | `banlist.dat`         | Stores the addresses/subnets of banned nodes (deprecated). `bitcoind` or `bitcoin-qt` no longer save the banlist to this file, but read it on startup if `banlist.json` is not present.
    61+`./`               | `banlist.json`        | Stores the addresses/subnets of banned nodes.
    


    hebasto commented at 1:22 pm on May 24, 2021:
    @vasild In your opinion, when will be an appropriate moment to move the banlist.dat description into the “Legacy subdirectories and files” subsection?

    promag commented at 1:24 pm on May 24, 2021:

    vasild commented at 1:26 pm on May 24, 2021:
    Good question! I am not sure. Either now or in a next release where we completely ignore banlist.dat?
  106. promag commented at 1:26 pm on May 24, 2021: member
    Code review ACK 88b843e49c84e331e40bfbe249269deaf2a6a2ac.
  107. in test/functional/rpc_setban.py:70 in 88b843e49c outdated
    73-        assert(not self.is_banned(node, ip_addr))
    74+        assert not self.is_banned(self.nodes[1], tor_addr)
    75+        assert not self.is_banned(node, ip_addr)
    76+
    77+        self.restart_node(1)
    78+        assert not self.is_banned(self.nodes[1], tor_addr)
    


    hebasto commented at 1:41 pm on May 24, 2021:

    hebasto commented at 1:42 pm on May 24, 2021:
  108. DrahtBot removed the label Needs rebase on May 24, 2021
  109. hebasto approved
  110. hebasto commented at 6:24 pm on May 24, 2021: member
    ACK 88b843e49c84e331e40bfbe249269deaf2a6a2ac, tested on Linux Mint 20.1 including error/exception handling.
  111. DrahtBot added the label Needs rebase on Jun 11, 2021
  112. vasild force-pushed on Jun 14, 2021
  113. vasild commented at 12:02 pm on June 14, 2021: member

    88b843e49c...4b52c7234f: rebase due to conflicts

    release notes in #20966 (comment)

  114. DrahtBot removed the label Needs rebase on Jun 14, 2021
  115. jonatack commented at 2:27 pm on June 14, 2021: member
    re-ACK 4b52c7234f3c2e3067d7d6c6fd7ebf2b96bb8a45 per git range-diff 5c4f0c4 88b843e 4b52c72, rebase only, debug build clean, test passes, repeatedly tested banning/unbanning peers on signet while inspecting banlist.json in my editor including through restarts. However, there are gotchas when downgrading/upgrading; it may be a good idea to warn and suggest best practices about that in the release note.
  116. jonatack commented at 3:22 pm on June 14, 2021: member
    • The list of banned hosts and networks (via setban RPC) is now saved on disk in JSON format in banlist.json instead of banlist.dat. banlist.dat is only read on startup if banlist.json is not present. Changes are only written to the new banlist.json. A future version of Bitcoin Core may completely ignore banlist.dat. (#20966)

    Regarding the release note, peers may be banned and unbanned via the GUI peers window in addition to the setban RPC. As mentioned in my previous comment, it may be a good idea to describe user gotchas and best practices for upgrading and downgrading.

  117. sipa commented at 3:59 am on June 15, 2021: member

    utACK 4b52c7234f3c2e3067d7d6c6fd7ebf2b96bb8a45

    I think it would be useful to make the code ignore future version numbers, so it will not completely wipe banlists on downgrading (from future versions that introduce additional features to this format), but that can be done later.

  118. promag commented at 1:15 pm on June 15, 2021: member
    Code review ACK 4b52c7234f3c2e3067d7d6c6fd7ebf2b96bb8a45.
  119. DrahtBot added the label Needs rebase on Jun 17, 2021
  120. MarcoFalke removed this from the milestone 0.21.2 on Jun 21, 2021
  121. MarcoFalke added this to the milestone 22.0 on Jun 21, 2021
  122. banman: save the banlist in a JSON format on disk
    Save the banlist in `banlist.json` instead of `banlist.dat`.
    
    This makes it possible to store Tor v3 entries in the banlist on disk
    (and any other addresses that cannot be serialized in addrv1 format).
    
    Only read `banlist.dat` if it exists and `banlist.json` does not
    exist (first start after an upgrade).
    
    Supersedes https://github.com/bitcoin/bitcoin/pull/20904
    Resolves https://github.com/bitcoin/bitcoin/issues/19748
    d197977ae2
  123. test: ensure banlist can be read from disk after restart
    With `banlist.dat` (being written in addrv1 format) if we would try to
    write a Tor v3 subnet, it would serialize as a dummy-all-0s IPv6
    address and subsequently, when deserialized will not result in the same
    subnet.
    
    This problem does not exist with `banlist.json` where the data is saved
    in textual, human-readable form.
    dd4e957dcd
  124. doc: fix grammar in doc/files.md 24b10ebda3
  125. style: remove () from assert in rpc_setban.py bb719a08db
  126. vasild force-pushed on Jun 21, 2021
  127. vasild commented at 1:31 pm on June 21, 2021: member
    4b52c7234f...5fcc96457d: rebase due to conflicts
  128. vasild force-pushed on Jun 21, 2021
  129. vasild commented at 1:33 pm on June 21, 2021: member
    5fcc96457d...bb719a08db: minor tweaks in rpc_setban.py - log a message and use node instead of self.nodes[1]
  130. jonatack commented at 2:10 pm on June 21, 2021: member
    Code review re-ACK bb719a08db173a753984a04534de6f148b87b17a per git range-diff 6a67366 4b52c72 bb719a0
  131. DrahtBot removed the label Needs rebase on Jun 21, 2021
  132. achow101 commented at 10:43 pm on June 22, 2021: member
    Code Review ACK bb719a08db173a753984a04534de6f148b87b17a
  133. MarcoFalke removed the label Needs backport (0.21) on Jun 23, 2021
  134. MarcoFalke merged this on Jun 23, 2021
  135. MarcoFalke closed this on Jun 23, 2021

  136. vasild deleted the branch on Jun 23, 2021
  137. MarcoFalke commented at 8:08 am on June 23, 2021: member
    Removed from backport, but if someone wants to backport, it will need to be done together with #20852 (comment)
  138. MarcoFalke commented at 8:15 am on June 23, 2021: member
  139. sidhujag referenced this in commit 620c144a12 on Jun 24, 2021
  140. laanwj referenced this in commit 2d0bdb2089 on Dec 15, 2021
  141. sidhujag referenced this in commit a210f0c43e on Dec 15, 2021
  142. gwillen referenced this in commit e8b84fa091 on Jun 1, 2022
  143. DrahtBot locked this on Aug 16, 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: 2024-07-03 10:13 UTC

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