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/fs.h>
15 : : #include <util/obfuscation.h>
16 : :
17 : : #include <cstddef>
18 : : #include <cstdint>
19 : : #include <exception>
20 : : #include <memory>
21 : : #include <optional>
22 : : #include <span>
23 : : #include <stdexcept>
24 : : #include <string>
25 : :
26 : : namespace leveldb {
27 : : class Env;
28 : : } // namespace leveldb
29 : :
30 : : static const size_t DBWRAPPER_PREALLOC_KEY_SIZE = 64;
31 : : static const size_t DBWRAPPER_PREALLOC_VALUE_SIZE = 1024;
32 : : static const size_t DBWRAPPER_MAX_FILE_SIZE{32_MiB};
33 : :
34 : : //! User-controlled performance and debug options.
35 : : struct DBOptions {
36 : : //! Compact database on startup.
37 : : bool force_compact = false;
38 : : };
39 : :
40 : : //! Application-specific storage settings.
41 [ + - ]: 78749 : struct DBParams {
[ - - + - ]
42 : : //! Location in the filesystem where leveldb data will be stored.
43 : : fs::path path;
44 : : //! Configures various leveldb cache settings.
45 : : uint64_t cache_bytes;
46 : : //! If true, use leveldb's memory environment.
47 : : bool memory_only = false;
48 : : //! If true, remove all existing data.
49 : : bool wipe_data = false;
50 : : //! If true, store data obfuscated via simple XOR. If false, XOR with a
51 : : //! zero'd byte array.
52 : : bool obfuscate = false;
53 : : //! If true, build a LevelDB bloom filter to accelerate point lookups.
54 : : bool bloom_filter = true;
55 : : //! Passed-through options.
56 : : DBOptions options{};
57 : : //! If non-null, use this as the leveldb::Env instead of the default.
58 : : //! Caller retains ownership.
59 : : leveldb::Env* testing_env = nullptr;
60 : : //! Maximum LevelDB SST file size. Larger values reduce the frequency
61 : : //! of compactions but increase their duration.
62 : : size_t max_file_size = DBWRAPPER_MAX_FILE_SIZE;
63 : : };
64 : :
65 : : class dbwrapper_error : public std::runtime_error
66 : : {
67 : : public:
68 [ # # ]: 0 : explicit dbwrapper_error(const std::string& msg) : std::runtime_error(msg) {}
69 : : };
70 : :
71 : : class CDBWrapper;
72 : :
73 : : /** These should be considered an implementation detail of the specific database.
74 : : */
75 : : namespace dbwrapper_private {
76 : :
77 : : /** Work around circular dependency, as well as for testing in dbwrapper_tests.
78 : : * Database obfuscation should be considered an implementation detail of the
79 : : * specific database.
80 : : */
81 : : const Obfuscation& GetObfuscation(const CDBWrapper&);
82 : : }; // namespace dbwrapper_private
83 : :
84 : : bool DestroyDB(const std::string& path_str);
85 : :
86 : : /** Batch of changes queued to be written to a CDBWrapper */
87 : : class CDBBatch
88 : : {
89 : : friend class CDBWrapper;
90 : :
91 : : private:
92 : : const CDBWrapper &parent;
93 : :
94 : : struct WriteBatchImpl;
95 : : const std::unique_ptr<WriteBatchImpl> m_impl_batch;
96 : :
97 : : DataStream m_key_scratch{};
98 : : DataStream m_value_scratch{};
99 : :
100 : : void WriteImpl(std::span<const std::byte> key, DataStream& value);
101 : : void EraseImpl(std::span<const std::byte> key);
102 : :
103 : : public:
104 : : /**
105 : : * @param[in] _parent CDBWrapper that this batch is to be submitted to
106 : : */
107 : : explicit CDBBatch(const CDBWrapper& _parent);
108 : : ~CDBBatch();
109 : : void Clear();
110 : :
111 : : template <typename K, typename V>
112 : 3867455 : void Write(const K& key, const V& value)
113 : : {
114 : 3867455 : ScopedDataStreamUsage scoped_key{m_key_scratch}, scoped_value{m_value_scratch};
115 [ + - ]: 3867455 : m_key_scratch << key;
116 [ + - ]: 3867455 : m_value_scratch << value;
117 [ - + + - ]: 3867455 : WriteImpl(m_key_scratch, m_value_scratch);
118 : 3867455 : }
119 : :
120 : : template <typename K>
121 : 640436 : void Erase(const K& key)
122 : : {
123 : 640436 : ScopedDataStreamUsage scoped_key{m_key_scratch};
124 [ + - ]: 640436 : m_key_scratch << key;
125 [ - + + - ]: 640436 : EraseImpl(m_key_scratch);
126 : 640436 : }
127 : :
128 : : size_t ApproximateSize() const;
129 : : };
130 : :
131 : : class CDBIterator
132 : : {
133 : : public:
134 : : struct IteratorImpl;
135 : :
136 : : private:
137 : : const CDBWrapper &parent;
138 : : const std::unique_ptr<IteratorImpl> m_impl_iter;
139 : : DataStream m_scratch{};
140 : :
141 : : void SeekImpl(std::span<const std::byte> key);
142 : : std::span<const std::byte> GetKeyImpl() const;
143 : : std::span<const std::byte> GetValueImpl() const;
144 : :
145 : : public:
146 : :
147 : : /**
148 : : * @param[in] _parent Parent CDBWrapper instance.
149 : : * @param[in] _piter The original leveldb iterator.
150 : : */
151 : : CDBIterator(const CDBWrapper& _parent, std::unique_ptr<IteratorImpl> _piter);
152 : : ~CDBIterator();
153 : :
154 : : bool Valid() const;
155 : :
156 : : void SeekToFirst();
157 : :
158 : 132102 : template<typename K> void Seek(const K& key) {
159 : 132102 : ScopedDataStreamUsage scoped_scratch{m_scratch};
160 [ + - ]: 132102 : m_scratch << key;
161 [ - + + - ]: 132102 : SeekImpl(m_scratch);
162 : 132102 : }
163 : :
164 : : void Next();
165 : :
166 : 5748036 : template<typename K> bool GetKey(K& key) {
167 : : try {
168 [ + - + + ]: 5748036 : SpanReader ssKey{GetKeyImpl()};
169 : 5748036 : ssKey >> key;
170 [ - + ]: 2204 : } catch (const std::exception&) {
171 : : return false;
172 : : }
173 : : return true;
174 : : }
175 : :
176 : 5730379 : template<typename V> bool GetValue(V& value) {
177 : : try {
178 : 5730379 : ScopedDataStreamUsage scoped_scratch{m_scratch};
179 [ + - + - ]: 5730379 : m_scratch.write(GetValueImpl());
180 [ + - - + ]: 5730379 : dbwrapper_private::GetObfuscation(parent)(m_scratch);
181 [ + - ]: 5730379 : m_scratch >> value;
182 [ - - ]: 5730379 : } catch (const std::exception&) {
183 : : return false;
184 : : }
185 : 5730379 : return true;
186 : : }
187 : : };
188 : :
189 : : struct LevelDBContext;
190 : :
191 : : class CDBWrapper
192 : : {
193 : : friend const Obfuscation& dbwrapper_private::GetObfuscation(const CDBWrapper&);
194 : : private:
195 : : //! holds all leveldb-specific fields of this class
196 : : std::unique_ptr<LevelDBContext> m_db_context;
197 : :
198 : : //! the name of this database
199 : : std::string m_name;
200 : :
201 : : //! optional XOR-obfuscation of the database
202 : : Obfuscation m_obfuscation;
203 : :
204 : : //! obfuscation key storage key, null-prefixed to avoid collisions
205 : : inline static const std::string OBFUSCATION_KEY{"\000obfuscate_key", 14}; // explicit size to avoid truncation at leading \0
206 : :
207 : : std::optional<std::string> ReadImpl(std::span<const std::byte> key) const;
208 : : bool ExistsImpl(std::span<const std::byte> key) const;
209 : : size_t EstimateSizeImpl(std::span<const std::byte> key1, std::span<const std::byte> key2) const;
210 [ - + ]: 8969651 : auto& DBContext() const LIFETIMEBOUND { return *Assert(m_db_context); }
211 : :
212 : : public:
213 : : CDBWrapper(const DBParams& params);
214 : : ~CDBWrapper();
215 : :
216 : : CDBWrapper(const CDBWrapper&) = delete;
217 : : CDBWrapper& operator=(const CDBWrapper&) = delete;
218 : :
219 : : template <typename K, typename V>
220 : 3559311 : bool Read(const K& key, V& value) const
221 : : {
222 [ + - ]: 3559311 : DataStream ssKey{};
223 [ + - ]: 3559311 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
224 [ - + ]: 3559311 : ssKey << key;
225 [ + - ]: 3559311 : std::optional<std::string> strValue{ReadImpl(ssKey)};
226 [ + + ]: 3559311 : if (!strValue) {
227 : : return false;
228 : : }
229 : : try {
230 : 679542 : std::span ssValue{MakeWritableByteSpan(*strValue)};
231 [ + - ]: 679542 : m_obfuscation(ssValue);
232 [ + - ]: 4238751 : SpanReader{ssValue} >> value;
233 [ - + ]: 29 : } catch (const std::exception&) {
234 : : return false;
235 : : }
236 : : return true;
237 : 3559311 : }
238 : :
239 : : template <typename K, typename V>
240 : 8393 : void Write(const K& key, const V& value, bool fSync = false)
241 : : {
242 : 8393 : CDBBatch batch(*this);
243 [ + - ]: 8393 : batch.Write(key, value);
244 [ + - ]: 8393 : WriteBatch(batch, fSync);
245 : 8393 : }
246 : :
247 : : template <typename K>
248 : 9869 : bool Exists(const K& key) const
249 : : {
250 [ + - ]: 9869 : DataStream ssKey{};
251 [ + - ]: 9869 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
252 [ - + ]: 9869 : ssKey << key;
253 [ + - ]: 9869 : return ExistsImpl(ssKey);
254 : 9869 : }
255 : :
256 : : template <typename K>
257 : 4457 : void Erase(const K& key, bool fSync = false)
258 : : {
259 : 4457 : CDBBatch batch(*this);
260 [ + - ]: 4457 : batch.Erase(key);
261 [ + - ]: 4457 : WriteBatch(batch, fSync);
262 : 4457 : }
263 : :
264 : : void WriteBatch(CDBBatch& batch, bool fSync = false);
265 : :
266 : : //! Perform a blocking full compaction of the underlying LevelDB.
267 : : void CompactFull();
268 : :
269 : : //! Return a LevelDB property value, if available.
270 : : std::optional<std::string> GetProperty(const std::string& property) const;
271 : :
272 : : // Get an estimate of LevelDB memory usage (in bytes).
273 : : size_t DynamicMemoryUsage() const;
274 : :
275 : : CDBIterator* NewIterator();
276 : :
277 : : /**
278 : : * Return true if the database managed by this class contains no entries.
279 : : */
280 : : bool IsEmpty();
281 : :
282 : : template<typename K>
283 : 125876 : size_t EstimateSize(const K& key_begin, const K& key_end) const
284 : : {
285 [ + - ]: 125876 : DataStream ssKey1{}, ssKey2{};
286 [ + - ]: 125876 : ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
287 [ + - ]: 125876 : ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
288 [ + - ]: 125876 : ssKey1 << key_begin;
289 [ - + ]: 125876 : ssKey2 << key_end;
290 [ - + + - ]: 125876 : return EstimateSizeImpl(ssKey1, ssKey2);
291 : 125876 : }
292 : : };
293 : :
294 : : #endif // BITCOIN_DBWRAPPER_H
|