dbwrapper.h raw
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 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 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 void Write(const K& key, const V& value)
113 {
114 ScopedDataStreamUsage scoped_key{m_key_scratch}, scoped_value{m_value_scratch};
115 m_key_scratch << key;
116 m_value_scratch << value;
117 WriteImpl(m_key_scratch, m_value_scratch);
118 }
119
120 template <typename K>
121 void Erase(const K& key)
122 {
123 ScopedDataStreamUsage scoped_key{m_key_scratch};
124 m_key_scratch << key;
125 EraseImpl(m_key_scratch);
126 }
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 template<typename K> void Seek(const K& key) {
159 ScopedDataStreamUsage scoped_scratch{m_scratch};
160 m_scratch << key;
161 SeekImpl(m_scratch);
162 }
163
164 void Next();
165
166 template<typename K> bool GetKey(K& key) {
167 try {
168 SpanReader ssKey{GetKeyImpl()};
169 ssKey >> key;
170 } catch (const std::exception&) {
171 return false;
172 }
173 return true;
174 }
175
176 template<typename V> bool GetValue(V& value) {
177 try {
178 ScopedDataStreamUsage scoped_scratch{m_scratch};
179 m_scratch.write(GetValueImpl());
180 dbwrapper_private::GetObfuscation(parent)(m_scratch);
181 m_scratch >> value;
182 } catch (const std::exception&) {
183 return false;
184 }
185 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 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 bool Read(const K& key, V& value) const
221 {
222 DataStream ssKey{};
223 ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
224 ssKey << key;
225 std::optional<std::string> strValue{ReadImpl(ssKey)};
226 if (!strValue) {
227 return false;
228 }
229 try {
230 std::span ssValue{MakeWritableByteSpan(*strValue)};
231 m_obfuscation(ssValue);
232 SpanReader{ssValue} >> value;
233 } catch (const std::exception&) {
234 return false;
235 }
236 return true;
237 }
238
239 template <typename K, typename V>
240 void Write(const K& key, const V& value, bool fSync = false)
241 {
242 CDBBatch batch(*this);
243 batch.Write(key, value);
244 WriteBatch(batch, fSync);
245 }
246
247 template <typename K>
248 bool Exists(const K& key) const
249 {
250 DataStream ssKey{};
251 ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
252 ssKey << key;
253 return ExistsImpl(ssKey);
254 }
255
256 template <typename K>
257 void Erase(const K& key, bool fSync = false)
258 {
259 CDBBatch batch(*this);
260 batch.Erase(key);
261 WriteBatch(batch, fSync);
262 }
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 size_t EstimateSize(const K& key_begin, const K& key_end) const
284 {
285 DataStream ssKey1{}, ssKey2{};
286 ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
287 ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
288 ssKey1 << key_begin;
289 ssKey2 << key_end;
290 return EstimateSizeImpl(ssKey1, ssKey2);
291 }
292 };
293
294 #endif // BITCOIN_DBWRAPPER_H
295