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 : :
22 : : static const size_t DBWRAPPER_PREALLOC_KEY_SIZE = 64;
23 : : static const size_t DBWRAPPER_PREALLOC_VALUE_SIZE = 1024;
24 : : static const size_t DBWRAPPER_MAX_FILE_SIZE = 32 << 20; // 32 MiB
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 [ + - ]: 38998 : 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 Obfuscation& GetObfuscation(const CDBWrapper&);
66 : : }; // namespace dbwrapper_private
67 : :
68 : : bool DestroyDB(const std::string& path_str);
69 : :
70 : : /** Batch of changes queued to be written to a CDBWrapper */
71 : : class CDBBatch
72 : : {
73 : : friend class CDBWrapper;
74 : :
75 : : private:
76 : : const CDBWrapper &parent;
77 : :
78 : : struct WriteBatchImpl;
79 : : const std::unique_ptr<WriteBatchImpl> m_impl_batch;
80 : :
81 : : DataStream ssKey{};
82 : : DataStream ssValue{};
83 : :
84 : : void WriteImpl(std::span<const std::byte> key, DataStream& ssValue);
85 : : void EraseImpl(std::span<const std::byte> key);
86 : :
87 : : public:
88 : : /**
89 : : * @param[in] _parent CDBWrapper that this batch is to be submitted to
90 : : */
91 : : explicit CDBBatch(const CDBWrapper& _parent);
92 : : ~CDBBatch();
93 : : void Clear();
94 : :
95 : : template <typename K, typename V>
96 : 8114394 : void Write(const K& key, const V& value)
97 : : {
98 : 8114394 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
99 : 8114394 : ssValue.reserve(DBWRAPPER_PREALLOC_VALUE_SIZE);
100 : 8114394 : ssKey << key;
101 : 8114394 : ssValue << value;
102 : 8114394 : WriteImpl(ssKey, ssValue);
103 [ + - ]: 8114394 : ssKey.clear();
104 [ + - ]: 8114394 : ssValue.clear();
105 : 8114394 : }
106 : :
107 : : template <typename K>
108 : 5356863 : void Erase(const K& key)
109 : : {
110 : 5356863 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
111 : 5356863 : ssKey << key;
112 : 5356863 : EraseImpl(ssKey);
113 [ + - ]: 5356863 : ssKey.clear();
114 : 5356863 : }
115 : :
116 : : size_t ApproximateSize() const;
117 : : };
118 : :
119 : : class CDBIterator
120 : : {
121 : : public:
122 : : struct IteratorImpl;
123 : :
124 : : private:
125 : : const CDBWrapper &parent;
126 : : const std::unique_ptr<IteratorImpl> m_impl_iter;
127 : :
128 : : void SeekImpl(std::span<const std::byte> key);
129 : : std::span<const std::byte> GetKeyImpl() const;
130 : : std::span<const std::byte> GetValueImpl() const;
131 : :
132 : : public:
133 : :
134 : : /**
135 : : * @param[in] _parent Parent CDBWrapper instance.
136 : : * @param[in] _piter The original leveldb iterator.
137 : : */
138 : : CDBIterator(const CDBWrapper& _parent, std::unique_ptr<IteratorImpl> _piter);
139 : : ~CDBIterator();
140 : :
141 : : bool Valid() const;
142 : :
143 : : void SeekToFirst();
144 : :
145 : 148275 : template<typename K> void Seek(const K& key) {
146 [ + - ]: 148275 : DataStream ssKey{};
147 [ + - ]: 148275 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
148 [ + - ]: 148275 : ssKey << key;
149 [ + - ]: 148275 : SeekImpl(ssKey);
150 : 148275 : }
151 : :
152 : : void Next();
153 : :
154 : 7375050 : template<typename K> bool GetKey(K& key) {
155 : : try {
156 [ + - + - ]: 7375050 : DataStream ssKey{GetKeyImpl()};
157 : 7374426 : ssKey >> key;
158 [ - + ]: 624 : } catch (const std::exception&) {
159 : : return false;
160 : : }
161 : 7374426 : return true;
162 : : }
163 : :
164 : 7372309 : template<typename V> bool GetValue(V& value) {
165 : : try {
166 [ + - + - ]: 7372309 : DataStream ssValue{GetValueImpl()};
167 [ + - + - ]: 7372309 : dbwrapper_private::GetObfuscation(parent)(ssValue);
168 : 7372309 : ssValue >> value;
169 [ - - ]: 0 : } catch (const std::exception&) {
170 : : return false;
171 : : }
172 : 7372309 : return true;
173 : : }
174 : : };
175 : :
176 : : struct LevelDBContext;
177 : :
178 : : class CDBWrapper
179 : : {
180 : : friend const Obfuscation& dbwrapper_private::GetObfuscation(const CDBWrapper&);
181 : : private:
182 : : //! holds all leveldb-specific fields of this class
183 : : std::unique_ptr<LevelDBContext> m_db_context;
184 : :
185 : : //! the name of this database
186 : : std::string m_name;
187 : :
188 : : //! optional XOR-obfuscation of the database
189 : : Obfuscation m_obfuscation;
190 : :
191 : : //! obfuscation key storage key, null-prefixed to avoid collisions
192 : : inline static const std::string OBFUSCATION_KEY_KEY{"\000obfuscate_key", 14}; // explicit size to avoid truncation at leading \0
193 : :
194 : : //! path to filesystem storage
195 : : const fs::path m_path;
196 : :
197 : : //! whether or not the database resides in memory
198 : : bool m_is_memory;
199 : :
200 : : std::optional<std::string> ReadImpl(std::span<const std::byte> key) const;
201 : : bool ExistsImpl(std::span<const std::byte> key) const;
202 : : size_t EstimateSizeImpl(std::span<const std::byte> key1, std::span<const std::byte> key2) const;
203 : 16646289 : auto& DBContext() const LIFETIMEBOUND { return *Assert(m_db_context); }
204 : :
205 : : public:
206 : : CDBWrapper(const DBParams& params);
207 : : ~CDBWrapper();
208 : :
209 : : CDBWrapper(const CDBWrapper&) = delete;
210 : : CDBWrapper& operator=(const CDBWrapper&) = delete;
211 : :
212 : : template <typename K, typename V>
213 : 5162658 : bool Read(const K& key, V& value) const
214 : : {
215 [ + - ]: 5162658 : DataStream ssKey{};
216 [ + - ]: 5162658 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
217 [ + - ]: 5162658 : ssKey << key;
218 [ + - ]: 5162658 : std::optional<std::string> strValue{ReadImpl(ssKey)};
219 [ + + ]: 5162658 : if (!strValue) {
220 : : return false;
221 : : }
222 : : try {
223 [ + - ]: 3125656 : DataStream ssValue{MakeByteSpan(*strValue)};
224 [ + - ]: 3125656 : m_obfuscation(ssValue);
225 : 3125656 : ssValue >> value;
226 [ - - ]: 0 : } catch (const std::exception&) {
227 : : return false;
228 : : }
229 : 3125656 : return true;
230 : 5162658 : }
231 : :
232 : : template <typename K, typename V>
233 : 5866 : bool Write(const K& key, const V& value, bool fSync = false)
234 : : {
235 : 5866 : CDBBatch batch(*this);
236 [ + - ]: 5866 : batch.Write(key, value);
237 [ + - ]: 11732 : return WriteBatch(batch, fSync);
238 : 5866 : }
239 : :
240 : : //! @returns filesystem path to the on-disk data.
241 : 0 : std::optional<fs::path> StoragePath() {
242 [ # # ]: 0 : if (m_is_memory) {
[ # # # # ]
243 : 0 : return {};
244 : : }
245 : 0 : return m_path;
246 : : }
247 : :
248 : : template <typename K>
249 : 6998 : bool Exists(const K& key) const
250 : : {
251 [ + - ]: 6998 : DataStream ssKey{};
252 [ + - ]: 6998 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
253 [ + - ]: 6998 : ssKey << key;
254 [ + - ]: 6998 : return ExistsImpl(ssKey);
255 : 6998 : }
256 : :
257 : : template <typename K>
258 : 624 : bool Erase(const K& key, bool fSync = false)
259 : : {
260 : 624 : CDBBatch batch(*this);
261 [ + - ]: 624 : batch.Erase(key);
262 [ + - ]: 1248 : return WriteBatch(batch, fSync);
263 : 624 : }
264 : :
265 : : bool WriteBatch(CDBBatch& batch, bool fSync = false);
266 : :
267 : : // Get an estimate of LevelDB memory usage (in bytes).
268 : : size_t DynamicMemoryUsage() const;
269 : :
270 : : CDBIterator* NewIterator();
271 : :
272 : : /**
273 : : * Return true if the database managed by this class contains no entries.
274 : : */
275 : : bool IsEmpty();
276 : :
277 : : template<typename K>
278 : 146311 : size_t EstimateSize(const K& key_begin, const K& key_end) const
279 : : {
280 [ + - ]: 146311 : DataStream ssKey1{}, ssKey2{};
281 [ + - ]: 146311 : ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
282 [ + - ]: 146311 : ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
283 [ + - ]: 146311 : ssKey1 << key_begin;
284 [ + - ]: 146311 : ssKey2 << key_end;
285 [ + - ]: 146311 : return EstimateSizeImpl(ssKey1, ssKey2);
286 : 146311 : }
287 : : };
288 : :
289 : : #endif // BITCOIN_DBWRAPPER_H
|