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