Multiple blocks per file

Change the block storage layer again, this time with multiple files
per block, but tracked by txindex.dat database entries. The file
format is exactly the same as the earlier blk00001.dat, but with
smaller files (128 MiB for now).

The database entries track how many bytes each block file already
uses, how many blocks are in it, which range of heights is present
and which range of dates.
This commit is contained in:
Pieter Wuille 2012-08-13 19:11:05 +02:00
parent 8adf48dc9b
commit 5382bcf8cd
4 changed files with 265 additions and 102 deletions

View File

@ -546,6 +546,22 @@ bool CTxDB::WriteBlockIndex(const CDiskBlockIndex& blockindex)
return Write(make_pair(string("blockindex"), blockindex.GetBlockHash()), blockindex); return Write(make_pair(string("blockindex"), blockindex.GetBlockHash()), blockindex);
} }
bool CTxDB::WriteBlockFileInfo(int nFile, const CBlockFileInfo &info) {
return Write(make_pair(string("blockfile"), nFile), info);
}
bool CTxDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) {
return Read(make_pair(string("blockfile"), nFile), info);
}
bool CTxDB::WriteLastBlockFile(int nFile) {
return Write(string("lastblockfile"), nFile);
}
bool CTxDB::ReadLastBlockFile(int &nFile) {
return Read(string("lastblockfile"), nFile);
}
bool CTxDB::ReadHashBestChain(uint256& hashBestChain) bool CTxDB::ReadHashBestChain(uint256& hashBestChain)
{ {
return Read(string("hashBestChain"), hashBestChain); return Read(string("hashBestChain"), hashBestChain);
@ -609,6 +625,12 @@ bool CTxDB::LoadBlockIndex()
pindex->bnChainWork = (pindex->pprev ? pindex->pprev->bnChainWork : 0) + pindex->GetBlockWork(); pindex->bnChainWork = (pindex->pprev ? pindex->pprev->bnChainWork : 0) + pindex->GetBlockWork();
} }
// Load block file info
ReadLastBlockFile(nLastBlockFile);
printf("LoadBlockIndex(): last block file = %i\n", nLastBlockFile);
if (ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile))
printf("LoadBlockIndex(): last block file: %s\n", infoLastBlockFile.ToString().c_str());
// Load hashBestChain pointer to end of best chain // Load hashBestChain pointer to end of best chain
if (!ReadHashBestChain(hashBestChain)) if (!ReadHashBestChain(hashBestChain))
{ {
@ -788,7 +810,8 @@ bool CTxDB::LoadBlockIndexGuts()
pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev);
pindexNew->pnext = InsertBlockIndex(diskindex.hashNext); pindexNew->pnext = InsertBlockIndex(diskindex.hashNext);
pindexNew->nHeight = diskindex.nHeight; pindexNew->nHeight = diskindex.nHeight;
pindexNew->nAlternative = diskindex.nAlternative; pindexNew->pos = diskindex.pos;
pindexNew->nUndoPos = diskindex.nUndoPos;
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

@ -339,6 +339,10 @@ public:
bool WriteHashBestChain(uint256 hashBestChain); bool WriteHashBestChain(uint256 hashBestChain);
bool ReadBestInvalidWork(CBigNum& bnBestInvalidWork); bool ReadBestInvalidWork(CBigNum& bnBestInvalidWork);
bool WriteBestInvalidWork(CBigNum bnBestInvalidWork); bool WriteBestInvalidWork(CBigNum bnBestInvalidWork);
bool ReadBlockFileInfo(int nFile, CBlockFileInfo &fileinfo);
bool WriteBlockFileInfo(int nFile, const CBlockFileInfo &fileinfo);
bool ReadLastBlockFile(int &nFile);
bool WriteLastBlockFile(int nFile);
bool LoadBlockIndex(); bool LoadBlockIndex();
private: private:
bool LoadBlockIndexGuts(); bool LoadBlockIndexGuts();

View File

@ -1399,6 +1399,8 @@ bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex)
return true; return true;
} }
bool FindUndoPos(CTxDB &txdb, int nFile, CDiskBlockPos &pos, unsigned int nAddSize);
bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
{ {
// Check it again in case a previous version let a bad block in // Check it again in case a previous version let a bad block in
@ -1430,7 +1432,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
// 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 = ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - 1 + GetSizeOfCompactSize(vtx.size()); nTxPos = pindex->pos.nPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - 1 + GetSizeOfCompactSize(vtx.size());
CBlockUndo blockundo; CBlockUndo blockundo;
@ -1507,6 +1509,17 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
return error("ConnectBlock() : UpdateTxIndex failed"); return error("ConnectBlock() : UpdateTxIndex failed");
} }
// Write undo information to disk
if (pindex->GetUndoPos().IsNull() && pindex->nHeight > Checkpoints::GetTotalBlocksEstimate())
{
CDiskBlockPos pos;
if (!FindUndoPos(txdb, 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");
pindex->nUndoPos = pos.nPos;
}
// Update block index on disk without changing it in memory. // Update block index on disk without changing it in memory.
// The memory index structure will be changed after the db commits. // The memory index structure will be changed after the db commits.
if (pindex->pprev) if (pindex->pprev)
@ -1517,13 +1530,6 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck)
return error("ConnectBlock() : WriteBlockIndex failed"); return error("ConnectBlock() : WriteBlockIndex failed");
} }
// Write undo information to disk
if (pindex->nHeight > Checkpoints::GetTotalBlocksEstimate())
{
CAutoFile fileUndo(fopen(pindex->GetBlockPos().GetUndoFile(GetDataDir()).string().c_str(), "wb"), SER_DISK, CLIENT_VERSION);
fileUndo << blockundo;
}
// Watch for transactions paying to me // Watch for transactions paying to me
BOOST_FOREACH(CTransaction& tx, vtx) BOOST_FOREACH(CTransaction& tx, vtx)
SyncWithWallets(tx, this, true); SyncWithWallets(tx, this, true);
@ -1788,8 +1794,8 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos)
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->pos = pos;
pindexNew->nAlternative = pos.nAlternative; pindexNew->nUndoPos = 0;
CTxDB txdb; CTxDB txdb;
if (!txdb.TxnBegin()) if (!txdb.TxnBegin())
@ -1819,6 +1825,57 @@ bool CBlock::AddToBlockIndex(const CDiskBlockPos &pos)
bool FindBlockPos(CTxDB &txdb, CDiskBlockPos &pos, unsigned int nAddSize, unsigned int nHeight, uint64 nTime)
{
bool fUpdatedLast = false;
LOCK(cs_LastBlockFile);
while (infoLastBlockFile.nSize + nAddSize >= MAX_BLOCKFILE_SIZE) {
printf("Leaving block file %i: %s\n", nLastBlockFile, infoLastBlockFile.ToString().c_str());
nLastBlockFile++;
infoLastBlockFile.SetNull();
txdb.ReadBlockFileInfo(nLastBlockFile, infoLastBlockFile); // check whether data for the new file somehow already exist; can fail just fine
fUpdatedLast = true;
}
pos.nFile = nLastBlockFile;
pos.nPos = infoLastBlockFile.nSize;
infoLastBlockFile.nSize += nAddSize;
infoLastBlockFile.AddBlock(nHeight, nTime);
if (!txdb.WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
return error("FindBlockPos() : cannot write updated block info");
if (fUpdatedLast)
txdb.WriteLastBlockFile(nLastBlockFile);
return true;
}
bool FindUndoPos(CTxDB &txdb, int nFile, CDiskBlockPos &pos, unsigned int nAddSize)
{
pos.nFile = nFile;
LOCK(cs_LastBlockFile);
if (nFile == nLastBlockFile) {
pos.nPos = infoLastBlockFile.nUndoSize;
infoLastBlockFile.nUndoSize += nAddSize;
if (!txdb.WriteBlockFileInfo(nLastBlockFile, infoLastBlockFile))
return error("FindUndoPos() : cannot write updated block info");
return true;
}
CBlockFileInfo info;
if (!txdb.ReadBlockFileInfo(nFile, info))
return error("FindUndoPos() : cannot read block info");
pos.nPos = info.nUndoSize;
info.nUndoSize += nAddSize;
if (!txdb.WriteBlockFileInfo(nFile, info))
return error("FindUndoPos() : cannot write updated block info");
return true;
}
bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const
{ {
@ -1928,9 +1985,15 @@ bool CBlock::AcceptBlock()
} }
// Write block to history file // Write block to history file
CDiskBlockPos blockPos = CDiskBlockPos(nHeight); unsigned int nBlockSize = ::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION);
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");
CDiskBlockPos blockPos;
{
CTxDB txdb;
if (!FindBlockPos(txdb, blockPos, nBlockSize+8, nHeight, nTime))
return error("AcceptBlock() : FindBlockPos failed");
}
if (!WriteToDisk(blockPos)) if (!WriteToDisk(blockPos))
return error("AcceptBlock() : WriteToDisk failed"); return error("AcceptBlock() : WriteToDisk failed");
if (!AddToBlockIndex(blockPos)) if (!AddToBlockIndex(blockPos))
@ -2067,18 +2130,39 @@ bool CheckDiskSpace(uint64 nAdditionalBytes)
return true; return true;
} }
FILE* OpenBlockFile(const CDiskBlockPos &pos, const char* pszMode) CCriticalSection cs_LastBlockFile;
CBlockFileInfo infoLastBlockFile;
int nLastBlockFile = 0;
FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly)
{ {
boost::filesystem::path path = pos.GetFileName(GetDataDir());
boost::filesystem::create_directories(path.parent_path());
if (pos.IsNull() || pos.IsMemPool()) if (pos.IsNull() || pos.IsMemPool())
return NULL; return NULL;
FILE* file = fopen(path.string().c_str(), pszMode); boost::filesystem::path path = GetDataDir() / "blocks" / strprintf("%s%05u.dat", prefix, pos.nFile);
boost::filesystem::create_directories(path.parent_path());
FILE* file = fopen(path.string().c_str(), "rb+");
if (!file && !fReadOnly)
file = fopen(path.string().c_str(), "wb+");
if (!file) if (!file)
return NULL; return NULL;
if (pos.nPos) {
if (fseek(file, pos.nPos, SEEK_SET)) {
printf("Unable to seek to position %u of %s\n", pos.nPos, path.string().c_str());
fclose(file);
return NULL;
}
}
return file; return file;
} }
FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly) {
return OpenDiskFile(pos, "blk", fReadOnly);
}
FILE *OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) {
return OpenDiskFile(pos, "rev", fReadOnly);
}
bool LoadBlockIndex(bool fAllowNew) bool LoadBlockIndex(bool fAllowNew)
{ {
if (fTestNet) if (fTestNet)
@ -2146,7 +2230,13 @@ bool LoadBlockIndex(bool fAllowNew)
assert(hash == hashGenesisBlock); assert(hash == hashGenesisBlock);
// Start new block file // Start new block file
CDiskBlockPos blockPos(0); unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION);
CDiskBlockPos blockPos;
{
CTxDB txdb;
if (!FindBlockPos(txdb, blockPos, nBlockSize+8, 0, block.nTime))
return error("AcceptBlock() : FindBlockPos failed");
}
if (!block.WriteToDisk(blockPos)) if (!block.WriteToDisk(blockPos))
return error("LoadBlockIndex() : writing genesis block to disk failed"); return error("LoadBlockIndex() : writing genesis block to disk failed");
if (!block.AddToBlockIndex(blockPos)) if (!block.AddToBlockIndex(blockPos))
@ -2203,9 +2293,9 @@ void PrintBlockTree()
// print item // print item
CBlock block; CBlock block;
block.ReadFromDisk(pindex); block.ReadFromDisk(pindex);
printf("%d (%s) %s tx %"PRIszu"", printf("%d (blk%05u.dat:0x%lx) %s tx %"PRIszu"",
pindex->nHeight, pindex->nHeight,
pindex->GetBlockPos().GetFileName("").string().c_str(), pindex->GetBlockPos().nFile, pindex->GetBlockPos().nPos,
DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()).c_str(), DateTimeStrFormat("%x %H:%M:%S", block.GetBlockTime()).c_str(),
block.vtx.size()); block.vtx.size());

