在blk文件中找寻目标块及交易示例

在blk文件中找寻目标块及交易示例

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 ="<

示例输出:

===================================================================================================
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

你可能感兴趣的:(在blk文件中找寻目标块及交易示例)