Merge #11221: Refactor: simpler read
[bitcoinplatinum.git] / src / dbwrapper.cpp
blobdfc90f3ab9d863e925cba9d7e7b1df5daa76b886
1 // Copyright (c) 2012-2016 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.
5 #include "dbwrapper.h"
7 #include "fs.h"
8 #include "util.h"
9 #include "random.h"
11 #include <leveldb/cache.h>
12 #include <leveldb/env.h>
13 #include <leveldb/filter_policy.h>
14 #include <memenv.h>
15 #include <stdint.h>
16 #include <algorithm>
18 class CBitcoinLevelDBLogger : public leveldb::Logger {
19 public:
20 // This code is adapted from posix_logger.h, which is why it is using vsprintf.
21 // Please do not do this in normal code
22 void Logv(const char * format, va_list ap) override {
23 if (!LogAcceptCategory(BCLog::LEVELDB)) {
24 return;
26 char buffer[500];
27 for (int iter = 0; iter < 2; iter++) {
28 char* base;
29 int bufsize;
30 if (iter == 0) {
31 bufsize = sizeof(buffer);
32 base = buffer;
34 else {
35 bufsize = 30000;
36 base = new char[bufsize];
38 char* p = base;
39 char* limit = base + bufsize;
41 // Print the message
42 if (p < limit) {
43 va_list backup_ap;
44 va_copy(backup_ap, ap);
45 // Do not use vsnprintf elsewhere in bitcoin source code, see above.
46 p += vsnprintf(p, limit - p, format, backup_ap);
47 va_end(backup_ap);
50 // Truncate to available space if necessary
51 if (p >= limit) {
52 if (iter == 0) {
53 continue; // Try again with larger buffer
55 else {
56 p = limit - 1;
60 // Add newline if necessary
61 if (p == base || p[-1] != '\n') {
62 *p++ = '\n';
65 assert(p <= limit);
66 base[std::min(bufsize - 1, (int)(p - base))] = '\0';
67 LogPrintStr(base);
68 if (base != buffer) {
69 delete[] base;
71 break;
76 static leveldb::Options GetOptions(size_t nCacheSize)
78 leveldb::Options options;
79 options.block_cache = leveldb::NewLRUCache(nCacheSize / 2);
80 options.write_buffer_size = nCacheSize / 4; // up to two write buffers may be held in memory simultaneously
81 options.filter_policy = leveldb::NewBloomFilterPolicy(10);
82 options.compression = leveldb::kNoCompression;
83 options.max_open_files = 64;
84 options.info_log = new CBitcoinLevelDBLogger();
85 if (leveldb::kMajorVersion > 1 || (leveldb::kMajorVersion == 1 && leveldb::kMinorVersion >= 16)) {
86 // LevelDB versions before 1.16 consider short writes to be corruption. Only trigger error
87 // on corruption in later versions.
88 options.paranoid_checks = true;
90 return options;
93 CDBWrapper::CDBWrapper(const fs::path& path, size_t nCacheSize, bool fMemory, bool fWipe, bool obfuscate)
95 penv = nullptr;
96 readoptions.verify_checksums = true;
97 iteroptions.verify_checksums = true;
98 iteroptions.fill_cache = false;
99 syncoptions.sync = true;
100 options = GetOptions(nCacheSize);
101 options.create_if_missing = true;
102 if (fMemory) {
103 penv = leveldb::NewMemEnv(leveldb::Env::Default());
104 options.env = penv;
105 } else {
106 if (fWipe) {
107 LogPrintf("Wiping LevelDB in %s\n", path.string());
108 leveldb::Status result = leveldb::DestroyDB(path.string(), options);
109 dbwrapper_private::HandleError(result);
111 TryCreateDirectories(path);
112 LogPrintf("Opening LevelDB in %s\n", path.string());
114 leveldb::Status status = leveldb::DB::Open(options, path.string(), &pdb);
115 dbwrapper_private::HandleError(status);
116 LogPrintf("Opened LevelDB successfully\n");
118 if (gArgs.GetBoolArg("-forcecompactdb", false)) {
119 LogPrintf("Starting database compaction of %s\n", path.string());
120 pdb->CompactRange(nullptr, nullptr);
121 LogPrintf("Finished database compaction of %s\n", path.string());
124 // The base-case obfuscation key, which is a noop.
125 obfuscate_key = std::vector<unsigned char>(OBFUSCATE_KEY_NUM_BYTES, '\000');
127 bool key_exists = Read(OBFUSCATE_KEY_KEY, obfuscate_key);
129 if (!key_exists && obfuscate && IsEmpty()) {
130 // Initialize non-degenerate obfuscation if it won't upset
131 // existing, non-obfuscated data.
132 std::vector<unsigned char> new_key = CreateObfuscateKey();
134 // Write `new_key` so we don't obfuscate the key with itself
135 Write(OBFUSCATE_KEY_KEY, new_key);
136 obfuscate_key = new_key;
138 LogPrintf("Wrote new obfuscate key for %s: %s\n", path.string(), HexStr(obfuscate_key));
141 LogPrintf("Using obfuscation key for %s: %s\n", path.string(), HexStr(obfuscate_key));
144 CDBWrapper::~CDBWrapper()
146 delete pdb;
147 pdb = nullptr;
148 delete options.filter_policy;
149 options.filter_policy = nullptr;
150 delete options.info_log;
151 options.info_log = nullptr;
152 delete options.block_cache;
153 options.block_cache = nullptr;
154 delete penv;
155 options.env = nullptr;
158 bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync)
160 leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch);
161 dbwrapper_private::HandleError(status);
162 return true;
165 // Prefixed with null character to avoid collisions with other keys
167 // We must use a string constructor which specifies length so that we copy
168 // past the null-terminator.
169 const std::string CDBWrapper::OBFUSCATE_KEY_KEY("\000obfuscate_key", 14);
171 const unsigned int CDBWrapper::OBFUSCATE_KEY_NUM_BYTES = 8;
174 * Returns a string (consisting of 8 random bytes) suitable for use as an
175 * obfuscating XOR key.
177 std::vector<unsigned char> CDBWrapper::CreateObfuscateKey() const
179 unsigned char buff[OBFUSCATE_KEY_NUM_BYTES];
180 GetRandBytes(buff, OBFUSCATE_KEY_NUM_BYTES);
181 return std::vector<unsigned char>(&buff[0], &buff[OBFUSCATE_KEY_NUM_BYTES]);
185 bool CDBWrapper::IsEmpty()
187 std::unique_ptr<CDBIterator> it(NewIterator());
188 it->SeekToFirst();
189 return !(it->Valid());
192 CDBIterator::~CDBIterator() { delete piter; }
193 bool CDBIterator::Valid() const { return piter->Valid(); }
194 void CDBIterator::SeekToFirst() { piter->SeekToFirst(); }
195 void CDBIterator::Next() { piter->Next(); }
197 namespace dbwrapper_private {
199 void HandleError(const leveldb::Status& status)
201 if (status.ok())
202 return;
203 LogPrintf("%s\n", status.ToString());
204 if (status.IsCorruption())
205 throw dbwrapper_error("Database corrupted");
206 if (status.IsIOError())
207 throw dbwrapper_error("Database I/O error");
208 if (status.IsNotFound())
209 throw dbwrapper_error("Database entry missing");
210 throw dbwrapper_error("Unknown database error");
213 const std::vector<unsigned char>& GetObfuscateKey(const CDBWrapper &w)
215 return w.obfuscate_key;
218 } // namespace dbwrapper_private