]>
Commit | Line | Data |
---|---|---|
2d8a4829 | 1 | // Copyright (c) 2009-2010 Satoshi Nakamoto |
57702541 | 2 | // Copyright (c) 2009-2014 The Bitcoin 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 | |
df852d2b | 8 | #include "pow.h" |
51ed9ec9 BD |
9 | #include "uint256.h" |
10 | ||
11 | #include <stdint.h> | |
2d8a4829 | 12 | |
611116d4 PK |
13 | #include <boost/thread.hpp> |
14 | ||
2d8a4829 PW |
15 | using namespace std; |
16 | ||
17 | void static BatchWriteCoins(CLevelDBBatch &batch, const uint256 &hash, const CCoins &coins) { | |
18 | if (coins.IsPruned()) | |
19 | batch.Erase(make_pair('c', hash)); | |
20 | else | |
21 | batch.Write(make_pair('c', hash), coins); | |
22 | } | |
23 | ||
24 | void static BatchWriteHashBestChain(CLevelDBBatch &batch, const uint256 &hash) { | |
25 | batch.Write('B', hash); | |
26 | } | |
27 | ||
8fdc94cc | 28 | CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe) { |
2d8a4829 PW |
29 | } |
30 | ||
a3dc587a | 31 | bool CCoinsViewDB::GetCoins(const uint256 &txid, CCoins &coins) const { |
96e5f61d | 32 | return db.Read(make_pair('c', txid), coins); |
2d8a4829 PW |
33 | } |
34 | ||
a3dc587a | 35 | bool CCoinsViewDB::HaveCoins(const uint256 &txid) const { |
96e5f61d | 36 | return db.Exists(make_pair('c', txid)); |
2d8a4829 PW |
37 | } |
38 | ||
a3dc587a | 39 | uint256 CCoinsViewDB::GetBestBlock() const { |
2d8a4829 PW |
40 | uint256 hashBestChain; |
41 | if (!db.Read('B', hashBestChain)) | |
84674082 PW |
42 | return uint256(0); |
43 | return hashBestChain; | |
2d8a4829 PW |
44 | } |
45 | ||
b0875eb3 | 46 | bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { |
2d8a4829 | 47 | CLevelDBBatch batch; |
058b08c1 PW |
48 | size_t count = 0; |
49 | size_t changed = 0; | |
b0875eb3 | 50 | for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { |
058b08c1 PW |
51 | if (it->second.flags & CCoinsCacheEntry::DIRTY) { |
52 | BatchWriteCoins(batch, it->first, it->second.coins); | |
53 | changed++; | |
54 | } | |
55 | count++; | |
b0875eb3 PW |
56 | CCoinsMap::iterator itOld = it++; |
57 | mapCoins.erase(itOld); | |
58 | } | |
84674082 PW |
59 | if (hashBlock != uint256(0)) |
60 | BatchWriteHashBestChain(batch, hashBlock); | |
2d8a4829 | 61 | |
058b08c1 | 62 | LogPrint("coindb", "Committing %u changed transactions (out of %u) to coin database...\n", (unsigned int)changed, (unsigned int)count); |
2d8a4829 PW |
63 | return db.WriteBatch(batch); |
64 | } | |
65 | ||
b64187d0 | 66 | CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) { |
e1bfbab8 PW |
67 | } |
68 | ||
2d8a4829 PW |
69 | bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) { |
70 | return Read(make_pair('f', nFile), info); | |
71 | } | |
72 | ||
7fea4846 PW |
73 | bool CBlockTreeDB::WriteReindexing(bool fReindexing) { |
74 | if (fReindexing) | |
75 | return Write('R', '1'); | |
76 | else | |
77 | return Erase('R'); | |
78 | } | |
79 | ||
80 | bool CBlockTreeDB::ReadReindexing(bool &fReindexing) { | |
81 | fReindexing = Exists('R'); | |
82 | return true; | |
83 | } | |
84 | ||
2d8a4829 PW |
85 | bool CBlockTreeDB::ReadLastBlockFile(int &nFile) { |
86 | return Read('l', nFile); | |
87 | } | |
88 | ||
a3dc587a DK |
89 | bool CCoinsViewDB::GetStats(CCoinsStats &stats) const { |
90 | /* It seems that there are no "const iterators" for LevelDB. Since we | |
91 | only need read operations on it, use a const-cast to get around | |
92 | that restriction. */ | |
33dfbf57 | 93 | boost::scoped_ptr<leveldb::Iterator> pcursor(const_cast<CLevelDBWrapper*>(&db)->NewIterator()); |
beeb5761 PW |
94 | pcursor->SeekToFirst(); |
95 | ||
e31aa7c9 | 96 | CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); |
84674082 | 97 | stats.hashBlock = GetBestBlock(); |
e31aa7c9 | 98 | ss << stats.hashBlock; |
a372168e | 99 | CAmount nTotalAmount = 0; |
beeb5761 | 100 | while (pcursor->Valid()) { |
b31499ec | 101 | boost::this_thread::interruption_point(); |
beeb5761 PW |
102 | try { |
103 | leveldb::Slice slKey = pcursor->key(); | |
104 | CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); | |
105 | char chType; | |
106 | ssKey >> chType; | |
b31499ec | 107 | if (chType == 'c') { |
beeb5761 PW |
108 | leveldb::Slice slValue = pcursor->value(); |
109 | CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); | |
110 | CCoins coins; | |
111 | ssValue >> coins; | |
112 | uint256 txhash; | |
113 | ssKey >> txhash; | |
e31aa7c9 PW |
114 | ss << txhash; |
115 | ss << VARINT(coins.nVersion); | |
96e5f61d | 116 | ss << (coins.fCoinBase ? 'c' : 'n'); |
e31aa7c9 | 117 | ss << VARINT(coins.nHeight); |
beeb5761 | 118 | stats.nTransactions++; |
e31aa7c9 PW |
119 | for (unsigned int i=0; i<coins.vout.size(); i++) { |
120 | const CTxOut &out = coins.vout[i]; | |
121 | if (!out.IsNull()) { | |
beeb5761 | 122 | stats.nTransactionOutputs++; |
e31aa7c9 PW |
123 | ss << VARINT(i+1); |
124 | ss << out; | |
125 | nTotalAmount += out.nValue; | |
126 | } | |
beeb5761 PW |
127 | } |
128 | stats.nSerializedSize += 32 + slValue.size(); | |
e31aa7c9 | 129 | ss << VARINT(0); |
beeb5761 PW |
130 | } |
131 | pcursor->Next(); | |
132 | } catch (std::exception &e) { | |
1cc7f54a | 133 | return error("%s : Deserialize or I/O error - %s", __func__, e.what()); |
beeb5761 PW |
134 | } |
135 | } | |
84674082 | 136 | stats.nHeight = mapBlockIndex.find(GetBestBlock())->second->nHeight; |
e31aa7c9 PW |
137 | stats.hashSerialized = ss.GetHash(); |
138 | stats.nTotalAmount = nTotalAmount; | |
beeb5761 PW |
139 | return true; |
140 | } | |
141 | ||
63d1ae55 PW |
142 | bool CBlockTreeDB::WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo) { |
143 | CLevelDBBatch batch; | |
144 | for (std::vector<std::pair<int, const CBlockFileInfo*> >::const_iterator it=fileInfo.begin(); it != fileInfo.end(); it++) { | |
145 | batch.Write(make_pair('f', it->first), *it->second); | |
146 | } | |
147 | batch.Write('l', nLastFile); | |
148 | for (std::vector<const CBlockIndex*>::const_iterator it=blockinfo.begin(); it != blockinfo.end(); it++) { | |
149 | batch.Write(make_pair('b', (*it)->GetBlockHash()), CDiskBlockIndex(*it)); | |
150 | } | |
151 | return WriteBatch(batch, true); | |
152 | } | |
153 | ||
2d1fa42e PW |
154 | bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) { |
155 | return Read(make_pair('t', txid), pos); | |
156 | } | |
157 | ||
158 | bool CBlockTreeDB::WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> >&vect) { | |
159 | CLevelDBBatch batch; | |
160 | for (std::vector<std::pair<uint256,CDiskTxPos> >::const_iterator it=vect.begin(); it!=vect.end(); it++) | |
161 | batch.Write(make_pair('t', it->first), it->second); | |
162 | return WriteBatch(batch); | |
163 | } | |
164 | ||
165 | bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) { | |
166 | return Write(std::make_pair('F', name), fValue ? '1' : '0'); | |
167 | } | |
168 | ||
169 | bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) { | |
170 | char ch; | |
171 | if (!Read(std::make_pair('F', name), ch)) | |
172 | return false; | |
173 | fValue = ch == '1'; | |
174 | return true; | |
175 | } | |
176 | ||
2d8a4829 PW |
177 | bool CBlockTreeDB::LoadBlockIndexGuts() |
178 | { | |
5cbda4f1 | 179 | boost::scoped_ptr<leveldb::Iterator> pcursor(NewIterator()); |
2d8a4829 PW |
180 | |
181 | CDataStream ssKeySet(SER_DISK, CLIENT_VERSION); | |
182 | ssKeySet << make_pair('b', uint256(0)); | |
183 | pcursor->Seek(ssKeySet.str()); | |
184 | ||
185 | // Load mapBlockIndex | |
186 | while (pcursor->Valid()) { | |
b31499ec | 187 | boost::this_thread::interruption_point(); |
2d8a4829 PW |
188 | try { |
189 | leveldb::Slice slKey = pcursor->key(); | |
190 | CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); | |
191 | char chType; | |
192 | ssKey >> chType; | |
b31499ec | 193 | if (chType == 'b') { |
2d8a4829 PW |
194 | leveldb::Slice slValue = pcursor->value(); |
195 | CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); | |
196 | CDiskBlockIndex diskindex; | |
197 | ssValue >> diskindex; | |
198 | ||
199 | // Construct block index object | |
200 | CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash()); | |
201 | pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); | |
202 | pindexNew->nHeight = diskindex.nHeight; | |
203 | pindexNew->nFile = diskindex.nFile; | |
204 | pindexNew->nDataPos = diskindex.nDataPos; | |
205 | pindexNew->nUndoPos = diskindex.nUndoPos; | |
206 | pindexNew->nVersion = diskindex.nVersion; | |
207 | pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot; | |
208 | pindexNew->nTime = diskindex.nTime; | |
209 | pindexNew->nBits = diskindex.nBits; | |
210 | pindexNew->nNonce = diskindex.nNonce; | |
211 | pindexNew->nStatus = diskindex.nStatus; | |
212 | pindexNew->nTx = diskindex.nTx; | |
213 | ||
df852d2b | 214 | if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits)) |
215 | return error("LoadBlockIndex() : CheckProofOfWork failed: %s", pindexNew->ToString()); | |
2d8a4829 PW |
216 | |
217 | pcursor->Next(); | |
218 | } else { | |
219 | break; // if shutdown requested or finished loading block index | |
220 | } | |
221 | } catch (std::exception &e) { | |
1cc7f54a | 222 | return error("%s : Deserialize or I/O error - %s", __func__, e.what()); |
2d8a4829 PW |
223 | } |
224 | } | |
2d8a4829 PW |
225 | |
226 | return true; | |
227 | } |