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());
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());
}
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());
{
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);
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;
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())
// ********************************************************* 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<uint256,CBlockIndex*>::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<CBlockIndex*> 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<CBlockIndex*>::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"))
{
uint256 hashBestChain = 0;
CBlockIndex* pindexBest = NULL;
int64 nTimeBestReceived = 0;
+set<CBlockIndex*, CBlockIndexTrustComparator> setBlockIndexValid; // may contain all CBlockIndex*'s that have validness >=BLOCK_VALID_TRANSACTIONS, and must contain those who aren't failed
CMedianFilter<int> cPeerBlockCounts(5, 0); // Amount of blocks that other nodes claim to have
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<CBlockIndex*,CBlockIndexTrustComparator>::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<CBlockIndex*> 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)
{
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))
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())
vector<CTransaction> 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);
}
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:
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)))
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;
if (!chaindb.TxnCommit())
return false;
- // New best
- if (!SetBestChain(pindexNew))
+ // New best?
+ if (!ConnectBestBlock())
return false;
if (pindexNew == pindexBest)
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;
extern CScript COINBASE_FLAGS;
extern CCriticalSection cs_main;
extern std::map<uint256, CBlockIndex*> mapBlockIndex;
+extern std::set<CBlockIndex*, CBlockIndexTrustComparator> setBlockIndexValid;
extern std::set<std::pair<COutPoint, unsigned int> > setStakeSeen;
extern CBlockIndex* pindexGenesisBlock;
extern unsigned int nStakeMinAge;
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);
// 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);
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
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;
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;
}
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;
}
}
};
+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
{
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);