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 [ + - ]: 155491 : 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 : : //! Passed-through options.
54 : : DBOptions options{};
55 : : //! If non-null, use this as the leveldb::Env instead of the default.
56 : : //! Caller retains ownership.
57 : : leveldb::Env* testing_env = nullptr;
58 : : //! Maximum LevelDB SST file size. Larger values reduce the frequency
59 : : //! of compactions but increase their duration.
60 : : size_t max_file_size = DBWRAPPER_MAX_FILE_SIZE;
61 : : };
62 : :
63 : : class dbwrapper_error : public std::runtime_error
64 : : {
65 : : public:
66 [ # # ]: 0 : explicit dbwrapper_error(const std::string& msg) : std::runtime_error(msg) {}
67 : : };
68 : :
69 : : class CDBWrapper;
70 : :
71 : : /** These should be considered an implementation detail of the specific database.
72 : : */
73 : : namespace dbwrapper_private {
74 : :
75 : : /** Work around circular dependency, as well as for testing in dbwrapper_tests.
76 : : * Database obfuscation should be considered an implementation detail of the
77 : : * specific database.
78 : : */
79 : : const Obfuscation& GetObfuscation(const CDBWrapper&);
80 : : }; // namespace dbwrapper_private
81 : :
82 : : bool DestroyDB(const std::string& path_str);
83 : :
84 : : /** Batch of changes queued to be written to a CDBWrapper */
85 : : class CDBBatch
86 : : {
87 : : friend class CDBWrapper;
88 : :
89 : : private:
90 : : const CDBWrapper &parent;
91 : :
92 : : struct WriteBatchImpl;
93 : : const std::unique_ptr<WriteBatchImpl> m_impl_batch;
94 : :
95 : : DataStream m_key_scratch{};
96 : : DataStream m_value_scratch{};
97 : :
98 : : void WriteImpl(std::span<const std::byte> key, DataStream& value);
99 : : void EraseImpl(std::span<const std::byte> key);
100 : :
101 : : public:
102 : : /**
103 : : * @param[in] _parent CDBWrapper that this batch is to be submitted to
104 : : */
105 : : explicit CDBBatch(const CDBWrapper& _parent);
106 : : ~CDBBatch();
107 : : void Clear();
108 : :
109 : : template <typename K, typename V>
110 : 3821847 : void Write(const K& key, const V& value)
111 : : {
112 : 3821847 : ScopedDataStreamUsage scoped_key{m_key_scratch}, scoped_value{m_value_scratch};
113 [ + - ]: 3821847 : m_key_scratch << key;
114 [ + - ]: 3821847 : m_value_scratch << value;
115 [ - + + - ]: 3821847 : WriteImpl(m_key_scratch, m_value_scratch);
116 : 3821847 : }
117 : :
118 : : template <typename K>
119 : 750032 : void Erase(const K& key)
120 : : {
121 : 750032 : ScopedDataStreamUsage scoped_key{m_key_scratch};
122 [ + - ]: 750032 : m_key_scratch << key;
123 [ - + + - ]: 750032 : EraseImpl(m_key_scratch);
124 : 750032 : }
125 : :
126 : : size_t ApproximateSize() const;
127 : : };
128 : :
129 : : class CDBIterator
130 : : {
131 : : public:
132 : : struct IteratorImpl;
133 : :
134 : : private:
135 : : const CDBWrapper &parent;
136 : : const std::unique_ptr<IteratorImpl> m_impl_iter;
137 : : DataStream m_scratch{};
138 : :
139 : : void SeekImpl(std::span<const std::byte> key);
140 : : std::span<const std::byte> GetKeyImpl() const;
141 : : std::span<const std::byte> GetValueImpl() const;
142 : :
143 : : public:
144 : :
145 : : /**
146 : : * @param[in] _parent Parent CDBWrapper instance.
147 : : * @param[in] _piter The original leveldb iterator.
148 : : */
149 : : CDBIterator(const CDBWrapper& _parent, std::unique_ptr<IteratorImpl> _piter);
150 : : ~CDBIterator();
151 : :
152 : : bool Valid() const;
153 : :
154 : : void SeekToFirst();
155 : :
156 : 150586 : template<typename K> void Seek(const K& key) {
157 : 150586 : ScopedDataStreamUsage scoped_scratch{m_scratch};
158 [ + - ]: 150586 : m_scratch << key;
159 [ - + + - ]: 150586 : SeekImpl(m_scratch);
160 : 150586 : }
161 : :
162 : : void Next();
163 : :
164 : 6193807 : template<typename K> bool GetKey(K& key) {
165 : : try {
166 [ + - + + ]: 6193807 : SpanReader ssKey{GetKeyImpl()};
167 : 6193807 : ssKey >> key;
168 [ - + ]: 7719 : } catch (const std::exception&) {
169 : : return false;
170 : : }
171 : : return true;
172 : : }
173 : :
174 : 6100890 : template<typename V> bool GetValue(V& value) {
175 : : try {
176 : 6100890 : ScopedDataStreamUsage scoped_scratch{m_scratch};
177 [ + - + - ]: 6100890 : m_scratch.write(GetValueImpl());
178 [ + - - + ]: 6100890 : dbwrapper_private::GetObfuscation(parent)(m_scratch);
179 [ + - ]: 6100890 : m_scratch >> value;
180 [ - - ]: 6100890 : } catch (const std::exception&) {
181 : : return false;
182 : : }
183 : 6100890 : return true;
184 : : }
185 : : };
186 : :
187 : : struct LevelDBContext;
188 : :
189 : : class CDBWrapper
190 : : {
191 : : friend const Obfuscation& dbwrapper_private::GetObfuscation(const CDBWrapper&);
192 : : private:
193 : : //! holds all leveldb-specific fields of this class
194 : : std::unique_ptr<LevelDBContext> m_db_context;
195 : :
196 : : //! the name of this database
197 : : std::string m_name;
198 : :
199 : : //! optional XOR-obfuscation of the database
200 : : Obfuscation m_obfuscation;
201 : :
202 : : //! obfuscation key storage key, null-prefixed to avoid collisions
203 : : inline static const std::string OBFUSCATION_KEY{"\000obfuscate_key", 14}; // explicit size to avoid truncation at leading \0
204 : :
205 : : std::optional<std::string> ReadImpl(std::span<const std::byte> key) const;
206 : : bool ExistsImpl(std::span<const std::byte> key) const;
207 : : size_t EstimateSizeImpl(std::span<const std::byte> key1, std::span<const std::byte> key2) const;
208 [ - + ]: 10313261 : auto& DBContext() const LIFETIMEBOUND { return *Assert(m_db_context); }
209 : :
210 : : public:
211 : : CDBWrapper(const DBParams& params);
212 : : ~CDBWrapper();
213 : :
214 : : CDBWrapper(const CDBWrapper&) = delete;
215 : : CDBWrapper& operator=(const CDBWrapper&) = delete;
216 : :
217 : : template <typename K, typename V>
218 : 3690299 : bool Read(const K& key, V& value) const
219 : : {
220 [ + - ]: 3690299 : DataStream ssKey{};
221 [ + - ]: 3690299 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
222 [ - + ]: 3690299 : ssKey << key;
223 [ + - ]: 3690299 : std::optional<std::string> strValue{ReadImpl(ssKey)};
224 [ + + ]: 3690299 : if (!strValue) {
225 : : return false;
226 : : }
227 : : try {
228 : 789619 : std::span ssValue{MakeWritableByteSpan(*strValue)};
229 [ + - ]: 789619 : m_obfuscation(ssValue);
230 [ + - ]: 4477923 : SpanReader{ssValue} >> value;
231 [ - + ]: 1320 : } catch (const std::exception&) {
232 : : return false;
233 : : }
234 : : return true;
235 : 3690299 : }
236 : :
237 : : template <typename K, typename V>
238 : 19939 : void Write(const K& key, const V& value, bool fSync = false)
239 : : {
240 : 19939 : CDBBatch batch(*this);
241 [ + - ]: 19939 : batch.Write(key, value);
242 [ + - ]: 19939 : WriteBatch(batch, fSync);
243 : 19939 : }
244 : :
245 : : template <typename K>
246 : 28857 : bool Exists(const K& key) const
247 : : {
248 [ + - ]: 28857 : DataStream ssKey{};
249 [ + - ]: 28857 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
250 [ - + ]: 28857 : ssKey << key;
251 [ + - ]: 28857 : return ExistsImpl(ssKey);
252 : 28857 : }
253 : :
254 : : template <typename K>
255 : 45520 : void Erase(const K& key, bool fSync = false)
256 : : {
257 : 45520 : CDBBatch batch(*this);
258 [ + - ]: 45520 : batch.Erase(key);
259 [ + - ]: 45520 : WriteBatch(batch, fSync);
260 : 45520 : }
261 : :
262 : : void WriteBatch(CDBBatch& batch, bool fSync = false);
263 : :
264 : : //! Perform a blocking full compaction of the underlying LevelDB.
265 : : void CompactFull();
266 : :
267 : : //! Return a LevelDB property value, if available.
268 : : std::optional<std::string> GetProperty(const std::string& property) const;
269 : :
270 : : // Get an estimate of LevelDB memory usage (in bytes).
271 : : size_t DynamicMemoryUsage() const;
272 : :
273 : : CDBIterator* NewIterator();
274 : :
275 : : /**
276 : : * Return true if the database managed by this class contains no entries.
277 : : */
278 : : bool IsEmpty();
279 : :
280 : : template<typename K>
281 : 129594 : size_t EstimateSize(const K& key_begin, const K& key_end) const
282 : : {
283 [ + - ]: 129594 : DataStream ssKey1{}, ssKey2{};
284 [ + - ]: 129594 : ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
285 [ + - ]: 129594 : ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
286 [ + - ]: 129594 : ssKey1 << key_begin;
287 [ - + ]: 129594 : ssKey2 << key_end;
288 [ - + + - ]: 129594 : return EstimateSizeImpl(ssKey1, ssKey2);
289 : 129594 : }
290 : : };
291 : :
292 : : #endif // BITCOIN_DBWRAPPER_H
|