aboutsummaryrefslogtreecommitdiff
path: root/src/addrdb.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/addrdb.cpp')
-rw-r--r--src/addrdb.cpp121
1 files changed, 108 insertions, 13 deletions
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index c376aced10..b8fd019bab 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -11,19 +11,78 @@
#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)
{
// Write and commit header, data
try {
- CHashWriter hasher(SER_DISK, CLIENT_VERSION);
+ CHashWriter hasher(stream.GetType(), stream.GetVersion());
stream << Params().MessageStart() << data;
hasher << Params().MessageStart() << data;
stream << hasher.GetHash();
@@ -35,7 +94,7 @@ bool SerializeDB(Stream& stream, const Data& data)
}
template <typename Data>
-bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data)
+bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data, int version)
{
// Generate random temporary filename
uint16_t randv = 0;
@@ -45,7 +104,7 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data
// open temp output file, and associate with CAutoFile
fs::path pathTmp = gArgs.GetDataDirNet() / tmpfn;
FILE *file = fsbridge::fopen(pathTmp, "wb");
- CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
+ CAutoFile fileout(file, SER_DISK, version);
if (fileout.IsNull()) {
fileout.fclose();
remove(pathTmp);
@@ -106,11 +165,11 @@ bool DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true)
}
template <typename Data>
-bool DeserializeFileDB(const fs::path& path, Data& data)
+bool DeserializeFileDB(const fs::path& path, Data& data, int version)
{
// open input file, and associate with CAutoFile
FILE* file = fsbridge::fopen(path, "rb");
- CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
+ CAutoFile filein(file, SER_DISK, version);
if (filein.IsNull()) {
LogPrintf("Missing or invalid file %s\n", path.string());
return false;
@@ -119,18 +178,54 @@ bool DeserializeFileDB(const fs::path& path, Data& data)
}
} // 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);
+ 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);
+ // 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()
@@ -140,12 +235,12 @@ CAddrDB::CAddrDB()
bool CAddrDB::Write(const CAddrMan& addr)
{
- return SerializeFileDB("peers", pathAddr, addr);
+ return SerializeFileDB("peers", pathAddr, addr, CLIENT_VERSION);
}
bool CAddrDB::Read(CAddrMan& addr)
{
- return DeserializeFileDB(pathAddr, addr);
+ return DeserializeFileDB(pathAddr, addr, CLIENT_VERSION);
}
bool CAddrDB::Read(CAddrMan& addr, CDataStream& ssPeers)
@@ -161,13 +256,13 @@ bool CAddrDB::Read(CAddrMan& addr, CDataStream& ssPeers)
void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors)
{
LOG_TIME_SECONDS(strprintf("Flush %d outbound block-relay-only peer addresses to anchors.dat", anchors.size()));
- SerializeFileDB("anchors", anchors_db_path, anchors);
+ SerializeFileDB("anchors", anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT);
}
std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)
{
std::vector<CAddress> anchors;
- if (DeserializeFileDB(anchors_db_path, anchors)) {
+ if (DeserializeFileDB(anchors_db_path, anchors, CLIENT_VERSION | ADDRV2_FORMAT)) {
LogPrintf("Loaded %i addresses from %s\n", anchors.size(), anchors_db_path.filename());
} else {
anchors.clear();