Add Google's LevelDB support
[novacoin.git] / src / txdb-leveldb.h
1 // Copyright (c) 2009-2012 The Bitcoin Developers.
2 // Authored by Google, Inc.
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6 #ifndef BITCOIN_LEVELDB_H
7 #define BITCOIN_LEVELDB_H
8
9 #include "main.h"
10
11 #include <map>
12 #include <string>
13 #include <vector>
14
15 #include <leveldb/db.h>
16 #include <leveldb/write_batch.h>
17
18 // Class that provides access to a LevelDB. Note that this class is frequently
19 // instantiated on the stack and then destroyed again, so instantiation has to
20 // be very cheap. Unfortunately that means, a CTxDB instance is actually just a
21 // wrapper around some global state.
22 //
23 // A LevelDB is a key/value store that is optimized for fast usage on hard
24 // disks. It prefers long read/writes to seeks and is based on a series of
25 // sorted key/value mapping files that are stacked on top of each other, with
26 // newer files overriding older files. A background thread compacts them
27 // together when too many files stack up.
28 //
29 // Learn more: http://code.google.com/p/leveldb/
30 class CTxDB
31 {
32 public:
33     CTxDB(const char* pszMode="r+");
34     ~CTxDB() {
35         // Note that this is not the same as Close() because it deletes only
36         // data scoped to this TxDB object.
37         delete activeBatch;
38     }
39
40     // Destroys the underlying shared global state accessed by this TxDB.
41     void Close();
42
43 private:
44     leveldb::DB *pdb;  // Points to the global instance.
45     // A batch stores up writes and deletes for atomic application. When this
46     // field is non-NULL, writes/deletes go there instead of directly to disk.
47     leveldb::WriteBatch *activeBatch;
48     leveldb::Options options;
49     bool fReadOnly;
50
51 protected:
52     // Returns true and sets (value,false) if activeBatch contains the given key
53     // or leaves value alone and sets deleted = true if activeBatch contains a
54     // delete for it.
55     bool ScanBatch(const CDataStream &key, std::string *value, bool *deleted) const;
56
57     template<typename K, typename T>
58     bool Read(const K& key, T& value)
59     {
60         CDataStream ssKey(SER_DISK, CLIENT_VERSION);
61         ssKey.reserve(1000);
62         ssKey << key;
63         std::string strValue;
64
65         bool readFromDb = true;
66         if (activeBatch) {
67             // First we must search for it in the currently pending set of
68             // changes to the db. If not found in the batch, go on to read disk.
69             bool deleted = false;
70             readFromDb = ScanBatch(ssKey, &strValue, &deleted) == false;
71             if (deleted) {
72                 return false;
73             }
74         }
75         if (readFromDb) {
76             leveldb::Status status = pdb->Get(leveldb::ReadOptions(),
77                                               ssKey.str(), &strValue);
78             if (!status.ok()) {
79                 if (status.IsNotFound())
80                     return false;
81                 // Some unexpected error.
82                 printf("LevelDB read failure: %s\n", status.ToString().c_str());
83                 return false;
84             }
85         }
86         // Unserialize value
87         try {
88             CDataStream ssValue(strValue.data(), strValue.data() + strValue.size(),
89                                 SER_DISK, CLIENT_VERSION);
90             ssValue >> value;
91         }
92         catch (std::exception &e) {
93             return false;
94         }
95         return true;
96     }
97
98     template<typename K, typename T>
99     bool Write(const K& key, const T& value)
100     {
101         if (fReadOnly)
102             assert(!"Write called on database in read-only mode");
103
104         CDataStream ssKey(SER_DISK, CLIENT_VERSION);
105         ssKey.reserve(1000);
106         ssKey << key;
107         CDataStream ssValue(SER_DISK, CLIENT_VERSION);
108         ssValue.reserve(10000);
109         ssValue << value;
110
111         if (activeBatch) {
112             activeBatch->Put(ssKey.str(), ssValue.str());
113             return true;
114         }
115         leveldb::Status status = pdb->Put(leveldb::WriteOptions(), ssKey.str(), ssValue.str());
116         if (!status.ok()) {
117             printf("LevelDB write failure: %s\n", status.ToString().c_str());
118             return false;
119         }
120         return true;
121     }
122
123     template<typename K>
124     bool Erase(const K& key)
125     {
126         if (!pdb)
127             return false;
128         if (fReadOnly)
129             assert(!"Erase called on database in read-only mode");
130
131         CDataStream ssKey(SER_DISK, CLIENT_VERSION);
132         ssKey.reserve(1000);
133         ssKey << key;
134         if (activeBatch) {
135             activeBatch->Delete(ssKey.str());
136             return true;
137         }
138         leveldb::Status status = pdb->Delete(leveldb::WriteOptions(), ssKey.str());
139         return (status.ok() || status.IsNotFound());
140     }
141
142     template<typename K>
143     bool Exists(const K& key)
144     {
145         CDataStream ssKey(SER_DISK, CLIENT_VERSION);
146         ssKey.reserve(1000);
147         ssKey << key;
148         std::string unused;
149
150         if (activeBatch) {
151             bool deleted;
152             if (ScanBatch(ssKey, &unused, &deleted) && !deleted) {
153                 return true;
154             }
155         }
156
157
158         leveldb::Status status = pdb->Get(leveldb::ReadOptions(), ssKey.str(), &unused);
159         return status.IsNotFound() == false;
160     }
161
162
163 public:
164     bool TxnBegin();
165     bool TxnCommit();
166     bool TxnAbort()
167     {
168         delete activeBatch;
169         activeBatch = NULL;
170         return true;
171     }
172
173     bool ReadVersion(int& nVersion)
174     {
175         nVersion = 0;
176         return Read(std::string("version"), nVersion);
177     }
178
179     bool WriteVersion(int nVersion)
180     {
181         return Write(std::string("version"), nVersion);
182     }
183
184     bool ReadTxIndex(uint256 hash, CTxIndex& txindex);
185     bool UpdateTxIndex(uint256 hash, const CTxIndex& txindex);
186     bool AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight);
187     bool EraseTxIndex(const CTransaction& tx);
188     bool ContainsTx(uint256 hash);
189     bool ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex);
190     bool ReadDiskTx(uint256 hash, CTransaction& tx);
191     bool ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex);
192     bool ReadDiskTx(COutPoint outpoint, CTransaction& tx);
193     bool WriteBlockIndex(const CDiskBlockIndex& blockindex);
194     bool ReadHashBestChain(uint256& hashBestChain);
195     bool WriteHashBestChain(uint256 hashBestChain);
196     bool ReadBestInvalidTrust(CBigNum& bnBestInvalidTrust);
197     bool WriteBestInvalidTrust(CBigNum bnBestInvalidTrust);
198     bool ReadSyncCheckpoint(uint256& hashCheckpoint);
199     bool WriteSyncCheckpoint(uint256 hashCheckpoint);
200     bool ReadCheckpointPubKey(std::string& strPubKey);
201     bool WriteCheckpointPubKey(const std::string& strPubKey);
202     bool LoadBlockIndex();
203 private:
204     bool LoadBlockIndexGuts();
205 };
206
207 // Called from the initialization code. Checks to see if there is an old
208 // blkindex.dat file. If so, deletes it and begins re-importing the block
209 // chain, which will create the new database.
210 enum LevelDBMigrationResult {
211   NONE_NEEDED,
212   INSUFFICIENT_DISK_SPACE,
213   COMPLETED,
214   OTHER_ERROR,
215 };
216
217 typedef boost::signals2::signal<void (double progress)> LevelDBMigrationProgress;
218
219 LevelDBMigrationResult MaybeMigrateToLevelDB(LevelDBMigrationProgress &progress);
220
221 #endif // BITCOIN_DB_H