Branch data 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 : 98 : 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 : 11 : bool Read(const K& key, V& value) const 222 : : { 223 : 11 : DataStream ssKey{}; 224 [ + - + - : 11 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); + - # # ] 225 [ + - + - : 11 : ssKey << key; + - # # ] 226 [ + - + - : 11 : std::optional<std::string> strValue{ReadImpl(ssKey)}; + - + - + - + - # # # # ] 227 [ - + - + : 11 : if (!strValue) { - + # # ] 228 : 11 : 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 : 11 : } 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