One file per block

Refactor of the block storage code, which now stores one file per block.
This will allow easier pruning, as blocks can be removed individually.
This commit is contained in:
Pieter Wuille 2012-06-19 01:36:43 +02:00
parent 44ac1c0fe3
commit 630fd8dcb6
3 changed files with 155 additions and 124 deletions

View file

@ -637,12 +637,12 @@ bool CTxDB::LoadBlockIndex()
nCheckDepth = nBestHeight; nCheckDepth = nBestHeight;
printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); printf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel);
CBlockIndex* pindexFork = NULL; CBlockIndex* pindexFork = NULL;
map<pair<unsigned int, unsigned int>, CBlockIndex*> mapBlockPos;
for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev)
{ {
if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth) if (fRequestShutdown || pindex->nHeight < nBestHeight-nCheckDepth)
break; break;
CBlock block; CBlock block;
CDiskBlockPos blockPos = pindex->GetBlockPos();
if (!block.ReadFromDisk(pindex)) if (!block.ReadFromDisk(pindex))
return error("LoadBlockIndex() : block.ReadFromDisk failed"); return error("LoadBlockIndex() : block.ReadFromDisk failed");
// check level 1: verify block validity // check level 1: verify block validity
@ -654,8 +654,6 @@ bool CTxDB::LoadBlockIndex()
// check level 2: verify transaction index validity // check level 2: verify transaction index validity
if (nCheckLevel>1) if (nCheckLevel>1)
{ {
pair<unsigned int, unsigned int> pos = make_pair(pindex->nFile, pindex->nBlockPos);
mapBlockPos[pos] = pindex;
BOOST_FOREACH(const CTransaction &tx, block.vtx) BOOST_FOREACH(const CTransaction &tx, block.vtx)
{ {
uint256 hashTx = tx.GetHash(); uint256 hashTx = tx.GetHash();
@ -663,7 +661,7 @@ bool CTxDB::LoadBlockIndex()
if (ReadTxIndex(hashTx, txindex)) if (ReadTxIndex(hashTx, txindex))
{ {
// check level 3: checker transaction hashes // check level 3: checker transaction hashes
if (nCheckLevel>2 || pindex->nFile != txindex.pos.nFile || pindex->nBlockPos != txindex.pos.nBlockPos) if (nCheckLevel>2 || blockPos != txindex.pos.blockPos)
{ {
// either an error or a duplicate transaction // either an error or a duplicate transaction
CTransaction txFound; CTransaction txFound;
@ -687,12 +685,6 @@ bool CTxDB::LoadBlockIndex()
{ {
if (!txpos.IsNull()) if (!txpos.IsNull())
{ {
pair<unsigned int, unsigned int> posFind = make_pair(txpos.nFile, txpos.nBlockPos);
if (!mapBlockPos.count(posFind))
{
printf("LoadBlockIndex(): *** found bad spend at %d, hashBlock=%s, hashTx=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str(), hashTx.ToString().c_str());
pindexFork = pindex->pprev;
}
// check level 6: check whether spent txouts were spent by a valid transaction that consume them // check level 6: check whether spent txouts were spent by a valid transaction that consume them
if (nCheckLevel>5) if (nCheckLevel>5)
{ {
@ -795,9 +787,8 @@ bool CTxDB::LoadBlockIndexGuts()
CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash()); CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash());
pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev);
pindexNew->pnext = InsertBlockIndex(diskindex.hashNext); pindexNew->pnext = InsertBlockIndex(diskindex.hashNext);
pindexNew->nFile = diskindex.nFile;
pindexNew->nBlockPos = diskindex.nBlockPos;
pindexNew->nHeight = diskindex.nHeight; pindexNew->nHeight = diskindex.nHeight;
pindexNew->nAlternative = diskindex.nAlternative;
pindexNew->nVersion = diskindex.nVersion; pindexNew->nVersion = diskindex.nVersion;
pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot; pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
pindexNew->nTime = diskindex.nTime; pindexNew->nTime = diskindex.nTime;

View file

@ -389,7 +389,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock)
CTxIndex txindex; CTxIndex txindex;
if (!CTxDB("r").ReadTxIndex(GetHash(), txindex)) if (!CTxDB("r").ReadTxIndex(GetHash(), txindex))
return 0; return 0;
if (!blockTmp.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos)) if (!blockTmp.ReadFromDisk(txindex.pos.blockPos))
return 0; return 0;
pblock = &blockTmp; pblock = &blockTmp;
} }
@ -646,7 +646,7 @@ bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, bool fCheckInputs,
// Check against previous transactions // Check against previous transactions
// This is done last to help prevent CPU exhaustion denial-of-service attacks. // This is done last to help prevent CPU exhaustion denial-of-service attacks.
if (!tx.ConnectInputs(mapInputs, mapUnused, CDiskTxPos(1,1,1), pindexBest, false, false)) if (!tx.ConnectInputs(mapInputs, mapUnused, CDiskTxPos(true), pindexBest, false, false))
{ {
return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str()); return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().substr(0,10).c_str());
} }
@ -817,7 +817,7 @@ int CTxIndex::GetDepthInMainChain() const
{ {
// Read block header // Read block header
CBlock block; CBlock block;
if (!block.ReadFromDisk(pos.nFile, pos.nBlockPos, false)) if (!block.ReadFromDisk(pos.blockPos, false))
return 0; return 0;
// Find the block in the index // Find the block in the index
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(block.GetHash()); map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(block.GetHash());
@ -847,7 +847,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock)
if (tx.ReadFromDisk(txdb, COutPoint(hash, 0), txindex)) if (tx.ReadFromDisk(txdb, COutPoint(hash, 0), txindex))
{ {
CBlock block; CBlock block;
if (block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) if (block.ReadFromDisk(txindex.pos.blockPos, false))
hashBlock = block.GetHash(); hashBlock = block.GetHash();
return true; return true;
} }
@ -892,7 +892,7 @@ bool CBlock::ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions)
*this = pindex->GetBlockHeader(); *this = pindex->GetBlockHeader();
return true; return true;
} }
if (!ReadFromDisk(pindex->nFile, pindex->nBlockPos, fReadTransactions)) if (!ReadFromDisk(pindex->GetBlockPos(), fReadTransactions))
return false; return false;
if (GetHash() != pindex->GetBlockHash()) if (GetHash() != pindex->GetBlockHash())
return error("CBlock::ReadFromDisk() : GetHash() doesn't match index"); return error("CBlock::ReadFromDisk() : GetHash() doesn't match index");
@ -1156,7 +1156,7 @@ bool CTransaction::FetchInputs(CTxDB& txdb, const map<uint256, CTxIndex>& mapTes
// Read txPrev // Read txPrev
CTransaction& txPrev = inputsRet[prevout.hash].second; CTransaction& txPrev = inputsRet[prevout.hash].second;
if (!fFound || txindex.pos == CDiskTxPos(1,1,1)) if (!fFound || txindex.pos.IsMemPool())
{ {
// Get prev tx from single transactions in memory // Get prev tx from single transactions in memory
{ {
@ -1262,7 +1262,7 @@ bool CTransaction::ConnectInputs(MapPrevTx inputs,
// If prev is coinbase, check that it's matured // If prev is coinbase, check that it's matured
if (txPrev.IsCoinBase()) if (txPrev.IsCoinBase())
for (const CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < COINBASE_MATURITY; pindex = pindex->pprev) for (const CBlockIndex* pindex = pindexBlock; pindex && pindexBlock->nHeight - pindex->nHeight < COINBASE_MATURITY; pindex = pindex->pprev)
if (pindex->nBlockPos == txindex.pos.nBlockPos && pindex->nFile == txindex.pos.nFile) if (pindex->GetBlockPos() == txindex.pos.blockPos)
return error("ConnectInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - pindex->nHeight); return error("ConnectInputs() : tried to spend coinbase at depth %d", pindexBlock->nHeight - pindex->nHeight);
// Check for negative or overflow input values // Check for negative or overflow input values
@ -1427,11 +1427,10 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
//// issue here: it doesn't know the version //// issue here: it doesn't know the version
unsigned int nTxPos; unsigned int nTxPos;
if (fJustCheck) if (fJustCheck)
// FetchInputs treats CDiskTxPos(1,1,1) as a special "refer to memorypool" indicator
// Since we're just checking the block and not actually connecting it, it might not (and probably shouldn't) be on the disk to get the transaction from // Since we're just checking the block and not actually connecting it, it might not (and probably shouldn't) be on the disk to get the transaction from
nTxPos = 1; nTxPos = 1;
else else
nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - 1 + GetSizeOfCompactSize(vtx.size()); nTxPos = ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - 1 + GetSizeOfCompactSize(vtx.size());
map<uint256, CTxIndex> mapQueuedChanges; map<uint256, CTxIndex> mapQueuedChanges;
int64 nFees = 0; int64 nFees = 0;
@ -1453,9 +1452,11 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
if (nSigOps > MAX_BLOCK_SIGOPS) if (nSigOps > MAX_BLOCK_SIGOPS)
return DoS(100, error("ConnectBlock() : too many sigops")); return DoS(100, error("ConnectBlock() : too many sigops"));
CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos); CDiskTxPos posThisTx(pindex->GetBlockPos(), nTxPos);
if (!fJustCheck) if (!fJustCheck)
nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
else
posThisTx = CDiskTxPos(true);
MapPrevTx mapInputs; MapPrevTx mapInputs;
if (!tx.IsCoinBase()) if (!tx.IsCoinBase())
@ -1750,7 +1751,7 @@ bool CBlock::SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew)
} }
bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos)
{ {
// Check for duplicate // Check for duplicate
uint256 hash = GetHash(); uint256 hash = GetHash();
@ -1758,7 +1759,7 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos)
return error("AddToBlockIndex() : %s already exists", hash.ToString().substr(0,20).c_str()); return error("AddToBlockIndex() : %s already exists", hash.ToString().substr(0,20).c_str());
// Construct new block index object // Construct new block index object
CBlockIndex* pindexNew = new CBlockIndex(nFile, nBlockPos, *this); CBlockIndex* pindexNew = new CBlockIndex(*this);
if (!pindexNew) if (!pindexNew)
return error("AddToBlockIndex() : new CBlockIndex failed"); return error("AddToBlockIndex() : new CBlockIndex failed");
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
@ -1770,6 +1771,8 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos)
pindexNew->nHeight = pindexNew->pprev->nHeight + 1; pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
} }
pindexNew->bnChainWork = (pindexNew->pprev ? pindexNew->pprev->bnChainWork : 0) + pindexNew->GetBlockWork(); pindexNew->bnChainWork = (pindexNew->pprev ? pindexNew->pprev->bnChainWork : 0) + pindexNew->GetBlockWork();
assert(pos.nHeight == pindexNew->nHeight);
pindexNew->nAlternative = pos.nAlternative;
CTxDB txdb; CTxDB txdb;
if (!txdb.TxnBegin()) if (!txdb.TxnBegin())
@ -1908,13 +1911,12 @@ bool CBlock::AcceptBlock()
} }
// Write block to history file // Write block to history file
CDiskBlockPos blockPos = CDiskBlockPos(nHeight);
if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION))) if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION)))
return error("AcceptBlock() : out of disk space"); return error("AcceptBlock() : out of disk space");
unsigned int nFile = -1; if (!WriteToDisk(blockPos))
unsigned int nBlockPos = 0;
if (!WriteToDisk(nFile, nBlockPos))
return error("AcceptBlock() : WriteToDisk failed"); return error("AcceptBlock() : WriteToDisk failed");
if (!AddToBlockIndex(nFile, nBlockPos)) if (!AddToBlockIndex(blockPos))
return error("AcceptBlock() : AddToBlockIndex failed"); return error("AcceptBlock() : AddToBlockIndex failed");
// Relay inventory, but don't relay old inventory during initial block download // Relay inventory, but don't relay old inventory during initial block download
@ -2048,53 +2050,18 @@ bool CheckDiskSpace(uint64 nAdditionalBytes)
return true; return true;
} }
static filesystem::path BlockFilePath(unsigned int nFile) FILE* OpenBlockFile(const CDiskBlockPos &pos, const char* pszMode)
{ {
string strBlockFn = strprintf("blk%04u.dat", nFile); boost::filesystem::path path = pos.GetFileName(GetDataDir());
return GetDataDir() / strBlockFn; boost::filesystem::create_directories(path.parent_path());
} if (pos.IsNull() || pos.IsMemPool())
FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode)
{
if ((nFile < 1) || (nFile == (unsigned int) -1))
return NULL; return NULL;
FILE* file = fopen(BlockFilePath(nFile).string().c_str(), pszMode); FILE* file = fopen(path.string().c_str(), pszMode);
if (!file) if (!file)
return NULL; return NULL;
if (nBlockPos != 0 && !strchr(pszMode, 'a') && !strchr(pszMode, 'w'))
{
if (fseek(file, nBlockPos, SEEK_SET) != 0)
{
fclose(file);
return NULL;
}
}
return file; return file;
} }
static unsigned int nCurrentBlockFile = 1;
FILE* AppendBlockFile(unsigned int& nFileRet)
{
nFileRet = 0;
loop
{
FILE* file = OpenBlockFile(nCurrentBlockFile, 0, "ab");
if (!file)
return NULL;
if (fseek(file, 0, SEEK_END) != 0)
return NULL;
// FAT32 file size max 4GB, fseek and ftell max 2GB, so we must stay under 2GB
if (ftell(file) < (long)(0x7F000000 - MAX_SIZE))
{
nFileRet = nCurrentBlockFile;
return file;
}
fclose(file);
nCurrentBlockFile++;
}
}
bool LoadBlockIndex(bool fAllowNew) bool LoadBlockIndex(bool fAllowNew)
{ {
if (fTestNet) if (fTestNet)
@ -2153,19 +2120,19 @@ bool LoadBlockIndex(bool fAllowNew)
} }
//// debug print //// debug print
printf("%s\n", block.GetHash().ToString().c_str()); uint256 hash = block.GetHash();
printf("%s\n", hash.ToString().c_str());
printf("%s\n", hashGenesisBlock.ToString().c_str()); printf("%s\n", hashGenesisBlock.ToString().c_str());
printf("%s\n", block.hashMerkleRoot.ToString().c_str()); printf("%s\n", block.hashMerkleRoot.ToString().c_str());
assert(block.hashMerkleRoot == uint256("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); assert(block.hashMerkleRoot == uint256("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"));
block.print(); block.print();
assert(block.GetHash() == hashGenesisBlock); assert(hash == hashGenesisBlock);
// Start new block file // Start new block file
unsigned int nFile; CDiskBlockPos blockPos(0);
unsigned int nBlockPos; if (!block.WriteToDisk(blockPos))
if (!block.WriteToDisk(nFile, nBlockPos))
return error("LoadBlockIndex() : writing genesis block to disk failed"); return error("LoadBlockIndex() : writing genesis block to disk failed");
if (!block.AddToBlockIndex(nFile, nBlockPos)) if (!block.AddToBlockIndex(blockPos))
return error("LoadBlockIndex() : genesis block not accepted"); return error("LoadBlockIndex() : genesis block not accepted");
} }
@ -2219,11 +2186,9 @@ void PrintBlockTree()
// print item // print item
CBlock block; CBlock block;
block.ReadFromDisk(pindex); block.ReadFromDisk(pindex);
printf("%d (%u,%u) %s %s tx %"PRIszu"", printf("%d (%s) %s tx %"PRIszu"",
pindex->nHeight, pindex->nHeight,
pindex->nFile, pindex->GetBlockPos().GetFileName("").string().c_str(),
pindex->nBlockPos,
block.GetHash().ToString().substr(0,20).c_str(),
DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()).c_str(), DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()).c_str(),
block.vtx.size()); block.vtx.size());
@ -3693,9 +3658,9 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
continue; continue;
if (!tx.ConnectInputs(mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, false, true)) if (!tx.ConnectInputs(mapInputs, mapTestPoolTmp, CDiskTxPos(true), pindexPrev, false, true))
continue; continue;
mapTestPoolTmp[tx.GetHash()] = CTxIndex(CDiskTxPos(1,1,1), tx.vout.size()); mapTestPoolTmp[tx.GetHash()] = CTxIndex(CDiskTxPos(true), tx.vout.size());
swap(mapTestPool, mapTestPoolTmp); swap(mapTestPool, mapTestPoolTmp);
// Added // Added
@ -3743,7 +3708,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
pblock->nNonce = 0; pblock->nNonce = 0;
pblock->vtx[0].vin[0].scriptSig = scriptDummy; pblock->vtx[0].vin[0].scriptSig = scriptDummy;
CBlockIndex indexDummy(1, 1, *pblock); CBlockIndex indexDummy(*pblock);
indexDummy.pprev = pindexPrev; indexDummy.pprev = pindexPrev;
indexDummy.nHeight = pindexPrev->nHeight + 1; indexDummy.nHeight = pindexPrev->nHeight + 1;
if (!pblock->ConnectBlock(txdb, &indexDummy, true)) if (!pblock->ConnectBlock(txdb, &indexDummy, true))

