git / moonbite-core / src/dbwrapper.cpp · raw

// Copyright (c) 2012-2019 The Bitcoin Core developers// Distributed under the MIT software license, see the accompanying// file COPYING or http://www.opensource.org/licenses/mit-license.php.#include <dbwrapper.h>#include <memory>#include <random.h>#include <leveldb/cache.h>#include <leveldb/env.h>#include <leveldb/filter_policy.h>#include <memenv.h>#include <stdint.h>#include <algorithm>class CBitcoinLevelDBLogger : public leveldb::Logger {public:    // This code is adapted from posix_logger.h, which is why it is using vsprintf.    // Please do not do this in normal code    void Logv(const char * format, va_list ap) override {            if (!LogAcceptCategory(BCLog::LEVELDB)) {                return;            }            char buffer[500];            for (int iter = 0; iter < 2; iter++) {                char* base;                int bufsize;                if (iter == 0) {                    bufsize = sizeof(buffer);                    base = buffer;                }                else {                    bufsize = 30000;                    base = new char[bufsize];                }                char* p = base;                char* limit = base + bufsize;                // Print the message                if (p < limit) {                    va_list backup_ap;                    va_copy(backup_ap, ap);                    // Do not use vsnprintf elsewhere in bitcoin source code, see above.                    p += vsnprintf(p, limit - p, format, backup_ap);                    va_end(backup_ap);                }                // Truncate to available space if necessary                if (p >= limit) {                    if (iter == 0) {                        continue;       // Try again with larger buffer                    }                    else {                        p = limit - 1;                    }                }                // Add newline if necessary                if (p == base || p[-1] != '\n') {                    *p++ = '\n';                }                assert(p <= limit);                base[std::min(bufsize - 1, (int)(p - base))] = '\0';                LogPrintf("leveldb: %s", base);  /* Continued */                if (base != buffer) {                    delete[] base;                }                break;            }    }};static void SetMaxOpenFiles(leveldb::Options *options) {    // On most platforms the default setting of max_open_files (which is 1000)    // is optimal. On Windows using a large file count is OK because the handles    // do not interfere with select() loops. On 64-bit Unix hosts this value is    // also OK, because up to that amount LevelDB will use an mmap    // implementation that does not use extra file descriptors (the fds are    // closed after being mmap'ed).    //    // Increasing the value beyond the default is dangerous because LevelDB will    // fall back to a non-mmap implementation when the file count is too large.    // On 32-bit Unix host we should decrease the value because the handles use    // up real fds, and we want to avoid fd exhaustion issues.    //    // See PR #12495 for further discussion.    int default_open_files = options->max_open_files;#ifndef WIN32    if (sizeof(void*) < 8) {        options->max_open_files = 64;    }#endif    LogPrint(BCLog::LEVELDB, "LevelDB using max_open_files=%d (default=%d)\n",             options->max_open_files, default_open_files);}static leveldb::Options GetOptions(size_t nCacheSize){    leveldb::Options options;    options.block_cache = leveldb::NewLRUCache(nCacheSize / 2);    options.write_buffer_size = nCacheSize / 4; // up to two write buffers may be held in memory simultaneously    options.filter_policy = leveldb::NewBloomFilterPolicy(10);    options.compression = leveldb::kNoCompression;    options.info_log = new CBitcoinLevelDBLogger();    if (leveldb::kMajorVersion > 1 || (leveldb::kMajorVersion == 1 && leveldb::kMinorVersion >= 16)) {        // LevelDB versions before 1.16 consider short writes to be corruption. Only trigger error        // on corruption in later versions.        options.paranoid_checks = true;    }    SetMaxOpenFiles(&options);    return options;}CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bool fWipe, bool obfuscate)    : m_name{path.stem().string()}{    penv = nullptr;    readoptions.verify_checksums = true;    iteroptions.verify_checksums = true;    iteroptions.fill_cache = false;    syncoptions.sync = true;    options = GetOptions(nCacheSize);    options.create_if_missing = true;    if (fMemory) {        penv = leveldb::NewMemEnv(leveldb::Env::Default());        options.env = penv;    } else {        if (fWipe) {            LogPrintf("Wiping LevelDB in %s\n", path.string());            leveldb::Status result = leveldb::DestroyDB(path.string(), options);            dbwrapper_private::HandleError(result);        }        TryCreateDirectories(path);        LogPrintf("Opening LevelDB in %s\n", path.string());    }    leveldb::Status status = leveldb::DB::Open(options, path.string(), &pdb);    dbwrapper_private::HandleError(status);    LogPrintf("Opened LevelDB successfully\n");    if (gArgs.GetBoolArg("-forcecompactdb", false)) {        LogPrintf("Starting database compaction of %s\n", path.string());        pdb->CompactRange(nullptr, nullptr);        LogPrintf("Finished database compaction of %s\n", path.string());    }    // The base-case obfuscation key, which is a noop.    obfuscate_key = std::vector<unsigned char>(OBFUSCATE_KEY_NUM_BYTES, '\000');    bool key_exists = Read(OBFUSCATE_KEY_KEY, obfuscate_key);    if (!key_exists && obfuscate && IsEmpty()) {        // Initialize non-degenerate obfuscation if it won't upset        // existing, non-obfuscated data.        std::vector<unsigned char> new_key = CreateObfuscateKey();        // Write `new_key` so we don't obfuscate the key with itself        Write(OBFUSCATE_KEY_KEY, new_key);        obfuscate_key = new_key;        LogPrintf("Wrote new obfuscate key for %s: %s\n", path.string(), HexStr(obfuscate_key));    }    LogPrintf("Using obfuscation key for %s: %s\n", path.string(), HexStr(obfuscate_key));}CDBWrapper::~CDBWrapper(){    delete pdb;    pdb = nullptr;    delete options.filter_policy;    options.filter_policy = nullptr;    delete options.info_log;    options.info_log = nullptr;    delete options.block_cache;    options.block_cache = nullptr;    delete penv;    options.env = nullptr;}bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync){    const bool log_memory = LogAcceptCategory(BCLog::LEVELDB);    double mem_before = 0;    if (log_memory) {        mem_before = DynamicMemoryUsage() / 1024.0 / 1024;    }    leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch);    dbwrapper_private::HandleError(status);    if (log_memory) {        double mem_after = DynamicMemoryUsage() / 1024.0 / 1024;        LogPrint(BCLog::LEVELDB, "WriteBatch memory usage: db=%s, before=%.1fMiB, after=%.1fMiB\n",                 m_name, mem_before, mem_after);    }    return true;}size_t CDBWrapper::DynamicMemoryUsage() const {    std::string memory;    if (!pdb->GetProperty("leveldb.approximate-memory-usage", &memory)) {        LogPrint(BCLog::LEVELDB, "Failed to get approximate-memory-usage property\n");        return 0;    }    return stoul(memory);}// Prefixed with null character to avoid collisions with other keys//// We must use a string constructor which specifies length so that we copy// past the null-terminator.const std::string CDBWrapper::OBFUSCATE_KEY_KEY("\000obfuscate_key", 14);const unsigned int CDBWrapper::OBFUSCATE_KEY_NUM_BYTES = 8;/** * Returns a string (consisting of 8 random bytes) suitable for use as an * obfuscating XOR key. */std::vector<unsigned char> CDBWrapper::CreateObfuscateKey() const{    unsigned char buff[OBFUSCATE_KEY_NUM_BYTES];    GetRandBytes(buff, OBFUSCATE_KEY_NUM_BYTES);    return std::vector<unsigned char>(&buff[0], &buff[OBFUSCATE_KEY_NUM_BYTES]);}bool CDBWrapper::IsEmpty(){    std::unique_ptr<CDBIterator> it(NewIterator());    it->SeekToFirst();    return !(it->Valid());}CDBIterator::~CDBIterator() { delete piter; }bool CDBIterator::Valid() const { return piter->Valid(); }void CDBIterator::SeekToFirst() { piter->SeekToFirst(); }void CDBIterator::Next() { piter->Next(); }namespace dbwrapper_private {void HandleError(const leveldb::Status& status){    if (status.ok())        return;    const std::string errmsg = "Fatal LevelDB error: " + status.ToString();    LogPrintf("%s\n", errmsg);    LogPrintf("You can use -debug=leveldb to get more complete diagnostic messages\n");    throw dbwrapper_error(errmsg);}const std::vector<unsigned char>& GetObfuscateKey(const CDBWrapper &w){    return w.obfuscate_key;}} // namespace dbwrapper_private