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