diff --git a/src/addrdb.cpp b/src/addrdb.cpp index 23dd88893c..b9d8500420 100644 --- a/src/addrdb.cpp +++ b/src/addrdb.cpp @@ -206,6 +206,15 @@ std::optional LoadAddrman(const std::vector& asmap, const A addrman = std::make_unique(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman); LogPrintf("Creating peers.dat because of invalid or corrupt file (%s)\n", e.what()); DumpPeerAddresses(args, *addrman); + } catch (const InvalidAddrManVersionError&) { + if (!RenameOver(path_addr, (fs::path)path_addr + ".bak")) { + addrman = nullptr; + return strprintf(_("Failed to rename invalid peers.dat file. Please move or delete it and try again.")); + } + // Addrman can be in an inconsistent state after failure, reset it + addrman = std::make_unique(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman); + LogPrintf("Creating new peers.dat because the file version was not compatible (%s). Original backed up to peers.dat.bak\n", fs::quoted(fs::PathToString(path_addr))); + DumpPeerAddresses(args, *addrman); } catch (const std::exception& e) { addrman = nullptr; return strprintf(_("Invalid or corrupt peers.dat (%s). If you believe this is a bug, please report it to %s. As a workaround, you can move the file (%s) out of the way (rename, move, or delete) to have a new one created on the next start."), diff --git a/src/addrman.cpp b/src/addrman.cpp index 2caedd3f12..27f6d1c3f6 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -250,7 +250,7 @@ void AddrManImpl::Unserialize(Stream& s_) s >> compat; const uint8_t lowest_compatible = compat - INCOMPATIBILITY_BASE; if (lowest_compatible > FILE_FORMAT) { - throw std::ios_base::failure(strprintf( + throw InvalidAddrManVersionError(strprintf( "Unsupported format of addrman database: %u. It is compatible with formats >=%u, " "but the maximum supported by this version of %s is %u.", uint8_t{format}, uint8_t{lowest_compatible}, PACKAGE_NAME, uint8_t{FILE_FORMAT})); diff --git a/src/addrman.h b/src/addrman.h index 10b81728bf..836aef0c71 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -17,6 +17,22 @@ #include #include +class DbInconsistentError : public std::exception +{ + using std::exception::exception; + const std::string error; + +public: + explicit DbInconsistentError(const std::string _error) : error{_error} {} + const char* what() const noexcept override { return error.c_str(); } +}; + +class InvalidAddrManVersionError : public std::ios_base::failure +{ +public: + InvalidAddrManVersionError(std::string msg) : std::ios_base::failure(msg) { } +}; + class AddrInfo; class AddrManImpl; @@ -48,16 +64,6 @@ struct AddressPosition { : tried{tried_in}, multiplicity{multiplicity_in}, bucket{bucket_in}, position{position_in} {} }; -class DbInconsistentError : public std::exception -{ - using std::exception::exception; - const std::string error; - -public: - explicit DbInconsistentError(const std::string _error) : error{_error} {} - const char* what() const noexcept override { return error.c_str(); } -}; - /** Stochastic address manager * * Design goals: diff --git a/test/functional/feature_addrman.py b/test/functional/feature_addrman.py index 06efac8abb..84d025afd9 100755 --- a/test/functional/feature_addrman.py +++ b/test/functional/feature_addrman.py @@ -67,17 +67,16 @@ class AddrmanTest(BitcoinTestFramework): self.start_node(0, extra_args=["-checkaddrman=1"]) assert_equal(self.nodes[0].getnodeaddresses(), []) - self.log.info("Check that addrman from future cannot be read") + self.log.info("Check that addrman from future is overwritten with new addrman") self.stop_node(0) write_addrman(peers_dat, lowest_compatible=111) - self.nodes[0].assert_start_raises_init_error( - expected_msg=init_error( - "Unsupported format of addrman database: 1. It is compatible with " - "formats >=111, but the maximum supported by this version of " - f"{self.config['environment']['PACKAGE_NAME']} is 4.: (.+)" - ), - match=ErrorMatch.FULL_REGEX, - ) + assert_equal(os.path.exists(peers_dat + ".bak"), False) + with self.nodes[0].assert_debug_log([ + f'Creating new peers.dat because the file version was not compatible ("{peers_dat}"). Original backed up to peers.dat.bak', + ]): + self.start_node(0) + assert_equal(self.nodes[0].getnodeaddresses(), []) + assert_equal(os.path.exists(peers_dat + ".bak"), True) self.log.info("Check that corrupt addrman cannot be read (EOF)") self.stop_node(0)