View file

@ -80,14 +80,14 @@ static const uint64 nMinDiskSpace = 52428800;
class CReserveKey; class CReserveKey;
class CTxDB; class CTxDB;
class CTxIndex; class CTxIndex;
class CDiskBlockPos;
void RegisterWallet(CWallet* pwalletIn); void RegisterWallet(CWallet* pwalletIn);
void UnregisterWallet(CWallet* pwalletIn); void UnregisterWallet(CWallet* pwalletIn);
void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false); void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL, bool fUpdate = false);
bool ProcessBlock(CNode* pfrom, CBlock* pblock); bool ProcessBlock(CNode* pfrom, CBlock* pblock);
bool CheckDiskSpace(uint64 nAdditionalBytes=0); bool CheckDiskSpace(uint64 nAdditionalBytes=0);
FILE* OpenBlockFile(unsigned int nFile, unsigned int nBlockPos, const char* pszMode="rb"); FILE* OpenBlockFile(const CDiskBlockPos &pos, const char* pszMode="rb");
FILE* AppendBlockFile(unsigned int& nFileRet);
bool LoadBlockIndex(bool fAllowNew=true); bool LoadBlockIndex(bool fAllowNew=true);
void PrintBlockTree(); void PrintBlockTree();
CBlockIndex* FindBlockByHeight(int nHeight); CBlockIndex* FindBlockByHeight(int nHeight);
@ -118,34 +118,115 @@ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock);
bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut); bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut);
class CDiskBlockPos
{
public:
int nHeight;
int nAlternative;
CDiskBlockPos() {
SetNull();
}
CDiskBlockPos(int nHeightIn, int nAlternativeIn = 0) {
nHeight = nHeightIn;
nAlternative = nAlternativeIn;
}
std::string GetAlternative() const {
char c[9]={0,0,0,0,0,0,0,0,0};
char *cp = &c[8];
unsigned int n = nAlternative;
while (n > 0 && cp>c) {
n--;
*(--cp) = 'a' + (n % 26);
n /= 26;
}
return std::string(cp);
}
boost::filesystem::path GetDirectory(const boost::filesystem::path &base) const {
assert(nHeight != -1);
return base / strprintf("era%02u", nHeight / 210000) /
strprintf("cycle%04u", nHeight / 2016);
}
boost::filesystem::path GetFileName(const boost::filesystem::path &base) const {
return GetDirectory(base) / strprintf("%08u%s.blk", nHeight, GetAlternative().c_str());
}
// TODO: make thread-safe (lockfile, atomic file creation, ...?)
void MakeUnique(const boost::filesystem::path &base) {
while (boost::filesystem::exists(GetFileName(base)))
nAlternative++;
}
IMPLEMENT_SERIALIZE(({
CDiskBlockPos *me = const_cast<CDiskBlockPos*>(this);
if (!fRead) {
unsigned int nCode = (nHeight + 1) * 2 + (nAlternative > 0);
READWRITE(VARINT(nCode));
if (nAlternative > 0) {
unsigned int nAlt = nAlternative - 1;
READWRITE(VARINT(nAlt));
}
} else {
unsigned int nCode = 0;
READWRITE(VARINT(nCode));
me->nHeight = (nCode / 2) - 1;
if (nCode & 1) {
unsigned int nAlt = 0;
READWRITE(VARINT(nAlt));
me->nAlternative = 1 + nAlt;
} else {
me->nAlternative = 0;
}
}
});)
friend bool operator==(const CDiskBlockPos &a, const CDiskBlockPos &b) {
return ((a.nHeight == b.nHeight) && (a.nAlternative == b.nAlternative));
}
friend bool operator!=(const CDiskBlockPos &a, const CDiskBlockPos &b) {
return !(a == b);
}
void SetNull() { nHeight = -1; nAlternative = 0; }
bool IsNull() const { return ((nHeight == -1) && (nAlternative == 0)); }
void SetMemPool() { nHeight = -1; nAlternative = -1; }
bool IsMemPool() const { return ((nHeight == -1) && (nAlternative == -1)); }
};
/** Position on disk for a particular transaction. */ /** Position on disk for a particular transaction. */
class CDiskTxPos class CDiskTxPos
{ {
public: public:
unsigned int nFile; CDiskBlockPos blockPos;
unsigned int nBlockPos;
unsigned int nTxPos; unsigned int nTxPos;
CDiskTxPos() CDiskTxPos(bool fInMemPool = false)
{ {
SetNull(); SetNull();
if (fInMemPool)
blockPos.SetMemPool();
} }
CDiskTxPos(unsigned int nFileIn, unsigned int nBlockPosIn, unsigned int nTxPosIn) CDiskTxPos(const CDiskBlockPos &block, unsigned int nTxPosIn) : blockPos(block), nTxPos(nTxPosIn) { }
{
nFile = nFileIn;
nBlockPos = nBlockPosIn;
nTxPos = nTxPosIn;
}
IMPLEMENT_SERIALIZE( READWRITE(FLATDATA(*this)); ) IMPLEMENT_SERIALIZE(
void SetNull() { nFile = (unsigned int) -1; nBlockPos = 0; nTxPos = 0; } READWRITE(blockPos);
bool IsNull() const { return (nFile == (unsigned int) -1); } READWRITE(VARINT(nTxPos));
)
void SetNull() { blockPos.SetNull(); nTxPos = 0; }
bool IsNull() const { return blockPos.IsNull(); }
bool IsMemPool() const { return blockPos.IsMemPool(); }
friend bool operator==(const CDiskTxPos& a, const CDiskTxPos& b) friend bool operator==(const CDiskTxPos& a, const CDiskTxPos& b)
{ {
return (a.nFile == b.nFile && return (a.blockPos == b.blockPos &&
a.nBlockPos == b.nBlockPos &&
a.nTxPos == b.nTxPos); a.nTxPos == b.nTxPos);
} }
@ -158,8 +239,10 @@ public:
{ {
if (IsNull()) if (IsNull())
return "null"; return "null";
else if (blockPos.IsMemPool())
return "mempool";
else else
return strprintf("(nFile=%u, nBlockPos=%u, nTxPos=%u)", nFile, nBlockPos, nTxPos); return strprintf("(%s, nTxPos=%u)", blockPos.GetFileName("").string().c_str(), nTxPos);
} }
void print() const void print() const
@ -545,7 +628,7 @@ public:
bool ReadFromDisk(CDiskTxPos pos, FILE** pfileRet=NULL) bool ReadFromDisk(CDiskTxPos pos, FILE** pfileRet=NULL)
{ {
CAutoFile filein = CAutoFile(OpenBlockFile(pos.nFile, 0, pfileRet ? "rb+" : "rb"), SER_DISK, CLIENT_VERSION); CAutoFile filein = CAutoFile(OpenBlockFile(pos.blockPos, pfileRet ? "rb+" : "rb"), SER_DISK, CLIENT_VERSION);
if (!filein) if (!filein)
return error("CTransaction::ReadFromDisk() : OpenBlockFile failed"); return error("CTransaction::ReadFromDisk() : OpenBlockFile failed");
@ -1215,22 +1298,15 @@ public:
} }
bool WriteToDisk(unsigned int& nFileRet, unsigned int& nBlockPosRet) bool WriteToDisk(CDiskBlockPos &pos)
{ {
// Open history file to append // Open history file to append
CAutoFile fileout = CAutoFile(AppendBlockFile(nFileRet), SER_DISK, CLIENT_VERSION); pos.MakeUnique(GetDataDir());
CAutoFile fileout = CAutoFile(OpenBlockFile(pos, "ab"), SER_DISK, CLIENT_VERSION);
if (!fileout) if (!fileout)
return error("CBlock::WriteToDisk() : AppendBlockFile failed"); return error("CBlock::WriteToDisk() : AppendBlockFile failed");
// Write index header
unsigned int nSize = fileout.GetSerializeSize(*this);
fileout << FLATDATA(pchMessageStart) << nSize;
// Write block // Write block
long fileOutPos = ftell(fileout);
if (fileOutPos < 0)
return error("CBlock::WriteToDisk() : ftell failed");
nBlockPosRet = fileOutPos;
fileout << *this; fileout << *this;
// Flush stdio buffers and commit to disk before returning // Flush stdio buffers and commit to disk before returning
@ -1241,12 +1317,12 @@ public:
return true; return true;
} }
bool ReadFromDisk(unsigned int nFile, unsigned int nBlockPos, bool fReadTransactions=true) bool ReadFromDisk(const CDiskBlockPos &pos, bool fReadTransactions = true)
{ {
SetNull(); SetNull();
// Open history file to read // Open history file to read
CAutoFile filein = CAutoFile(OpenBlockFile(nFile, nBlockPos, "rb"), SER_DISK, CLIENT_VERSION); CAutoFile filein = CAutoFile(OpenBlockFile(pos, "rb"), SER_DISK, CLIENT_VERSION);
if (!filein) if (!filein)
return error("CBlock::ReadFromDisk() : OpenBlockFile failed"); return error("CBlock::ReadFromDisk() : OpenBlockFile failed");
if (!fReadTransactions) if (!fReadTransactions)
@ -1294,7 +1370,7 @@ public:
bool ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck=false); bool ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck=false);
bool ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions=true); bool ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions=true);
bool SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew); bool SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew);
bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos); bool AddToBlockIndex(const CDiskBlockPos &pos);
bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true) const; bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true) const;
bool AcceptBlock(); bool AcceptBlock();
@ -1320,9 +1396,8 @@ public:
const uint256* phashBlock; const uint256* phashBlock;
CBlockIndex* pprev; CBlockIndex* pprev;
CBlockIndex* pnext; CBlockIndex* pnext;
unsigned int nFile;
unsigned int nBlockPos;
int nHeight; int nHeight;
unsigned int nAlternative;
CBigNum bnChainWork; CBigNum bnChainWork;
// block header // block header
@ -1338,10 +1413,9 @@ public:
phashBlock = NULL; phashBlock = NULL;
pprev = NULL; pprev = NULL;
pnext = NULL; pnext = NULL;
nFile = 0;
nBlockPos = 0;
nHeight = 0; nHeight = 0;
bnChainWork = 0; bnChainWork = 0;
nAlternative = 0;
nVersion = 0; nVersion = 0;
hashMerkleRoot = 0; hashMerkleRoot = 0;
@ -1350,15 +1424,14 @@ public:
nNonce = 0; nNonce = 0;
} }
CBlockIndex(unsigned int nFileIn, unsigned int nBlockPosIn, CBlock& block) CBlockIndex(CBlock& block)
{ {
phashBlock = NULL; phashBlock = NULL;
pprev = NULL; pprev = NULL;
pnext = NULL; pnext = NULL;
nFile = nFileIn;
nBlockPos = nBlockPosIn;
nHeight = 0; nHeight = 0;
bnChainWork = 0; bnChainWork = 0;
nAlternative = 0;
nVersion = block.nVersion; nVersion = block.nVersion;
hashMerkleRoot = block.hashMerkleRoot; hashMerkleRoot = block.hashMerkleRoot;
@ -1367,6 +1440,10 @@ public:
nNonce = block.nNonce; nNonce = block.nNonce;
} }
CDiskBlockPos GetBlockPos() const {
return CDiskBlockPos(nHeight, nAlternative);
}
CBlock GetBlockHeader() const CBlock GetBlockHeader() const
{ {
CBlock block; CBlock block;
@ -1444,11 +1521,10 @@ public:
static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart,
unsigned int nRequired, unsigned int nToCheck); unsigned int nRequired, unsigned int nToCheck);
std::string ToString() const std::string ToString() const
{ {
return strprintf("CBlockIndex(pprev=%p, pnext=%p, nFile=%u, nBlockPos=%-6u nHeight=%d, merkle=%s, hashBlock=%s)", return strprintf("CBlockIndex(pprev=%p, pnext=%p, nHeight=%d, merkle=%s, hashBlock=%s)",
pprev, pnext, nFile, nBlockPos, nHeight, pprev, pnext, nHeight,
hashMerkleRoot.ToString().substr(0,10).c_str(), hashMerkleRoot.ToString().substr(0,10).c_str(),
GetBlockHash().ToString().substr(0,20).c_str()); GetBlockHash().ToString().substr(0,20).c_str());
} }
@ -1486,9 +1562,8 @@ public:
READWRITE(nVersion); READWRITE(nVersion);
READWRITE(hashNext); READWRITE(hashNext);
READWRITE(nFile);
READWRITE(nBlockPos);
READWRITE(nHeight); READWRITE(nHeight);
READWRITE(nAlternative);
// block header // block header
READWRITE(this->nVersion); READWRITE(this->nVersion);