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/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 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 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 void Write(const K& key, const V& value) 97 { 98 ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); 99 ssValue.reserve(DBWRAPPER_PREALLOC_VALUE_SIZE); 100 ssKey << key; 101 ssValue << value; 102 WriteImpl(ssKey, ssValue); 103 ssKey.clear(); 104 ssValue.clear(); 105 } 106 107 template <typename K> 108 void Erase(const K& key) 109 { 110 ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); 111 ssKey << key; 112 EraseImpl(ssKey); 113 ssKey.clear(); 114 } 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 template<typename K> void Seek(const K& key) { 146 DataStream ssKey{}; 147 ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); 148 ssKey << key; 149 SeekImpl(ssKey); 150 } 151 152 void Next(); 153 154 template<typename K> bool GetKey(K& key) { 155 try { 156 DataStream ssKey{GetKeyImpl()}; 157 ssKey >> key; 158 } catch (const std::exception&) { 159 return false; 160 } 161 return true; 162 } 163 164 template<typename V> bool GetValue(V& value) { 165 try { 166 DataStream ssValue{GetValueImpl()}; 167 dbwrapper_private::GetObfuscation(parent)(ssValue); 168 ssValue >> value; 169 } catch (const std::exception&) { 170 return false; 171 } 172 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{"\000obfuscate_key", 14}; // explicit size to avoid truncation at leading \0 193 194 std::optional<std::string> ReadImpl(std::span<const std::byte> key) const; 195 bool ExistsImpl(std::span<const std::byte> key) const; 196 size_t EstimateSizeImpl(std::span<const std::byte> key1, std::span<const std::byte> key2) const; 197 auto& DBContext() const LIFETIMEBOUND { return *Assert(m_db_context); } 198 199 public: 200 CDBWrapper(const DBParams& params); 201 ~CDBWrapper(); 202 203 CDBWrapper(const CDBWrapper&) = delete; 204 CDBWrapper& operator=(const CDBWrapper&) = delete; 205 206 template <typename K, typename V> 207 bool Read(const K& key, V& value) const 208 { 209 DataStream ssKey{}; 210 ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); 211 ssKey << key; 212 std::optional<std::string> strValue{ReadImpl(ssKey)}; 213 if (!strValue) { 214 return false; 215 } 216 try { 217 std::span ssValue{MakeWritableByteSpan(*strValue)}; 218 m_obfuscation(ssValue); 219 SpanReader{ssValue} >> value; 220 } catch (const std::exception&) { 221 return false; 222 } 223 return true; 224 } 225 226 template <typename K, typename V> 227 void Write(const K& key, const V& value, bool fSync = false) 228 { 229 CDBBatch batch(*this); 230 batch.Write(key, value); 231 WriteBatch(batch, fSync); 232 } 233 234 template <typename K> 235 bool Exists(const K& key) const 236 { 237 DataStream ssKey{}; 238 ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); 239 ssKey << key; 240 return ExistsImpl(ssKey); 241 } 242 243 template <typename K> 244 void Erase(const K& key, bool fSync = false) 245 { 246 CDBBatch batch(*this); 247 batch.Erase(key); 248 WriteBatch(batch, fSync); 249 } 250 251 void WriteBatch(CDBBatch& batch, bool fSync = false); 252 253 // Get an estimate of LevelDB memory usage (in bytes). 254 size_t DynamicMemoryUsage() const; 255 256 CDBIterator* NewIterator(); 257 258 /** 259 * Return true if the database managed by this class contains no entries. 260 */ 261 bool IsEmpty(); 262 263 template<typename K> 264 size_t EstimateSize(const K& key_begin, const K& key_end) const 265 { 266 DataStream ssKey1{}, ssKey2{}; 267 ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); 268 ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); 269 ssKey1 << key_begin; 270 ssKey2 << key_end; 271 return EstimateSizeImpl(ssKey1, ssKey2); 272 } 273 }; 274 275 #endif // BITCOIN_DBWRAPPER_H