Branch data Line data Source code
# 1 : : // Copyright (c) 2012-2021 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 <clientversion.h>
# 9 : : #include <fs.h>
# 10 : : #include <serialize.h>
# 11 : : #include <span.h>
# 12 : : #include <streams.h>
# 13 : : #include <util/strencodings.h>
# 14 : : #include <util/system.h>
# 15 : :
# 16 : : #include <leveldb/db.h>
# 17 : : #include <leveldb/write_batch.h>
# 18 : :
# 19 : : static const size_t DBWRAPPER_PREALLOC_KEY_SIZE = 64;
# 20 : : static const size_t DBWRAPPER_PREALLOC_VALUE_SIZE = 1024;
# 21 : :
# 22 : : class dbwrapper_error : public std::runtime_error
# 23 : : {
# 24 : : public:
# 25 : 2 : explicit dbwrapper_error(const std::string& msg) : std::runtime_error(msg) {}
# 26 : : };
# 27 : :
# 28 : : class CDBWrapper;
# 29 : :
# 30 : : /** These should be considered an implementation detail of the specific database.
# 31 : : */
# 32 : : namespace dbwrapper_private {
# 33 : :
# 34 : : /** Handle database error by throwing dbwrapper_error exception.
# 35 : : */
# 36 : : void HandleError(const leveldb::Status& status);
# 37 : :
# 38 : : /** Work around circular dependency, as well as for testing in dbwrapper_tests.
# 39 : : * Database obfuscation should be considered an implementation detail of the
# 40 : : * specific database.
# 41 : : */
# 42 : : const std::vector<unsigned char>& GetObfuscateKey(const CDBWrapper &w);
# 43 : :
# 44 : : };
# 45 : :
# 46 : : /** Batch of changes queued to be written to a CDBWrapper */
# 47 : : class CDBBatch
# 48 : : {
# 49 : : friend class CDBWrapper;
# 50 : :
# 51 : : private:
# 52 : : const CDBWrapper &parent;
# 53 : : leveldb::WriteBatch batch;
# 54 : :
# 55 : : CDataStream ssKey;
# 56 : : CDataStream ssValue;
# 57 : :
# 58 : : size_t size_estimate;
# 59 : :
# 60 : : public:
# 61 : : /**
# 62 : : * @param[in] _parent CDBWrapper that this batch is to be submitted to
# 63 : : */
# 64 : 13678 : explicit CDBBatch(const CDBWrapper &_parent) : parent(_parent), ssKey(SER_DISK, CLIENT_VERSION), ssValue(SER_DISK, CLIENT_VERSION), size_estimate(0) { };
# 65 : :
# 66 : : void Clear()
# 67 : 0 : {
# 68 : 0 : batch.Clear();
# 69 : 0 : size_estimate = 0;
# 70 : 0 : }
# 71 : :
# 72 : : template <typename K, typename V>
# 73 : : void Write(const K& key, const V& value)
# 74 : 322689 : {
# 75 : 322689 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
# 76 : 322689 : ssKey << key;
# 77 : 322689 : leveldb::Slice slKey((const char*)ssKey.data(), ssKey.size());
# 78 : :
# 79 : 322689 : ssValue.reserve(DBWRAPPER_PREALLOC_VALUE_SIZE);
# 80 : 322689 : ssValue << value;
# 81 : 322689 : ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent));
# 82 : 322689 : leveldb::Slice slValue((const char*)ssValue.data(), ssValue.size());
# 83 : :
# 84 : 322689 : batch.Put(slKey, slValue);
# 85 : : // LevelDB serializes writes as:
# 86 : : // - byte: header
# 87 : : // - varint: key length (1 byte up to 127B, 2 bytes up to 16383B, ...)
# 88 : : // - byte[]: key
# 89 : : // - varint: value length
# 90 : : // - byte[]: value
# 91 : : // The formula below assumes the key and value are both less than 16k.
# 92 : 322689 : size_estimate += 3 + (slKey.size() > 127) + slKey.size() + (slValue.size() > 127) + slValue.size();
# 93 : 322689 : ssKey.clear();
# 94 : 322689 : ssValue.clear();
# 95 : 322689 : }
# 96 : :
# 97 : : template <typename K>
# 98 : : void Erase(const K& key)
# 99 : 42702 : {
# 100 : 42702 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
# 101 : 42702 : ssKey << key;
# 102 : 42702 : leveldb::Slice slKey((const char*)ssKey.data(), ssKey.size());
# 103 : :
# 104 : 42702 : batch.Delete(slKey);
# 105 : : // LevelDB serializes erases as:
# 106 : : // - byte: header
# 107 : : // - varint: key length
# 108 : : // - byte[]: key
# 109 : : // The formula below assumes the key is less than 16kB.
# 110 : 42702 : size_estimate += 2 + (slKey.size() > 127) + slKey.size();
# 111 : 42702 : ssKey.clear();
# 112 : 42702 : }
# 113 : :
# 114 : 405630 : size_t SizeEstimate() const { return size_estimate; }
# 115 : : };
# 116 : :
# 117 : : class CDBIterator
# 118 : : {
# 119 : : private:
# 120 : : const CDBWrapper &parent;
# 121 : : leveldb::Iterator *piter;
# 122 : :
# 123 : : public:
# 124 : :
# 125 : : /**
# 126 : : * @param[in] _parent Parent CDBWrapper instance.
# 127 : : * @param[in] _piter The original leveldb iterator.
# 128 : : */
# 129 : : CDBIterator(const CDBWrapper &_parent, leveldb::Iterator *_piter) :
# 130 : 3379 : parent(_parent), piter(_piter) { };
# 131 : : ~CDBIterator();
# 132 : :
# 133 : : bool Valid() const;
# 134 : :
# 135 : : void SeekToFirst();
# 136 : :
# 137 : 2884 : template<typename K> void Seek(const K& key) {
# 138 : 2884 : CDataStream ssKey(SER_DISK, CLIENT_VERSION);
# 139 : 2884 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
# 140 : 2884 : ssKey << key;
# 141 : 2884 : leveldb::Slice slKey((const char*)ssKey.data(), ssKey.size());
# 142 : 2884 : piter->Seek(slKey);
# 143 : 2884 : }
# 144 : :
# 145 : : void Next();
# 146 : :
# 147 : 98813 : template<typename K> bool GetKey(K& key) {
# 148 : 98813 : leveldb::Slice slKey = piter->key();
# 149 : 98813 : try {
# 150 : 98813 : CDataStream ssKey{MakeByteSpan(slKey), SER_DISK, CLIENT_VERSION};
# 151 : 98813 : ssKey >> key;
# 152 : 98813 : } catch (const std::exception&) {
# 153 : 472 : return false;
# 154 : 472 : }
# 155 : 98341 : return true;
# 156 : 98813 : }
# 157 : :
# 158 : 97919 : template<typename V> bool GetValue(V& value) {
# 159 : 97919 : leveldb::Slice slValue = piter->value();
# 160 : 97919 : try {
# 161 : 97919 : CDataStream ssValue{MakeByteSpan(slValue), SER_DISK, CLIENT_VERSION};
# 162 : 97919 : ssValue.Xor(dbwrapper_private::GetObfuscateKey(parent));
# 163 : 97919 : ssValue >> value;
# 164 : 97919 : } catch (const std::exception&) {
# 165 : 0 : return false;
# 166 : 0 : }
# 167 : 97919 : return true;
# 168 : 97919 : }
# 169 : :
# 170 : 0 : unsigned int GetValueSize() {
# 171 : 0 : return piter->value().size();
# 172 : 0 : }
# 173 : :
# 174 : : };
# 175 : :
# 176 : : class CDBWrapper
# 177 : : {
# 178 : : friend const std::vector<unsigned char>& dbwrapper_private::GetObfuscateKey(const CDBWrapper &w);
# 179 : : private:
# 180 : : //! custom environment this database is using (may be nullptr in case of default environment)
# 181 : : leveldb::Env* penv;
# 182 : :
# 183 : : //! database options used
# 184 : : leveldb::Options options;
# 185 : :
# 186 : : //! options used when reading from the database
# 187 : : leveldb::ReadOptions readoptions;
# 188 : :
# 189 : : //! options used when iterating over values of the database
# 190 : : leveldb::ReadOptions iteroptions;
# 191 : :
# 192 : : //! options used when writing to the database
# 193 : : leveldb::WriteOptions writeoptions;
# 194 : :
# 195 : : //! options used when sync writing to the database
# 196 : : leveldb::WriteOptions syncoptions;
# 197 : :
# 198 : : //! the database itself
# 199 : : leveldb::DB* pdb;
# 200 : :
# 201 : : //! the name of this database
# 202 : : std::string m_name;
# 203 : :
# 204 : : //! a key used for optional XOR-obfuscation of the database
# 205 : : std::vector<unsigned char> obfuscate_key;
# 206 : :
# 207 : : //! the key under which the obfuscation key is stored
# 208 : : static const std::string OBFUSCATE_KEY_KEY;
# 209 : :
# 210 : : //! the length of the obfuscate key in number of bytes
# 211 : : static const unsigned int OBFUSCATE_KEY_NUM_BYTES;
# 212 : :
# 213 : : std::vector<unsigned char> CreateObfuscateKey() const;
# 214 : :
# 215 : : public:
# 216 : : /**
# 217 : : * @param[in] path Location in the filesystem where leveldb data will be stored.
# 218 : : * @param[in] nCacheSize Configures various leveldb cache settings.
# 219 : : * @param[in] fMemory If true, use leveldb's memory environment.
# 220 : : * @param[in] fWipe If true, remove all existing data.
# 221 : : * @param[in] obfuscate If true, store data obfuscated via simple XOR. If false, XOR
# 222 : : * with a zero'd byte array.
# 223 : : */
# 224 : : CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false, bool obfuscate = false);
# 225 : : ~CDBWrapper();
# 226 : :
# 227 : : CDBWrapper(const CDBWrapper&) = delete;
# 228 : : CDBWrapper& operator=(const CDBWrapper&) = delete;
# 229 : :
# 230 : : template <typename K, typename V>
# 231 : : bool Read(const K& key, V& value) const
# 232 : 10082988 : {
# 233 : 10082988 : CDataStream ssKey(SER_DISK, CLIENT_VERSION);
# 234 : 10082988 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
# 235 : 10082988 : ssKey << key;
# 236 : 10082988 : leveldb::Slice slKey((const char*)ssKey.data(), ssKey.size());
# 237 : :
# 238 : 10082988 : std::string strValue;
# 239 : 10082988 : leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
# 240 [ + + ][ + + ]: 10082988 : if (!status.ok()) {
# [ - + ][ + + ]
# [ - + ][ + + ]
# [ - + ][ + - ]
# [ - + ][ + + ]
# [ + + ][ + + ]
# [ + + ][ + + ]
# [ - + ][ # # ]
# [ + + ][ + + ]
# [ - + ][ + + ]
# 241 [ + - ][ # # ]: 9916073 : if (status.IsNotFound())
# [ + - ][ + - ]
# [ # # ][ # # ]
# [ + - ][ + - ]
# [ + - ][ + - ]
# [ # # ][ + - ]
# [ # # ][ + - ]
# [ # # ][ + - ]
# [ # # ][ + - ]
# [ + - ][ + - ]
# 242 : 9916073 : return false;
# 243 : 0 : LogPrintf("LevelDB read failure: %s\n", status.ToString());
# 244 : 0 : dbwrapper_private::HandleError(status);
# 245 : 0 : }
# 246 : 166915 : try {
# 247 : 166915 : CDataStream ssValue{MakeByteSpan(strValue), SER_DISK, CLIENT_VERSION};
# 248 : 166915 : ssValue.Xor(obfuscate_key);
# 249 : 166915 : ssValue >> value;
# 250 : 166915 : } catch (const std::exception&) {
# 251 : 0 : return false;
# 252 : 0 : }
# 253 : 166915 : return true;
# 254 : 166915 : }
# 255 : :
# 256 : : template <typename K, typename V>
# 257 : : bool Write(const K& key, const V& value, bool fSync = false)
# 258 : 8366 : {
# 259 : 8366 : CDBBatch batch(*this);
# 260 : 8366 : batch.Write(key, value);
# 261 : 8366 : return WriteBatch(batch, fSync);
# 262 : 8366 : }
# 263 : :
# 264 : : template <typename K>
# 265 : : bool Exists(const K& key) const
# 266 : 945 : {
# 267 : 945 : CDataStream ssKey(SER_DISK, CLIENT_VERSION);
# 268 : 945 : ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
# 269 : 945 : ssKey << key;
# 270 : 945 : leveldb::Slice slKey((const char*)ssKey.data(), ssKey.size());
# 271 : :
# 272 : 945 : std::string strValue;
# 273 : 945 : leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
# 274 [ + - ][ # # ]: 945 : if (!status.ok()) {
# 275 [ + - ][ # # ]: 945 : if (status.IsNotFound())
# 276 : 945 : return false;
# 277 : 0 : LogPrintf("LevelDB read failure: %s\n", status.ToString());
# 278 : 0 : dbwrapper_private::HandleError(status);
# 279 : 0 : }
# 280 : 0 : return true;
# 281 : 945 : }
# 282 : :
# 283 : : template <typename K>
# 284 : : bool Erase(const K& key, bool fSync = false)
# 285 : 10 : {
# 286 : 10 : CDBBatch batch(*this);
# 287 : 10 : batch.Erase(key);
# 288 : 10 : return WriteBatch(batch, fSync);
# 289 : 10 : }
# 290 : :
# 291 : : bool WriteBatch(CDBBatch& batch, bool fSync = false);
# 292 : :
# 293 : : // Get an estimate of LevelDB memory usage (in bytes).
# 294 : : size_t DynamicMemoryUsage() const;
# 295 : :
# 296 : : CDBIterator *NewIterator()
# 297 : 3379 : {
# 298 : 3379 : return new CDBIterator(*this, pdb->NewIterator(iteroptions));
# 299 : 3379 : }
# 300 : :
# 301 : : /**
# 302 : : * Return true if the database managed by this class contains no entries.
# 303 : : */
# 304 : : bool IsEmpty();
# 305 : :
# 306 : : template<typename K>
# 307 : : size_t EstimateSize(const K& key_begin, const K& key_end) const
# 308 : 27 : {
# 309 : 27 : CDataStream ssKey1(SER_DISK, CLIENT_VERSION), ssKey2(SER_DISK, CLIENT_VERSION);
# 310 : 27 : ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
# 311 : 27 : ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
# 312 : 27 : ssKey1 << key_begin;
# 313 : 27 : ssKey2 << key_end;
# 314 : 27 : leveldb::Slice slKey1((const char*)ssKey1.data(), ssKey1.size());
# 315 : 27 : leveldb::Slice slKey2((const char*)ssKey2.data(), ssKey2.size());
# 316 : 27 : uint64_t size = 0;
# 317 : 27 : leveldb::Range range(slKey1, slKey2);
# 318 : 27 : pdb->GetApproximateSizes(&range, 1, &size);
# 319 : 27 : return size;
# 320 : 27 : }
# 321 : :
# 322 : : /**
# 323 : : * Compact a certain range of keys in the database.
# 324 : : */
# 325 : : template<typename K>
# 326 : : void CompactRange(const K& key_begin, const K& key_end) const
# 327 : : {
# 328 : : CDataStream ssKey1(SER_DISK, CLIENT_VERSION), ssKey2(SER_DISK, CLIENT_VERSION);
# 329 : : ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
# 330 : : ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE);
# 331 : : ssKey1 << key_begin;
# 332 : : ssKey2 << key_end;
# 333 : : leveldb::Slice slKey1((const char*)ssKey1.data(), ssKey1.size());
# 334 : : leveldb::Slice slKey2((const char*)ssKey2.data(), ssKey2.size());
# 335 : : pdb->CompactRange(&slKey1, &slKey2);
# 336 : : }
# 337 : : };
# 338 : :
# 339 : : #endif // BITCOIN_DBWRAPPER_H
|