From: alex Date: Wed, 22 Jan 2014 20:00:57 +0000 (+0400) Subject: Prepare database format for multi-stage block processing X-Git-Tag: v0.4.4.7-nvc-next-testing~7 X-Git-Url: https://git.novaco.in/?p=novacoin.git;a=commitdiff_plain;h=05c59739cf697a0f5a6ce83ee48502d0f6f5e105 Prepare database format for multi-stage block processing This commit adds a status field and a transaction counter to the block indexes. --- diff --git a/src/checkpoints.cpp b/src/checkpoints.cpp index f8c00c6..e9567a2 100644 --- a/src/checkpoints.cpp +++ b/src/checkpoints.cpp @@ -168,7 +168,7 @@ namespace Checkpoints CBlock block; if (!block.ReadFromDisk(pindexCheckpoint)) return error("AcceptPendingSyncCheckpoint: ReadFromDisk failed for sync checkpoint %s", hashPendingCheckpoint.ToString().c_str()); - if (!block.SetBestChain(pindexCheckpoint)) + if (!SetBestChain(pindexCheckpoint)) { hashInvalidCheckpoint = hashPendingCheckpoint; return error("AcceptPendingSyncCheckpoint: SetBestChain failed for sync checkpoint %s", hashPendingCheckpoint.ToString().c_str()); @@ -256,7 +256,7 @@ namespace Checkpoints CBlock block; if (!block.ReadFromDisk(mapBlockIndex[hash])) return error("ResetSyncCheckpoint: ReadFromDisk failed for hardened checkpoint %s", hash.ToString().c_str()); - if (!block.SetBestChain(mapBlockIndex[hash])) + if (!SetBestChain(mapBlockIndex[hash])) { return error("ResetSyncCheckpoint: SetBestChain failed for hardened checkpoint %s", hash.ToString().c_str()); } @@ -419,7 +419,7 @@ bool CSyncCheckpoint::ProcessSyncCheckpoint(CNode* pfrom) CBlock block; if (!block.ReadFromDisk(pindexCheckpoint)) return error("ProcessSyncCheckpoint: ReadFromDisk failed for sync checkpoint %s", hashCheckpoint.ToString().c_str()); - if (!block.SetBestChain(pindexCheckpoint)) + if (!SetBestChain(pindexCheckpoint)) { Checkpoints::hashInvalidCheckpoint = hashCheckpoint; return error("ProcessSyncCheckpoint: SetBestChain failed for sync checkpoint %s", hashCheckpoint.ToString().c_str()); diff --git a/src/db.cpp b/src/db.cpp index 9fbcc8a..0f01dd6 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -649,6 +649,9 @@ bool LoadBlockIndex(CChainDB &chaindb) { CBlockIndex* pindex = item.second; pindex->nChainTrust = (pindex->pprev ? pindex->pprev->nChainTrust : 0) + pindex->GetBlockTrust(); + pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx; + if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS && !(pindex->nStatus & BLOCK_FAILED_MASK)) + setBlockIndexValid.insert(pindex); // Calculate stake modifier checksum pindex->nStakeModifierChecksum = GetStakeModifierChecksum(pindex); @@ -769,7 +772,8 @@ bool CChainDB::LoadBlockIndexGuts() CBlockIndex* pindexNew = InsertBlockIndex(blockHash); pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); pindexNew->nHeight = diskindex.nHeight; - pindexNew->pos = diskindex.pos; + pindexNew->nFile = diskindex.nFile; + pindexNew->nDataPos = diskindex.nDataPos; pindexNew->nUndoPos = diskindex.nUndoPos; pindexNew->nMint = diskindex.nMint; pindexNew->nMoneySupply = diskindex.nMoneySupply; @@ -783,13 +787,15 @@ bool CChainDB::LoadBlockIndexGuts() pindexNew->nTime = diskindex.nTime; pindexNew->nBits = diskindex.nBits; pindexNew->nNonce = diskindex.nNonce; + pindexNew->nStatus = diskindex.nStatus; + pindexNew->nTx = diskindex.nTx; // Watch for genesis block if (pindexGenesisBlock == NULL && blockHash == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) pindexGenesisBlock = pindexNew; if (!pindexNew->CheckIndex()) - return error("LoadBlockIndex() : CheckIndex failed at %d", pindexNew->nHeight); + return error("LoadBlockIndex() : CheckIndex failed: %s", pindexNew->ToString().c_str()); // Build setStakeSeen if (pindexNew->IsProofOfStake()) diff --git a/src/init.cpp b/src/init.cpp index 565eec7..7281e01 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -847,38 +847,9 @@ bool AppInit2() // ********************************************************* Step 9: import blocks // scan for better chains in the block chain database, that are not yet connected in the active best chain - CBlockIndex *pindexFoundBest = pindexBest; - for (std::map::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) { - CBlockIndex *pindex = it->second; - if (pindexFoundBest==NULL || pindex->nChainTrust > pindexFoundBest->nChainTrust) - pindexFoundBest = pindex; - } - if (pindexFoundBest != pindexBest) { - uiInterface.InitMessage(_("Importing blocks from block database...")); - uint64 nTxs = 0; - uint64 nBlocks = 0; - std::vector vAttach; - vAttach.reserve(pindexFoundBest->nHeight - (pindexBest==NULL ? 0 : pindexBest->nHeight)); - while (pindexFoundBest && pindexFoundBest->nChainTrust > (pindexBest==NULL ? 0 : pindexBest->nChainTrust)) { - vAttach.push_back(pindexFoundBest); - pindexFoundBest = pindexFoundBest->pprev; - } - for (std::vector::reverse_iterator it = vAttach.rbegin(); it != vAttach.rend(); it++) { - CBlockIndex *pindex = *it; - CBlock block; - if (!block.ReadFromDisk(pindex)) - break; - nTxs += block.vtx.size(); - nBlocks++; - if (pindex->nHeight == 0 || nTxs + nBlocks*3 > 500) { - nTxs=0; - nBlocks=0; - block.SetBestChain(pindex); - } - if (fRequestShutdown) - break; - } - } + uiInterface.InitMessage(_("Importing blocks from block database...")); + if (!ConnectBestBlock()) + strErrors << "Failed to connect best block"; if (mapArgs.count("-loadblock")) { diff --git a/src/main.cpp b/src/main.cpp index ab7a7ba..c4fead8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -58,6 +58,7 @@ uint256 nBestInvalidTrust = 0; uint256 hashBestChain = 0; CBlockIndex* pindexBest = NULL; int64 nTimeBestReceived = 0; +set setBlockIndexValid; // may contain all CBlockIndex*'s that have validness >=BLOCK_VALID_TRANSACTIONS, and must contain those who aren't failed CMedianFilter cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have @@ -1320,6 +1321,61 @@ void static InvalidChainFound(CBlockIndex* pindexNew) DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str()); } +void static InvalidBlockFound(CBlockIndex *pindex) { + pindex->nStatus |= BLOCK_FAILED_VALID; + CChainDB().WriteBlockIndex(CDiskBlockIndex(pindex)); + setBlockIndexValid.erase(pindex); + InvalidChainFound(pindex); + if (pindex->pnext) + ConnectBestBlock(); // reorganise away from the failed block +} + +bool ConnectBestBlock() { + do { + CBlockIndex *pindexNewBest; + + { + std::set::reverse_iterator it = setBlockIndexValid.rbegin(); + if (it == setBlockIndexValid.rend()) + return true; + pindexNewBest = *it; + } + + if (pindexNewBest == pindexBest) + return true; // nothing to do + + // check ancestry + CBlockIndex *pindexTest = pindexNewBest; + std::vector vAttach; + do { + if (pindexTest->nStatus & BLOCK_FAILED_MASK) { + // mark descendants failed + CChainDB chaindb; + CBlockIndex *pindexFailed = pindexNewBest; + while (pindexTest != pindexFailed) { + pindexFailed->nStatus |= BLOCK_FAILED_CHILD; + setBlockIndexValid.erase(pindexFailed); + chaindb.WriteBlockIndex(CDiskBlockIndex(pindexFailed)); + pindexFailed = pindexFailed->pprev; + } + InvalidChainFound(pindexNewBest); + break; + } + + if (pindexBest == NULL || pindexTest->nChainTrust > pindexBest->nChainTrust) + vAttach.push_back(pindexTest); + + if (pindexTest->pprev == NULL || pindexTest->pnext != NULL) { + reverse(vAttach.begin(), vAttach.end()); + BOOST_FOREACH(CBlockIndex *pindexSwitch, vAttach) + if (!SetBestChain(pindexSwitch)) + return false; + return true; + } + pindexTest = pindexTest->pprev; + } while(true); + } while(true); +} void CBlock::UpdateTime(const CBlockIndex* pindexPrev) { @@ -1720,24 +1776,29 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust if (fJustCheck) return true; - CChainDB chaindb; - CDiskBlockPos pos; - // Write undo information to disk - if (pindex->GetUndoPos().IsNull()) + if (pindex->GetUndoPos().IsNull() || (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS) { - if (!FindUndoPos(chaindb, pindex->pos.nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 8)) - return error("ConnectBlock() : FindUndoPos failed"); - if (!blockundo.WriteToDisk(pos)) - return error("ConnectBlock() : CBlockUndo::WriteToDisk failed"); + CChainDB chaindb; - // update nUndoPos in block index - pindex->nUndoPos = pos.nPos + 1; - } + if (pindex->GetUndoPos().IsNull()) { + CDiskBlockPos pos; + if (!FindUndoPos(chaindb, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 8)) + return error("ConnectBlock() : FindUndoPos failed"); + if (!blockundo.WriteToDisk(pos)) + return error("ConnectBlock() : CBlockUndo::WriteToDisk failed"); + + // update nUndoPos in block index + pindex->nUndoPos = pos.nPos; + pindex->nStatus |= BLOCK_HAVE_UNDO; + } + + pindex->nStatus = (pindex->nStatus & ~BLOCK_VALID_MASK) | BLOCK_VALID_SCRIPTS; - CDiskBlockIndex blockindex(pindex); - if (!chaindb.WriteBlockIndex(blockindex)) - return error("ConnectBlock() : WriteBlockIndex failed"); + CDiskBlockIndex blockindex(pindex); + if (!chaindb.WriteBlockIndex(blockindex)) + return error("ConnectBlock() : WriteBlockIndex failed"); + } // add this block to the view's blockchain if (!view.SetBestBlock(pindex)) @@ -1754,13 +1815,13 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust return true; } -bool CBlock::SetBestChain(CBlockIndex* pindexNew) +bool SetBestChain(CBlockIndex* pindexNew) { CCoinsViewCache &view = *pcoinsTip; // special case for attaching the genesis block // note that no ConnectBlock is called, so its coinbase output is non-spendable - if (pindexGenesisBlock == NULL && pindexNew->GetBlockHash() == (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) + if (pindexGenesisBlock == NULL && pindexNew->GetBlockHash() == hashGenesisBlock) { view.SetBestBlock(pindexNew); if (!view.Flush()) @@ -1825,24 +1886,19 @@ bool CBlock::SetBestChain(CBlockIndex* pindexNew) vector vDelete; BOOST_FOREACH(CBlockIndex *pindex, vConnect) { CBlock block; - CBlock *pblock; - if (pindex == pindexNew) // connecting *this block - pblock = this; - else { // other block; read it from disk - if (!block.ReadFromDisk(pindex)) - return error("SetBestBlock() : ReadFromDisk for connect failed"); - pblock = █ - } + if (!block.ReadFromDisk(pindex)) + return error("SetBestBlock() : ReadFromDisk for connect failed"); CCoinsViewCache viewTemp(view, true); - if (!pblock->ConnectBlock(pindex, viewTemp)) { + if (!block.ConnectBlock(pindex, viewTemp)) { InvalidChainFound(pindexNew); + InvalidBlockFound(pindex); return error("SetBestBlock() : ConnectBlock %s failed", pindex->GetBlockHash().ToString().substr(0,20).c_str()); } if (!viewTemp.Flush()) return error("SetBestBlock() : Cache flush failed after connect"); // Queue memory transactions to delete - BOOST_FOREACH(const CTransaction& tx, pblock->vtx) + BOOST_FOREACH(const CTransaction& tx, block.vtx) vDelete.push_back(tx); } @@ -1891,10 +1947,8 @@ bool CBlock::SetBestChain(CBlockIndex* pindexNew) uint256 nBestBlockTrust = pindexBest->nHeight != 0 ? (pindexBest->nChainTrust - pindexBest->pprev->nChainTrust) : pindexBest->nChainTrust; - printf("SetBestChain: new best=%s height=%d trust=%s blocktrust=%"PRI64d" date=%s\n", - hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, - CBigNum(nBestChainTrust).ToString().c_str(), - nBestBlockTrust.Get64(), + printf("SetBestChain: new best=%s height=%d trust=%s blocktrust=%s tx=%lu date=%s\n", + hashBestChain.ToString().substr(0,20).c_str(), nBestHeight, CBigNum(nBestChainTrust).ToString().c_str(), CBigNum(nBestBlockTrust).ToString().c_str(), (unsigned long)pindexNew->nChainTx, DateTimeStrFormat("%x %H:%M:%S", pindexBest->GetBlockTime()).c_str()); // Check the version of the last 100 blocks to see if we need to upgrade: @@ -2008,9 +2062,13 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos) pindexNew->pprev = (*miPrev).second; pindexNew->nHeight = pindexNew->pprev->nHeight + 1; } + pindexNew->nTx = vtx.size(); pindexNew->nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + pindexNew->GetBlockTrust(); - pindexNew->pos = pos; + pindexNew->nChainTx = (pindexNew->pprev ? pindexNew->pprev->nChainTx : 0) + pindexNew->nTx; + pindexNew->nFile = pos.nFile; + pindexNew->nDataPos = pos.nPos; pindexNew->nUndoPos = 0; + pindexNew->nStatus = BLOCK_VALID_TRANSACTIONS | BLOCK_HAVE_DATA; // Compute stake entropy bit for stake modifier if (!pindexNew->SetStakeEntropyBit(GetStakeEntropyBit(pindexNew->nTime))) @@ -2034,6 +2092,8 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos) if (!CheckStakeModifierCheckpoints(pindexNew->nHeight, pindexNew->nStakeModifierChecksum)) return error("AddToBlockIndex() : Rejected by stake modifier checkpoint height=%d, modifier=0x%016"PRI64x, pindexNew->nHeight, nStakeModifier); + setBlockIndexValid.insert(pindexNew); + CChainDB chaindb; if (!chaindb.TxnBegin()) return false; @@ -2041,8 +2101,8 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos) if (!chaindb.TxnCommit()) return false; - // New best - if (!SetBestChain(pindexNew)) + // New best? + if (!ConnectBestBlock()) return false; if (pindexNew == pindexBest) diff --git a/src/main.h b/src/main.h index 038eb36..195d62e 100644 --- a/src/main.h +++ b/src/main.h @@ -25,6 +25,7 @@ class CAddress; class CInv; class CRequestTracker; class CNode; +class CBlockIndexTrustComparator; static const unsigned int MAX_BLOCK_SIZE = 1000000; static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2; @@ -72,6 +73,7 @@ extern libzerocoin::Params* ZCParams; extern CScript COINBASE_FLAGS; extern CCriticalSection cs_main; extern std::map mapBlockIndex; +extern std::set setBlockIndexValid; extern std::set > setStakeSeen; extern CBlockIndex* pindexGenesisBlock; extern unsigned int nStakeMinAge; @@ -135,6 +137,8 @@ int GetNumBlocksOfPeers(); bool IsInitialBlockDownload(); std::string GetWarnings(std::string strFor); bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow); +bool SetBestChain(CBlockIndex* pindexNew); +bool ConnectBestBlock(); uint256 WantedByOrphan(const CBlock* pblockOrphan); const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake); void StakeMiner(CWallet *pwallet); @@ -1385,9 +1389,6 @@ public: // Read a block from disk bool ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions=true); - // Make this block (with given index) the new tip of the active block chain - bool SetBestChain(CBlockIndex* pindexNew); - // Add this block to the block index, and if necessary, switch the active block chain to this bool AddToBlockIndex(const CDiskBlockPos &pos); @@ -1472,6 +1473,24 @@ extern CCriticalSection cs_LastBlockFile; extern CBlockFileInfo infoLastBlockFile; extern int nLastBlockFile; +enum BlockStatus { + BLOCK_VALID_UNKNOWN = 0, + BLOCK_VALID_HEADER = 1, // parsed, version ok, hash satisfies claimed PoW, 1 <= vtx count <= max, timestamp not in future + BLOCK_VALID_TREE = 2, // parent found, difficulty matches, timestamp >= median previous, checkpoint + BLOCK_VALID_TRANSACTIONS = 3, // only first tx is coinbase, 2 <= coinbase input script length <= 100, transactions valid, no duplicate txids, sigops, size, merkle root + BLOCK_VALID_CHAIN = 4, // outputs do not overspend inputs, no double spends, coinbase output ok, immature coinbase spends, BIP30 + BLOCK_VALID_SCRIPTS = 5, // scripts/signatures ok + BLOCK_VALID_MASK = 7, + + BLOCK_HAVE_DATA = 8, // full block available in blk*.dat + BLOCK_HAVE_UNDO = 16, // undo data available in rev*.dat + BLOCK_HAVE_MASK = 24, + + BLOCK_FAILED_VALID = 32, // stage after last reached validness failed + BLOCK_FAILED_CHILD = 64, // descends from failed block + BLOCK_FAILED_MASK = 96 +}; + /** The block chain is a tree shaped structure starting with the * genesis block at the root, with each block potentially having multiple * candidates to be the next block. pprev and pnext link a path through the @@ -1482,50 +1501,92 @@ extern int nLastBlockFile; class CBlockIndex { public: + // pointer to the hash of the block, if any. memory is owned by this CBlockIndex const uint256* phashBlock; + + // pointer to the index of the predecessor of this block CBlockIndex* pprev; + + // (memory only) pointer to the index of the *active* successor of this block CBlockIndex* pnext; + + // height of the entry in the chain. The genesis block has height 0 int nHeight; - CDiskBlockPos pos; + + // Which # file this block is stored in (blk?????.dat) + int nFile; + + // Byte offset within blk?????.dat where this block's data is stored + unsigned int nDataPos; + + // Byte offset within rev?????.dat where this block's undo data is stored unsigned int nUndoPos; - uint256 nChainTrust; // trust score of block chain + // (memory only) Trust score of block chain up to and including this block + uint256 nChainTrust; + + // Number of transactions in this block. + unsigned int nTx; + + // (memory only) Number of transactions in the chain up to and including this block + unsigned int nChainTx; + + // Verification status of this block. See enum BlockStatus for detailed info + unsigned int nStatus; + + // Coins amount created by this block int64 nMint; + + // Total coins created in this block chain up to and including this block int64 nMoneySupply; + // Block flags unsigned int nFlags; enum { - BLOCK_PROOF_OF_STAKE = (1 << 0), // is proof-of-stake block - BLOCK_STAKE_ENTROPY = (1 << 1), // entropy bit for stake modifier - BLOCK_STAKE_MODIFIER = (1 << 2), // regenerated stake modifier + // is proof-of-stake block + BLOCK_PROOF_OF_STAKE = (1 << 0), + // entropy bit for stake modifier + BLOCK_STAKE_ENTROPY = (1 << 1), + // regenerated stake modifier + BLOCK_STAKE_MODIFIER = (1 << 2), }; - uint64 nStakeModifier; // hash modifier for proof-of-stake - unsigned int nStakeModifierChecksum; // checksum of index; in-memeory only + // Hash modifier for proof-of-stake kernel + uint64 nStakeModifier; - // proof-of-stake specific fields + // Checksum of index in-memory only + unsigned int nStakeModifierChecksum; + + // Predecessor of coinstake transaction COutPoint prevoutStake; + + // Timestamp of coinstake transaction unsigned int nStakeTime; + + // Kernel hash uint256 hashProofOfStake; - // block header + // Block header int nVersion; uint256 hashMerkleRoot; unsigned int nTime; unsigned int nBits; unsigned int nNonce; - CBlockIndex() { phashBlock = NULL; pprev = NULL; pnext = NULL; nHeight = 0; - pos.SetNull(); + nFile = 0; + nDataPos = 0; nUndoPos = 0; nChainTrust = 0; + nTx = 0; + nChainTx = 0; + nStatus = 0; nMint = 0; nMoneySupply = 0; nFlags = 0; @@ -1548,9 +1609,13 @@ public: pprev = NULL; pnext = NULL; nHeight = 0; - pos.SetNull(); + nFile = 0; + nDataPos = 0; nUndoPos = 0; nChainTrust = 0; + nTx = 0; + nChainTx = 0; + nStatus = 0; nMint = 0; nMoneySupply = 0; nFlags = 0; @@ -1577,15 +1642,22 @@ public: } CDiskBlockPos GetBlockPos() const { - return pos; + CDiskBlockPos ret; + if (nStatus & BLOCK_HAVE_DATA) { + ret.nFile = nFile; + ret.nPos = nDataPos; + } else + ret.SetNull(); + return ret; } CDiskBlockPos GetUndoPos() const { - CDiskBlockPos ret = pos; - if (nUndoPos == 0) + CDiskBlockPos ret; + if (nStatus & BLOCK_HAVE_UNDO) { + ret.nFile = nFile; + ret.nPos = nUndoPos; + } else ret.SetNull(); - else - ret.nPos = nUndoPos - 1; return ret; } @@ -1719,6 +1791,16 @@ public: } }; +struct CBlockIndexTrustComparator +{ + bool operator()(CBlockIndex *pa, CBlockIndex *pb) { + if (pa->nChainTrust > pb->nChainTrust) return false; + if (pa->nChainTrust < pb->nChainTrust) return true; + + return false; // identical blocks + } +}; + /** Used to marshal pointers into hashes for db storage. */ class CDiskBlockIndex : public CBlockIndex { @@ -1739,11 +1821,17 @@ public: IMPLEMENT_SERIALIZE ( if (!(nType & SER_GETHASH)) - READWRITE(nVersion); - - READWRITE(nHeight); - READWRITE(pos); - READWRITE(nUndoPos); + READWRITE(VARINT(nVersion)); + + READWRITE(VARINT(nHeight)); + READWRITE(VARINT(nStatus)); + READWRITE(VARINT(nTx)); + if (nStatus & (BLOCK_HAVE_DATA | BLOCK_HAVE_UNDO)) + READWRITE(VARINT(nFile)); + if (nStatus & BLOCK_HAVE_DATA) + READWRITE(VARINT(nDataPos)); + if (nStatus & BLOCK_HAVE_UNDO) + READWRITE(VARINT(nUndoPos)); READWRITE(nMint); READWRITE(nMoneySupply); READWRITE(nFlags);