Branch data Line data Source code
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 : : #ifndef BITCOIN_DBWRAPPER_H
6 : : #define BITCOIN_DBWRAPPER_H
7 : :
8 : : #include <attributes.h>
9 : : #include <serialize.h>
10 : : #include <span.h>
11 : : #include <streams.h>
12 : : #include <util/byte_units.h>
13 : : #include <util/check.h>
14 : : #include <util/expected.h>
15 : : #include <util/fs.h>
16 : : #include <util/obfuscation.h>
17 : :
18 : : #include <cstddef>
19 : : #include <cstdint>
20 : : #include <exception>
21 : : #include <memory>
22 : : #include <optional>
23 : : #include <span>
24 : : #include <stdexcept>
25 : : #include <string>
26 : :
27 : : namespace leveldb {
28 : : class Env;
29 : : } // namespace leveldb
30 : :
31 : : inline constexpr size_t DBWRAPPER_PREALLOC_KEY_SIZE = 64;
32 : : inline constexpr size_t DBWRAPPER_PREALLOC_VALUE_SIZE = 1024;
33 : : inline constexpr size_t DBWRAPPER_MAX_FILE_SIZE{32_MiB};
34 : :
35 : : //! User-controlled performance and debug options.
36 : : struct DBOptions {
37 : : //! Compact database on startup.
38 : : bool force_compact = false;
39 : : };
40 : :
41 : : //! Application-specific storage settings.
42 [ + - ]: 263136 : struct DBParams {
[ + - + - ]
43 : : //! Location in the filesystem where leveldb data will be stored.
44 : : fs::path path;
45 : : //! Configures various leveldb cache settings.
46 : : uint64_t cache_bytes;
47 : : //! If true, use leveldb's memory environment.
48 : : bool memory_only = false;
49 : : //! If true, remove all existing data.
50 : : bool wipe_data = false;
51 : : //! If true, store data obfuscated via simple XOR. If false, XOR with a
52 : : //! zero'd byte array.
53 : : bool obfuscate = false;
54 : : //! If true, build a LevelDB bloom filter to accelerate point lookups.
55 : : bool bloom_filter = true;
56 : : //! Passed-through options.
57 : : DBOptions options{};
58 : : //! If non-null, use this as the leveldb::Env instead of the default.
59 : : //! Caller retains ownership.
60 : : leveldb::Env* testing_env = nullptr;
61 : : //! Maximum LevelDB SST file size. Larger values reduce the frequency
62 : : //! of compactions but increase their duration.
63 : : size_t max_file_size = DBWRAPPER_MAX_FILE_SIZE;
64 : : };
65 : :
66 : : class dbwrapper_error : public std::runtime_error
67 : : {
68 : : public:
69 [ # # ]: 0 : explicit dbwrapper_error(const std::string& msg) : std::runtime_error(msg) {}
[ # # # # ]
70 : : };
71 : :
72 : : class CDBWrapper;
73 : :
74 : : /** These should be considered an implementation detail of the specific database.
75 : : */
76 : : namespace dbwrapper_private {
77 : :
78 : : /** Work around circular dependency, as well as for testing in dbwrapper_tests.
79 : : * Database obfuscation should be considered an implementation detail of the
80 : : * specific database.
81 : : */
82 : : const Obfuscation& GetObfuscation(const CDBWrapper&);
83 : : }; // namespace dbwrapper_private
84 : :
85 : : bool DestroyDB(const std::string& path_str);
86 : :
87 : : /** Batch of changes queued to be written to a CDBWrapper */
88 : : class CDBBatch
89 : : {
90 : : friend class CDBWrapper;
91 : :
92 : : private:
93 : : const CDBWrapper &parent;
94 : :
95 : : struct WriteBatchImpl;
96 : : const std::unique_ptr<WriteBatchImpl> m_impl_batch;
97 : :
98 : : DataStream m_key_scratch{};
99 : : DataStream m_value_scratch{};
100 : :
101 : : void WriteImpl(std::span<const std::byte> key, DataStream& value);
102 : : void EraseImpl(std::span<const std::byte> key);
103 : :
104 : : public:
105 : : /**
106 : : * @param[in] _parent CDBWrapper that this batch is to be submitted to
107 : : */
108 : : explicit CDBBatch(const CDBWrapper& _parent);
109 : : ~CDBBatch();
110 : : void Clear();
111 : :
112 : : template <typename K, typename V>
113 : 9810368 : void Write(const K& key, const V& value)
114 : : {
115 : 9810368 : ScopedDataStreamUsage scoped_key{m_key_scratch}, scoped_value{m_value_scratch};
116 [ + - ]: 9810368 : m_key_scratch << key;
117 [ + - ]: 9810368 : m_value_scratch << value;
118 [ - + + - ]: 9810368 : WriteImpl(m_key_scratch, m_value_scratch);
119 : 9810368 : }
120 : :
121 : : template <typename K>
122 : 6497581 : void Erase(const K& key)
123 : : {
124 : 6497581 : ScopedDataStreamUsage scoped_key{m_key_scratch};
125 [ + - ]: 6497581 : m_key_scratch << key;
126 [ - + + - ]: 6497581 : EraseImpl(m_key_scratch);
127 : 6497581 : }
128 : :
129 : : size_t ApproximateSize() const;
130 : : };
131 : :
132 : : class CDBIterator
133 : : {
134 : : public:
135 : : struct IteratorImpl;
136 : :
137 : : private:
138 : : const CDBWrapper &parent;
139 : : const std::unique_ptr<IteratorImpl> m_impl_iter;
140 : : DataStream m_scratch{};
141 : :
142 : : void SeekImpl(std::span<const std::byte> key);
143 : : std::span<const std::byte> GetKeyImpl() const;
144 : : std::span<const std::byte> GetValueImpl() const;
145 : :
146 : : public:
147 : :
148 : : /**
149 : : * @param[in] _parent Parent CDBWrapper instance.
150 : : * @param[in] _piter The original leveldb iterator.
151 : : */
152 : : CDBIterator(const CDBWrapper& _parent, std::unique_ptr<IteratorImpl> _piter);
153 : : ~CDBIterator();
154 : :
155 : : bool Valid() const;
156 : :
157 : : void SeekToFirst();
158 : :
159 : 205262 : template<typename K> void Seek(const K& key) {
160 : 205262 : ScopedDataStreamUsage scoped_scratch{m_scratch};
161 [ + - ]: 205262 : m_scratch << key;
162 [ - + + - ]: 205262 : SeekImpl(m_scratch);
163 : 205262 : }
164 : :
165 : : void Next();
166 : :
167 : 7506128 : template<typename K> bool GetKey(K& key) {
168 : : try {
169 [ + - + + ]: 7506128 : SpanReader ssKey{GetKeyImpl()};
170 : 7506128 : ssKey >> key;
171 [ - + ]: 39576 : } catch (const std::exception&) {
172 : : return false;
173 : : }
174 : : return true;
175 : : }
176 : :
177 : 7318709 : template<typename V> bool GetValue(V& value) {
178 : : try {
179 : 7318709 : ScopedDataStreamUsage scoped_scratch{m_scratch};
180 [ + - + - ]: 7318709 : m_scratch.write(GetValueImpl());
181 [ + - - + ]: 7318709 : dbwrapper_private::GetObfuscation(parent)(m_scratch);
182 [ + - ]: 7318709 : m_scratch >> value;
183 [ - - ]: 7318709 : } catch (const std::exception&) {
184 : : return false;
185 : : }
186 : 7318709 : return true;
187 : : }
188 : : };
189 : :
190 : : struct LevelDBContext;
191 : :
192 : : class CDBWrapper
193 : : {
194 : : friend const Obfuscation& dbwrapper_private::GetObfuscation(const CDBWrapper&);
195 : : private:
196 : : //! holds all leveldb-specific fields of this class
197 : : std::unique_ptr<LevelDBContext> m_db_context;
198 : :
199 : : //! the name of this database
200 : : std::string m_name;
201 : :
202 : : //! optional XOR-obfuscation of the database
203 : : Obfuscation m_obfuscation;
204 : :
205 : : //! obfuscation key storage key, null-prefixed to avoid collisions
206 : : inline static const std::string OBFUSCATION_KEY{"\000obfuscate_key", 14}; // explicit size to avoid truncation at leading \0
207 : :
208 : : std::optional<std::string> ReadImpl(std::span<const std::byte> key) const;
209 : : bool ExistsImpl(std::span<const std::byte> key) const;
210 : : size_t EstimateSizeImpl(std::span<const std::byte> key1, std::span<const std::byte> key2) const;
211 [ - + ]: 24809917 : auto& DBContext() const LIFETIMEBOUND { return *Assert(m_db_context); }
212 : :
213 : : public:
214 : : CDBWrapper(const DBParams& params);
215 : : ~CDBWrapper();
216 : :
217 : : CDBWrapper(const CDBWrapper&) = delete;
218 : : CDBWrapper& operator=(const CDBWrapper&) = delete;
219 : :
220 : 4286 : struct ReadFailure {
221 : : enum class Code {
222 : : DeserializationError, //!< Key exists but value could not be deserialized.
223 : : DatabaseError, //!< Unexpected internal DB error.
224 : : };
225 : :
226 : : Code status;
227 : : std::string err_msg;
228 : : };
229 : :
230 : : using ReadStatus = util::Expected<bool, ReadFailure>;
231 : :
232 : : /**
233 : : * Read and deserialize a value from the database, with explicit error discrimination.
234 : : *
235 : : * Unlike Read(), this method distinguishes between a missing key, a deserialization
236 : : * failure (DeserializationError), and an internal DB error (DatabaseError),
237 : : * enabling callers to treat data corruption differently from an absent entry.
238 : : *
239 : : * @note Callers are expected to provide well-formed keys; key serialization
240 : : * is the only operation that may throw.
241 : : *
242 : : * @param[in] key The key to look up.
243 : : * @param[out] value Populated with the deserialized value when the returned
244 : : * Expected holds true; indeterminate otherwise.
245 : : * @return On success, true if the key was found (value populated) or false if
246 : : * the key was absent. On failure, a ReadFailure describing the error.
247 : : */
248 : : template <typename K, typename V>
249 : 7338939 : [[nodiscard]] ReadStatus TryRead(const K& key, V& value) const
250 : : {
251 [ + - ][ + - ]: 7338939 : DataStream ssKey{};
252 [ + - ][ + - ]: 7338939 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
253 : : // Key serialization is the only operation that may throw.
254 : : // Callers are expected to provide well-formed keys.
255 : 7338939 : ssKey << key;
256 : :
257 [ - + ]: 7338939 : std::optional<std::string> strValue;
258 : : try {
259 [ + - + + ]: 14677878 : strValue = ReadImpl(ssKey);
[ + - + + ]
260 [ + + ]: 7338939 : if (!strValue) {
261 : 3319047 : return false; // not found
262 : : }
263 [ - - ][ - - ]: 0 : } catch (const std::exception& e) {
264 : 0 : return util::Unexpected(ReadFailure{ReadFailure::Code::DatabaseError, e.what()});
265 : : }
266 : :
267 : : try {
268 : 4019892 : std::span ssValue{MakeWritableByteSpan(*strValue)};
269 [ + - ]: 4019892 : m_obfuscation(ssValue);
270 [ + - ]: 4019892 : SpanReader{ssValue} >> value;
271 [ - + ][ - - ]: 2143 : } catch (const std::exception& e) {
272 : 2143 : return util::Unexpected(ReadFailure{ReadFailure::Code::DeserializationError, e.what()});
273 : : }
274 : :
275 : 4017749 : return true;
276 [ - - + - ]: 7341082 : }
[ - - - - ]
277 : :
278 : : /**
279 : : * Wrapper around TryRead() that preserves the original Read() semantics:
280 : : * returns true on success, false if the key is absent or deserialization
281 : : * fails, and throws dbwrapper_error on an internal DB error.
282 : : *
283 : : * Prefer TryRead() when the caller needs to distinguish between a missing
284 : : * key and a corrupt value.
285 : : */
286 : : template <typename K, typename V>
287 : 3702848 : bool Read(const K& key, V& value) const
288 : : {
289 [ + + ]: 3702848 : const ReadStatus res = TryRead(key,value);
290 [ + + + - ]: 3702848 : if (res.has_value()) return res.value();
291 [ - - + ]: 2143 : switch (const auto& [err_code, err_msg] = res.error(); err_code) {
292 : : case ReadFailure::Code::DeserializationError: return false;
293 [ # # ]: 0 : case ReadFailure::Code::DatabaseError: throw dbwrapper_error(err_msg);
294 : : } // no default case, so the compiler can warn about missing cases
295 : 0 : std::abort(); // unreachable
296 : 3702848 : }
297 : :
298 : : template <typename K, typename V>
299 : 39362 : void Write(const K& key, const V& value, bool fSync = false)
300 : : {
301 : 39362 : CDBBatch batch(*this);
302 [ + - ]: 39362 : batch.Write(key, value);
303 [ + - ]: 39362 : WriteBatch(batch, fSync);
304 : 39362 : }
305 : :
306 : : template <typename K>
307 : 53417 : bool Exists(const K& key) const
308 : : {
309 [ + - ]: 53417 : DataStream ssKey{};
310 [ + - ]: 53417 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
311 [ - + ]: 53417 : ssKey << key;
312 [ + - ]: 53417 : return ExistsImpl(ssKey);
313 : 53417 : }
314 : :
315 : : template <typename K>
316 : 103680 : void Erase(const K& key, bool fSync = false)
317 : : {
318 : 103680 : CDBBatch batch(*this);
319 [ + - ]: 103680 : batch.Erase(key);
320 [ + - ]: 103680 : WriteBatch(batch, fSync);
321 : 103680 : }
322 : :
323 : : void WriteBatch(CDBBatch& batch, bool fSync = false);
324 : :
325 : : //! Perform a blocking full compaction of the underlying LevelDB.
326 : : void CompactFull();
327 : :
328 : : //! Return a LevelDB property value, if available.
329 : : std::optional<std::string> GetProperty(const std::string& property) const;
330 : :
331 : : // Get an estimate of LevelDB memory usage (in bytes).
332 : : size_t DynamicMemoryUsage() const;
333 : :
334 : : CDBIterator* NewIterator();
335 : :
336 : : /**
337 : : * Return true if the database managed by this class contains no entries.
338 : : */
339 : : bool IsEmpty();
340 : :
341 : : //! Probe an unopened database for a key prefix. Return true if a database at
342 : : //! path exists and contains at least 1 entry beginning with prefix; missing
343 : : //! or empty databases return false, and database errors throw dbwrapper_error.
344 : : static bool HasKeyStartingWith(const fs::path& path, uint8_t prefix);
345 : :
346 : : template<typename K>
347 : 155489 : size_t EstimateSize(const K& key_begin, const K& key_end) const
348 : : {
349 [ + - ]: 155489 : DataStream ssKey1{}, ssKey2{};
350 [ + - ]: 155489 : ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
351 [ + - ]: 155489 : ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
352 [ + - ]: 155489 : ssKey1 << key_begin;
353 [ - + ]: 155489 : ssKey2 << key_end;
354 [ - + + - ]: 155489 : return EstimateSizeImpl(ssKey1, ssKey2);
355 : 155489 : }
356 : : };
357 : :
358 : : #endif // BITCOIN_DBWRAPPER_H
|