]>
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 | ||
c66c731a JG |
34 | CCoinsViewDB::CCoinsViewDB(std::string dbName, size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / dbName, nCacheSize, fMemory, fWipe) { |
35 | } | |
36 | ||
0d81464b JB |
37 | CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe) |
38 | { | |
2d8a4829 PW |
39 | } |
40 | ||
9f25631d | 41 | |
434f3284 | 42 | bool CCoinsViewDB::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { |
bf2e3122 | 43 | if (rt == ZCIncrementalMerkleTree::empty_root()) { |
434f3284 SB |
44 | ZCIncrementalMerkleTree new_tree; |
45 | tree = new_tree; | |
9f25631d SB |
46 | return true; |
47 | } | |
48 | ||
434f3284 | 49 | bool read = db.Read(make_pair(DB_ANCHOR, rt), tree); |
9f25631d | 50 | |
434f3284 | 51 | return read; |
9f25631d SB |
52 | } |
53 | ||
22de1602 | 54 | bool CCoinsViewDB::GetNullifier(const uint256 &nf) const { |
45d6bee9 | 55 | bool spent = false; |
22de1602 | 56 | bool read = db.Read(make_pair(DB_NULLIFIER, nf), spent); |
45d6bee9 | 57 | |
d66877af | 58 | return read; |
45d6bee9 SB |
59 | } |
60 | ||
a3dc587a | 61 | bool CCoinsViewDB::GetCoins(const uint256 &txid, CCoins &coins) const { |
14d023f1 | 62 | return db.Read(make_pair(DB_COINS, txid), coins); |
2d8a4829 PW |
63 | } |
64 | ||
a3dc587a | 65 | bool CCoinsViewDB::HaveCoins(const uint256 &txid) const { |
14d023f1 | 66 | return db.Exists(make_pair(DB_COINS, txid)); |
2d8a4829 PW |
67 | } |
68 | ||
a3dc587a | 69 | uint256 CCoinsViewDB::GetBestBlock() const { |
2d8a4829 | 70 | uint256 hashBestChain; |
14d023f1 | 71 | if (!db.Read(DB_BEST_BLOCK, hashBestChain)) |
4f152496 | 72 | return uint256(); |
84674082 | 73 | return hashBestChain; |
2d8a4829 PW |
74 | } |
75 | ||
9f25631d SB |
76 | uint256 CCoinsViewDB::GetBestAnchor() const { |
77 | uint256 hashBestAnchor; | |
78 | if (!db.Read(DB_BEST_ANCHOR, hashBestAnchor)) | |
bf2e3122 | 79 | return ZCIncrementalMerkleTree::empty_root(); |
9f25631d SB |
80 | return hashBestAnchor; |
81 | } | |
82 | ||
83 | bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, | |
84 | const uint256 &hashBlock, | |
85 | const uint256 &hashAnchor, | |
45d6bee9 | 86 | CAnchorsMap &mapAnchors, |
bb64be52 | 87 | CNullifiersMap &mapNullifiers) { |
809a429e | 88 | CDBBatch batch(db); |
058b08c1 PW |
89 | size_t count = 0; |
90 | size_t changed = 0; | |
b0875eb3 | 91 | for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { |
058b08c1 | 92 | if (it->second.flags & CCoinsCacheEntry::DIRTY) { |
0d81464b JB |
93 | if (it->second.coins.IsPruned()) |
94 | batch.Erase(make_pair(DB_COINS, it->first)); | |
95 | else | |
96 | batch.Write(make_pair(DB_COINS, it->first), it->second.coins); | |
058b08c1 PW |
97 | changed++; |
98 | } | |
99 | count++; | |
b0875eb3 PW |
100 | CCoinsMap::iterator itOld = it++; |
101 | mapCoins.erase(itOld); | |
102 | } | |
9f25631d SB |
103 | |
104 | for (CAnchorsMap::iterator it = mapAnchors.begin(); it != mapAnchors.end();) { | |
105 | if (it->second.flags & CAnchorsCacheEntry::DIRTY) { | |
3a8e1d0c JG |
106 | if (!it->second.entered) |
107 | batch.Erase(make_pair(DB_ANCHOR, it->first)); | |
108 | else { | |
109 | batch.Write(make_pair(DB_ANCHOR, it->first), it->second.tree); | |
110 | } | |
9f25631d SB |
111 | // TODO: changed++? |
112 | } | |
113 | CAnchorsMap::iterator itOld = it++; | |
114 | mapAnchors.erase(itOld); | |
115 | } | |
116 | ||
bb64be52 | 117 | for (CNullifiersMap::iterator it = mapNullifiers.begin(); it != mapNullifiers.end();) { |
9e511dbb | 118 | if (it->second.flags & CNullifiersCacheEntry::DIRTY) { |
3a8e1d0c JG |
119 | if (!it->second.entered) |
120 | batch.Erase(make_pair(DB_NULLIFIER, it->first)); | |
121 | else | |
122 | batch.Write(make_pair(DB_NULLIFIER, it->first), true); | |
45d6bee9 SB |
123 | // TODO: changed++? |
124 | } | |
d889a287 | 125 | CNullifiersMap::iterator itOld = it++; |
bb64be52 | 126 | mapNullifiers.erase(itOld); |
45d6bee9 SB |
127 | } |
128 | ||
4f152496 | 129 | if (!hashBlock.IsNull()) |
0d81464b | 130 | batch.Write(DB_BEST_BLOCK, hashBlock); |
9f25631d | 131 | if (!hashAnchor.IsNull()) |
3a8e1d0c | 132 | batch.Write(DB_BEST_ANCHOR, hashAnchor); |
2d8a4829 | 133 | |
058b08c1 | 134 | LogPrint("coindb", "Committing %u changed transactions (out of %u) to coin database...\n", (unsigned int)changed, (unsigned int)count); |
2d8a4829 PW |
135 | return db.WriteBatch(batch); |
136 | } | |
137 | ||
f345c41e | 138 | CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) { |
e1bfbab8 PW |
139 | } |
140 | ||
2d8a4829 | 141 | bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) { |
14d023f1 | 142 | return Read(make_pair(DB_BLOCK_FILES, nFile), info); |
2d8a4829 PW |
143 | } |
144 | ||
7fea4846 PW |
145 | bool CBlockTreeDB::WriteReindexing(bool fReindexing) { |
146 | if (fReindexing) | |
14d023f1 | 147 | return Write(DB_REINDEX_FLAG, '1'); |
7fea4846 | 148 | else |
14d023f1 | 149 | return Erase(DB_REINDEX_FLAG); |
7fea4846 PW |
150 | } |
151 | ||
152 | bool CBlockTreeDB::ReadReindexing(bool &fReindexing) { | |
14d023f1 | 153 | fReindexing = Exists(DB_REINDEX_FLAG); |
7fea4846 PW |
154 | return true; |
155 | } | |
156 | ||
2d8a4829 | 157 | bool CBlockTreeDB::ReadLastBlockFile(int &nFile) { |
14d023f1 | 158 | return Read(DB_LAST_BLOCK, nFile); |
2d8a4829 PW |
159 | } |
160 | ||
a3dc587a DK |
161 | bool CCoinsViewDB::GetStats(CCoinsStats &stats) const { |
162 | /* It seems that there are no "const iterators" for LevelDB. Since we | |
163 | only need read operations on it, use a const-cast to get around | |
164 | that restriction. */ | |
f345c41e | 165 | boost::scoped_ptr<CDBIterator> pcursor(const_cast<CDBWrapper*>(&db)->NewIterator()); |
c7758696 | 166 | pcursor->Seek(DB_COINS); |
beeb5761 | 167 | |
e31aa7c9 | 168 | CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); |
84674082 | 169 | stats.hashBlock = GetBestBlock(); |
e31aa7c9 | 170 | ss << stats.hashBlock; |
a372168e | 171 | CAmount nTotalAmount = 0; |
beeb5761 | 172 | while (pcursor->Valid()) { |
b31499ec | 173 | boost::this_thread::interruption_point(); |
1ebf50b6 PW |
174 | std::pair<char, uint256> key; |
175 | CCoins coins; | |
c7758696 | 176 | if (pcursor->GetKey(key) && key.first == DB_COINS) { |
1ebf50b6 | 177 | if (pcursor->GetValue(coins)) { |
beeb5761 | 178 | stats.nTransactions++; |
e31aa7c9 PW |
179 | for (unsigned int i=0; i<coins.vout.size(); i++) { |
180 | const CTxOut &out = coins.vout[i]; | |
181 | if (!out.IsNull()) { | |
beeb5761 | 182 | stats.nTransactionOutputs++; |
e31aa7c9 PW |
183 | ss << VARINT(i+1); |
184 | ss << out; | |
185 | nTotalAmount += out.nValue; | |
186 | } | |
beeb5761 | 187 | } |
f95bf4c4 | 188 | stats.nSerializedSize += 32 + pcursor->GetValueSize(); |
e31aa7c9 | 189 | ss << VARINT(0); |
1ebf50b6 PW |
190 | } else { |
191 | return error("CCoinsViewDB::GetStats() : unable to read value"); | |
beeb5761 | 192 | } |
1ebf50b6 PW |
193 | } else { |
194 | break; | |
beeb5761 | 195 | } |
1ebf50b6 | 196 | pcursor->Next(); |
beeb5761 | 197 | } |
a0455eca WL |
198 | { |
199 | LOCK(cs_main); | |
200 | stats.nHeight = mapBlockIndex.find(stats.hashBlock)->second->nHeight; | |
201 | } | |
e31aa7c9 PW |
202 | stats.hashSerialized = ss.GetHash(); |
203 | stats.nTotalAmount = nTotalAmount; | |
beeb5761 PW |
204 | return true; |
205 | } | |
206 | ||
63d1ae55 | 207 | bool CBlockTreeDB::WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo) { |
809a429e | 208 | CDBBatch batch(*this); |
63d1ae55 | 209 | for (std::vector<std::pair<int, const CBlockFileInfo*> >::const_iterator it=fileInfo.begin(); it != fileInfo.end(); it++) { |
14d023f1 | 210 | batch.Write(make_pair(DB_BLOCK_FILES, it->first), *it->second); |
63d1ae55 | 211 | } |
14d023f1 | 212 | batch.Write(DB_LAST_BLOCK, nLastFile); |
63d1ae55 | 213 | for (std::vector<const CBlockIndex*>::const_iterator it=blockinfo.begin(); it != blockinfo.end(); it++) { |
14d023f1 | 214 | batch.Write(make_pair(DB_BLOCK_INDEX, (*it)->GetBlockHash()), CDiskBlockIndex(*it)); |
63d1ae55 PW |
215 | } |
216 | return WriteBatch(batch, true); | |
217 | } | |
218 | ||
f5007d89 | 219 | bool CBlockTreeDB::EraseBatchSync(const std::vector<const CBlockIndex*>& blockinfo) { |
2d7bae5b | 220 | CDBBatch batch(*this); |
f5007d89 JG |
221 | for (std::vector<const CBlockIndex*>::const_iterator it=blockinfo.begin(); it != blockinfo.end(); it++) { |
222 | batch.Erase(make_pair(DB_BLOCK_INDEX, (*it)->GetBlockHash())); | |
223 | } | |
224 | return WriteBatch(batch, true); | |
225 | } | |
226 | ||
2d1fa42e | 227 | bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) { |
14d023f1 | 228 | return Read(make_pair(DB_TXINDEX, txid), pos); |
2d1fa42e PW |
229 | } |
230 | ||
231 | bool CBlockTreeDB::WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> >&vect) { | |
809a429e | 232 | CDBBatch batch(*this); |
2d1fa42e | 233 | for (std::vector<std::pair<uint256,CDiskTxPos> >::const_iterator it=vect.begin(); it!=vect.end(); it++) |
14d023f1 | 234 | batch.Write(make_pair(DB_TXINDEX, it->first), it->second); |
2d1fa42e PW |
235 | return WriteBatch(batch); |
236 | } | |
237 | ||
238 | bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) { | |
14d023f1 | 239 | return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0'); |
2d1fa42e PW |
240 | } |
241 | ||
242 | bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) { | |
243 | char ch; | |
14d023f1 | 244 | if (!Read(std::make_pair(DB_FLAG, name), ch)) |
2d1fa42e PW |
245 | return false; |
246 | fValue = ch == '1'; | |
247 | return true; | |
248 | } | |
249 | ||
2d8a4829 PW |
250 | bool CBlockTreeDB::LoadBlockIndexGuts() |
251 | { | |
f345c41e | 252 | boost::scoped_ptr<CDBIterator> pcursor(NewIterator()); |
2d8a4829 | 253 | |
c7758696 | 254 | pcursor->Seek(make_pair(DB_BLOCK_INDEX, uint256())); |
2d8a4829 PW |
255 | |
256 | // Load mapBlockIndex | |
257 | while (pcursor->Valid()) { | |
b31499ec | 258 | boost::this_thread::interruption_point(); |
1ebf50b6 | 259 | std::pair<char, uint256> key; |
c7758696 | 260 | if (pcursor->GetKey(key) && key.first == DB_BLOCK_INDEX) { |
1ebf50b6 PW |
261 | CDiskBlockIndex diskindex; |
262 | if (pcursor->GetValue(diskindex)) { | |
2d8a4829 PW |
263 | // Construct block index object |
264 | CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash()); | |
265 | pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); | |
266 | pindexNew->nHeight = diskindex.nHeight; | |
267 | pindexNew->nFile = diskindex.nFile; | |
268 | pindexNew->nDataPos = diskindex.nDataPos; | |
269 | pindexNew->nUndoPos = diskindex.nUndoPos; | |
b6961fc1 | 270 | pindexNew->hashAnchor = diskindex.hashAnchor; |
2d8a4829 PW |
271 | pindexNew->nVersion = diskindex.nVersion; |
272 | pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot; | |
15fb13f6 | 273 | pindexNew->hashReserved = diskindex.hashReserved; |
2d8a4829 PW |
274 | pindexNew->nTime = diskindex.nTime; |
275 | pindexNew->nBits = diskindex.nBits; | |
276 | pindexNew->nNonce = diskindex.nNonce; | |
fdda3c50 | 277 | pindexNew->nSolution = diskindex.nSolution; |
2d8a4829 | 278 | pindexNew->nStatus = diskindex.nStatus; |
828940b1 | 279 | pindexNew->nCachedBranchId = diskindex.nCachedBranchId; |
2d8a4829 | 280 | pindexNew->nTx = diskindex.nTx; |
ad6a36ad | 281 | pindexNew->nSproutValue = diskindex.nSproutValue; |
2d8a4829 | 282 | |
704b7635 | 283 | // Consistency checks |
44488400 | 284 | auto header = pindexNew->GetBlockHeader(); |
704b7635 JG |
285 | if (header.GetHash() != pindexNew->GetBlockHash()) |
286 | return error("LoadBlockIndex(): block header inconsistency detected: on-disk = %s, in-memory = %s", | |
287 | diskindex.ToString(), pindexNew->ToString()); | |
d698ef69 | 288 | if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, Params().GetConsensus())) |
5262fde0 | 289 | return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew->ToString()); |
2d8a4829 PW |
290 | |
291 | pcursor->Next(); | |
292 | } else { | |
1ebf50b6 | 293 | return error("LoadBlockIndex() : failed to read value"); |
2d8a4829 | 294 | } |
1ebf50b6 PW |
295 | } else { |
296 | break; | |
2d8a4829 PW |
297 | } |
298 | } | |
2d8a4829 PW |
299 | |
300 | return true; | |
301 | } |