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/check.h>
13 : : #include <util/fs.h>
14 : :
15 : : #include <cstddef>
16 : : #include <exception>
17 : : #include <memory>
18 : : #include <optional>
19 : : #include <stdexcept>
20 : : #include <string>
21 : : #include <vector>
22 : :
23 : : static const size_t DBWRAPPER_PREALLOC_KEY_SIZE = 64;
24 : : static const size_t DBWRAPPER_PREALLOC_VALUE_SIZE = 1024;
25 : : static const size_t DBWRAPPER_MAX_FILE_SIZE = 32 << 20; // 32 MiB
26 : :
27 : : //! User-controlled performance and debug options.
28 : : struct DBOptions {
29 : : //! Compact database on startup.
30 : : bool force_compact = false;
31 : : };
32 : :
33 : : //! Application-specific storage settings.
34 [ + - ]: 11534 : struct DBParams {
35 : : //! Location in the filesystem where leveldb data will be stored.
36 : : fs::path path;
37 : : //! Configures various leveldb cache settings.
38 : : size_t cache_bytes;
39 : : //! If true, use leveldb's memory environment.
40 : : bool memory_only = false;
41 : : //! If true, remove all existing data.
42 : : bool wipe_data = false;
43 : : //! If true, store data obfuscated via simple XOR. If false, XOR with a
44 : : //! zero'd byte array.
45 : : bool obfuscate = false;
46 : : //! Passed-through options.
47 : : DBOptions options{};
48 : : };
49 : :
50 : : class dbwrapper_error : public std::runtime_error
51 : : {
52 : : public:
53 [ # # ]: 0 : explicit dbwrapper_error(const std::string& msg) : std::runtime_error(msg) {}
54 : : };
55 : :
56 : : class CDBWrapper;
57 : :
58 : : /** These should be considered an implementation detail of the specific database.
59 : : */
60 : : namespace dbwrapper_private {
61 : :
62 : : /** Work around circular dependency, as well as for testing in dbwrapper_tests.
63 : : * Database obfuscation should be considered an implementation detail of the
64 : : * specific database.
65 : : */
66 : : const std::vector<unsigned char>& GetObfuscateKey(const CDBWrapper &w);
67 : :
68 : : }; // namespace dbwrapper_private
69 : :
70 : : bool DestroyDB(const std::string& path_str);
71 : :
72 : : /** Batch of changes queued to be written to a CDBWrapper */
73 : : class CDBBatch
74 : : {
75 : : friend class CDBWrapper;
76 : :
77 : : private:
78 : : const CDBWrapper &parent;
79 : :
80 : : struct WriteBatchImpl;
81 : : const std::unique_ptr<WriteBatchImpl> m_impl_batch;
82 : :
83 : : DataStream ssKey{};
84 : : DataStream ssValue{};
85 : :
86 : : void WriteImpl(std::span<const std::byte> key, DataStream& ssValue);
87 : : void EraseImpl(std::span<const std::byte> key);
88 : :
89 : : public:
90 : : /**
91 : : * @param[in] _parent CDBWrapper that this batch is to be submitted to
92 : : */
93 : : explicit CDBBatch(const CDBWrapper& _parent);
94 : : ~CDBBatch();
95 : : void Clear();
96 : :
97 : : template <typename K, typename V>
98 : 1329324 : void Write(const K& key, const V& value)
99 : : {
100 : 1329324 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
101 : 1329324 : ssValue.reserve(DBWRAPPER_PREALLOC_VALUE_SIZE);
102 : 1329324 : ssKey << key;
103 : 1329324 : ssValue << value;
104 : 1329324 : WriteImpl(ssKey, ssValue);
105 [ + - ]: 1329324 : ssKey.clear();
106 [ + - ]: 1329324 : ssValue.clear();
107 : 1329324 : }
108 : :
109 : : template <typename K>
110 : 153416 : void Erase(const K& key)
111 : : {
112 : 153416 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
113 : 153416 : ssKey << key;
114 : 153416 : EraseImpl(ssKey);
115 [ + - ]: 153416 : ssKey.clear();
116 : 153416 : }
117 : :
118 : : size_t ApproximateSize() const;
119 : : };
120 : :
121 : : class CDBIterator
122 : : {
123 : : public:
124 : : struct IteratorImpl;
125 : :
126 : : private:
127 : : const CDBWrapper &parent;
128 : : const std::unique_ptr<IteratorImpl> m_impl_iter;
129 : :
130 : : void SeekImpl(std::span<const std::byte> key);
131 : : std::span<const std::byte> GetKeyImpl() const;
132 : : std::span<const std::byte> GetValueImpl() const;
133 : :
134 : : public:
135 : :
136 : : /**
137 : : * @param[in] _parent Parent CDBWrapper instance.
138 : : * @param[in] _piter The original leveldb iterator.
139 : : */
140 : : CDBIterator(const CDBWrapper& _parent, std::unique_ptr<IteratorImpl> _piter);
141 : : ~CDBIterator();
142 : :
143 : : bool Valid() const;
144 : :
145 : : void SeekToFirst();
146 : :
147 : 78505 : template<typename K> void Seek(const K& key) {
148 [ + - ]: 78505 : DataStream ssKey{};
149 [ + - ]: 78505 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
150 [ + - ]: 78505 : ssKey << key;
151 [ + - ]: 78505 : SeekImpl(ssKey);
152 : 78505 : }
153 : :
154 : : void Next();
155 : :
156 : 3939993 : template<typename K> bool GetKey(K& key) {
157 : : try {
158 [ + - + - ]: 3939993 : DataStream ssKey{GetKeyImpl()};
159 : 3939680 : ssKey >> key;
160 [ - + ]: 313 : } catch (const std::exception&) {
161 : : return false;
162 : : }
163 : 3939680 : return true;
164 : : }
165 : :
166 : 3939680 : template<typename V> bool GetValue(V& value) {
167 : : try {
168 [ + - + - ]: 3939680 : DataStream ssValue{GetValueImpl()};
169 [ + - + - ]: 3939680 : ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent));
170 : 3939680 : ssValue >> value;
171 [ - - ]: 0 : } catch (const std::exception&) {
172 : : return false;
173 : : }
174 : 3939680 : return true;
175 : : }
176 : : };
177 : :
178 : : struct LevelDBContext;
179 : :
180 : : class CDBWrapper
181 : : {
182 : : friend const std::vector<unsigned char>& dbwrapper_private::GetObfuscateKey(const CDBWrapper &w);
183 : : private:
184 : : //! holds all leveldb-specific fields of this class
185 : : std::unique_ptr<LevelDBContext> m_db_context;
186 : :
187 : : //! the name of this database
188 : : std::string m_name;
189 : :
190 : : //! a key used for optional XOR-obfuscation of the database
191 : : std::vector<unsigned char> obfuscate_key;
192 : :
193 : : //! the key under which the obfuscation key is stored
194 : : static const std::string OBFUSCATE_KEY_KEY;
195 : :
196 : : //! the length of the obfuscate key in number of bytes
197 : : static const unsigned int OBFUSCATE_KEY_NUM_BYTES;
198 : :
199 : : std::vector<unsigned char> CreateObfuscateKey() const;
200 : :
201 : : //! path to filesystem storage
202 : : const fs::path m_path;
203 : :
204 : : //! whether or not the database resides in memory
205 : : bool m_is_memory;
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 : 3565201 : 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 : 1469651 : bool Read(const K& key, V& value) const
221 : : {
222 [ + - ]: 1469651 : DataStream ssKey{};
223 [ + - ]: 1469651 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
224 [ + - ]: 1469651 : ssKey << key;
225 [ + - ]: 1469651 : std::optional<std::string> strValue{ReadImpl(ssKey)};
226 [ + + ]: 1469651 : if (!strValue) {
227 : : return false;
228 : : }
229 : : try {
230 [ + - ]: 244925 : DataStream ssValue{MakeByteSpan(*strValue)};
231 [ + - ]: 244925 : ssValue.Xor(obfuscate_key);
232 : 244925 : ssValue >> value;
233 [ - - ]: 0 : } catch (const std::exception&) {
234 : : return false;
235 : : }
236 : 244925 : return true;
237 : 1469651 : }
238 : :
239 : : template <typename K, typename V>
240 : 2750 : bool Write(const K& key, const V& value, bool fSync = false)
241 : : {
242 : 2750 : CDBBatch batch(*this);
243 [ + - ]: 2750 : batch.Write(key, value);
244 [ + - ]: 5500 : return WriteBatch(batch, fSync);
245 : 2750 : }
246 : :
247 : : //! @returns filesystem path to the on-disk data.
248 : 0 : std::optional<fs::path> StoragePath() {
249 [ # # # # ]: 0 : if (m_is_memory) {
[ # # ]
250 : 0 : return {};
251 : : }
252 : 0 : return m_path;
253 : : }
254 : :
255 : : template <typename K>
256 : 1836 : bool Exists(const K& key) const
257 : : {
258 [ + - ]: 1836 : DataStream ssKey{};
259 [ + - ]: 1836 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
260 [ # # ]: 0 : ssKey << key;
261 [ + - ]: 1836 : return ExistsImpl(ssKey);
262 : 1836 : }
263 : :
264 : : template <typename K>
265 : 313 : bool Erase(const K& key, bool fSync = false)
266 : : {
267 : 313 : CDBBatch batch(*this);
268 [ + - ]: 313 : batch.Erase(key);
269 [ + - ]: 626 : return WriteBatch(batch, fSync);
270 : 313 : }
271 : :
272 : : bool WriteBatch(CDBBatch& batch, bool fSync = false);
273 : :
274 : : // Get an estimate of LevelDB memory usage (in bytes).
275 : : size_t DynamicMemoryUsage() const;
276 : :
277 : : CDBIterator* NewIterator();
278 : :
279 : : /**
280 : : * Return true if the database managed by this class contains no entries.
281 : : */
282 : : bool IsEmpty();
283 : :
284 : : template<typename K>
285 : 75731 : size_t EstimateSize(const K& key_begin, const K& key_end) const
286 : : {
287 [ + - ]: 75731 : DataStream ssKey1{}, ssKey2{};
288 [ + - ]: 75731 : ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
289 [ + - ]: 75731 : ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
290 [ + - ]: 75731 : ssKey1 << key_begin;
291 [ + - ]: 75731 : ssKey2 << key_end;
292 [ + - ]: 75731 : return EstimateSizeImpl(ssKey1, ssKey2);
293 : 75731 : }
294 : : };
295 : :
296 : : #endif // BITCOIN_DBWRAPPER_H
|