/bitcoin/src/dbwrapper.cpp
Line | Count | Source |
1 | | // Copyright (c) 2012-present The Bitcoin Core developers |
2 | | // Distributed under the MIT software license, see the accompanying |
3 | | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
4 | | |
5 | | #include <dbwrapper.h> |
6 | | |
7 | | #include <logging.h> |
8 | | #include <random.h> |
9 | | #include <serialize.h> |
10 | | #include <span.h> |
11 | | #include <streams.h> |
12 | | #include <util/fs.h> |
13 | | #include <util/fs_helpers.h> |
14 | | #include <util/strencodings.h> |
15 | | |
16 | | #include <algorithm> |
17 | | #include <cassert> |
18 | | #include <cstdarg> |
19 | | #include <cstdint> |
20 | | #include <cstdio> |
21 | | #include <leveldb/cache.h> |
22 | | #include <leveldb/db.h> |
23 | | #include <leveldb/env.h> |
24 | | #include <leveldb/filter_policy.h> |
25 | | #include <leveldb/helpers/memenv/memenv.h> |
26 | | #include <leveldb/iterator.h> |
27 | | #include <leveldb/options.h> |
28 | | #include <leveldb/slice.h> |
29 | | #include <leveldb/status.h> |
30 | | #include <leveldb/write_batch.h> |
31 | | #include <memory> |
32 | | #include <optional> |
33 | | #include <utility> |
34 | | |
35 | 18.9M | static auto CharCast(const std::byte* data) { return reinterpret_cast<const char*>(data); } |
36 | | |
37 | | bool DestroyDB(const std::string& path_str) |
38 | 0 | { |
39 | 0 | return leveldb::DestroyDB(path_str, {}).ok(); |
40 | 0 | } |
41 | | |
42 | | /** Handle database error by throwing dbwrapper_error exception. |
43 | | */ |
44 | | static void HandleError(const leveldb::Status& status) |
45 | 2.36M | { |
46 | 2.36M | if (status.ok()) Branch (46:9): [True: 2.36M, False: 0]
|
47 | 2.36M | return; |
48 | 0 | const std::string errmsg = "Fatal LevelDB error: " + status.ToString(); |
49 | 0 | LogPrintf("%s\n", errmsg); |
50 | 0 | LogPrintf("You can use -debug=leveldb to get more complete diagnostic messages\n"); |
51 | 0 | throw dbwrapper_error(errmsg); |
52 | 2.36M | } |
53 | | |
54 | | class CBitcoinLevelDBLogger : public leveldb::Logger { |
55 | | public: |
56 | | // This code is adapted from posix_logger.h, which is why it is using vsprintf. |
57 | | // Please do not do this in normal code |
58 | 33.3k | void Logv(const char * format, va_list ap) override { |
59 | 33.3k | if (!LogAcceptCategory(BCLog::LEVELDB, BCLog::Level::Debug)) { Branch (59:17): [True: 33.3k, False: 0]
|
60 | 33.3k | return; |
61 | 33.3k | } |
62 | 0 | char buffer[500]; |
63 | 0 | for (int iter = 0; iter < 2; iter++) { Branch (63:32): [True: 0, False: 0]
|
64 | 0 | char* base; |
65 | 0 | int bufsize; |
66 | 0 | if (iter == 0) { Branch (66:21): [True: 0, False: 0]
|
67 | 0 | bufsize = sizeof(buffer); |
68 | 0 | base = buffer; |
69 | 0 | } |
70 | 0 | else { |
71 | 0 | bufsize = 30000; |
72 | 0 | base = new char[bufsize]; |
73 | 0 | } |
74 | 0 | char* p = base; |
75 | 0 | char* limit = base + bufsize; |
76 | | |
77 | | // Print the message |
78 | 0 | if (p < limit) { Branch (78:21): [True: 0, False: 0]
|
79 | 0 | va_list backup_ap; |
80 | 0 | va_copy(backup_ap, ap); |
81 | | // Do not use vsnprintf elsewhere in bitcoin source code, see above. |
82 | 0 | p += vsnprintf(p, limit - p, format, backup_ap); |
83 | 0 | va_end(backup_ap); |
84 | 0 | } |
85 | | |
86 | | // Truncate to available space if necessary |
87 | 0 | if (p >= limit) { Branch (87:21): [True: 0, False: 0]
|
88 | 0 | if (iter == 0) { Branch (88:25): [True: 0, False: 0]
|
89 | 0 | continue; // Try again with larger buffer |
90 | 0 | } |
91 | 0 | else { |
92 | 0 | p = limit - 1; |
93 | 0 | } |
94 | 0 | } |
95 | | |
96 | | // Add newline if necessary |
97 | 0 | if (p == base || p[-1] != '\n') { Branch (97:21): [True: 0, False: 0]
Branch (97:34): [True: 0, False: 0]
|
98 | 0 | *p++ = '\n'; |
99 | 0 | } |
100 | |
|
101 | 0 | assert(p <= limit); Branch (101:17): [True: 0, False: 0]
|
102 | 0 | base[std::min(bufsize - 1, (int)(p - base))] = '\0'; |
103 | 0 | LogDebug(BCLog::LEVELDB, "%s\n", util::RemoveSuffixView(base, "\n")); |
104 | 0 | if (base != buffer) { Branch (104:21): [True: 0, False: 0]
|
105 | 0 | delete[] base; |
106 | 0 | } |
107 | 0 | break; |
108 | 0 | } |
109 | 0 | } |
110 | | }; |
111 | | |
112 | 33.2k | static void SetMaxOpenFiles(leveldb::Options *options) { |
113 | | // On most platforms the default setting of max_open_files (which is 1000) |
114 | | // is optimal. On Windows using a large file count is OK because the handles |
115 | | // do not interfere with select() loops. On 64-bit Unix hosts this value is |
116 | | // also OK, because up to that amount LevelDB will use an mmap |
117 | | // implementation that does not use extra file descriptors (the fds are |
118 | | // closed after being mmap'ed). |
119 | | // |
120 | | // Increasing the value beyond the default is dangerous because LevelDB will |
121 | | // fall back to a non-mmap implementation when the file count is too large. |
122 | | // On 32-bit Unix host we should decrease the value because the handles use |
123 | | // up real fds, and we want to avoid fd exhaustion issues. |
124 | | // |
125 | | // See PR #12495 for further discussion. |
126 | | |
127 | 33.2k | int default_open_files = options->max_open_files; |
128 | 33.2k | #ifndef WIN32 |
129 | 33.2k | if (sizeof(void*) < 8) { Branch (129:9): [Folded - Ignored]
|
130 | 0 | options->max_open_files = 64; |
131 | 0 | } |
132 | 33.2k | #endif |
133 | 33.2k | LogDebug(BCLog::LEVELDB, "LevelDB using max_open_files=%d (default=%d)\n", |
134 | 33.2k | options->max_open_files, default_open_files); |
135 | 33.2k | } |
136 | | |
137 | | static leveldb::Options GetOptions(size_t nCacheSize) |
138 | 33.2k | { |
139 | 33.2k | leveldb::Options options; |
140 | 33.2k | options.block_cache = leveldb::NewLRUCache(nCacheSize / 2); |
141 | 33.2k | options.write_buffer_size = nCacheSize / 4; // up to two write buffers may be held in memory simultaneously |
142 | 33.2k | options.filter_policy = leveldb::NewBloomFilterPolicy(10); |
143 | 33.2k | options.compression = leveldb::kNoCompression; |
144 | 33.2k | options.info_log = new CBitcoinLevelDBLogger(); |
145 | 33.2k | if (leveldb::kMajorVersion > 1 || (leveldb::kMajorVersion == 1 && leveldb::kMinorVersion >= 16)) { Branch (145:9): [Folded - Ignored]
Branch (145:40): [Folded - Ignored]
Branch (145:71): [Folded - Ignored]
|
146 | | // LevelDB versions before 1.16 consider short writes to be corruption. Only trigger error |
147 | | // on corruption in later versions. |
148 | 33.2k | options.paranoid_checks = true; |
149 | 33.2k | } |
150 | 33.2k | options.max_file_size = std::max(options.max_file_size, DBWRAPPER_MAX_FILE_SIZE); |
151 | 33.2k | SetMaxOpenFiles(&options); |
152 | 33.2k | return options; |
153 | 33.2k | } |
154 | | |
155 | | struct CDBBatch::WriteBatchImpl { |
156 | | leveldb::WriteBatch batch; |
157 | | }; |
158 | | |
159 | | CDBBatch::CDBBatch(const CDBWrapper& _parent) |
160 | 2.32M | : parent{_parent}, |
161 | 2.32M | m_impl_batch{std::make_unique<CDBBatch::WriteBatchImpl>()} |
162 | 2.32M | { |
163 | 2.32M | Clear(); |
164 | 2.32M | }; |
165 | | |
166 | 2.32M | CDBBatch::~CDBBatch() = default; |
167 | | |
168 | | void CDBBatch::Clear() |
169 | 2.32M | { |
170 | 2.32M | m_impl_batch->batch.Clear(); |
171 | 2.32M | } |
172 | | |
173 | | void CDBBatch::WriteImpl(std::span<const std::byte> key, DataStream& ssValue) |
174 | 6.87M | { |
175 | 6.87M | leveldb::Slice slKey(CharCast(key.data()), key.size()); |
176 | 6.87M | ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent)); |
177 | 6.87M | leveldb::Slice slValue(CharCast(ssValue.data()), ssValue.size()); |
178 | 6.87M | m_impl_batch->batch.Put(slKey, slValue); |
179 | 6.87M | } |
180 | | |
181 | | void CDBBatch::EraseImpl(std::span<const std::byte> key) |
182 | 62.0k | { |
183 | 62.0k | leveldb::Slice slKey(CharCast(key.data()), key.size()); |
184 | 62.0k | m_impl_batch->batch.Delete(slKey); |
185 | 62.0k | } |
186 | | |
187 | | size_t CDBBatch::ApproximateSize() const |
188 | 2.25M | { |
189 | 2.25M | return m_impl_batch->batch.ApproximateSize(); |
190 | 2.25M | } |
191 | | |
192 | | struct LevelDBContext { |
193 | | //! custom environment this database is using (may be nullptr in case of default environment) |
194 | | leveldb::Env* penv; |
195 | | |
196 | | //! database options used |
197 | | leveldb::Options options; |
198 | | |
199 | | //! options used when reading from the database |
200 | | leveldb::ReadOptions readoptions; |
201 | | |
202 | | //! options used when iterating over values of the database |
203 | | leveldb::ReadOptions iteroptions; |
204 | | |
205 | | //! options used when writing to the database |
206 | | leveldb::WriteOptions writeoptions; |
207 | | |
208 | | //! options used when sync writing to the database |
209 | | leveldb::WriteOptions syncoptions; |
210 | | |
211 | | //! the database itself |
212 | | leveldb::DB* pdb; |
213 | | }; |
214 | | |
215 | | CDBWrapper::CDBWrapper(const DBParams& params) |
216 | 33.2k | : m_db_context{std::make_unique<LevelDBContext>()}, m_name{fs::PathToString(params.path.stem())}, m_path{params.path}, m_is_memory{params.memory_only} |
217 | 33.2k | { |
218 | 33.2k | DBContext().penv = nullptr; |
219 | 33.2k | DBContext().readoptions.verify_checksums = true; |
220 | 33.2k | DBContext().iteroptions.verify_checksums = true; |
221 | 33.2k | DBContext().iteroptions.fill_cache = false; |
222 | 33.2k | DBContext().syncoptions.sync = true; |
223 | 33.2k | DBContext().options = GetOptions(params.cache_bytes); |
224 | 33.2k | DBContext().options.create_if_missing = true; |
225 | 33.2k | if (params.memory_only) { Branch (225:9): [True: 0, False: 33.2k]
|
226 | 0 | DBContext().penv = leveldb::NewMemEnv(leveldb::Env::Default()); |
227 | 0 | DBContext().options.env = DBContext().penv; |
228 | 33.2k | } else { |
229 | 33.2k | if (params.wipe_data) { Branch (229:13): [True: 0, False: 33.2k]
|
230 | 0 | LogPrintf("Wiping LevelDB in %s\n", fs::PathToString(params.path)); |
231 | 0 | leveldb::Status result = leveldb::DestroyDB(fs::PathToString(params.path), DBContext().options); |
232 | 0 | HandleError(result); |
233 | 0 | } |
234 | 33.2k | TryCreateDirectories(params.path); |
235 | 33.2k | LogPrintf("Opening LevelDB in %s\n", fs::PathToString(params.path)); |
236 | 33.2k | } |
237 | | // PathToString() return value is safe to pass to leveldb open function, |
238 | | // because on POSIX leveldb passes the byte string directly to ::open(), and |
239 | | // on Windows it converts from UTF-8 to UTF-16 before calling ::CreateFileW |
240 | | // (see env_posix.cc and env_windows.cc). |
241 | 33.2k | leveldb::Status status = leveldb::DB::Open(DBContext().options, fs::PathToString(params.path), &DBContext().pdb); |
242 | 33.2k | HandleError(status); |
243 | 33.2k | LogPrintf("Opened LevelDB successfully\n"); |
244 | | |
245 | 33.2k | if (params.options.force_compact) { Branch (245:9): [True: 0, False: 33.2k]
|
246 | 0 | LogPrintf("Starting database compaction of %s\n", fs::PathToString(params.path)); |
247 | 0 | DBContext().pdb->CompactRange(nullptr, nullptr); |
248 | 0 | LogPrintf("Finished database compaction of %s\n", fs::PathToString(params.path)); |
249 | 0 | } |
250 | | |
251 | | // The base-case obfuscation key, which is a noop. |
252 | 33.2k | obfuscate_key = std::vector<unsigned char>(OBFUSCATE_KEY_NUM_BYTES, '\000'); |
253 | | |
254 | 33.2k | bool key_exists = Read(OBFUSCATE_KEY_KEY, obfuscate_key); |
255 | | |
256 | 33.2k | if (!key_exists && params.obfuscate && IsEmpty()) { Branch (256:9): [True: 33.2k, False: 0]
Branch (256:24): [True: 11.0k, False: 22.1k]
Branch (256:44): [True: 11.0k, False: 0]
|
257 | | // Initialize non-degenerate obfuscation if it won't upset |
258 | | // existing, non-obfuscated data. |
259 | 11.0k | std::vector<unsigned char> new_key = CreateObfuscateKey(); |
260 | | |
261 | | // Write `new_key` so we don't obfuscate the key with itself |
262 | 11.0k | Write(OBFUSCATE_KEY_KEY, new_key); |
263 | 11.0k | obfuscate_key = new_key; |
264 | | |
265 | 11.0k | LogPrintf("Wrote new obfuscate key for %s: %s\n", fs::PathToString(params.path), HexStr(obfuscate_key)); |
266 | 11.0k | } |
267 | | |
268 | 33.2k | LogPrintf("Using obfuscation key for %s: %s\n", fs::PathToString(params.path), HexStr(obfuscate_key)); |
269 | 33.2k | } |
270 | | |
271 | | CDBWrapper::~CDBWrapper() |
272 | 33.2k | { |
273 | 33.2k | delete DBContext().pdb; |
274 | 33.2k | DBContext().pdb = nullptr; |
275 | 33.2k | delete DBContext().options.filter_policy; |
276 | 33.2k | DBContext().options.filter_policy = nullptr; |
277 | 33.2k | delete DBContext().options.info_log; |
278 | 33.2k | DBContext().options.info_log = nullptr; |
279 | 33.2k | delete DBContext().options.block_cache; |
280 | 33.2k | DBContext().options.block_cache = nullptr; |
281 | 33.2k | delete DBContext().penv; |
282 | 33.2k | DBContext().options.env = nullptr; |
283 | 33.2k | } |
284 | | |
285 | | bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync) |
286 | 2.32M | { |
287 | 2.32M | const bool log_memory = LogAcceptCategory(BCLog::LEVELDB, BCLog::Level::Debug); |
288 | 2.32M | double mem_before = 0; |
289 | 2.32M | if (log_memory) { Branch (289:9): [True: 0, False: 2.32M]
|
290 | 0 | mem_before = DynamicMemoryUsage() / 1024.0 / 1024; |
291 | 0 | } |
292 | 2.32M | leveldb::Status status = DBContext().pdb->Write(fSync ? DBContext().syncoptions : DBContext().writeoptions, &batch.m_impl_batch->batch); Branch (292:53): [True: 29.2k, False: 2.30M]
|
293 | 2.32M | HandleError(status); |
294 | 2.32M | if (log_memory) { Branch (294:9): [True: 0, False: 2.32M]
|
295 | 0 | double mem_after = DynamicMemoryUsage() / 1024.0 / 1024; |
296 | 0 | LogDebug(BCLog::LEVELDB, "WriteBatch memory usage: db=%s, before=%.1fMiB, after=%.1fMiB\n", |
297 | 0 | m_name, mem_before, mem_after); |
298 | 0 | } |
299 | 2.32M | return true; |
300 | 2.32M | } |
301 | | |
302 | | size_t CDBWrapper::DynamicMemoryUsage() const |
303 | 0 | { |
304 | 0 | std::string memory; |
305 | 0 | std::optional<size_t> parsed; |
306 | 0 | if (!DBContext().pdb->GetProperty("leveldb.approximate-memory-usage", &memory) || !(parsed = ToIntegral<size_t>(memory))) { Branch (306:9): [True: 0, False: 0]
Branch (306:9): [True: 0, False: 0]
Branch (306:87): [True: 0, False: 0]
|
307 | 0 | LogDebug(BCLog::LEVELDB, "Failed to get approximate-memory-usage property\n"); |
308 | 0 | return 0; |
309 | 0 | } |
310 | 0 | return parsed.value(); |
311 | 0 | } |
312 | | |
313 | | // Prefixed with null character to avoid collisions with other keys |
314 | | // |
315 | | // We must use a string constructor which specifies length so that we copy |
316 | | // past the null-terminator. |
317 | | const std::string CDBWrapper::OBFUSCATE_KEY_KEY("\000obfuscate_key", 14); |
318 | | |
319 | | const unsigned int CDBWrapper::OBFUSCATE_KEY_NUM_BYTES = 8; |
320 | | |
321 | | /** |
322 | | * Returns a string (consisting of 8 random bytes) suitable for use as an |
323 | | * obfuscating XOR key. |
324 | | */ |
325 | | std::vector<unsigned char> CDBWrapper::CreateObfuscateKey() const |
326 | 11.0k | { |
327 | 11.0k | std::vector<uint8_t> ret(OBFUSCATE_KEY_NUM_BYTES); |
328 | 11.0k | GetRandBytes(ret); |
329 | 11.0k | return ret; |
330 | 11.0k | } |
331 | | |
332 | | std::optional<std::string> CDBWrapper::ReadImpl(std::span<const std::byte> key) const |
333 | 5.04M | { |
334 | 5.04M | leveldb::Slice slKey(CharCast(key.data()), key.size()); |
335 | 5.04M | std::string strValue; |
336 | 5.04M | leveldb::Status status = DBContext().pdb->Get(DBContext().readoptions, slKey, &strValue); |
337 | 5.04M | if (!status.ok()) { Branch (337:9): [True: 5.02M, False: 20.7k]
|
338 | 5.02M | if (status.IsNotFound()) Branch (338:13): [True: 5.02M, False: 0]
|
339 | 5.02M | return std::nullopt; |
340 | 0 | LogPrintf("LevelDB read failure: %s\n", status.ToString()); |
341 | 0 | HandleError(status); |
342 | 0 | } |
343 | 20.7k | return strValue; |
344 | 5.04M | } |
345 | | |
346 | | bool CDBWrapper::ExistsImpl(std::span<const std::byte> key) const |
347 | 22.1k | { |
348 | 22.1k | leveldb::Slice slKey(CharCast(key.data()), key.size()); |
349 | | |
350 | 22.1k | std::string strValue; |
351 | 22.1k | leveldb::Status status = DBContext().pdb->Get(DBContext().readoptions, slKey, &strValue); |
352 | 22.1k | if (!status.ok()) { Branch (352:9): [True: 22.1k, False: 0]
|
353 | 22.1k | if (status.IsNotFound()) Branch (353:13): [True: 22.1k, False: 0]
|
354 | 22.1k | return false; |
355 | 0 | LogPrintf("LevelDB read failure: %s\n", status.ToString()); |
356 | 0 | HandleError(status); |
357 | 0 | } |
358 | 0 | return true; |
359 | 22.1k | } |
360 | | |
361 | | size_t CDBWrapper::EstimateSizeImpl(std::span<const std::byte> key1, std::span<const std::byte> key2) const |
362 | 0 | { |
363 | 0 | leveldb::Slice slKey1(CharCast(key1.data()), key1.size()); |
364 | 0 | leveldb::Slice slKey2(CharCast(key2.data()), key2.size()); |
365 | 0 | uint64_t size = 0; |
366 | 0 | leveldb::Range range(slKey1, slKey2); |
367 | 0 | DBContext().pdb->GetApproximateSizes(&range, 1, &size); |
368 | 0 | return size; |
369 | 0 | } |
370 | | |
371 | | bool CDBWrapper::IsEmpty() |
372 | 11.0k | { |
373 | 11.0k | std::unique_ptr<CDBIterator> it(NewIterator()); |
374 | 11.0k | it->SeekToFirst(); |
375 | 11.0k | return !(it->Valid()); |
376 | 11.0k | } |
377 | | |
378 | | struct CDBIterator::IteratorImpl { |
379 | | const std::unique_ptr<leveldb::Iterator> iter; |
380 | | |
381 | 35.8k | explicit IteratorImpl(leveldb::Iterator* _iter) : iter{_iter} {} |
382 | | }; |
383 | | |
384 | 35.8k | CDBIterator::CDBIterator(const CDBWrapper& _parent, std::unique_ptr<IteratorImpl> _piter) : parent(_parent), |
385 | 35.8k | m_impl_iter(std::move(_piter)) {} |
386 | | |
387 | | CDBIterator* CDBWrapper::NewIterator() |
388 | 35.8k | { |
389 | 35.8k | return new CDBIterator{*this, std::make_unique<CDBIterator::IteratorImpl>(DBContext().pdb->NewIterator(DBContext().iteroptions))}; |
390 | 35.8k | } |
391 | | |
392 | | void CDBIterator::SeekImpl(std::span<const std::byte> key) |
393 | 24.8k | { |
394 | 24.8k | leveldb::Slice slKey(CharCast(key.data()), key.size()); |
395 | 24.8k | m_impl_iter->iter->Seek(slKey); |
396 | 24.8k | } |
397 | | |
398 | | std::span<const std::byte> CDBIterator::GetKeyImpl() const |
399 | 6.51k | { |
400 | 6.51k | return MakeByteSpan(m_impl_iter->iter->key()); |
401 | 6.51k | } |
402 | | |
403 | | std::span<const std::byte> CDBIterator::GetValueImpl() const |
404 | 6.51k | { |
405 | 6.51k | return MakeByteSpan(m_impl_iter->iter->value()); |
406 | 6.51k | } |
407 | | |
408 | 35.8k | CDBIterator::~CDBIterator() = default; |
409 | 33.2k | bool CDBIterator::Valid() const { return m_impl_iter->iter->Valid(); } |
410 | 11.0k | void CDBIterator::SeekToFirst() { m_impl_iter->iter->SeekToFirst(); } |
411 | 6.51k | void CDBIterator::Next() { m_impl_iter->iter->Next(); } |
412 | | |
413 | | namespace dbwrapper_private { |
414 | | |
415 | | const std::vector<unsigned char>& GetObfuscateKey(const CDBWrapper &w) |
416 | 6.88M | { |
417 | 6.88M | return w.obfuscate_key; |
418 | 6.88M | } |
419 | | |
420 | | } // namespace dbwrapper_private |