bitcoin所有得交易信息都以block的形式存放在文件中,存访文件名为blkxxxxx.dat,文件存放在blocks目录下。如测试网的block文件存放在testnet3\blocks目录下。
首先,设置当前数据主目录,
gArgs.SoftSetArg("-datadir",PATH);
其中PATH为主目录,可使用函数
GetDataDir()
来查看当前数据路径。
使用结构体CDiskBlockPos来存访block文件所在位置,其定义如下:
struct CDiskBlockPos
{
int nFile; // blk文件编号,如129,表示blk000129.dat
unsigned int nPos; // 文件指针偏移值
ADD_SERIALIZE_METHODS;
template
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(VARINT(nFile));
READWRITE(VARINT(nPos));
}
CDiskBlockPos() {
SetNull();
}
CDiskBlockPos(int nFileIn, unsigned int nPosIn) {
nFile = nFileIn;
nPos = nPosIn;
}
friend bool operator==(const CDiskBlockPos &a, const CDiskBlockPos &b) {
return (a.nFile == b.nFile && a.nPos == b.nPos);
}
friend bool operator!=(const CDiskBlockPos &a, const CDiskBlockPos &b) {
return !(a == b);
}
void SetNull() { nFile = -1; nPos = 0; }
bool IsNull() const { return (nFile == -1); }
std::string ToString() const
{
return strprintf("CBlockDiskPos(nFile=%i, nPos=%i)", nFile, nPos);
}
};
使用函数OpenBlockFile打开区块文件,定义如下:
/** Open a block file (blk?????.dat) */
FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false);
打开成功后,返回一个文件指针。
使用类CBufferedFile来读取文件内容,定义如下:
/** Non-refcounted RAII wrapper around a FILE* that implements a ring buffer to
* deserialize from. It guarantees the ability to rewind a given number of bytes.
*
* Will automatically close the file when it goes out of scope if not null.
* If you need to close the file early, use file.fclose() instead of fclose(file).
*/
class CBufferedFile
{
private:
const int nType;
const int nVersion;
FILE *src; // source file
uint64_t nSrcPos; // how many bytes have been read from source
uint64_t nReadPos; // how many bytes have been read from this
uint64_t nReadLimit; // up to which position we're allowed to read
uint64_t nRewind; // how many bytes we guarantee to rewind
std::vector vchBuf; // the buffer
protected:
// read data from the source to fill the buffer
bool Fill() {
unsigned int pos = nSrcPos % vchBuf.size();
unsigned int readNow = vchBuf.size() - pos;
unsigned int nAvail = vchBuf.size() - (nSrcPos - nReadPos) - nRewind;
if (nAvail < readNow)
readNow = nAvail;
if (readNow == 0)
return false;
size_t nBytes = fread((void*)&vchBuf[pos], 1, readNow, src);
if (nBytes == 0) {
throw std::ios_base::failure(feof(src) ? "CBufferedFile::Fill: end of file" : "CBufferedFile::Fill: fread failed");
} else {
nSrcPos += nBytes;
return true;
}
}
public:
CBufferedFile(FILE *fileIn, uint64_t nBufSize, uint64_t nRewindIn, int nTypeIn, int nVersionIn) :
nType(nTypeIn), nVersion(nVersionIn), nSrcPos(0), nReadPos(0), nReadLimit((uint64_t)(-1)), nRewind(nRewindIn), vchBuf(nBufSize, 0)
{
src = fileIn;
}
~CBufferedFile()
{
fclose();
}
// Disallow copies
CBufferedFile(const CBufferedFile&) = delete;
CBufferedFile& operator=(const CBufferedFile&) = delete;
int GetVersion() const { return nVersion; }
int GetType() const { return nType; }
void fclose()
{
if (src) {
::fclose(src);
src = nullptr;
}
}
// check whether we're at the end of the source file
bool eof() const {
return nReadPos == nSrcPos && feof(src);
}
// read a number of bytes
void read(char *pch, size_t nSize) {
if (nSize + nReadPos > nReadLimit)
throw std::ios_base::failure("Read attempted past buffer limit");
if (nSize + nRewind > vchBuf.size())
throw std::ios_base::failure("Read larger than buffer size");
while (nSize > 0) {
if (nReadPos == nSrcPos)
Fill();
unsigned int pos = nReadPos % vchBuf.size();
size_t nNow = nSize;
if (nNow + pos > vchBuf.size())
nNow = vchBuf.size() - pos;
if (nNow + nReadPos > nSrcPos)
nNow = nSrcPos - nReadPos;
memcpy(pch, &vchBuf[pos], nNow);
nReadPos += nNow;
pch += nNow;
nSize -= nNow;
}
}
// return the current reading position
uint64_t GetPos() const {
return nReadPos;
}
// rewind to a given reading position
bool SetPos(uint64_t nPos) {
nReadPos = nPos;
if (nReadPos + nRewind < nSrcPos) {
nReadPos = nSrcPos - nRewind;
return false;
} else if (nReadPos > nSrcPos) {
nReadPos = nSrcPos;
return false;
} else {
return true;
}
}
bool Seek(uint64_t nPos) {
long nLongPos = nPos;
if (nPos != (uint64_t)nLongPos)
return false;
if (fseek(src, nLongPos, SEEK_SET))
return false;
nLongPos = ftell(src);
nSrcPos = nLongPos;
nReadPos = nLongPos;
return true;
}
// prevent reading beyond a certain position
// no argument removes the limit
bool SetLimit(uint64_t nPos = (uint64_t)(-1)) {
if (nPos < nReadPos)
return false;
nReadLimit = nPos;
return true;
}
template
CBufferedFile& operator>>(T& obj) {
// Unserialize from this stream
::Unserialize(*this, obj);
return (*this);
}
// search for a given byte in the stream, and remain positioned on it
void FindByte(char ch) {
while (true) {
if (nReadPos == nSrcPos)
Fill();
if (vchBuf[nReadPos % vchBuf.size()] == ch)
break;
nReadPos++;
}
}
};
每条block信息都有一个信息头,以测试网为例,信息头为:
pchMessageStart[0] = 0x0b;
pchMessageStart[1] = 0x11;
pchMessageStart[2] = 0x09;
pchMessageStart[3] = 0x07;
信息头校验成功后,读取一个block数据:
uint64_t nBlockPos = blkdat.GetPos();
blkdat.SetLimit(nBlockPos + nSize);
blkdat.SetPos(nBlockPos);
std::shared_ptr pblock = std::make_shared();
CBlock& block = *pblock;
blkdat >> block;
示例中,需要查找的区块哈希值为:
0x00000000000001a9055c41e6f0c4d86c66ba5e3fae87c38f5281cf48bf5b1e82
交易哈希值为:
0x67fcaf25ee12ee132ce6bbe179d064777a2e81e749fb51e103fd19e438337080
打开blk文件实现如下:
// 获取block文件名
fs::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix)
{
return GetDataDir() / "blocks" / strprintf("%s%05u.dat", prefix, pos.nFile);
}
// 打开文件
static FILE* OpenDiskFile(const CDiskBlockPos &pos, const char *prefix, bool fReadOnly)
{
if (pos.IsNull())
return nullptr;
fs::path path = GetBlockPosFilename(pos, prefix);
fs::create_directories(path.parent_path());
FILE* file = fsbridge::fopen(path, fReadOnly ? "rb": "rb+");
if (!file && !fReadOnly)
file = fsbridge::fopen(path, "wb+");
if (!file) {
std::cout<<"Unable to open file "<
测试主函数如下:
int main_findblock(){
// 设置为测试网络
SelectParams(CBaseChainParams::TESTNET);
// 设置数据存储目录
gArgs.SoftSetArg("-datadir",".\\");
// 获取数据存储目录
std::cout<<"Current Data Dir. "<> FLATDATA(buf);
if (memcmp(buf, chainparams.MessageStart(), CMessageHeader::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();
blkdat.SetLimit(nBlockPos + nSize);
blkdat.SetPos(nBlockPos);
std::shared_ptr pblock = std::make_shared();
CBlock& block = *pblock;
blkdat >> block;
nRewind = blkdat.GetPos();
// 检测是否是目标块
uint256 hash = block.GetHash();
if(hash == TestBlkHash){
std::cout<<"File ="<GetHash()==TestTxHash){
std::cout<ToString()<
示例输出:
===================================================================================================
Find block
===================================================================================================
Current Data Dir. "*********\testnet3"
Finding block in file blk00129.dat...
File =129, Offset =38418855
===================================================================================================
Find block:= 00000000000001a9055c41e6f0c4d86c66ba5e3fae87c38f5281cf48bf5b1e82
===================================================================================================
Find transaction
===================================================================================================
CTransaction(hash=67fcaf25ee, ver=2, vin.size=1, vout.size=1, nLockTime=0)
CTxIn(COutPoint(423fd7b469, 0), scriptSig=2200206f0f2d495c106ccb69)
CScriptWitness(, 3045022100cf5d662011e226a82d8cb860290565acfe42cd332a16d2c4c81a60fa01b3c37802201983bd20263889acc18c06a47ee36cf77c6594fd1b0ddf8f9d20b954071bda1701, 304402205637494e4efbe71052d084f133c81f6af7d134170f12b546c3ca926ff80bbff9022037bffc1e9cba1c485032d5fc6cf92f73d8fe76295c467ff48b0b9cdcfe2a520101, 522103e3bd2f408e4415aa57c747f6550937823a8605706c358facdc6325b4a99f216121020e80933a750e84b4c35c10bc797ca34d1c885e4e65531a7499170a1c78ffdd9721039155f9024807d126be4df4d09273c5fece4767e89b4527c68b48414a2877eddd53ae)
CTxOut(nValue=0.00499800, scriptPubKey=a9147c7020ed135a05c86bcec1c491)
===================================================================================================
源码下载地址:
https://github.com/babylco0/bitcoin_code_examples/blob/master/TestKey-1.5.0.tar.gz