aboutsummaryrefslogtreecommitdiff
path: root/src/addrdb.cpp
diff options
context:
space:
mode:
authorVasil Dimov <vd@FreeBSD.org>2021-01-14 09:33:04 +0100
committerVasil Dimov <vd@FreeBSD.org>2021-06-21 14:39:44 +0200
commitd197977ae2076903ed12ab7616a7f93e88be02e1 (patch)
treef52c91c3abad65cffa13a8334c37da415c708c70 /src/addrdb.cpp
parent6a67366fdc3e1d383fe7cbfa209d7e85f0d96638 (diff)
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
Diffstat (limited to 'src/addrdb.cpp')
-rw-r--r--src/addrdb.cpp103
1 files changed, 99 insertions, 4 deletions
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index bf2f6c7614..b8fd019bab 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -11,13 +11,72 @@
#include <cstdint>
#include <hash.h>
#include <logging/timer.h>
+#include <netbase.h>
#include <random.h>
#include <streams.h>
#include <tinyformat.h>
+#include <univalue.h>
+#include <util/settings.h>
#include <util/system.h>
+CBanEntry::CBanEntry(const UniValue& json)
+ : nVersion(json["version"].get_int()), nCreateTime(json["ban_created"].get_int64()),
+ nBanUntil(json["banned_until"].get_int64())
+{
+}
+
+UniValue CBanEntry::ToJson() const
+{
+ UniValue json(UniValue::VOBJ);
+ json.pushKV("version", nVersion);
+ json.pushKV("ban_created", nCreateTime);
+ json.pushKV("banned_until", nBanUntil);
+ return json;
+}
+
namespace {
+static const char* BANMAN_JSON_ADDR_KEY = "address";
+
+/**
+ * Convert a `banmap_t` object to a JSON array.
+ * @param[in] bans Bans list to convert.
+ * @return a JSON array, similar to the one returned by the `listbanned` RPC. Suitable for
+ * passing to `BanMapFromJson()`.
+ */
+UniValue BanMapToJson(const banmap_t& bans)
+{
+ UniValue bans_json(UniValue::VARR);
+ for (const auto& it : bans) {
+ const auto& address = it.first;
+ const auto& ban_entry = it.second;
+ UniValue j = ban_entry.ToJson();
+ j.pushKV(BANMAN_JSON_ADDR_KEY, address.ToString());
+ bans_json.push_back(j);
+ }
+ return bans_json;
+}
+
+/**
+ * Convert a JSON array to a `banmap_t` object.
+ * @param[in] bans_json JSON to convert, must be as returned by `BanMapToJson()`.
+ * @param[out] bans Bans list to create from the JSON.
+ * @throws std::runtime_error if the JSON does not have the expected fields or they contain
+ * unparsable values.
+ */
+void BanMapFromJson(const UniValue& bans_json, banmap_t& bans)
+{
+ for (const auto& ban_entry_json : bans_json.getValues()) {
+ CSubNet subnet;
+ const auto& subnet_str = ban_entry_json[BANMAN_JSON_ADDR_KEY].get_str();
+ if (!LookupSubNet(subnet_str, subnet)) {
+ throw std::runtime_error(
+ strprintf("Cannot parse banned address or subnet: %s", subnet_str));
+ }
+ bans.insert_or_assign(subnet, CBanEntry{ban_entry_json});
+ }
+}
+
template <typename Stream, typename Data>
bool SerializeDB(Stream& stream, const Data& data)
{
@@ -119,18 +178,54 @@ bool DeserializeFileDB(const fs::path& path, Data& data, int version)
}
} // namespace
-CBanDB::CBanDB(fs::path ban_list_path) : m_ban_list_path(std::move(ban_list_path))
+CBanDB::CBanDB(fs::path ban_list_path)
+ : m_banlist_dat(ban_list_path.string() + ".dat"),
+ m_banlist_json(ban_list_path.string() + ".json")
{
}
bool CBanDB::Write(const banmap_t& banSet)
{
- return SerializeFileDB("banlist", m_ban_list_path, banSet, CLIENT_VERSION);
+ std::vector<std::string> errors;
+ if (util::WriteSettings(m_banlist_json, {{JSON_KEY, BanMapToJson(banSet)}}, errors)) {
+ return true;
+ }
+
+ for (const auto& err : errors) {
+ error("%s", err);
+ }
+ return false;
}
-bool CBanDB::Read(banmap_t& banSet)
+bool CBanDB::Read(banmap_t& banSet, bool& dirty)
{
- return DeserializeFileDB(m_ban_list_path, banSet, CLIENT_VERSION);
+ // If the JSON banlist does not exist, then try to read the non-upgraded banlist.dat.
+ if (!fs::exists(m_banlist_json)) {
+ // If this succeeds then we need to flush to disk in order to create the JSON banlist.
+ dirty = true;
+ return DeserializeFileDB(m_banlist_dat, banSet, CLIENT_VERSION);
+ }
+
+ dirty = false;
+
+ std::map<std::string, util::SettingsValue> settings;
+ std::vector<std::string> errors;
+
+ if (!util::ReadSettings(m_banlist_json, settings, errors)) {
+ for (const auto& err : errors) {
+ LogPrintf("Cannot load banlist %s: %s\n", m_banlist_json.string(), err);
+ }
+ return false;
+ }
+
+ try {
+ BanMapFromJson(settings[JSON_KEY], banSet);
+ } catch (const std::runtime_error& e) {
+ LogPrintf("Cannot parse banlist %s: %s\n", m_banlist_json.string(), e.what());
+ return false;
+ }
+
+ return true;
}
CAddrDB::CAddrDB()