比特币的数据存储(文件或者内存数据库)都会使用到序列化、反序列化。
如果自定义一些结构的话,涉及到持久化就需要在这个类中实现序列化反序列化的实现。
比特币对基本类型都有序列化,比如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 <typename Stream, typename Operation>
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<typename Stream> \
void Serialize(Stream& s) const { \
NCONST_PTR(this)->SerializationOp(s, CSerActionSerialize()); \
} \
template<typename Stream> \
void Unserialize(Stream& s) { \
SerializationOp(s, CSerActionUnserialize()); \
}
/*实际序列化反序列化的调用*/
#define READWRITE(obj) (::SerReadWrite(s, (obj), ser_action))
/* 序列化 反序列化的实现 */
template<typename Stream, typename T>
inline void SerReadWrite(Stream& s, const T& obj, CSerActionSerialize ser_action)
{
/* 根据obj的是积类型调用对应的序列化函数模板uint8_t、uint16_t、std::basic_string等*/
::Serialize(s, obj);
}
template<typename Stream, typename T>
inline void SerReadWrite(Stream& s, T& obj, CSerActionUnserialize ser_action)
{
::Unserialize(s, obj);
}
使用序列化与反序列化的时候需要注意一点,如果子类进行序列化的时候,父类中也有序列化与反序列化的需求,需要对this指针进行强制类型转换,然后调用父类的相关函数,否则子类不会自动调用父类的相应函数,出现异常。
比如下面的区块序列化实现
class CBlock : public CBlockHeader
{
public:
// network and disk
std::vector<CTransactionRef> vtx;
// memory only
mutable bool fChecked;
CBlock()
{
SetNull();
}
CBlock(const CBlockHeader &header)
{
SetNull();
*((CBlockHeader*)this) = header;
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
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<CTxIn> vin;
std::vector<CTxOut> vout;
uint32_t nLockTime;
CMutableTransaction();
CMutableTransaction(const CTransaction& tx);
template <typename Stream>
inline void Serialize(Stream& s) const {
SerializeTransaction(*this, s);
}
template <typename Stream>
inline void Unserialize(Stream& s) {
UnserializeTransaction(*this, s);
}
template <typename Stream>
CMutableTransaction(deserialize_type, Stream& s) {
Unserialize(s);
}
...
};
/*这是交易的序列化的实现, 有判定是否是隔离见证,所以需要单独拿出来自己写*/
template<typename Stream, typename TxType>
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<CTxIn> 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;
}
如果需要在比特币的代码里面实现自己的业务逻辑,也涉及到序列化反序列化,里面的逻辑比较复杂,建议参考交易的处理方式。自己单独写个函数来实现相关操作