]>
Commit | Line | Data |
---|---|---|
2d8a4829 PW |
1 | // Copyright (c) 2009-2010 Satoshi Nakamoto |
2 | // Copyright (c) 2009-2012 The Bitcoin developers | |
3 | // Distributed under the MIT/X11 software license, see the accompanying | |
4 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | |
5 | ||
4ca60bba | 6 | #include "txdb.h" |
2d8a4829 PW |
7 | #include "main.h" |
8 | ||
9 | using namespace std; | |
10 | ||
11 | void static BatchWriteCoins(CLevelDBBatch &batch, const uint256 &hash, const CCoins &coins) { | |
12 | if (coins.IsPruned()) | |
13 | batch.Erase(make_pair('c', hash)); | |
14 | else | |
15 | batch.Write(make_pair('c', hash), coins); | |
16 | } | |
17 | ||
18 | void static BatchWriteHashBestChain(CLevelDBBatch &batch, const uint256 &hash) { | |
19 | batch.Write('B', hash); | |
20 | } | |
21 | ||
8fdc94cc | 22 | CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe) { |
2d8a4829 PW |
23 | } |
24 | ||
f369d02c | 25 | bool CCoinsViewDB::GetCoins(const uint256 &txid, CCoins &coins) { |
2d8a4829 PW |
26 | return db.Read(make_pair('c', txid), coins); |
27 | } | |
28 | ||
f369d02c | 29 | bool CCoinsViewDB::SetCoins(const uint256 &txid, const CCoins &coins) { |
2d8a4829 PW |
30 | CLevelDBBatch batch; |
31 | BatchWriteCoins(batch, txid, coins); | |
32 | return db.WriteBatch(batch); | |
33 | } | |
34 | ||
f369d02c | 35 | bool CCoinsViewDB::HaveCoins(const uint256 &txid) { |
2d8a4829 PW |
36 | return db.Exists(make_pair('c', txid)); |
37 | } | |
38 | ||
39 | CBlockIndex *CCoinsViewDB::GetBestBlock() { | |
40 | uint256 hashBestChain; | |
41 | if (!db.Read('B', hashBestChain)) | |
42 | return NULL; | |
43 | std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(hashBestChain); | |
44 | if (it == mapBlockIndex.end()) | |
45 | return NULL; | |
46 | return it->second; | |
47 | } | |
48 | ||
49 | bool CCoinsViewDB::SetBestBlock(CBlockIndex *pindex) { | |
50 | CLevelDBBatch batch; | |
51 | BatchWriteHashBestChain(batch, pindex->GetBlockHash()); | |
52 | return db.WriteBatch(batch); | |
53 | } | |
54 | ||
55 | bool CCoinsViewDB::BatchWrite(const std::map<uint256, CCoins> &mapCoins, CBlockIndex *pindex) { | |
56 | printf("Committing %u changed transactions to coin database...\n", (unsigned int)mapCoins.size()); | |
57 | ||
58 | CLevelDBBatch batch; | |
59 | for (std::map<uint256, CCoins>::const_iterator it = mapCoins.begin(); it != mapCoins.end(); it++) | |
60 | BatchWriteCoins(batch, it->first, it->second); | |
0b297a61 PW |
61 | if (pindex) |
62 | BatchWriteHashBestChain(batch, pindex->GetBlockHash()); | |
2d8a4829 PW |
63 | |
64 | return db.WriteBatch(batch); | |
65 | } | |
66 | ||
8fdc94cc | 67 | CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDB(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) { |
e1bfbab8 PW |
68 | } |
69 | ||
2d8a4829 PW |
70 | bool CBlockTreeDB::WriteBlockIndex(const CDiskBlockIndex& blockindex) |
71 | { | |
72 | return Write(make_pair('b', blockindex.GetBlockHash()), blockindex); | |
73 | } | |
74 | ||
75 | bool CBlockTreeDB::ReadBestInvalidWork(CBigNum& bnBestInvalidWork) | |
76 | { | |
77 | return Read('I', bnBestInvalidWork); | |
78 | } | |
79 | ||
80 | bool CBlockTreeDB::WriteBestInvalidWork(const CBigNum& bnBestInvalidWork) | |
81 | { | |
82 | return Write('I', bnBestInvalidWork); | |
83 | } | |
84 | ||
85 | bool CBlockTreeDB::WriteBlockFileInfo(int nFile, const CBlockFileInfo &info) { | |
86 | return Write(make_pair('f', nFile), info); | |
87 | } | |
88 | ||
89 | bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) { | |
90 | return Read(make_pair('f', nFile), info); | |
91 | } | |
92 | ||
93 | bool CBlockTreeDB::WriteLastBlockFile(int nFile) { | |
94 | return Write('l', nFile); | |
95 | } | |
96 | ||
7fea4846 PW |
97 | bool CBlockTreeDB::WriteReindexing(bool fReindexing) { |
98 | if (fReindexing) | |
99 | return Write('R', '1'); | |
100 | else | |
101 | return Erase('R'); | |
102 | } | |
103 | ||
104 | bool CBlockTreeDB::ReadReindexing(bool &fReindexing) { | |
105 | fReindexing = Exists('R'); | |
106 | return true; | |
107 | } | |
108 | ||
2d8a4829 PW |
109 | bool CBlockTreeDB::ReadLastBlockFile(int &nFile) { |
110 | return Read('l', nFile); | |
111 | } | |
112 | ||
beeb5761 PW |
113 | bool CCoinsViewDB::GetStats(CCoinsStats &stats) { |
114 | leveldb::Iterator *pcursor = db.NewIterator(); | |
115 | pcursor->SeekToFirst(); | |
116 | ||
117 | while (pcursor->Valid()) { | |
118 | try { | |
119 | leveldb::Slice slKey = pcursor->key(); | |
120 | CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); | |
121 | char chType; | |
122 | ssKey >> chType; | |
123 | if (chType == 'c' && !fRequestShutdown) { | |
124 | leveldb::Slice slValue = pcursor->value(); | |
125 | CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); | |
126 | CCoins coins; | |
127 | ssValue >> coins; | |
128 | uint256 txhash; | |
129 | ssKey >> txhash; | |
130 | ||
131 | stats.nTransactions++; | |
132 | BOOST_FOREACH(const CTxOut &out, coins.vout) { | |
133 | if (!out.IsNull()) | |
134 | stats.nTransactionOutputs++; | |
135 | } | |
136 | stats.nSerializedSize += 32 + slValue.size(); | |
137 | } | |
138 | pcursor->Next(); | |
139 | } catch (std::exception &e) { | |
140 | return error("%s() : deserialize error", __PRETTY_FUNCTION__); | |
141 | } | |
142 | } | |
143 | delete pcursor; | |
144 | stats.nHeight = GetBestBlock()->nHeight; | |
145 | return true; | |
146 | } | |
147 | ||
2d1fa42e PW |
148 | bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) { |
149 | return Read(make_pair('t', txid), pos); | |
150 | } | |
151 | ||
152 | bool CBlockTreeDB::WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> >&vect) { | |
153 | CLevelDBBatch batch; | |
154 | for (std::vector<std::pair<uint256,CDiskTxPos> >::const_iterator it=vect.begin(); it!=vect.end(); it++) | |
155 | batch.Write(make_pair('t', it->first), it->second); | |
156 | return WriteBatch(batch); | |
157 | } | |
158 | ||
159 | bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) { | |
160 | return Write(std::make_pair('F', name), fValue ? '1' : '0'); | |
161 | } | |
162 | ||
163 | bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) { | |
164 | char ch; | |
165 | if (!Read(std::make_pair('F', name), ch)) | |
166 | return false; | |
167 | fValue = ch == '1'; | |
168 | return true; | |
169 | } | |
170 | ||
2d8a4829 PW |
171 | bool CBlockTreeDB::LoadBlockIndexGuts() |
172 | { | |
173 | leveldb::Iterator *pcursor = NewIterator(); | |
174 | ||
175 | CDataStream ssKeySet(SER_DISK, CLIENT_VERSION); | |
176 | ssKeySet << make_pair('b', uint256(0)); | |
177 | pcursor->Seek(ssKeySet.str()); | |
178 | ||
179 | // Load mapBlockIndex | |
180 | while (pcursor->Valid()) { | |
181 | try { | |
182 | leveldb::Slice slKey = pcursor->key(); | |
183 | CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); | |
184 | char chType; | |
185 | ssKey >> chType; | |
186 | if (chType == 'b' && !fRequestShutdown) { | |
187 | leveldb::Slice slValue = pcursor->value(); | |
188 | CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); | |
189 | CDiskBlockIndex diskindex; | |
190 | ssValue >> diskindex; | |
191 | ||
192 | // Construct block index object | |
193 | CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash()); | |
194 | pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); | |
195 | pindexNew->nHeight = diskindex.nHeight; | |
196 | pindexNew->nFile = diskindex.nFile; | |
197 | pindexNew->nDataPos = diskindex.nDataPos; | |
198 | pindexNew->nUndoPos = diskindex.nUndoPos; | |
199 | pindexNew->nVersion = diskindex.nVersion; | |
200 | pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot; | |
201 | pindexNew->nTime = diskindex.nTime; | |
202 | pindexNew->nBits = diskindex.nBits; | |
203 | pindexNew->nNonce = diskindex.nNonce; | |
204 | pindexNew->nStatus = diskindex.nStatus; | |
205 | pindexNew->nTx = diskindex.nTx; | |
206 | ||
207 | // Watch for genesis block | |
208 | if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == hashGenesisBlock) | |
209 | pindexGenesisBlock = pindexNew; | |
210 | ||
211 | if (!pindexNew->CheckIndex()) | |
212 | return error("LoadBlockIndex() : CheckIndex failed: %s", pindexNew->ToString().c_str()); | |
213 | ||
214 | pcursor->Next(); | |
215 | } else { | |
216 | break; // if shutdown requested or finished loading block index | |
217 | } | |
218 | } catch (std::exception &e) { | |
219 | return error("%s() : deserialize error", __PRETTY_FUNCTION__); | |
220 | } | |
221 | } | |
222 | delete pcursor; | |
223 | ||
224 | return true; | |
225 | } |