View File

@ -28,6 +28,7 @@ static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2;
static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50; static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50;
static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100; static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100;
static const unsigned int MAX_INV_SZ = 50000; static const unsigned int MAX_INV_SZ = 50000;
static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
static const int64 MIN_TX_FEE = 50000; static const int64 MIN_TX_FEE = 50000;
static const int64 MIN_RELAY_TX_FEE = 10000; static const int64 MIN_RELAY_TX_FEE = 10000;
static const int64 MAX_MONEY = 21000000 * COIN; static const int64 MAX_MONEY = 21000000 * COIN;
@ -87,7 +88,8 @@ 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(const CDiskBlockPos &pos, const char* pszMode="rb"); FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false);
FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false);
bool LoadBlockIndex(bool fAllowNew=true); bool LoadBlockIndex(bool fAllowNew=true);
void PrintBlockTree(); void PrintBlockTree();
CBlockIndex* FindBlockByHeight(int nHeight); CBlockIndex* FindBlockByHeight(int nHeight);
@ -121,86 +123,27 @@ bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut);
class CDiskBlockPos class CDiskBlockPos
{ {
public: public:
int nHeight; int nFile;
int nAlternative; unsigned int nPos;
CDiskBlockPos() { IMPLEMENT_SERIALIZE(
SetNull(); READWRITE(VARINT(nFile));
} READWRITE(VARINT(nPos));
)
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());
}
boost::filesystem::path GetUndoFile(const boost::filesystem::path &base) const {
return GetDirectory(base) / strprintf("%08u%s.und", 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) { friend bool operator==(const CDiskBlockPos &a, const CDiskBlockPos &b) {
return ((a.nHeight == b.nHeight) && (a.nAlternative == b.nAlternative)); return (a.nFile == b.nFile && a.nPos == b.nPos);
} }
friend bool operator!=(const CDiskBlockPos &a, const CDiskBlockPos &b) { friend bool operator!=(const CDiskBlockPos &a, const CDiskBlockPos &b) {
return !(a == b); return !(a == b);
} }
void SetNull() { nHeight = -1; nAlternative = 0; } void SetNull() { nFile = -1; nPos = 0; }
bool IsNull() const { return ((nHeight == -1) && (nAlternative == 0)); } bool IsNull() const { return (nFile == -1); }
void SetMemPool() { nHeight = -1; nAlternative = -1; } void SetMemPool() { nFile = -2; nPos = 0; }
bool IsMemPool() const { return ((nHeight == -1) && (nAlternative == -1)); } bool IsMemPool() const { return (nFile == -2); }
}; };
/** Position on disk for a particular transaction. */ /** Position on disk for a particular transaction. */
@ -225,7 +168,7 @@ public:
) )
void SetNull() { blockPos.SetNull(); nTxPos = 0; } void SetNull() { blockPos.SetNull(); nTxPos = 0; }
bool IsNull() const { return blockPos.IsNull(); } bool IsNull() const { return (nTxPos == 0); }
bool IsMemPool() const { return blockPos.IsMemPool(); } bool IsMemPool() const { return blockPos.IsMemPool(); }
friend bool operator==(const CDiskTxPos& a, const CDiskTxPos& b) friend bool operator==(const CDiskTxPos& a, const CDiskTxPos& b)
@ -246,7 +189,7 @@ public:
else if (blockPos.IsMemPool()) else if (blockPos.IsMemPool())
return "mempool"; return "mempool";
else else
return strprintf("(%s, nTxPos=%u)", blockPos.GetFileName("").string().c_str(), nTxPos); return strprintf("\"blk%05i.dat:0x%x\"", blockPos.nFile, nTxPos);
} }
void print() const void print() const
@ -632,7 +575,7 @@ public:
bool ReadFromDisk(CDiskTxPos pos, FILE** pfileRet=NULL) bool ReadFromDisk(CDiskTxPos pos, FILE** pfileRet=NULL)
{ {
CAutoFile filein = CAutoFile(OpenBlockFile(pos.blockPos, pfileRet ? "rb+" : "rb"), SER_DISK, CLIENT_VERSION); CAutoFile filein = CAutoFile(OpenBlockFile(pos.blockPos, pfileRet==NULL), SER_DISK, CLIENT_VERSION);
if (!filein) if (!filein)
return error("CTransaction::ReadFromDisk() : OpenBlockFile failed"); return error("CTransaction::ReadFromDisk() : OpenBlockFile failed");
@ -823,6 +766,33 @@ public:
IMPLEMENT_SERIALIZE( IMPLEMENT_SERIALIZE(
READWRITE(vtxundo); READWRITE(vtxundo);
) )
bool WriteToDisk(CDiskBlockPos &pos)
{
// Open history file to append
CAutoFile fileout = CAutoFile(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION);
if (!fileout)
return error("CBlockUndo::WriteToDisk() : OpenUndoFile failed");
// Write index header
unsigned int nSize = fileout.GetSerializeSize(*this);
fileout << FLATDATA(pchMessageStart) << nSize;
// Write undo data
long fileOutPos = ftell(fileout);
if (fileOutPos < 0)
return error("CBlock::WriteToDisk() : ftell failed");
pos.nPos = (unsigned int)fileOutPos;
fileout << *this;
// Flush stdio buffers and commit to disk before returning
fflush(fileout);
if (!IsInitialBlockDownload() || (nBestHeight+1) % 500 == 0)
FileCommit(fileout);
return true;
}
}; };
/** pruned version of CTransaction: only retains metadata and unspent transaction outputs /** pruned version of CTransaction: only retains metadata and unspent transaction outputs
@ -1316,12 +1286,19 @@ public:
bool WriteToDisk(CDiskBlockPos &pos) bool WriteToDisk(CDiskBlockPos &pos)
{ {
// Open history file to append // Open history file to append
pos.MakeUnique(GetDataDir()); CAutoFile fileout = CAutoFile(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION);
CAutoFile fileout = CAutoFile(OpenBlockFile(pos, "ab"), SER_DISK, CLIENT_VERSION);
if (!fileout) if (!fileout)
return error("CBlock::WriteToDisk() : AppendBlockFile failed"); return error("CBlock::WriteToDisk() : OpenBlockFile 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");
pos.nPos = (unsigned int)fileOutPos;
fileout << *this; fileout << *this;
// Flush stdio buffers and commit to disk before returning // Flush stdio buffers and commit to disk before returning
@ -1337,7 +1314,7 @@ public:
SetNull(); SetNull();
// Open history file to read // Open history file to read
CAutoFile filein = CAutoFile(OpenBlockFile(pos, "rb"), SER_DISK, CLIENT_VERSION); CAutoFile filein = CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION);
if (!filein) if (!filein)
return error("CBlock::ReadFromDisk() : OpenBlockFile failed"); return error("CBlock::ReadFromDisk() : OpenBlockFile failed");
if (!fReadTransactions) if (!fReadTransactions)
@ -1397,6 +1374,62 @@ private:
class CBlockFileInfo
{
public:
unsigned int nBlocks; // number of blocks stored in file
unsigned int nSize; // number of used bytes of block file
unsigned int nUndoSize; // number of used bytes in the undo file
unsigned int nHeightFirst; // lowest height of block in file
unsigned int nHeightLast; // highest height of block in file
uint64 nTimeFirst; // earliest time of block in file
uint64 nTimeLast; // latest time of block in file
IMPLEMENT_SERIALIZE(
READWRITE(VARINT(nBlocks));
READWRITE(VARINT(nSize));
READWRITE(VARINT(nUndoSize));
READWRITE(VARINT(nHeightFirst));
READWRITE(VARINT(nHeightLast));
READWRITE(VARINT(nTimeFirst));
READWRITE(VARINT(nTimeLast));
)
void SetNull() {
nBlocks = 0;
nSize = 0;
nUndoSize = 0;
nHeightFirst = 0;
nHeightLast = 0;
nTimeFirst = 0;
nTimeLast = 0;
}
CBlockFileInfo() {
SetNull();
}
std::string ToString() const {
return strprintf("CBlockFileInfo(blocks=%u, size=%lu, heights=%u..%u, time=%s..%s)", nBlocks, nSize, nHeightFirst, nHeightLast, DateTimeStrFormat("%Y-%m-%d", nTimeFirst).c_str(), DateTimeStrFormat("%Y-%m-%d", nTimeLast).c_str());
}
// update statistics (does not update nSize)
void AddBlock(unsigned int nHeightIn, uint64 nTimeIn) {
if (nBlocks==0 || nHeightFirst > nHeightIn)
nHeightFirst = nHeightIn;
if (nBlocks==0 || nTimeFirst > nTimeIn)
nTimeFirst = nTimeIn;
nBlocks++;
if (nHeightIn > nHeightFirst)
nHeightLast = nHeightIn;
if (nTimeIn > nTimeLast)
nTimeLast = nTimeIn;
}
};
extern CCriticalSection cs_LastBlockFile;
extern CBlockFileInfo infoLastBlockFile;
extern int nLastBlockFile;
/** The block chain is a tree shaped structure starting with the /** The block chain is a tree shaped structure starting with the
* genesis block at the root, with each block potentially having multiple * genesis block at the root, with each block potentially having multiple
@ -1412,7 +1445,8 @@ public:
CBlockIndex* pprev; CBlockIndex* pprev;
CBlockIndex* pnext; CBlockIndex* pnext;
int nHeight; int nHeight;
unsigned int nAlternative; CDiskBlockPos pos;
unsigned int nUndoPos;
CBigNum bnChainWork; CBigNum bnChainWork;
// block header // block header
@ -1429,8 +1463,9 @@ public:
pprev = NULL; pprev = NULL;
pnext = NULL; pnext = NULL;
nHeight = 0; nHeight = 0;
pos.SetNull();
nUndoPos = (unsigned int)(-1);
bnChainWork = 0; bnChainWork = 0;
nAlternative = 0;
nVersion = 0; nVersion = 0;
hashMerkleRoot = 0; hashMerkleRoot = 0;
@ -1445,8 +1480,9 @@ public:
pprev = NULL; pprev = NULL;
pnext = NULL; pnext = NULL;
nHeight = 0; nHeight = 0;
pos.SetNull();
nUndoPos = 0;
bnChainWork = 0; bnChainWork = 0;
nAlternative = 0;
nVersion = block.nVersion; nVersion = block.nVersion;
hashMerkleRoot = block.hashMerkleRoot; hashMerkleRoot = block.hashMerkleRoot;
@ -1456,7 +1492,16 @@ public:
} }
CDiskBlockPos GetBlockPos() const { CDiskBlockPos GetBlockPos() const {
return CDiskBlockPos(nHeight, nAlternative); return pos;
}
CDiskBlockPos GetUndoPos() const {
CDiskBlockPos ret = pos;
if (nUndoPos == (unsigned int)(-1))
ret.SetNull();
else
ret.nPos = nUndoPos;
return ret;
} }
CBlock GetBlockHeader() const CBlock GetBlockHeader() const
@ -1578,7 +1623,8 @@ public:
READWRITE(hashNext); READWRITE(hashNext);
READWRITE(nHeight); READWRITE(nHeight);
READWRITE(nAlternative); READWRITE(pos);
READWRITE(nUndoPos);
// block header // block header
READWRITE(this->nVersion); READWRITE(this->nVersion);