比特币源码分析之序列化

比特币的数据存储(文件或者内存数据库)都会使用到序列化反序列化

如果自定义一些结构的话,涉及到持久化就需要在这个类中实现序列化反序列化的实现。

比特币对基本类型都有序列化,比如int、std::string、uint256、vector都有实现,几乎不需要自己去添加基础类型序列化函数。

比特币的序列化、反序列接口都是函数模板,第一个参数是具体的进行序列化的对象, 第二个参数是序列化到哪里磁盘文件、网络、哈希等。

序列化反序列化的时候可以使用 操作符 <<  >>进行。

下面以区块头的结构进行举例分析,底层实现涉及到宏定义。

class CBlockHeader
{
public:
    // header
    int32_t nVersion;
    uint256 hashPrevBlock;
    uint256 hashMerkleRoot;
    uint32_t nTime;
    uint32_t nBits;
    uint32_t nNonce;

    CBlockHeader()
    {
        SetNull();
    }

    /* 这个宏定义了两个函数模板:序列化与反序列化, 下面会给出定义的代码 */
    ADD_SERIALIZE_METHODS;

    /* 
     * 在序列化与反序列化的函数中,会通过this指针调用这个函数进行实际的序列化与反序列化
     * 通过第二个参数 ser_action进行区分是序列化还是反序列化
     */
    template 
    inline void SerializationOp(Stream& s, Operation ser_action) {
        READWRITE(this->nVersion);
        READWRITE(hashPrevBlock);
        READWRITE(hashMerkleRoot);
        READWRITE(nTime);
        READWRITE(nBits);
        READWRITE(nNonce);
    }
....
};

/* 定义的序列化反序列化函数 */
#define ADD_SERIALIZE_METHODS                                         \
    template                                         \
    void Serialize(Stream& s) const {                                 \
        NCONST_PTR(this)->SerializationOp(s, CSerActionSerialize());  \
    }                                                                 \
    template                                         \
    void Unserialize(Stream& s) {                                     \
        SerializationOp(s, CSerActionUnserialize());                  \
    }


/*实际序列化反序列化的调用*/
#define READWRITE(obj)      (::SerReadWrite(s, (obj), ser_action))


/* 序列化 反序列化的实现 */
template
inline void SerReadWrite(Stream& s, const T& obj, CSerActionSerialize ser_action)
{
    /* 根据obj的是积类型调用对应的序列化函数模板uint8_t、uint16_t、std::basic_string等*/
    ::Serialize(s, obj);
}

template
inline void SerReadWrite(Stream& s, T& obj, CSerActionUnserialize ser_action)
{
    ::Unserialize(s, obj);
}



使用序列化与反序列化的时候需要注意一点,如果子类进行序列化的时候,父类中也有序列化与反序列化的需求,需要对this指针进行强制类型转换,然后调用父类的相关函数,否则子类不会自动调用父类的相应函数,出现异常。

 

比如下面的区块序列化实现

class CBlock : public CBlockHeader
{
public:
    // network and disk
    std::vector vtx;

    // memory only
    mutable bool fChecked;

    CBlock()
    {
        SetNull();
    }

    CBlock(const CBlockHeader &header)
    {
        SetNull();
        *((CBlockHeader*)this) = header;
    }

    ADD_SERIALIZE_METHODS;

    template 
    inline void SerializationOp(Stream& s, Operation ser_action) {
        /* 先序列化反序列化父类的信息,然后再处理自己的信息*/
        READWRITE(*(CBlockHeader*)this);
        READWRITE(vtx);
    }

    void SetNull()
    {
        CBlockHeader::SetNull();
        vtx.clear();
        fChecked = false;
    }

    CBlockHeader GetBlockHeader() const
    {
        CBlockHeader block;
        block.nVersion       = nVersion;
        block.hashPrevBlock  = hashPrevBlock;
        block.hashMerkleRoot = hashMerkleRoot;
        block.nTime          = nTime;
        block.nBits          = nBits;
        block.nNonce         = nNonce;
        return block;
    }

    std::string ToString() const;
};

上面是一种实现方式。

接下来分析讲一下另一种实现方式(比特币的交易的序列化与反序列化)。比特币的交易有两种类型struct CMutableTransaction、class CTransaction,第一种是可以变化的,会用来创建交易使用。

/* 
 * 内部直接定义两个函数Serialize, Unserialize,其实和前面的一致,只不过交易的序列化逻辑比较复杂
 * 单独写比较方便,内部有很多逻辑处理、条件判断,直接通过READWRITE很难处理,单独写了两个函数模板
 */
struct CMutableTransaction
{
    int32_t nVersion;
    std::vector vin;
    std::vector vout;
    uint32_t nLockTime;

    CMutableTransaction();
    CMutableTransaction(const CTransaction& tx);

    template 
    inline void Serialize(Stream& s) const {
        SerializeTransaction(*this, s);
    }


    template 
    inline void Unserialize(Stream& s) {
        UnserializeTransaction(*this, s);
    }

    template 
    CMutableTransaction(deserialize_type, Stream& s) {
        Unserialize(s);
    }

...
};

/*这是交易的序列化的实现, 有判定是否是隔离见证,所以需要单独拿出来自己写*/
template
inline void SerializeTransaction(const TxType& tx, Stream& s) {
    const bool fAllowWitness = !(s.GetVersion() & SERIALIZE_TRANSACTION_NO_WITNESS);

    s << tx.nVersion;
    unsigned char flags = 0;
    // Consistency check
    if (fAllowWitness) {
        /* Check whether witnesses need to be serialized. */
        if (tx.HasWitness()) {
            flags |= 1;
        }
    }
    if (flags) {
        /* Use extended format in case witnesses are to be serialized. */
        std::vector vinDummy;
        s << vinDummy;
        s << flags;
    }
    s << tx.vin;
    s << tx.vout;
    if (flags & 1) {
        for (size_t i = 0; i < tx.vin.size(); i++) {
            s << tx.vin[i].scriptWitness.stack;
        }
    }
    s << tx.nLockTime;
}

如果需要在比特币的代码里面实现自己的业务逻辑,也涉及到序列化反序列化,里面的逻辑比较复杂,建议参考交易的处理方式。自己单独写个函数来实现相关操作

你可能感兴趣的:(区块链,比特币原理剖析)