// Copyright (c) 2012-present The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_DBWRAPPER_H #define BITCOIN_DBWRAPPER_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace leveldb { class Env; } // namespace leveldb static const size_t DBWRAPPER_PREALLOC_KEY_SIZE = 64; static const size_t DBWRAPPER_PREALLOC_VALUE_SIZE = 1024; static const size_t DBWRAPPER_MAX_FILE_SIZE{32_MiB}; //! User-controlled performance and debug options. struct DBOptions { //! Compact database on startup. bool force_compact = false; }; //! Application-specific storage settings. struct DBParams { //! Location in the filesystem where leveldb data will be stored. fs::path path; //! Configures various leveldb cache settings. uint64_t cache_bytes; //! If true, use leveldb's memory environment. bool memory_only = false; //! If true, remove all existing data. bool wipe_data = false; //! If true, store data obfuscated via simple XOR. If false, XOR with a //! zero'd byte array. bool obfuscate = false; //! If true, build a LevelDB bloom filter to accelerate point lookups. bool bloom_filter = true; //! Passed-through options. DBOptions options{}; //! If non-null, use this as the leveldb::Env instead of the default. //! Caller retains ownership. leveldb::Env* testing_env = nullptr; //! Maximum LevelDB SST file size. Larger values reduce the frequency //! of compactions but increase their duration. size_t max_file_size = DBWRAPPER_MAX_FILE_SIZE; }; class dbwrapper_error : public std::runtime_error { public: explicit dbwrapper_error(const std::string& msg) : std::runtime_error(msg) {} }; class CDBWrapper; /** These should be considered an implementation detail of the specific database. */ namespace dbwrapper_private { /** Work around circular dependency, as well as for testing in dbwrapper_tests. * Database obfuscation should be considered an implementation detail of the * specific database. */ const Obfuscation& GetObfuscation(const CDBWrapper&); }; // namespace dbwrapper_private bool DestroyDB(const std::string& path_str); /** Batch of changes queued to be written to a CDBWrapper */ class CDBBatch { friend class CDBWrapper; private: const CDBWrapper &parent; struct WriteBatchImpl; const std::unique_ptr m_impl_batch; DataStream m_key_scratch{}; DataStream m_value_scratch{}; void WriteImpl(std::span key, DataStream& value); void EraseImpl(std::span key); public: /** * @param[in] _parent CDBWrapper that this batch is to be submitted to */ explicit CDBBatch(const CDBWrapper& _parent); ~CDBBatch(); void Clear(); template void Write(const K& key, const V& value) { ScopedDataStreamUsage scoped_key{m_key_scratch}, scoped_value{m_value_scratch}; m_key_scratch << key; m_value_scratch << value; WriteImpl(m_key_scratch, m_value_scratch); } template void Erase(const K& key) { ScopedDataStreamUsage scoped_key{m_key_scratch}; m_key_scratch << key; EraseImpl(m_key_scratch); } size_t ApproximateSize() const; }; class CDBIterator { public: struct IteratorImpl; private: const CDBWrapper &parent; const std::unique_ptr m_impl_iter; DataStream m_scratch{}; void SeekImpl(std::span key); std::span GetKeyImpl() const; std::span GetValueImpl() const; public: /** * @param[in] _parent Parent CDBWrapper instance. * @param[in] _piter The original leveldb iterator. */ CDBIterator(const CDBWrapper& _parent, std::unique_ptr _piter); ~CDBIterator(); bool Valid() const; void SeekToFirst(); template void Seek(const K& key) { ScopedDataStreamUsage scoped_scratch{m_scratch}; m_scratch << key; SeekImpl(m_scratch); } void Next(); template bool GetKey(K& key) { try { SpanReader ssKey{GetKeyImpl()}; ssKey >> key; } catch (const std::exception&) { return false; } return true; } template bool GetValue(V& value) { try { ScopedDataStreamUsage scoped_scratch{m_scratch}; m_scratch.write(GetValueImpl()); dbwrapper_private::GetObfuscation(parent)(m_scratch); m_scratch >> value; } catch (const std::exception&) { return false; } return true; } }; struct LevelDBContext; class CDBWrapper { friend const Obfuscation& dbwrapper_private::GetObfuscation(const CDBWrapper&); private: //! holds all leveldb-specific fields of this class std::unique_ptr m_db_context; //! the name of this database std::string m_name; //! optional XOR-obfuscation of the database Obfuscation m_obfuscation; //! obfuscation key storage key, null-prefixed to avoid collisions inline static const std::string OBFUSCATION_KEY{"\000obfuscate_key", 14}; // explicit size to avoid truncation at leading \0 std::optional ReadImpl(std::span key) const; bool ExistsImpl(std::span key) const; size_t EstimateSizeImpl(std::span key1, std::span key2) const; auto& DBContext() const LIFETIMEBOUND { return *Assert(m_db_context); } public: CDBWrapper(const DBParams& params); ~CDBWrapper(); CDBWrapper(const CDBWrapper&) = delete; CDBWrapper& operator=(const CDBWrapper&) = delete; template bool Read(const K& key, V& value) const { DataStream ssKey{}; ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); ssKey << key; std::optional strValue{ReadImpl(ssKey)}; if (!strValue) { return false; } try { std::span ssValue{MakeWritableByteSpan(*strValue)}; m_obfuscation(ssValue); SpanReader{ssValue} >> value; } catch (const std::exception&) { return false; } return true; } template void Write(const K& key, const V& value, bool fSync = false) { CDBBatch batch(*this); batch.Write(key, value); WriteBatch(batch, fSync); } template bool Exists(const K& key) const { DataStream ssKey{}; ssKey.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); ssKey << key; return ExistsImpl(ssKey); } template void Erase(const K& key, bool fSync = false) { CDBBatch batch(*this); batch.Erase(key); WriteBatch(batch, fSync); } void WriteBatch(CDBBatch& batch, bool fSync = false); //! Perform a blocking full compaction of the underlying LevelDB. void CompactFull(); //! Return a LevelDB property value, if available. std::optional GetProperty(const std::string& property) const; // Get an estimate of LevelDB memory usage (in bytes). size_t DynamicMemoryUsage() const; CDBIterator* NewIterator(); /** * Return true if the database managed by this class contains no entries. */ bool IsEmpty(); template size_t EstimateSize(const K& key_begin, const K& key_end) const { DataStream ssKey1{}, ssKey2{}; ssKey1.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); ssKey2.reserve(DBWRAPPER_PREALLOC_KEY_SIZE); ssKey1 << key_begin; ssKey2 << key_end; return EstimateSizeImpl(ssKey1, ssKey2); } }; #endif // BITCOIN_DBWRAPPER_H