Skip to content

Commit b565485

Browse files
committed
Merge bitcoin/bitcoin#28186: kernel: Prune leveldb headers
d8f1222 refactor: Correct dbwrapper key naming (TheCharlatan) be8f159 build: Remove leveldb from BITCOIN_INCLUDES (TheCharlatan) c95b37d refactor: Move CDBWrapper leveldb members to their own context struct (TheCharlatan) c534a61 refactor: Split dbwrapper CDBWrapper::EstimateSize implementation (TheCharlatan) 5864488 refactor: Move HandleError to dbwrapper implementation (TheCharlatan) dede0ee refactor: Split dbwrapper CDBWrapper::Exists implementation (TheCharlatan) a5c2eb5 refactor: Fix logging.h includes (TheCharlatan) 84058e0 refactor: Split dbwrapper CDBWrapper::Read implementation (TheCharlatan) e4af240 refactor: Pimpl leveldb::Iterator for CDBIterator (TheCharlatan) ef941ff refactor: Split dbwrapper CDBIterator::GetValue implementation (TheCharlatan) b7a1ab5 refactor: Split dbwrapper CDBIterator::GetKey implementation (TheCharlatan) d743790 refactor: Split dbwrapper CDBIterator::Seek implementation (TheCharlatan) ea8135d refactor: Pimpl leveldb::batch for CDBBatch (TheCharlatan) b9870c9 refactor: Split dbwrapper CDBatch::Erase implementation (TheCharlatan) 532ee81 refactor: Split dbwrapper CDBBatch::Write implementation (TheCharlatan) afc534d refactor: Wrap DestroyDB in dbwrapper helper (TheCharlatan) Pull request description: Leveldb headers are currently included in the `dbwrapper.h` file and thus available to many of Bitcoin Core's source files. However, leveldb-specific functionality should be abstracted by the `dbwrapper` and does not need to be available to the rest of the code. Having leveldb included in a widely-used header such as `dbwrapper.h` bloats the entire project's header tree. The `dbwrapper` is a key component of the libbitcoinkernel library. Future users of this library would not want to contend with having the leveldb headers exposed and potentially polluting their project's namespace. For these reasons, the leveldb headers are removed from the `dbwrapper` by moving leveldb-specific code to the implementation file and creating a [pimpl](https://en.cppreference.com/w/cpp/language/pimpl) where leveldb member variables are indispensable. As a final step, the leveldb include flags are removed from the `BITCOIN_INCLUDES` and moved to places where the dbwrapper is compiled. --- This pull request is part of the [libbitcoinkernel project](bitcoin/bitcoin#27587), and more specifically its stage 1 step 3 "Decouple most non-consensus headers from libbitcoinkernel". ACKs for top commit: stickies-v: re-ACK bitcoin/bitcoin@d8f1222 MarcoFalke: ACK d8f1222 🔠 Tree-SHA512: 0f58309be165af0162e648233451cd80fda88726fc10c0da7bfe4ec2ffa9afe63fbf7ffae9493698d3f39653b4ad870c372eee652ecc90ab1c29d86c387070f3
2 parents 064919e + d8f1222 commit b565485

15 files changed

+254
-159
lines changed

src/Makefile.am

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ check_PROGRAMS =
2424
TESTS =
2525
BENCHMARKS =
2626

27-
BITCOIN_INCLUDES=-I$(builddir) -I$(srcdir)/$(MINISKETCH_INCLUDE_DIR_INT) -I$(srcdir)/secp256k1/include -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT) $(LEVELDB_CPPFLAGS)
27+
BITCOIN_INCLUDES=-I$(builddir) -I$(srcdir)/$(MINISKETCH_INCLUDE_DIR_INT) -I$(srcdir)/secp256k1/include -I$(srcdir)/$(UNIVALUE_INCLUDE_DIR_INT)
2828

2929
LIBBITCOIN_NODE=libbitcoin_node.a
3030
LIBBITCOIN_COMMON=libbitcoin_common.a
@@ -370,7 +370,7 @@ obj/build.h: FORCE
370370
libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h
371371

