Bitcoinがブロックデータを保存する方法を理解しようとしている——とりわけブロックチェーン/トランザクション履歴の統計を実行し、Bitcoinが実際にどの程度匿名なのかを確認したい。そこでBitcoinがブロックデータをファイルに読み書きする方法をソースで確認した。
(補足:これは0.3.2のものだ)
main.hには以下がある:
Code: (CBlock::WriteToDisk)bool WriteToDisk(bool fWriteTransactions, unsigned int& nFileRet, unsigned int& nBlockPosRet) { // Open history file to append CAutoFile fileout = AppendBlockFile(nFileRet); if (!fileout) return error(“CBlock::WriteToDisk() : AppendBlockFile failed”); if (!fWriteTransactions) fileout.nType |= SER_BLOCKHEADERONLY;
// Write index header
unsigned int nSize = fileout.GetSerializeSize(*this);
fileout << FLATDATA(pchMessageStart) << nSize;
// Write block
nBlockPosRet = ftell(fileout);
if (nBlockPosRet == -1)
return error("CBlock::WriteToDisk() : ftell failed");
fileout << *this;
// Flush stdio buffers and commit to disk before returning
fflush(fileout);
#ifdef WXMSW _commit(_fileno(fileout)); #else fsync(fileno(fileout)); #endif
return true;
}
そして
Code: (CBlock::ReadFromDisk)bool ReadFromDisk(unsigned int nFile, unsigned int nBlockPos, bool fReadTransactions=true) { SetNull();
// Open history file to read
CAutoFile filein = OpenBlockFile(nFile, nBlockPos, "rb");
if (!filein)
return error("CBlock::ReadFromDisk() : OpenBlockFile failed");
if (!fReadTransactions)
filein.nType |= SER_BLOCKHEADERONLY;
// Read block
filein >> *this;
// Check the header
if (CBigNum().SetCompact(nBits) > bnProofOfWorkLimit)
return error("CBlock::ReadFromDisk() : nBits errors in block header");
if (GetHash() > CBigNum().SetCompact(nBits).getuint256())
return error("CBlock::ReadFromDisk() : GetHash() errors in block header");
return true;
}
FLATDATAはserialize.hで以下のように定義されている: Code: (FLATDATA)#define FLATDATA(obj) REF(CFlatData((char*)&(obj), (char*)&(obj) + sizeof(obj)))
さて——読み間違いなら訂正してほしいが——私の理解では、FLATDATAの呼び出しはCBlockオブジェクトの生バイトを文字の配列として解釈する。CBlock::WriteToDiskメソッドは定数4バイトのメッセージヘッダー(0xf9, 0xbe, 0xb4, 0xd9)、CBlockオブジェクトのバイトサイズ、そしてCBlockのFLATDATA——つまりCBlockオブジェクトの生バイトそのもの——をディスクに書き込む。したがってヘッダーの後、ファイルに書き込まれるデータはメモリ上のCBlockオブジェクトとバイト単位で同一となる。また、正しく読んでいれば、CBlock::ReadFromFileはそれらのバイトをメモリ上のCBlockオブジェクト用に確保された領域に直接コピーしてブロックを再生成する。これは正しいだろうか?
関連する質問——C++クラスのインスタンスが内部的にどう表現されるかは標準では保証されていないという印象がある。異なるコンパイラや異なる最適化フラグでプログラムをコンパイルすると、メンバー変数がメモリに格納される順序が変わる可能性があり、一部のデバッグモードコンパイラはメモリ検査を容易にするためにメンバー変数間に数バイトを追加することさえある。これについて確信は持てない、ただ聞きかじっただけで真剣に疑問を持ったことはない。