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