21 #include <boost/thread.hpp> 23 static const char DB_COIN =
'C';
24 static const char DB_COINS =
'c';
25 static const char DB_BLOCK_FILES =
'f';
26 static const char DB_TXINDEX =
't';
27 static const char DB_ADDRESSINDEX =
'a';
28 static const char DB_ADDRESSUNSPENTINDEX =
'u';
29 static const char DB_TIMESTAMPINDEX =
's';
30 static const char DB_BLOCKHASHINDEX =
'z';
31 static const char DB_SPENTINDEX =
'p';
32 static const char DB_BLOCK_INDEX =
'b';
34 static const char DB_BEST_BLOCK =
'B';
35 static const char DB_HEAD_BLOCKS =
'H';
36 static const char DB_FLAG =
'F';
37 static const char DB_REINDEX_FLAG =
'R';
38 static const char DB_LAST_BLOCK =
'l';
45 explicit CoinEntry(
const COutPoint* ptr) : outpoint(const_cast<
COutPoint*>(ptr)), key(DB_COIN) {}
47 template<
typename Stream>
54 template<
typename Stream>
69 return db.
Read(CoinEntry(&outpoint), coin);
73 return db.
Exists(CoinEntry(&outpoint));
78 if (!
db.
Read(DB_BEST_BLOCK, hashBestChain))
84 std::vector<uint256> vhashHeadBlocks;
85 if (!
db.
Read(DB_HEAD_BLOCKS, vhashHeadBlocks)) {
86 return std::vector<uint256>();
88 return vhashHeadBlocks;
95 size_t batch_size = (size_t)
gArgs.
GetArg(
"-dbbatchsize", nDefaultDbBatchSize);
96 int crash_simulate =
gArgs.
GetArg(
"-dbcrashratio", 0);
97 assert(!hashBlock.
IsNull());
100 if (old_tip.IsNull()) {
103 if (old_heads.size() == 2) {
104 assert(old_heads[0] == hashBlock);
105 old_tip = old_heads[1];
113 batch.
Erase(DB_BEST_BLOCK);
114 batch.
Write(DB_HEAD_BLOCKS, std::vector<uint256>{hashBlock, old_tip});
116 for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
118 CoinEntry entry(&it->first);
119 if (it->second.coin.IsSpent())
122 batch.
Write(entry, it->second.coin);
126 CCoinsMap::iterator itOld = it++;
127 mapCoins.erase(itOld);
132 if (crash_simulate) {
134 if (rng.
randrange(crash_simulate) == 0) {
135 LogPrintf(
"Simulating a crash. Goodbye.\n");
143 batch.
Erase(DB_HEAD_BLOCKS);
144 batch.
Write(DB_BEST_BLOCK, hashBlock);
148 LogPrint(
BCLog::COINDB,
"Committed %u changed transaction outputs (out of %u) to coin database...\n", (
unsigned int)changed, (
unsigned int)count);
161 return Read(std::make_pair(DB_BLOCK_FILES, nFile), info);
166 return Write(DB_REINDEX_FLAG,
'1');
168 return Erase(DB_REINDEX_FLAG);
172 fReindexing =
Exists(DB_REINDEX_FLAG);
177 return Read(DB_LAST_BLOCK, nFile);
189 CoinEntry entry(&i->
keyTmp.second);
191 i->
keyTmp.first = entry.key;
201 if (keyTmp.first == DB_COIN) {
210 return pcursor->GetValue(coin);
215 return pcursor->GetValueSize();
220 return keyTmp.first == DB_COIN;
226 CoinEntry entry(&keyTmp.second);
227 if (!pcursor->Valid() || !pcursor->GetKey(entry)) {
230 keyTmp.first = entry.key;
234 bool CBlockTreeDB::WriteBatchSync(
const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo,
int nLastFile,
const std::vector<const CBlockIndex*>& blockinfo) {
236 for (std::vector<std::pair<int, const CBlockFileInfo*> >::const_iterator it=fileInfo.begin(); it != fileInfo.end(); it++) {
237 batch.
Write(std::make_pair(DB_BLOCK_FILES, it->first), *it->second);
239 batch.
Write(DB_LAST_BLOCK, nLastFile);
240 for (std::vector<const CBlockIndex*>::const_iterator it=blockinfo.begin(); it != blockinfo.end(); it++) {
247 return Read(std::make_pair(DB_TXINDEX, txid), pos);
252 for (std::vector<std::pair<uint256,CDiskTxPos> >::const_iterator it=vect.begin(); it!=vect.end(); it++)
253 batch.
Write(std::make_pair(DB_TXINDEX, it->first), it->second);
258 return Read(std::make_pair(DB_SPENTINDEX, key), value);
263 for (std::vector<std::pair<CSpentIndexKey,CSpentIndexValue> >::const_iterator it=vect.begin(); it!=vect.end(); it++) {
264 if (it->second.IsNull()) {
265 batch.
Erase(std::make_pair(DB_SPENTINDEX, it->first));
267 batch.
Write(std::make_pair(DB_SPENTINDEX, it->first), it->second);
275 for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=vect.begin(); it!=vect.end(); it++) {
276 if (it->second.IsNull()) {
277 batch.
Erase(std::make_pair(DB_ADDRESSUNSPENTINDEX, it->first));
279 batch.
Write(std::make_pair(DB_ADDRESSUNSPENTINDEX, it->first), it->second);
286 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > &unspentOutputs) {
288 boost::scoped_ptr<CDBIterator> pcursor(
NewIterator());
292 while (pcursor->Valid()) {
293 boost::this_thread::interruption_point();
294 std::pair<char,CAddressUnspentKey> key;
295 if (pcursor->GetKey(key) && key.first == DB_ADDRESSUNSPENTINDEX && key.second.hashBytes == addressHash
296 && (assetName.empty() || key.second.asset == assetName)) {
298 if (pcursor->GetValue(nValue)) {
299 unspentOutputs.push_back(std::make_pair(key.second, nValue));
302 return error(
"failed to get address unspent value");
313 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > &unspentOutputs) {
315 boost::scoped_ptr<CDBIterator> pcursor(
NewIterator());
319 while (pcursor->Valid()) {
320 boost::this_thread::interruption_point();
321 std::pair<char,CAddressUnspentKey> key;
322 if (pcursor->GetKey(key) && key.first == DB_ADDRESSUNSPENTINDEX && key.second.hashBytes == addressHash) {
324 if (pcursor->GetValue(nValue)) {
325 if (key.second.asset !=
"RVN") {
326 unspentOutputs.push_back(std::make_pair(key.second, nValue));
330 return error(
"failed to get address unspent value");
342 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=vect.begin(); it!=vect.end(); it++)
343 batch.
Write(std::make_pair(DB_ADDRESSINDEX, it->first), it->second);
349 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=vect.begin(); it!=vect.end(); it++)
350 batch.
Erase(std::make_pair(DB_ADDRESSINDEX, it->first));
355 std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex,
356 int start,
int end) {
358 boost::scoped_ptr<CDBIterator> pcursor(
NewIterator());
360 if (!assetName.empty() && start > 0 && end > 0) {
361 pcursor->Seek(std::make_pair(DB_ADDRESSINDEX,
363 }
else if (!assetName.empty()) {
369 while (pcursor->Valid()) {
370 boost::this_thread::interruption_point();
371 std::pair<char,CAddressIndexKey> key;
372 if (pcursor->GetKey(key) && key.first == DB_ADDRESSINDEX && key.second.hashBytes == addressHash
373 && (assetName.empty() || key.second.asset == assetName)) {
374 if (end > 0 && key.second.blockHeight > end) {
378 if (pcursor->GetValue(nValue)) {
379 addressIndex.push_back(std::make_pair(key.second, nValue));
382 return error(
"failed to get address index value");
393 std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex,
394 int start,
int end) {
401 batch.
Write(std::make_pair(DB_TIMESTAMPINDEX, timestampIndex), 0);
407 boost::scoped_ptr<CDBIterator> pcursor(
NewIterator());
411 while (pcursor->Valid()) {
412 boost::this_thread::interruption_point();
413 std::pair<char, CTimestampIndexKey> key;
414 if (pcursor->GetKey(key) && key.first == DB_TIMESTAMPINDEX && key.second.timestamp <
high) {
417 hashes.push_back(std::make_pair(key.second.blockHash, key.second.timestamp));
420 hashes.push_back(std::make_pair(key.second.blockHash, key.second.timestamp));
434 batch.
Write(std::make_pair(DB_BLOCKHASHINDEX, blockhashIndex), logicalts);
441 if (!
Read(std::make_pair(DB_BLOCKHASHINDEX, hash), lts))
444 ltimestamp = lts.ltimestamp;
449 return Write(std::make_pair(DB_FLAG, name), fValue ?
'1' :
'0');
454 if (!
Read(std::make_pair(DB_FLAG, name), ch))
462 std::unique_ptr<CDBIterator> pcursor(
NewIterator());
464 pcursor->Seek(std::make_pair(DB_BLOCK_INDEX,
uint256()));
467 while (pcursor->Valid()) {
468 boost::this_thread::interruption_point();
469 std::pair<char, uint256> key;
470 if (pcursor->GetKey(key) && key.first == DB_BLOCK_INDEX) {
472 if (pcursor->GetValue(diskindex)) {
486 pindexNew->
nTx = diskindex.
nTx;
489 return error(
"%s: CheckProofOfWork failed: %s", __func__, pindexNew->
ToString());
493 return error(
"%s: failed to read value", __func__);
513 std::vector<CTxOut> vout;
519 CCoins() : fCoinBase(
false), vout(0), nHeight(0) { }
521 template<
typename Stream>
523 unsigned int nCode = 0;
529 fCoinBase = nCode & 1;
530 std::vector<bool> vAvail(2,
false);
531 vAvail[0] = (nCode & 2) != 0;
532 vAvail[1] = (nCode & 4) != 0;
533 unsigned int nMaskCode = (nCode / 8) + ((nCode & 6) != 0 ? 0 : 1);
535 while (nMaskCode > 0) {
536 unsigned char chAvail = 0;
538 for (
unsigned int p = 0; p < 8; p++) {
539 bool f = (chAvail & (1 << p)) != 0;
546 vout.assign(vAvail.size(),
CTxOut());
547 for (
unsigned int i = 0; i < vAvail.size(); i++) {
563 std::unique_ptr<CDBIterator> pcursor(db.NewIterator());
564 pcursor->Seek(std::make_pair(DB_COINS,
uint256()));
565 if (!pcursor->Valid()) {
570 LogPrintf(
"Upgrading utxo-set database...\n");
573 size_t batch_size = 1 << 24;
576 std::pair<unsigned char, uint256> key;
577 std::pair<unsigned char, uint256> prev_key = {DB_COINS,
uint256()};
578 while (pcursor->Valid()) {
579 boost::this_thread::interruption_point();
583 if (pcursor->GetKey(key) && key.first == DB_COINS) {
584 if (count++ % 256 == 0) {
585 uint32_t
high = 0x100 * *key.second.begin() + *(key.second.begin() + 1);
586 int percentageDone = (int)(high * 100.0 / 65536.0 + 0.5);
588 if (reportDone < percentageDone/10) {
591 reportDone = percentageDone/10;
595 if (!pcursor->GetValue(old_coins)) {
596 return error(
"%s: cannot parse CCoins record", __func__);
599 for (
size_t i = 0; i < old_coins.vout.size(); ++i) {
600 if (!old_coins.vout[i].IsNull() && !old_coins.vout[i].scriptPubKey.IsUnspendable()) {
601 Coin newcoin(std::move(old_coins.vout[i]), old_coins.nHeight, old_coins.fCoinBase);
603 CoinEntry entry(&outpoint);
604 batch.
Write(entry, newcoin);
609 db.WriteBatch(batch);
611 db.CompactRange(prev_key, key);
619 db.WriteBatch(batch);
620 db.CompactRange({DB_COINS,
uint256()}, key);
bool Exists(const K &key) const
bool GetValue(Coin &coin) const override
bool ReadTimestampBlockIndex(const uint256 &hash, unsigned int &logicalTS)
bool GetCoin(const COutPoint &outpoint, Coin &coin) const override
Retrieve the Coin (unspent transaction output) for a given outpoint.
std::string ToString() const
bool Upgrade()
Attempt to update from an older database format. Returns whether an error occurred.
Specialization of CCoinsViewCursor to iterate over a CCoinsViewDB.
CBlockIndex * pprev
pointer to the index of the predecessor of this block
Batch of changes queued to be written to a CDBWrapper.
uint32_t nStatus
Verification status of this block. See enum BlockStatus.
bool ReadTimestampIndex(const unsigned int &high, const unsigned int &low, const bool fActiveOnly, std::vector< std::pair< uint256, unsigned int > > &vect)
bool ReadLastBlockFile(int &nFile)
wrapper for CTxOut that provides a more compact serialization
std::unique_ptr< CDBIterator > pcursor
uint64_t randrange(uint64_t range)
Generate a random integer in the range [0..range).
bool WriteTimestampBlockIndex(const CTimestampBlockIndexKey &blockhashIndex, const CTimestampBlockIndexValue &logicalts)
int nFile
Which # file this block is stored in (blk?????.dat)
bool GetKey(COutPoint &key) const override
bool ReadSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value)
bool WriteReindexing(bool fReindexing)
CDBIterator * NewIterator()
void Serialize(Stream &s, char a)
bool ReadReindexing(bool &fReindexing)
int64_t CAmount
Amount in corbies (Can be negative)
uint256 GetBlockHash() const
bool EraseAddressIndex(const std::vector< std::pair< CAddressIndexKey, CAmount > > &vect)
bool ReadAddressUnspentIndex(uint160 addressHash, int type, std::string assetName, std::vector< std::pair< CAddressUnspentKey, CAddressUnspentValue > > &vect)
bool UpdateAddressUnspentIndex(const std::vector< std::pair< CAddressUnspentKey, CAddressUnspentValue > > &vect)
CBlockTreeDB(size_t nCacheSize, bool fMemory=false, bool fWipe=false, size_t maxFileSize=2<< 20)
bool Erase(const K &key, bool fSync=false)
unsigned int nDataPos
Byte offset within blk?????.dat where this block's data is stored.
bool WriteTimestampIndex(const CTimestampIndexKey ×tampIndex)
std::unordered_map< COutPoint, CCoinsCacheEntry, SaltedOutpointHasher > CCoinsMap
void Write(const K &key, const V &value)
size_t SizeEstimate() const
An output of a transaction.
Used to marshal pointers into hashes for db storage.
size_t EstimateSize() const override
Estimate database size (0 if not implemented)
Parameters that influence chain consensus.
bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params ¶ms)
Check whether a block hash satisfies the proof-of-work requirement specified by nBits.
An outpoint - a combination of a transaction hash and an index n into its vout.
std::pair< char, COutPoint > keyTmp
bool Read(const K &key, V &value) const
bool WriteAddressIndex(const std::vector< std::pair< CAddressIndexKey, CAmount > > &vect)
bool HaveCoin(const COutPoint &outpoint) const override
Just check whether a given outpoint is unspent.
bool ReadFlag(const std::string &name, bool &fValue)
bool WriteBatchSync(const std::vector< std::pair< int, const CBlockFileInfo *> > &fileInfo, int nLastFile, const std::vector< const CBlockIndex *> &blockinfo)
bool ReadBlockFileInfo(int nFile, CBlockFileInfo &info)
bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos)
bool HashOnchainActive(const uint256 &hash)
CCoinsViewDB(size_t nCacheSize, bool fMemory=false, bool fWipe=false)
unsigned int nUndoPos
Byte offset within rev?????.dat where this block's undo data is stored.
uint256 GetBestBlock() const override
Retrieve the block hash whose state this CCoinsView currently represents.
#define LogPrint(category,...)
int32_t nVersion
block header
bool Write(const K &key, const V &value, bool fSync=false)
The block chain is a tree shaped structure starting with the genesis block at the root...
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
void Unserialize(Stream &s, char &a)
bool error(const char *fmt, const Args &... args)
bool LoadBlockIndexGuts(const Consensus::Params &consensusParams, std::function< CBlockIndex *(const uint256 &)> insertBlockIndex)
bool WriteFlag(const std::string &name, bool fValue)
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) override
Do a bulk modification (multiple Coin changes + BestBlock change).
const fs::path & GetDataDir(bool fNetSpecific)
CCoinsViewCursor * Cursor() const override
Get a cursor to iterate over the whole state.
bool WriteBatch(CDBBatch &batch, bool fSync=false)
bool Valid() const override
CClientUIInterface uiInterface
int nHeight
height of the entry in the chain. The genesis block has height 0
uint256 GetBlockHash() const
unsigned int GetValueSize() const override
size_t EstimateSize(const K &key_begin, const K &key_end) const
bool UpdateSpentIndex(const std::vector< std::pair< CSpentIndexKey, CSpentIndexValue > > &vect)
T & REF(const T &val)
Used to bypass the rule against non-const reference to temporary where it makes sense with wrappers s...
boost::signals2::signal< void(const std::string &title, int nProgress, bool resume_possible)> ShowProgress
Show progress e.g.
bool ReadAddressIndex(uint160 addressHash, int type, std::string assetName, std::vector< std::pair< CAddressIndexKey, CAmount > > &addressIndex, int start=0, int end=0)
std::vector< uint256 > GetHeadBlocks() const override
Retrieve the range of blocks that may have been only partially written.
bool WriteTxIndex(const std::vector< std::pair< uint256, CDiskTxPos > > &vect)
unsigned int nTx
Number of transactions in this block.
std::string _(const char *psz)
Translation function: Call Translate signal on UI interface, which returns a boost::optional result...
Cursor for iterating over CoinsView state.