虽然首先使用win10的linux layer在Windows上先编译并成功运行了bitcoin core,但是对于更习惯VS开发工具的人来说,总是在VS下跟踪调试更顺手,因此准备建立一个VS工程来编译bitcoin,并逐步学习了解它的实现比特币存盘文件blkxxxx.dat格式
为了便于VS上编译运行,先从存盘的数据文件开始,这部分涉及的代码应比较容易在VS上运行起来。
工程建立步骤如下:
#define MESSAGE_START_SIZE 4
unsigned char pchMessageStart[MESSAGE_START_SIZE] = { 0xf9, 0xbe, 0xb4, 0xd9 };
bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp)
{
int nLoaded = 0;
try {
// This takes over fileIn and calls fclose() on it in the CBufferedFile destructor
CBufferedFile blkdat(fileIn, 2 * MAX_BLOCK_SERIALIZED_SIZE, MAX_BLOCK_SERIALIZED_SIZE + 8, SER_DISK, CLIENT_VERSION);
uint64_t nRewind = blkdat.GetPos();
while (!blkdat.eof()) {
blkdat.SetPos(nRewind);
nRewind++; // start one byte further next time, in case of failure
blkdat.SetLimit(); // remove former limit
unsigned int nSize = 0;
try {
// locate a header
unsigned char buf[MESSAGE_START_SIZE];
blkdat.FindByte(pchMessageStart[0]);
nRewind = blkdat.GetPos() + 1;
blkdat >> FLATDATA(buf);
if (memcmp(buf, pchMessageStart, MESSAGE_START_SIZE))
continue;
// read size
blkdat >> nSize;
if (nSize < 80 || nSize > MAX_BLOCK_SERIALIZED_SIZE)
continue;
}
catch (const std::exception&) {
// no valid block header found; don't complain
break;
}
try {
// read block
uint64_t nBlockPos = blkdat.GetPos();
if (dbp)
dbp->nPos = nBlockPos;
blkdat.SetLimit(nBlockPos + nSize);
blkdat.SetPos(nBlockPos);
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
CBlock& block = *pblock;
blkdat >> block;
nRewind = blkdat.GetPos();
printf("%s\n\n", block.ToString().c_str());
}
catch (const std::exception& e) {
printf("%s: Deserialize or I/O error - %s\n", __func__, e.what());
}
nLoaded++;
}
}
catch (const std::runtime_error& e) {
printf("System error: %s\n", e.what());
}
if (nLoaded > 0)
printf("Loaded %i blocks from external file\n", nLoaded);
return nLoaded > 0;
}
这个函数的主体内容是从比特币的验证代码部分抄的,之所以抄是因为一旦引入验证代码,整个工程会复杂很多。
int main(void)
{
// Todo: Change to the real path where you store the bitcoin block file
char path[] = "G:\\blockchain\\bitcoin\\bin\\Bitcoin\\blocks\\blk00000.dat";
FILE *file = fsbridge::fopen(path, "rb");
LoadExternalBlockFile(file, NULL);
return 0;
}
这里我偷懒了,直接写死了一个存盘的block文件路径,反正只是为了学习嘛。
到这里,工程文件都有了,但是编译还有问题,因为比特币的源代码有依赖。得益于vs集成的nuget包管理工具,可以不用自己下载依赖的源代码编译(也是累死人的工作哦,开源代码在vs下的编译都很烦)。
解决方案处点击鼠标右键,在弹出的菜单中选择“管理解决方案的NuGet程序包”
搜索boost_filesystem-vc141及boost_system-vc141,选择
写本文时此错误的修复已经提交,但是仍未被接受,在被接受后再下载源代码将不用修改。
至此,就可以跟踪调试来看整个文件的格式了!
字节数 | 内容 | 说明 |
---|---|---|
4字节 | 0xF9 0xBE 0xB4 0xD9 | 所谓的Magic,用于确认block开始 |
4字节 | Block Length Little-Endian | Little-Endian的unsigned int |
Block Length | CBlock类序列化后的数据 | CBlock类定义在block.h文件中 |
字节数 | 内容 | 说明 |
---|---|---|
4字节 | 版本号 | Little-Endian的int32_t |
32字节 | 前一个block的hash值 | Little-Endian的uint256 |
32字节 | MerkleRoot | Little-Endian的uint256 |
4字节 | 时间戳 | 块生成时的网络时间 |
4字节 | 目标值 | 当前区块生成所达成目标值的特征 |
4字节 | 随机数 | 用于挖矿时生成符合要求的块哈希 |
紧缩格式整数的读取,在serialize.h的ReadCompactSize模板中,其规则为若第一个字节是255,则读取后续8字节作为uint64;若第一字节是254,则读取后续4字节作为uint32;若第一字节是253,则读取后续2字节作为uint16;否则将第一个字节作为uint8。
因此,这是一个可变长度的数据,最小占用1字节,最大占用9字节。
【原创首发地址:http://http://blog.csdn.net/lazypiggy/article/details/79414709,转发请保留此链接】