1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2019 The Bitcoin Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #include <addrdb.h>
7 
8 #include <addrman.h>
9 #include <chainparams.h>
10 #include <clientversion.h>
11 #include <cstdint>
12 #include <hash.h>
13 #include <logging/timer.h>
14 #include <random.h>
15 #include <streams.h>
16 #include <tinyformat.h>
17 #include <util/system.h>
18 
19 namespace {
20 
21 template <typename Stream, typename Data>
SerializeDB(Stream & stream,const Data & data)22 bool SerializeDB(Stream& stream, const Data& data)
23 {
24     // Write and commit header, data
25     try {
26         CHashWriter hasher(SER_DISK, CLIENT_VERSION);
27         stream << Params().MessageStart() << data;
28         hasher << Params().MessageStart() << data;
29         stream << hasher.GetHash();
30     } catch (const std::exception& e) {
31         return error("%s: Serialize or I/O error - %s", __func__, e.what());
32     }
33 
34     return true;
35 }
36 
37 template <typename Data>
SerializeFileDB(const std::string & prefix,const fs::path & path,const Data & data)38 bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data& data)
39 {
40     // Generate random temporary filename
41     uint16_t randv = 0;
42     GetRandBytes((unsigned char*)&randv, sizeof(randv));
43     std::string tmpfn = strprintf("%s.%04x", prefix, randv);
44 
45     // open temp output file, and associate with CAutoFile
46     fs::path pathTmp = GetDataDir() / tmpfn;
47     FILE *file = fsbridge::fopen(pathTmp, "wb");
48     CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
49     if (fileout.IsNull()) {
50         fileout.fclose();
51         remove(pathTmp);
52         return error("%s: Failed to open file %s", __func__, pathTmp.string());
53     }
54 
55     // Serialize
56     if (!SerializeDB(fileout, data)) {
57         fileout.fclose();
58         remove(pathTmp);
59         return false;
60     }
61     if (!FileCommit(fileout.Get())) {
62         fileout.fclose();
63         remove(pathTmp);
64         return error("%s: Failed to flush file %s", __func__, pathTmp.string());
65     }
66     fileout.fclose();
67 
68     // replace existing file, if any, with new file
69     if (!RenameOver(pathTmp, path)) {
70         remove(pathTmp);
71         return error("%s: Rename-into-place failed", __func__);
72     }
73 
74     return true;
75 }
76 
77 template <typename Stream, typename Data>
DeserializeDB(Stream & stream,Data & data,bool fCheckSum=true)78 bool DeserializeDB(Stream& stream, Data& data, bool fCheckSum = true)
79 {
80     try {
81         CHashVerifier<Stream> verifier(&stream);
82         // de-serialize file header (network specific magic number) and ..
83         unsigned char pchMsgTmp[4];
84         verifier >> pchMsgTmp;
85         // ... verify the network matches ours
86         if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
87             return error("%s: Invalid network magic number", __func__);
88 
89         // de-serialize data
90         verifier >> data;
91 
92         // verify checksum
93         if (fCheckSum) {
94             uint256 hashTmp;
95             stream >> hashTmp;
96             if (hashTmp != verifier.GetHash()) {
97                 return error("%s: Checksum mismatch, data corrupted", __func__);
98             }
99         }
100     }
101     catch (const std::exception& e) {
102         return error("%s: Deserialize or I/O error - %s", __func__, e.what());
103     }
104 
105     return true;
106 }
107 
108 template <typename Data>
DeserializeFileDB(const fs::path & path,Data & data)109 bool DeserializeFileDB(const fs::path& path, Data& data)
110 {
111     // open input file, and associate with CAutoFile
112     FILE *file = fsbridge::fopen(path, "rb");
113     CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
114     if (filein.IsNull())
115         return error("%s: Failed to open file %s", __func__, path.string());
116 
117     return DeserializeDB(filein, data);
118 }
119 
120 }
121 
CBanDB(fs::path ban_list_path)122 CBanDB::CBanDB(fs::path ban_list_path) : m_ban_list_path(std::move(ban_list_path))
123 {
124 }
125 
Write(const banmap_t & banSet)126 bool CBanDB::Write(const banmap_t& banSet)
127 {
128     return SerializeFileDB("banlist", m_ban_list_path, banSet);
129 }
130 
Read(banmap_t & banSet)131 bool CBanDB::Read(banmap_t& banSet)
132 {
133     return DeserializeFileDB(m_ban_list_path, banSet);
134 }
135 
CAddrDB()136 CAddrDB::CAddrDB()
137 {
138     pathAddr = GetDataDir() / "peers.dat";
139 }
140 
Write(const CAddrMan & addr)141 bool CAddrDB::Write(const CAddrMan& addr)
142 {
143     return SerializeFileDB("peers", pathAddr, addr);
144 }
145 
Read(CAddrMan & addr)146 bool CAddrDB::Read(CAddrMan& addr)
147 {
148     return DeserializeFileDB(pathAddr, addr);
149 }
150 
Read(CAddrMan & addr,CDataStream & ssPeers)151 bool CAddrDB::Read(CAddrMan& addr, CDataStream& ssPeers)
152 {
153     bool ret = DeserializeDB(ssPeers, addr, false);
154     if (!ret) {
155         // Ensure addrman is left in a clean state
156         addr.Clear();
157     }
158     return ret;
159 }
160 
DumpAnchors(const fs::path & anchors_db_path,const std::vector<CAddress> & anchors)161 void DumpAnchors(const fs::path& anchors_db_path, const std::vector<CAddress>& anchors)
162 {
163     LOG_TIME_SECONDS(strprintf("Flush %d outbound block-relay-only peer addresses to anchors.dat", anchors.size()));
164     SerializeFileDB("anchors", anchors_db_path, anchors);
165 }
166 
ReadAnchors(const fs::path & anchors_db_path)167 std::vector<CAddress> ReadAnchors(const fs::path& anchors_db_path)
168 {
169     std::vector<CAddress> anchors;
170     if (DeserializeFileDB(anchors_db_path, anchors)) {
171         LogPrintf("Loaded %i addresses from %s\n", anchors.size(), anchors_db_path.filename());
172     } else {
173         anchors.clear();
174     }
175 
176     fs::remove(anchors_db_path);
177     return anchors;
178 }
179