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 0 : struct DBOptions { 28 : //! Compact database on startup. 29 0 : bool force_compact = false; 30 : }; 31 : 32 : //! Application-specific storage settings. 33 : 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 1 : void Write(const K& key, const V& value) 100 : { 101 1 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); 102 1 : ssValue.reserve(DBWRAPPER_PREALLOC_VALUE_SIZE); 103 1 : ssKey << key; 104 1 : ssValue << value; 105 1 : WriteImpl(ssKey, ssValue); 106 1 : ssKey.clear(); 107 1 : ssValue.clear(); 108 1 : } 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 2 : template<typename K> void Seek(const K& key) { 149 2 : DataStream ssKey{}; 150 2 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); 151 2 : ssKey << key; 152 2 : SeekImpl(ssKey); 153 2 : } 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 0 : return false; 163 0 : } 164 0 : return true; 165 0 : } 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 0 : return false; 174 0 : } 175 0 : return true; 176 0 : } 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 38610 : 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 19267 : bool Read(const K& key, V& value) const 222 : { 223 19267 : DataStream ssKey{}; 224 19267 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); 225 19267 : ssKey << key; 226 19267 : std::optional<std::string> strValue{ReadImpl(ssKey)}; 227 19267 : if (!strValue) { 228 19267 : 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 0 : return false; 236 0 : } 237 0 : return true; 238 19267 : } 239 : 240 : template <typename K, typename V> 241 1 : bool Write(const K& key, const V& value, bool fSync = false) 242 : { 243 1 : CDBBatch batch(*this); 244 1 : batch.Write(key, value); 245 1 : return WriteBatch(batch, fSync); 246 1 : } 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 0 : } 255 : 256 : template <typename K> 257 1 : bool Exists(const K& key) const 258 : { 259 1 : DataStream ssKey{}; 260 1 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); 261 1 : ssKey << key; 262 1 : return ExistsImpl(ssKey); 263 1 : } 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