比特币源码分析之序列化
作者:互联网
比特币源码分析之序列化
https://blog.csdn.net/guoguangwu/article/details/88874251
比特币的数据存储(文件或者内存数据库)都会使用到序列化、反序列化。
如果自定义一些结构的话,涉及到持久化就需要在这个类中实现序列化反序列化的实现。
比特币对基本类型都有序列化,比如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;
-
}
如果需要在比特币的代码里面实现自己的业务逻辑,也涉及到序列化反序列化,里面的逻辑比较复杂,建议参考交易的处理方式。自己单独写个函数来实现相关操作
标签:const,Stream,比特,READWRITE,void,源码,template,序列化 来源: https://blog.csdn.net/TuxedoLinux/article/details/89639136