Do not break backward compatibility during wallet encryption
[bitcoinplatinum.git] / src / dbwrapper.cpp
blobcd441add4bb7a8217226c458e70eeacde6249fb8
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 "util.h"
8 #include "random.h"
10 #include <boost/filesystem.hpp>
12 #include <leveldb/cache.h>
13 #include <leveldb/env.h>
14 #include <leveldb/filter_policy.h>
15 #include <memenv.h>
16 #include <stdint.h>
17 #include <algorithm>
19 class CBitcoinLevelDBLogger : public leveldb::Logger {
20 public:
21 // This code is adapted from posix_logger.h, which is why it is using vsprintf.
22 // Please do not do this in normal code
23 virtual void Logv(const char * format, va_list ap) override {
24 if (!LogAcceptCategory("leveldb"))
25 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 boost::filesystem::path& path, size_t nCacheSize, bool fMemory, bool fWipe, bool obfuscate)
95 penv = NULL;
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 TryCreateDirectory(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 // The base-case obfuscation key, which is a noop.
119 obfuscate_key = std::vector<unsigned char>(OBFUSCATE_KEY_NUM_BYTES, '\000');
121 bool key_exists = Read(OBFUSCATE_KEY_KEY, obfuscate_key);
123 if (!key_exists && obfuscate && IsEmpty()) {
124 // Initialize non-degenerate obfuscation if it won't upset
125 // existing, non-obfuscated data.
126 std::vector<unsigned char> new_key = CreateObfuscateKey();
128 // Write `new_key` so we don't obfuscate the key with itself
129 Write(OBFUSCATE_KEY_KEY, new_key);
130 obfuscate_key = new_key;
132 LogPrintf("Wrote new obfuscate key for %s: %s\n", path.string(), HexStr(obfuscate_key));
135 LogPrintf("Using obfuscation key for %s: %s\n", path.string(), HexStr(obfuscate_key));
138 CDBWrapper::~CDBWrapper()
140 delete pdb;
141 pdb = NULL;
142 delete options.filter_policy;
143 options.filter_policy = NULL;
144 delete options.info_log;
145 options.info_log = NULL;
146 delete options.block_cache;
147 options.block_cache = NULL;
148 delete penv;
149 options.env = NULL;
152 bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync)
154 leveldb::Status status = pdb->Write(fSync ? syncoptions : writeoptions, &batch.batch);
155 dbwrapper_private::HandleError(status);
156 return true;
159 // Prefixed with null character to avoid collisions with other keys
161 // We must use a string constructor which specifies length so that we copy
162 // past the null-terminator.
163 const std::string CDBWrapper::OBFUSCATE_KEY_KEY("\000obfuscate_key", 14);
165 const unsigned int CDBWrapper::OBFUSCATE_KEY_NUM_BYTES = 8;
168 * Returns a string (consisting of 8 random bytes) suitable for use as an
169 * obfuscating XOR key.
171 std::vector<unsigned char> CDBWrapper::CreateObfuscateKey() const
173 unsigned char buff[OBFUSCATE_KEY_NUM_BYTES];
174 GetRandBytes(buff, OBFUSCATE_KEY_NUM_BYTES);
175 return std::vector<unsigned char>(&buff[0], &buff[OBFUSCATE_KEY_NUM_BYTES]);
179 bool CDBWrapper::IsEmpty()
181 std::unique_ptr<CDBIterator> it(NewIterator());
182 it->SeekToFirst();
183 return !(it->Valid());
186 CDBIterator::~CDBIterator() { delete piter; }
187 bool CDBIterator::Valid() { return piter->Valid(); }
188 void CDBIterator::SeekToFirst() { piter->SeekToFirst(); }
189 void CDBIterator::Next() { piter->Next(); }
191 namespace dbwrapper_private {
193 void HandleError(const leveldb::Status& status)
195 if (status.ok())
196 return;
197 LogPrintf("%s\n", status.ToString());
198 if (status.IsCorruption())
199 throw dbwrapper_error("Database corrupted");
200 if (status.IsIOError())
201 throw dbwrapper_error("Database I/O error");
202 if (status.IsNotFound())
203 throw dbwrapper_error("Database entry missing");
204 throw dbwrapper_error("Unknown database error");
207 const std::vector<unsigned char>& GetObfuscateKey(const CDBWrapper &w)
209 return w.obfuscate_key;