Line data Source code
1 : // Copyright (c) 2012-2014 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_LEVELDBWRAPPER_H
6 : #define BITCOIN_LEVELDBWRAPPER_H
7 :
8 : #include "clientversion.h"
9 : #include "serialize.h"
10 : #include "streams.h"
11 : #include "util.h"
12 : #include "utilstrencodings.h"
13 : #include "version.h"
14 :
15 : #include <boost/filesystem/path.hpp>
16 :
17 : #include <leveldb/db.h>
18 : #include <leveldb/write_batch.h>
19 :
20 0 : class leveldb_error : public std::runtime_error
21 : {
22 : public:
23 0 : leveldb_error(const std::string& msg) : std::runtime_error(msg) {}
24 : };
25 :
26 : void HandleError(const leveldb::Status& status) throw(leveldb_error);
27 :
28 : /** Batch of changes queued to be written to a CLevelDBWrapper */
29 1102 : class CLevelDBBatch
30 : {
31 : friend class CLevelDBWrapper;
32 :
33 : private:
34 : leveldb::WriteBatch batch;
35 : const std::vector<unsigned char> obfuscate_key;
36 :
37 : public:
38 : /**
39 : * @param[in] obfuscate_key If passed, XOR data with this key.
40 : */
41 551 : CLevelDBBatch(const std::vector<unsigned char>& obfuscate_key) : obfuscate_key(obfuscate_key) { };
42 :
43 : template <typename K, typename V>
44 11518 : void Write(const K& key, const V& value)
45 : {
46 : CDataStream ssKey(SER_DISK, CLIENT_VERSION);
47 23036 : ssKey.reserve(ssKey.GetSerializeSize(key));
48 317 : ssKey << key;
49 23036 : leveldb::Slice slKey(&ssKey[0], ssKey.size());
50 :
51 : CDataStream ssValue(SER_DISK, CLIENT_VERSION);
52 23036 : ssValue.reserve(ssValue.GetSerializeSize(value));
53 218 : ssValue << value;
54 11518 : ssValue.Xor(obfuscate_key);
55 23036 : leveldb::Slice slValue(&ssValue[0], ssValue.size());
56 :
57 11518 : batch.Put(slKey, slValue);
58 11518 : }
59 :
60 : template <typename K>
61 313 : void Erase(const K& key)
62 : {
63 : CDataStream ssKey(SER_DISK, CLIENT_VERSION);
64 626 : ssKey.reserve(ssKey.GetSerializeSize(key));
65 1 : ssKey << key;
66 626 : leveldb::Slice slKey(&ssKey[0], ssKey.size());
67 :
68 313 : batch.Delete(slKey);
69 313 : }
70 : };
71 :
72 : class CLevelDBWrapper
73 : {
74 : private:
75 : //! custom environment this database is using (may be NULL in case of default environment)
76 : leveldb::Env* penv;
77 :
78 : //! database options used
79 : leveldb::Options options;
80 :
81 : //! options used when reading from the database
82 : leveldb::ReadOptions readoptions;
83 :
84 : //! options used when iterating over values of the database
85 : leveldb::ReadOptions iteroptions;
86 :
87 : //! options used when writing to the database
88 : leveldb::WriteOptions writeoptions;
89 :
90 : //! options used when sync writing to the database
91 : leveldb::WriteOptions syncoptions;
92 :
93 : //! the database itself
94 : leveldb::DB* pdb;
95 :
96 : //! a key used for optional XOR-obfuscation of the database
97 : std::vector<unsigned char> obfuscate_key;
98 :
99 : //! the key under which the obfuscation key is stored
100 : static const std::string OBFUSCATE_KEY_KEY;
101 :
102 : //! the length of the obfuscate key in number of bytes
103 : static const unsigned int OBFUSCATE_KEY_NUM_BYTES;
104 :
105 : std::vector<unsigned char> CreateObfuscateKey() const;
106 :
107 : public:
108 : /**
109 : * @param[in] path Location in the filesystem where leveldb data will be stored.
110 : * @param[in] nCacheSize Configures various leveldb cache settings.
111 : * @param[in] fMemory If true, use leveldb's memory environment.
112 : * @param[in] fWipe If true, remove all existing data.
113 : * @param[in] obfuscate If true, store data obfuscated via simple XOR. If false, XOR
114 : * with a zero'd byte array.
115 : */
116 : CLevelDBWrapper(const boost::filesystem::path& path, size_t nCacheSize, bool fMemory = false, bool fWipe = false, bool obfuscate = false);
117 : ~CLevelDBWrapper();
118 :
119 : template <typename K, typename V>
120 36565 : bool Read(const K& key, V& value) const throw(leveldb_error)
121 : {
122 : CDataStream ssKey(SER_DISK, CLIENT_VERSION);
123 73130 : ssKey.reserve(ssKey.GetSerializeSize(key));
124 313 : ssKey << key;
125 73130 : leveldb::Slice slKey(&ssKey[0], ssKey.size());
126 :
127 : std::string strValue;
128 36565 : leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
129 36565 : if (!status.ok()) {
130 24694 : if (status.IsNotFound())
131 : return false;
132 0 : LogPrintf("LevelDB read failure: %s\n", status.ToString());
133 0 : HandleError(status);
134 : }
135 : try {
136 23742 : CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(), SER_DISK, CLIENT_VERSION);
137 11871 : ssValue.Xor(obfuscate_key);
138 : ssValue >> value;
139 0 : } catch (const std::exception&) {
140 : return false;
141 : }
142 11871 : return true;
143 : }
144 :
145 : template <typename K, typename V>
146 133 : bool Write(const K& key, const V& value, bool fSync = false) throw(leveldb_error)
147 : {
148 133 : CLevelDBBatch batch(obfuscate_key);
149 133 : batch.Write(key, value);
150 133 : return WriteBatch(batch, fSync);
151 : }
152 :
153 : template <typename K>
154 93 : bool Exists(const K& key) const throw(leveldb_error)
155 : {
156 : CDataStream ssKey(SER_DISK, CLIENT_VERSION);
157 186 : ssKey.reserve(ssKey.GetSerializeSize(key));
158 93 : ssKey << key;
159 186 : leveldb::Slice slKey(&ssKey[0], ssKey.size());
160 :
161 : std::string strValue;
162 93 : leveldb::Status status = pdb->Get(readoptions, slKey, &strValue);
163 93 : if (!status.ok()) {
164 93 : if (status.IsNotFound())
165 : return false;
166 0 : LogPrintf("LevelDB read failure: %s\n", status.ToString());
167 0 : HandleError(status);
168 : }
169 : return true;
170 : }
171 :
172 : template <typename K>
173 1 : bool Erase(const K& key, bool fSync = false) throw(leveldb_error)
174 : {
175 1 : CLevelDBBatch batch(obfuscate_key);
176 1 : batch.Erase(key);
177 1 : return WriteBatch(batch, fSync);
178 : }
179 :
180 : bool WriteBatch(CLevelDBBatch& batch, bool fSync = false) throw(leveldb_error);
181 :
182 : // not available for LevelDB; provide for compatibility with BDB
183 : bool Flush()
184 : {
185 : return true;
186 : }
187 :
188 : bool Sync() throw(leveldb_error)
189 : {
190 : CLevelDBBatch batch(obfuscate_key);
191 : return WriteBatch(batch, true);
192 : }
193 :
194 : // not exactly clean encapsulation, but it's easiest for now
195 : leveldb::Iterator* NewIterator()
196 : {
197 159 : return pdb->NewIterator(iteroptions);
198 : }
199 :
200 : /**
201 : * Return true if the database managed by this class contains no entries.
202 : */
203 : bool IsEmpty();
204 :
205 : /**
206 : * Accessor for obfuscate_key.
207 : */
208 : const std::vector<unsigned char>& GetObfuscateKey() const;
209 :
210 : /**
211 : * Return the obfuscate_key as a hex-formatted string.
212 : */
213 : std::string GetObfuscateKeyHex() const;
214 :
215 : };
216 :
217 : #endif // BITCOIN_LEVELDBWRAPPER_H
218 :
|