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