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 : :
16 : : #include <cstddef>
17 : : #include <exception>
18 : : #include <memory>
19 : : #include <optional>
20 : : #include <stdexcept>
21 : : #include <string>
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_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 : 8120 : 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 [ + - ]: 10 : 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 Obfuscation& GetObfuscation(const CDBWrapper&);
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 m_key_scratch{};
83 : : DataStream m_value_scratch{};
84 : :
85 : : void WriteImpl(std::span<const std::byte> key, DataStream& value);
86 : : void EraseImpl(std::span<const std::byte> key);
87 : :
88 : : public:
89 : : /**
90 : : * @param[in] _parent CDBWrapper that this batch is to be submitted to
91 : : */
92 : : explicit CDBBatch(const CDBWrapper& _parent);
93 : : ~CDBBatch();
94 : : void Clear();
95 : :
96 : : template <typename K, typename V>
97 : 501083 : void Write(const K& key, const V& value)
98 : : {
99 : 501083 : ScopedDataStreamUsage scoped_key{m_key_scratch}, scoped_value{m_value_scratch};
100 [ + - ]: 501083 : m_key_scratch << key;
101 [ + - ]: 501083 : m_value_scratch << value;
102 [ - + + - ]: 501083 : WriteImpl(m_key_scratch, m_value_scratch);
103 : 501083 : }
104 : :
105 : : template <typename K>
106 : 39470 : void Erase(const K& key)
107 : : {
108 : 39470 : ScopedDataStreamUsage scoped_key{m_key_scratch};
109 [ + - ]: 39470 : m_key_scratch << key;
110 [ - + + - ]: 39470 : EraseImpl(m_key_scratch);
111 : 39470 : }
112 : :
113 : : size_t ApproximateSize() const;
114 : : };
115 : :
116 : : class CDBIterator
117 : : {
118 : : public:
119 : : struct IteratorImpl;
120 : :
121 : : private:
122 : : const CDBWrapper &parent;
123 : : const std::unique_ptr<IteratorImpl> m_impl_iter;
124 : : DataStream m_scratch{};
125 : :
126 : : void SeekImpl(std::span<const std::byte> key);
127 : : std::span<const std::byte> GetKeyImpl() const;
128 : : std::span<const std::byte> GetValueImpl() const;
129 : :
130 : : public:
131 : :
132 : : /**
133 : : * @param[in] _parent Parent CDBWrapper instance.
134 : : * @param[in] _piter The original leveldb iterator.
135 : : */
136 : : CDBIterator(const CDBWrapper& _parent, std::unique_ptr<IteratorImpl> _piter);
137 : : ~CDBIterator();
138 : :
139 : : bool Valid() const;
140 : :
141 : : void SeekToFirst();
142 : :
143 : 4512 : template<typename K> void Seek(const K& key) {
144 : 4512 : ScopedDataStreamUsage scoped_scratch{m_scratch};
145 [ + - ]: 4512 : m_scratch << key;
146 [ - + + - ]: 4512 : SeekImpl(m_scratch);
147 : 4512 : }
148 : :
149 : : void Next();
150 : :
151 : 407975 : template<typename K> bool GetKey(K& key) {
152 : : try {
153 [ + - + + ]: 407975 : SpanReader ssKey{GetKeyImpl()};
154 : 407975 : ssKey >> key;
155 [ - + ]: 781 : } catch (const std::exception&) {
156 : : return false;
157 : : }
158 : : return true;
159 : : }
160 : :
161 : 406980 : template<typename V> bool GetValue(V& value) {
162 : : try {
163 : 406980 : ScopedDataStreamUsage scoped_scratch{m_scratch};
164 [ + - + - ]: 406980 : m_scratch.write(GetValueImpl());
165 [ + - - + ]: 406980 : dbwrapper_private::GetObfuscation(parent)(m_scratch);
166 [ + + ]: 406980 : m_scratch >> value;
167 [ - + ]: 406982 : } catch (const std::exception&) {
168 : : return false;
169 : : }
170 : 406978 : return true;
171 : : }
172 : : };
173 : :
174 : : struct LevelDBContext;
175 : :
176 : : class CDBWrapper
177 : : {
178 : : friend const Obfuscation& dbwrapper_private::GetObfuscation(const CDBWrapper&);
179 : : private:
180 : : //! holds all leveldb-specific fields of this class
181 : : std::unique_ptr<LevelDBContext> m_db_context;
182 : :
183 : : //! the name of this database
184 : : std::string m_name;
185 : :
186 : : //! optional XOR-obfuscation of the database
187 : : Obfuscation m_obfuscation;
188 : :
189 : : //! obfuscation key storage key, null-prefixed to avoid collisions
190 : : inline static const std::string OBFUSCATION_KEY{"\000obfuscate_key", 14}; // explicit size to avoid truncation at leading \0
191 : :
192 : : std::optional<std::string> ReadImpl(std::span<const std::byte> key) const;
193 : : bool ExistsImpl(std::span<const std::byte> key) const;
194 : : size_t EstimateSizeImpl(std::span<const std::byte> key1, std::span<const std::byte> key2) const;
195 [ - + ]: 13722770 : auto& DBContext() const LIFETIMEBOUND { return *Assert(m_db_context); }
196 : :
197 : : public:
198 : : CDBWrapper(const DBParams& params);
199 : : ~CDBWrapper();
200 : :
201 : : CDBWrapper(const CDBWrapper&) = delete;
202 : : CDBWrapper& operator=(const CDBWrapper&) = delete;
203 : :
204 : : template <typename K, typename V>
205 : 6784842 : bool Read(const K& key, V& value) const
206 : : {
207 [ + - ]: 6784842 : DataStream ssKey{};
208 [ + - ]: 6784842 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
209 [ - + ]: 6784842 : ssKey << key;
210 [ + + ]: 6784842 : std::optional<std::string> strValue{ReadImpl(ssKey)};
211 [ + + ]: 6784841 : if (!strValue) {
212 : : return false;
213 : : }
214 : : try {
215 : 86338 : std::span ssValue{MakeWritableByteSpan(*strValue)};
216 [ + - ]: 86338 : m_obfuscation(ssValue);
217 [ + - ]: 6871179 : SpanReader{ssValue} >> value;
218 [ - - ]: 0 : } catch (const std::exception&) {
219 : : return false;
220 : : }
221 : : return true;
222 : 6784841 : }
223 : :
224 : : template <typename K, typename V>
225 : 30058 : void Write(const K& key, const V& value, bool fSync = false)
226 : : {
227 : 30058 : CDBBatch batch(*this);
228 [ + - ]: 30058 : batch.Write(key, value);
229 [ + - ]: 30058 : WriteBatch(batch, fSync);
230 : 30058 : }
231 : :
232 : : template <typename K>
233 : 1308 : bool Exists(const K& key) const
234 : : {
235 [ + - ]: 1308 : DataStream ssKey{};
236 [ + - ]: 1308 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
237 [ - + ]: 1308 : ssKey << key;
238 [ + - ]: 1308 : return ExistsImpl(ssKey);
239 : 1308 : }
240 : :
241 : : template <typename K>
242 : 17 : void Erase(const K& key, bool fSync = false)
243 : : {
244 : 17 : CDBBatch batch(*this);
245 [ + - ]: 17 : batch.Erase(key);
246 [ + - ]: 17 : WriteBatch(batch, fSync);
247 : 17 : }
248 : :
249 : : void WriteBatch(CDBBatch& batch, bool fSync = false);
250 : :
251 : : // Get an estimate of LevelDB memory usage (in bytes).
252 : : size_t DynamicMemoryUsage() const;
253 : :
254 : : CDBIterator* NewIterator();
255 : :
256 : : /**
257 : : * Return true if the database managed by this class contains no entries.
258 : : */
259 : : bool IsEmpty();
260 : :
261 : : template<typename K>
262 : 104 : size_t EstimateSize(const K& key_begin, const K& key_end) const
263 : : {
264 [ + - ]: 104 : DataStream ssKey1{}, ssKey2{};
265 [ + - ]: 104 : ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
266 [ + - ]: 104 : ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
267 [ + - ]: 104 : ssKey1 << key_begin;
268 [ - + ]: 104 : ssKey2 << key_end;
269 [ - + + - ]: 104 : return EstimateSizeImpl(ssKey1, ssKey2);
270 : 104 : }
271 : : };
272 : :
273 : : #endif // BITCOIN_DBWRAPPER_H
|