372372
# node #
373-
libbitcoin_node_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BOOST_CPPFLAGS) $(MINIUPNPC_CPPFLAGS) $(NATPMP_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
373+
libbitcoin_node_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(LEVELDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(MINIUPNPC_CPPFLAGS) $(NATPMP_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
374374
libbitcoin_node_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
375375
libbitcoin_node_a_SOURCES = \
376376
addrdb.cpp \

src/blockencodings.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <consensus/validation.h>
1010
#include <crypto/sha256.h>
1111
#include <crypto/siphash.h>
12+
#include <logging.h>
1213
#include <random.h>
1314
#include <streams.h>
1415
#include <txmempool.h>

src/dbwrapper.cpp

Lines changed: 192 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44

55
#include <dbwrapper.h>
66

7+
#include <clientversion.h>
78
#include <logging.h>
89
#include <random.h>
9-
#include <tinyformat.h>
10+
#include <serialize.h>
11+
#include <span.h>
12+
#include <streams.h>
1013
#include <util/fs.h>
1114
#include <util/fs_helpers.h>
1215
#include <util/strencodings.h>
@@ -23,9 +26,31 @@
2326
#include <leveldb/helpers/memenv/memenv.h>
2427
#include <leveldb/iterator.h>
2528
#include <leveldb/options.h>
29+
#include <leveldb/slice.h>
2630
#include <leveldb/status.h>
31+
#include <leveldb/write_batch.h>
2732
#include <memory>
2833
#include <optional>
34+
#include <utility>
35+
36+
static auto CharCast(const std::byte* data) { return reinterpret_cast<const char*>(data); }
37+
38+
bool DestroyDB(const std::string& path_str)
39+
{
40+
return leveldb::DestroyDB(path_str, {}).ok();
41+
}
42+
43+
/** Handle database error by throwing dbwrapper_error exception.
44+
*/
45+
static void HandleError(const leveldb::Status& status)
46+
{
47+
if (status.ok())
48+
return;
49+
const std::string errmsg = "Fatal LevelDB error: " + status.ToString();
50+
LogPrintf("%s\n", errmsg);
51+
LogPrintf("You can use -debug=leveldb to get more complete diagnostic messages\n");
52+
throw dbwrapper_error(errmsg);
53+
}
2954

3055
class CBitcoinLevelDBLogger : public leveldb::Logger {
3156
public:
@@ -127,24 +152,91 @@ static leveldb::Options GetOptions(size_t nCacheSize)
127152
return options;
128153
}
129154

155+
struct CDBBatch::WriteBatchImpl {
156+
leveldb::WriteBatch batch;
157+
};
158+
159+
CDBBatch::CDBBatch(const CDBWrapper& _parent) : parent(_parent),
160+
m_impl_batch{std::make_unique<CDBBatch::WriteBatchImpl>()},
161+
ssValue(SER_DISK, CLIENT_VERSION){};
162+
163+
CDBBatch::~CDBBatch() = default;
164+
165+
void CDBBatch::Clear()
166+
{
167+
m_impl_batch->batch.Clear();
168+
size_estimate = 0;
169+
}
170+
171+
void CDBBatch::WriteImpl(Span<const std::byte> key, CDataStream& ssValue)
172+
{
173+
leveldb::Slice slKey(CharCast(key.data()), key.size());
174+
ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent));
175+
leveldb::Slice slValue(CharCast(ssValue.data()), ssValue.size());
176+
m_impl_batch->batch.Put(slKey, slValue);
177+
// LevelDB serializes writes as:
178+
// - byte: header
179+
// - varint: key length (1 byte up to 127B, 2 bytes up to 16383B, ...)
180+
// - byte[]: key
181+
// - varint: value length
182+
// - byte[]: value
183+
// The formula below assumes the key and value are both less than 16k.
184+
size_estimate += 3 + (slKey.size() > 127) + slKey.size() + (slValue.size() > 127) + slValue.size();
185+
}
186+
187+
void CDBBatch::EraseImpl(Span<const std::byte> key)
188+
{
189+
leveldb::Slice slKey(CharCast(key.data()), key.size());
190+
m_impl_batch->batch.Delete(slKey);
191+
// LevelDB serializes erases as:
192+
// - byte: header
193+
// - varint: key length
194+
// - byte[]: key
195+
// The formula below assumes the key is less than 16kB.
196+
size_estimate += 2 + (slKey.size() > 127) + slKey.size();
197+
}
198+
199+
struct LevelDBContext {
200+
//! custom environment this database is using (may be nullptr in case of default environment)
201+
leveldb::Env* penv;
202+
203+
//! database options used
204+
leveldb::Options options;
205+
206+
//! options used when reading from the database
207+
leveldb::ReadOptions readoptions;
208+
209+
//! options used when iterating over values of the database
210+
leveldb::ReadOptions iteroptions;
211+
212+
//! options used when writing to the database
213+
leveldb::WriteOptions writeoptions;
214+
215+
//! options used when sync writing to the database
216+
leveldb::WriteOptions syncoptions;
217+
218+
//! the database itself
219+
leveldb::DB* pdb;
220+
};
221+
130222
CDBWrapper::CDBWrapper(const DBParams& params)
131-
: m_name{fs::PathToString(params.path.stem())}, m_path{params.path}, m_is_memory{params.memory_only}
223+
: m_db_context{std::make_unique<LevelDBContext>()}, m_name{fs::PathToString(params.path.stem())}, m_path{params.path}, m_is_memory{params.memory_only}
132224
{
133-
penv = nullptr;
134-
readoptions.verify_checksums = true;
135-
iteroptions.verify_checksums = true;
136-
iteroptions.fill_cache = false;
137-
syncoptions.sync = true;
138-
options = GetOptions(params.cache_bytes);
139-
options.create_if_missing = true;
225+
DBContext().penv = nullptr;
226+
DBContext().readoptions.verify_checksums = true;
227+
DBContext().iteroptions.verify_checksums = true;
228+
DBContext().iteroptions.fill_cache = false;
229+
DBContext().syncoptions.sync = true;
230+
DBContext().options = GetOptions(params.cache_bytes);
231+
DBContext().options.create_if_missing = true;
140232
if (params.memory_only) {
141-
penv = leveldb::NewMemEnv(leveldb::Env::Default());
142-
options.env = penv;
233+
DBContext().penv = leveldb::NewMemEnv(leveldb::Env::Default());
234+
DBContext().options.env = DBContext().penv;
143235
} else {
144236
if (params.wipe_data) {
145237
LogPrintf("Wiping LevelDB in %s\n", fs::PathToString(params.path));
146-
leveldb::Status result = leveldb::DestroyDB(fs::PathToString(params.path), options);
147-
dbwrapper_private::HandleError(result);
238+
leveldb::Status result = leveldb::DestroyDB(fs::PathToString(params.path), DBContext().options);
239+
HandleError(result);
148240
}
149241
TryCreateDirectories(params.path);
150242
LogPrintf("Opening LevelDB in %s\n", fs::PathToString(params.path));
@@ -153,13 +245,13 @@ CDBWrapper::CDBWrapper(const DBParams& params)
153245
// because on POSIX leveldb passes the byte string directly to ::open(), and
154246
// on Windows it converts from UTF-8 to UTF-16 before calling ::CreateFileW
155247
// (see env_posix.cc and env_windows.cc).
156-
leveldb::Status status = leveldb::DB::Open(options, fs::PathToString(params.path), &pdb);
157-
dbwrapper_private::HandleError(status);
248+
leveldb::Status status = leveldb::DB::Open(DBContext().options, fs::PathToString(params.path), &DBContext().pdb);
249+
HandleError(status);
158250
LogPrintf("Opened LevelDB successfully\n");
159251

160252
if (params.options.force_compact) {
161253
LogPrintf("Starting database compaction of %s\n", fs::PathToString(params.path));
162-
pdb->CompactRange(nullptr, nullptr);
254+
DBContext().pdb->CompactRange(nullptr, nullptr);
163255
LogPrintf("Finished database compaction of %s\n", fs::PathToString(params.path));
164256
}
165257

@@ -185,16 +277,16 @@ CDBWrapper::CDBWrapper(const DBParams& params)
185277

186278
CDBWrapper::~CDBWrapper()
187279
{
188-
delete pdb;
189-
pdb = nullptr;
190-
delete options.filter_policy;
191-
options.filter_policy = nullptr;
192-
delete options.info_log;
193-
options.info_log = nullptr;
194-
delete options.block_cache;
195-
options.block_cache = nullptr;
196-
delete penv;
197-
options.env = nullptr;
280+
delete DBContext().pdb;
281+
DBContext().pdb = nullptr;
282+
delete DBContext().options.filter_policy;
283+
DBContext().options.filter_policy = nullptr;
284+
delete DBContext().options.info_log;
285+
DBContext().options.info_log = nullptr;
286+
delete DBContext().options.block_cache;
287+
DBContext().options.block_cache = nullptr;
288+
delete DBContext().penv;
289+
DBContext().options.env = nullptr;
198290
}
199291

200292
bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync)
@@ -204,8 +296,8 @@ bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync)
204296
if (log_memory) {
205297
mem_before = DynamicMemoryUsage() / 1024.0 / 1024;
206298
}
207-
leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch);
208-
dbwrapper_private::HandleError(status);
299+
leveldb::Status status = DBContext().pdb->Write(fSync ? DBContext().syncoptions : DBContext().writeoptions, &batch.m_impl_batch->batch);
300+
HandleError(status);
209301
if (log_memory) {
210302
double mem_after = DynamicMemoryUsage() / 1024.0 / 1024;
211303
LogPrint(BCLog::LEVELDB, "WriteBatch memory usage: db=%s, before=%.1fMiB, after=%.1fMiB\n",
@@ -218,7 +310,7 @@ size_t CDBWrapper::DynamicMemoryUsage() const
218310
{
219311
std::string memory;
220312
std::optional<size_t> parsed;
221-
if (!pdb->GetProperty("leveldb.approximate-memory-usage", &memory) || !(parsed = ToIntegral<size_t>(memory))) {
313+
if (!DBContext().pdb->GetProperty("leveldb.approximate-memory-usage", &memory) || !(parsed = ToIntegral<size_t>(memory))) {
222314
LogPrint(BCLog::LEVELDB, "Failed to get approximate-memory-usage property\n");
223315
return 0;
224316
}
@@ -244,30 +336,89 @@ std::vector<unsigned char> CDBWrapper::CreateObfuscateKey() const
244336
return ret;
245337
}
246338

339+
std::optional<std::string> CDBWrapper::ReadImpl(Span<const std::byte> key) const
340+
{
341+
leveldb::Slice slKey(CharCast(key.data()), key.size());
342+
std::string strValue;
343+
leveldb::Status status = DBContext().pdb->Get(DBContext().readoptions, slKey, &strValue);
344+
if (!status.ok()) {
345+
if (status.IsNotFound())
346+
return std::nullopt;
347+
LogPrintf("LevelDB read failure: %s\n", status.ToString());
348+
HandleError(status);
349+
}
350+
return strValue;
351+
}
352+
353+
bool CDBWrapper::ExistsImpl(Span<const std::byte> key) const
354+
{
355+
leveldb::Slice slKey(CharCast(key.data()), key.size());
356+
357+
std::string strValue;
358+
leveldb::Status status = DBContext().pdb->Get(DBContext().readoptions, slKey, &strValue);
359+
if (!status.ok()) {
360+
if (status.IsNotFound())
361+
return false;
362+
LogPrintf("LevelDB read failure: %s\n", status.ToString());
363+
HandleError(status);
364+
}
365+
return true;
366+
}
367+
368+
size_t CDBWrapper::EstimateSizeImpl(Span<const std::byte> key1, Span<const std::byte> key2) const
369+
{
370+
leveldb::Slice slKey1(CharCast(key1.data()), key1.size());
371+
leveldb::Slice slKey2(CharCast(key2.data()), key2.size());
372+
uint64_t size = 0;
373+
leveldb::Range range(slKey1, slKey2);
374+
DBContext().pdb->GetApproximateSizes(&range, 1, &size);
375+
return size;
376+
}
377+
247378
bool CDBWrapper::IsEmpty()
248379
{
249380
std::unique_ptr<CDBIterator> it(NewIterator());
250381
it->SeekToFirst();
251382
return !(it->Valid());
252383
}
253384

254-
CDBIterator::~CDBIterator() { delete piter; }
255-
bool CDBIterator::Valid() const { return piter->Valid(); }
256-
void CDBIterator::SeekToFirst() { piter->SeekToFirst(); }
257-
void CDBIterator::Next() { piter->Next(); }
385+
struct CDBIterator::IteratorImpl {
386+
const std::unique_ptr<leveldb::Iterator> iter;
258387

259-
namespace dbwrapper_private {
388+
explicit IteratorImpl(leveldb::Iterator* _iter) : iter{_iter} {}
389+
};
260390

261-
void HandleError(const leveldb::Status& status)
391+
CDBIterator::CDBIterator(const CDBWrapper& _parent, std::unique_ptr<IteratorImpl> _piter) : parent(_parent),
392+
m_impl_iter(std::move(_piter)) {}
393+
394+
CDBIterator* CDBWrapper::NewIterator()
262395
{
263-
if (status.ok())
264-
return;
265-
const std::string errmsg = "Fatal LevelDB error: " + status.ToString();
266-
LogPrintf("%s\n", errmsg);
267-
LogPrintf("You can use -debug=leveldb to get more complete diagnostic messages\n");
268-
throw dbwrapper_error(errmsg);
396+
return new CDBIterator{*this, std::make_unique<CDBIterator::IteratorImpl>(DBContext().pdb->NewIterator(DBContext().iteroptions))};
269397
}
270398

399+
void CDBIterator::SeekImpl(Span<const std::byte> key)
400+
{
401+
leveldb::Slice slKey(CharCast(key.data()), key.size());
402+
m_impl_iter->iter->Seek(slKey);
403+
}
404+
405+
Span<const std::byte> CDBIterator::GetKeyImpl() const
406+
{
407+
return MakeByteSpan(m_impl_iter->iter->key());
408+
}
409+
410+
Span<const std::byte> CDBIterator::GetValueImpl() const
411+
{
412+
return MakeByteSpan(m_impl_iter->iter->value());
413+
}
414+
415+
CDBIterator::~CDBIterator() = default;
416+
bool CDBIterator::Valid() const { return m_impl_iter->iter->Valid(); }
417+
void CDBIterator::SeekToFirst() { m_impl_iter->iter->SeekToFirst(); }
418+
void CDBIterator::Next() { m_impl_iter->iter->Next(); }
419+
420+
namespace dbwrapper_private {
421+
271422
const std::vector<unsigned char>& GetObfuscateKey(const CDBWrapper &w)
272423
{
273424
return w.obfuscate_key;

0 commit comments

Comments
 (0)