dbwrapper.h
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 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 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 void Write(const K& key, const V& value) 100 { 101 ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); 102 ssValue.reserve(DBWRAPPER_PREALLOC_VALUE_SIZE); 103 ssKey << key; 104 ssValue << value; 105 WriteImpl(ssKey, ssValue); 106 ssKey.clear(); 107 ssValue.clear(); 108 } 109 110 template <typename K> 111 void Erase(const K& key) 112 { 113 ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); 114 ssKey << key; 115 EraseImpl(ssKey); 116 ssKey.clear(); 117 } 118 119 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 template<typename K> void Seek(const K& key) { 149 DataStream ssKey{}; 150 ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); 151 ssKey << key; 152 SeekImpl(ssKey); 153 } 154 155 void Next(); 156 157 template<typename K> bool GetKey(K& key) { 158 try { 159 DataStream ssKey{GetKeyImpl()}; 160 ssKey >> key; 161 } catch (const std::exception&) { 162 return false; 163 } 164 return true; 165 } 166 167 template<typename V> bool GetValue(V& value) { 168 try { 169 DataStream ssValue{GetValueImpl()}; 170 ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent)); 171 ssValue >> value; 172 } catch (const std::exception&) { 173 return false; 174 } 175 return true; 176 } 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 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 bool Read(const K& key, V& value) const 222 { 223 DataStream ssKey{}; 224 ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); 225 ssKey << key; 226 std::optional<std::string> strValue{ReadImpl(ssKey)}; 227 if (!strValue) { 228 return false; 229 } 230 try { 231 DataStream ssValue{MakeByteSpan(*strValue)}; 232 ssValue.Xor(obfuscate_key); 233 ssValue >> value; 234 } catch (const std::exception&) { 235 return false; 236 } 237 return true; 238 } 239 240 template <typename K, typename V> 241 bool Write(const K& key, const V& value, bool fSync = false) 242 { 243 CDBBatch batch(*this); 244 batch.Write(key, value); 245 return WriteBatch(batch, fSync); 246 } 247 248 //! @returns filesystem path to the on-disk data. 249 std::optional<fs::path> StoragePath() { 250 if (m_is_memory) { 251 return {}; 252 } 253 return m_path; 254 } 255 256 template <typename K> 257 bool Exists(const K& key) const 258 { 259 DataStream ssKey{}; 260 ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); 261 ssKey << key; 262 return ExistsImpl(ssKey); 263 } 264 265 template <typename K> 266 bool Erase(const K& key, bool fSync = false) 267 { 268 CDBBatch batch(*this); 269 batch.Erase(key); 270 return WriteBatch(batch, fSync); 271 } 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 size_t EstimateSize(const K& key_begin, const K& key_end) const 287 { 288 DataStream ssKey1{}, ssKey2{}; 289 ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); 290 ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); 291 ssKey1 << key_begin; 292 ssKey2 << key_end; 293 return EstimateSizeImpl(ssKey1, ssKey2); 294 } 295 }; 296 297 #endif // BITCOIN_DBWRAPPER_H