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