]>
Commit | Line | Data |
---|---|---|
2d8a4829 | 1 | // Copyright (c) 2009-2010 Satoshi Nakamoto |
f914f1a7 | 2 | // Copyright (c) 2009-2014 The Bitcoin Core developers |
78253fcb | 3 | // Distributed under the MIT software license, see the accompanying |
2d8a4829 PW |
4 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
5 | ||
4ca60bba | 6 | #include "txdb.h" |
51ed9ec9 | 7 | |
d698ef69 JT |
8 | #include "chainparams.h" |
9 | #include "hash.h" | |
8a893c94 | 10 | #include "main.h" |
df852d2b | 11 | #include "pow.h" |
51ed9ec9 BD |
12 | #include "uint256.h" |
13 | ||
14 | #include <stdint.h> | |
2d8a4829 | 15 | |
611116d4 PK |
16 | #include <boost/thread.hpp> |
17 | ||
2d8a4829 PW |
18 | using namespace std; |
19 | ||
9f25631d | 20 | static const char DB_ANCHOR = 'A'; |
22de1602 | 21 | static const char DB_NULLIFIER = 's'; |
14d023f1 E |
22 | static const char DB_COINS = 'c'; |
23 | static const char DB_BLOCK_FILES = 'f'; | |
24 | static const char DB_TXINDEX = 't'; | |
25 | static const char DB_BLOCK_INDEX = 'b'; | |
26 | ||
27 | static const char DB_BEST_BLOCK = 'B'; | |
9f25631d | 28 | static const char DB_BEST_ANCHOR = 'a'; |
14d023f1 E |
29 | static const char DB_FLAG = 'F'; |
30 | static const char DB_REINDEX_FLAG = 'R'; | |
31 | static const char DB_LAST_BLOCK = 'l'; | |
32 | ||
33 | ||
9f25631d SB |
34 | void static BatchWriteAnchor(CLevelDBBatch &batch, |
35 | const uint256 &croot, | |
434f3284 | 36 | const ZCIncrementalMerkleTree &tree, |
9f25631d SB |
37 | const bool &entered) |
38 | { | |
39 | if (!entered) | |
40 | batch.Erase(make_pair(DB_ANCHOR, croot)); | |
41 | else { | |
434f3284 | 42 | batch.Write(make_pair(DB_ANCHOR, croot), tree); |
9f25631d SB |
43 | } |
44 | } | |
45 | ||
22de1602 | 46 | void static BatchWriteNullifier(CLevelDBBatch &batch, const uint256 &nf, const bool &entered) { |
45d6bee9 | 47 | if (!entered) |
22de1602 | 48 | batch.Erase(make_pair(DB_NULLIFIER, nf)); |
45d6bee9 | 49 | else |
22de1602 | 50 | batch.Write(make_pair(DB_NULLIFIER, nf), true); |
45d6bee9 SB |
51 | } |
52 | ||
2d8a4829 PW |
53 | void static BatchWriteCoins(CLevelDBBatch &batch, const uint256 &hash, const CCoins &coins) { |
54 | if (coins.IsPruned()) | |
14d023f1 | 55 | batch.Erase(make_pair(DB_COINS, hash)); |
2d8a4829 | 56 | else |
14d023f1 | 57 | batch.Write(make_pair(DB_COINS, hash), coins); |
2d8a4829 PW |
58 | } |
59 | ||
60 | void static BatchWriteHashBestChain(CLevelDBBatch &batch, const uint256 &hash) { | |
14d023f1 | 61 | batch.Write(DB_BEST_BLOCK, hash); |
2d8a4829 PW |
62 | } |
63 | ||
9f25631d SB |
64 | void static BatchWriteHashBestAnchor(CLevelDBBatch &batch, const uint256 &hash) { |
65 | batch.Write(DB_BEST_ANCHOR, hash); | |
66 | } | |
67 | ||
8fdc94cc | 68 | CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe) { |
2d8a4829 PW |
69 | } |
70 | ||
9f25631d | 71 | |
434f3284 | 72 | bool CCoinsViewDB::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { |
bf2e3122 | 73 | if (rt == ZCIncrementalMerkleTree::empty_root()) { |
434f3284 SB |
74 | ZCIncrementalMerkleTree new_tree; |
75 | tree = new_tree; | |
9f25631d SB |
76 | return true; |
77 | } | |
78 | ||
434f3284 | 79 | bool read = db.Read(make_pair(DB_ANCHOR, rt), tree); |
9f25631d | 80 | |
434f3284 | 81 | return read; |
9f25631d SB |
82 | } |
83 | ||
22de1602 | 84 | bool CCoinsViewDB::GetNullifier(const uint256 &nf) const { |
45d6bee9 | 85 | bool spent = false; |
22de1602 | 86 | bool read = db.Read(make_pair(DB_NULLIFIER, nf), spent); |
45d6bee9 | 87 | |
d66877af | 88 | return read; |
45d6bee9 SB |
89 | } |
90 | ||
a3dc587a | 91 | bool CCoinsViewDB::GetCoins(const uint256 &txid, CCoins &coins) const { |
14d023f1 | 92 | return db.Read(make_pair(DB_COINS, txid), coins); |
2d8a4829 PW |
93 | } |
94 | ||
a3dc587a | 95 | bool CCoinsViewDB::HaveCoins(const uint256 &txid) const { |
14d023f1 | 96 | return db.Exists(make_pair(DB_COINS, txid)); |
2d8a4829 PW |
97 | } |
98 | ||
a3dc587a | 99 | uint256 CCoinsViewDB::GetBestBlock() const { |
2d8a4829 | 100 | uint256 hashBestChain; |
14d023f1 | 101 | if (!db.Read(DB_BEST_BLOCK, hashBestChain)) |
4f152496 | 102 | return uint256(); |
84674082 | 103 | return hashBestChain; |
2d8a4829 PW |
104 | } |
105 | ||
9f25631d SB |
106 | uint256 CCoinsViewDB::GetBestAnchor() const { |
107 | uint256 hashBestAnchor; | |
108 | if (!db.Read(DB_BEST_ANCHOR, hashBestAnchor)) | |
bf2e3122 | 109 | return ZCIncrementalMerkleTree::empty_root(); |
9f25631d SB |
110 | return hashBestAnchor; |
111 | } | |
112 | ||
113 | bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, | |
114 | const uint256 &hashBlock, | |
115 | const uint256 &hashAnchor, | |
45d6bee9 | 116 | CAnchorsMap &mapAnchors, |
bb64be52 | 117 | CNullifiersMap &mapNullifiers) { |
2d8a4829 | 118 | CLevelDBBatch batch; |
058b08c1 PW |
119 | size_t count = 0; |
120 | size_t changed = 0; | |
b0875eb3 | 121 | for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { |
058b08c1 PW |
122 | if (it->second.flags & CCoinsCacheEntry::DIRTY) { |
123 | BatchWriteCoins(batch, it->first, it->second.coins); | |
124 | changed++; | |
125 | } | |
126 | count++; | |
b0875eb3 PW |
127 | CCoinsMap::iterator itOld = it++; |
128 | mapCoins.erase(itOld); | |
129 | } | |
9f25631d SB |
130 | |
131 | for (CAnchorsMap::iterator it = mapAnchors.begin(); it != mapAnchors.end();) { | |
132 | if (it->second.flags & CAnchorsCacheEntry::DIRTY) { | |
133 | BatchWriteAnchor(batch, it->first, it->second.tree, it->second.entered); | |
134 | // TODO: changed++? | |
135 | } | |
136 | CAnchorsMap::iterator itOld = it++; | |
137 | mapAnchors.erase(itOld); | |
138 | } | |
139 | ||
bb64be52 | 140 | for (CNullifiersMap::iterator it = mapNullifiers.begin(); it != mapNullifiers.end();) { |
9e511dbb | 141 | if (it->second.flags & CNullifiersCacheEntry::DIRTY) { |
22de1602 | 142 | BatchWriteNullifier(batch, it->first, it->second.entered); |
45d6bee9 SB |
143 | // TODO: changed++? |
144 | } | |
d889a287 | 145 | CNullifiersMap::iterator itOld = it++; |
bb64be52 | 146 | mapNullifiers.erase(itOld); |
45d6bee9 SB |
147 | } |
148 | ||
4f152496 | 149 | if (!hashBlock.IsNull()) |
84674082 | 150 | BatchWriteHashBestChain(batch, hashBlock); |
9f25631d SB |
151 | if (!hashAnchor.IsNull()) |
152 | BatchWriteHashBestAnchor(batch, hashAnchor); | |
2d8a4829 | 153 | |
058b08c1 | 154 | LogPrint("coindb", "Committing %u changed transactions (out of %u) to coin database...\n", (unsigned int)changed, (unsigned int)count); |
2d8a4829 PW |
155 | return db.WriteBatch(batch); |
156 | } | |
157 | ||
b64187d0 | 158 | CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) { |
e1bfbab8 PW |
159 | } |
160 | ||
2d8a4829 | 161 | bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) { |
14d023f1 | 162 | return Read(make_pair(DB_BLOCK_FILES, nFile), info); |
2d8a4829 PW |
163 | } |
164 | ||
7fea4846 PW |
165 | bool CBlockTreeDB::WriteReindexing(bool fReindexing) { |
166 | if (fReindexing) | |
14d023f1 | 167 | return Write(DB_REINDEX_FLAG, '1'); |
7fea4846 | 168 | else |
14d023f1 | 169 | return Erase(DB_REINDEX_FLAG); |
7fea4846 PW |
170 | } |
171 | ||
172 | bool CBlockTreeDB::ReadReindexing(bool &fReindexing) { | |
14d023f1 | 173 | fReindexing = Exists(DB_REINDEX_FLAG); |
7fea4846 PW |
174 | return true; |
175 | } | |
176 | ||
2d8a4829 | 177 | bool CBlockTreeDB::ReadLastBlockFile(int &nFile) { |
14d023f1 | 178 | return Read(DB_LAST_BLOCK, nFile); |
2d8a4829 PW |
179 | } |
180 | ||
a3dc587a DK |
181 | bool CCoinsViewDB::GetStats(CCoinsStats &stats) const { |
182 | /* It seems that there are no "const iterators" for LevelDB. Since we | |
183 | only need read operations on it, use a const-cast to get around | |
184 | that restriction. */ | |
33dfbf57 | 185 | boost::scoped_ptr<leveldb::Iterator> pcursor(const_cast<CLevelDBWrapper*>(&db)->NewIterator()); |
beeb5761 PW |
186 | pcursor->SeekToFirst(); |
187 | ||
e31aa7c9 | 188 | CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); |
84674082 | 189 | stats.hashBlock = GetBestBlock(); |
e31aa7c9 | 190 | ss << stats.hashBlock; |
a372168e | 191 | CAmount nTotalAmount = 0; |
beeb5761 | 192 | while (pcursor->Valid()) { |
b31499ec | 193 | boost::this_thread::interruption_point(); |
beeb5761 PW |
194 | try { |
195 | leveldb::Slice slKey = pcursor->key(); | |
196 | CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); | |
197 | char chType; | |
198 | ssKey >> chType; | |
14d023f1 | 199 | if (chType == DB_COINS) { |
beeb5761 PW |
200 | leveldb::Slice slValue = pcursor->value(); |
201 | CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); | |
202 | CCoins coins; | |
203 | ssValue >> coins; | |
204 | uint256 txhash; | |
205 | ssKey >> txhash; | |
e31aa7c9 PW |
206 | ss << txhash; |
207 | ss << VARINT(coins.nVersion); | |
96e5f61d | 208 | ss << (coins.fCoinBase ? 'c' : 'n'); |
e31aa7c9 | 209 | ss << VARINT(coins.nHeight); |
beeb5761 | 210 | stats.nTransactions++; |
e31aa7c9 PW |
211 | for (unsigned int i=0; i<coins.vout.size(); i++) { |
212 | const CTxOut &out = coins.vout[i]; | |
213 | if (!out.IsNull()) { | |
beeb5761 | 214 | stats.nTransactionOutputs++; |
e31aa7c9 PW |
215 | ss << VARINT(i+1); |
216 | ss << out; | |
217 | nTotalAmount += out.nValue; | |
218 | } | |
beeb5761 PW |
219 | } |
220 | stats.nSerializedSize += 32 + slValue.size(); | |
e31aa7c9 | 221 | ss << VARINT(0); |
beeb5761 PW |
222 | } |
223 | pcursor->Next(); | |
27df4123 | 224 | } catch (const std::exception& e) { |
5262fde0 | 225 | return error("%s: Deserialize or I/O error - %s", __func__, e.what()); |
beeb5761 PW |
226 | } |
227 | } | |
84674082 | 228 | stats.nHeight = mapBlockIndex.find(GetBestBlock())->second->nHeight; |
e31aa7c9 PW |
229 | stats.hashSerialized = ss.GetHash(); |
230 | stats.nTotalAmount = nTotalAmount; | |
beeb5761 PW |
231 | return true; |
232 | } | |
233 | ||
63d1ae55 PW |
234 | bool CBlockTreeDB::WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo) { |
235 | CLevelDBBatch batch; | |
236 | for (std::vector<std::pair<int, const CBlockFileInfo*> >::const_iterator it=fileInfo.begin(); it != fileInfo.end(); it++) { | |
14d023f1 | 237 | batch.Write(make_pair(DB_BLOCK_FILES, it->first), *it->second); |
63d1ae55 | 238 | } |
14d023f1 | 239 | batch.Write(DB_LAST_BLOCK, nLastFile); |
63d1ae55 | 240 | for (std::vector<const CBlockIndex*>::const_iterator it=blockinfo.begin(); it != blockinfo.end(); it++) { |
14d023f1 | 241 | batch.Write(make_pair(DB_BLOCK_INDEX, (*it)->GetBlockHash()), CDiskBlockIndex(*it)); |
63d1ae55 PW |
242 | } |
243 | return WriteBatch(batch, true); | |
244 | } | |
245 | ||
2d1fa42e | 246 | bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) { |
14d023f1 | 247 | return Read(make_pair(DB_TXINDEX, txid), pos); |
2d1fa42e PW |
248 | } |
249 | ||
250 | bool CBlockTreeDB::WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> >&vect) { | |
251 | CLevelDBBatch batch; | |
252 | for (std::vector<std::pair<uint256,CDiskTxPos> >::const_iterator it=vect.begin(); it!=vect.end(); it++) | |
14d023f1 | 253 | batch.Write(make_pair(DB_TXINDEX, it->first), it->second); |
2d1fa42e PW |
254 | return WriteBatch(batch); |
255 | } | |
256 | ||
257 | bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) { | |
14d023f1 | 258 | return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0'); |
2d1fa42e PW |
259 | } |
260 | ||
261 | bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) { | |
262 | char ch; | |
14d023f1 | 263 | if (!Read(std::make_pair(DB_FLAG, name), ch)) |
2d1fa42e PW |
264 | return false; |
265 | fValue = ch == '1'; | |
266 | return true; | |
267 | } | |
268 | ||
5b39c60e | 269 | void komodo_index2pubkey33(uint8_t *pubkey33,CBlockIndex *pindex,int32_t height); |
ddcdf567 | 270 | |
2d8a4829 PW |
271 | bool CBlockTreeDB::LoadBlockIndexGuts() |
272 | { | |
5cbda4f1 | 273 | boost::scoped_ptr<leveldb::Iterator> pcursor(NewIterator()); |
2d8a4829 PW |
274 | |
275 | CDataStream ssKeySet(SER_DISK, CLIENT_VERSION); | |
14d023f1 | 276 | ssKeySet << make_pair(DB_BLOCK_INDEX, uint256()); |
2d8a4829 PW |
277 | pcursor->Seek(ssKeySet.str()); |
278 | ||
279 | // Load mapBlockIndex | |
280 | while (pcursor->Valid()) { | |
b31499ec | 281 | boost::this_thread::interruption_point(); |
2d8a4829 PW |
282 | try { |
283 | leveldb::Slice slKey = pcursor->key(); | |
284 | CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); | |
285 | char chType; | |
286 | ssKey >> chType; | |
14d023f1 | 287 | if (chType == DB_BLOCK_INDEX) { |
2d8a4829 PW |
288 | leveldb::Slice slValue = pcursor->value(); |
289 | CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); | |
290 | CDiskBlockIndex diskindex; | |
291 | ssValue >> diskindex; | |
292 | ||
293 | // Construct block index object | |
294 | CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash()); | |
295 | pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); | |
296 | pindexNew->nHeight = diskindex.nHeight; | |
297 | pindexNew->nFile = diskindex.nFile; | |
298 | pindexNew->nDataPos = diskindex.nDataPos; | |
299 | pindexNew->nUndoPos = diskindex.nUndoPos; | |
b6961fc1 | 300 | pindexNew->hashAnchor = diskindex.hashAnchor; |
2d8a4829 PW |
301 | pindexNew->nVersion = diskindex.nVersion; |
302 | pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot; | |
303 | pindexNew->nTime = diskindex.nTime; | |
304 | pindexNew->nBits = diskindex.nBits; | |
305 | pindexNew->nNonce = diskindex.nNonce; | |
fdda3c50 | 306 | pindexNew->nSolution = diskindex.nSolution; |
2d8a4829 PW |
307 | pindexNew->nStatus = diskindex.nStatus; |
308 | pindexNew->nTx = diskindex.nTx; | |
f2dd868d | 309 | uint8_t pubkey33[33]; |
5b39c60e | 310 | komodo_index2pubkey33(pubkey33,pindexNew,pindexNew->nHeight); |
9ebb63d7 | 311 | if (!CheckProofOfWork(pindexNew->nHeight,pubkey33,pindexNew->GetBlockHash(), pindexNew->nBits, Params().GetConsensus())) |
f2dd868d | 312 | return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew->ToString()); |
2d8a4829 PW |
313 | pcursor->Next(); |
314 | } else { | |
315 | break; // if shutdown requested or finished loading block index | |
316 | } | |
27df4123 | 317 | } catch (const std::exception& e) { |
5262fde0 | 318 | return error("%s: Deserialize or I/O error - %s", __func__, e.what()); |
2d8a4829 PW |
319 | } |
320 | } | |
2d8a4829 PW |
321 | |
322 | return true; | |
323 | } |