溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Bitcoin中如何使用序列化庫

發(fā)布時間:2021-07-30 17:36:48 來源:億速云 閱讀:126 作者:Leah 欄目:互聯(lián)網(wǎng)科技

Bitcoin中如何使用序列化庫,針對這個問題,這篇文章詳細(xì)介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

Bitcoin序列化功能主要實現(xiàn)在serialize.h文件,整個代碼主要是圍繞stream和參與序列化反序列化的類型T展開。 

stream這個模板形參表達(dá)具有read(char**, size_t)write(char**, size_t)方法的對象, 類似Golang 的io.reader ,io.writer。

簡單的使用例子:

#include <serialize.h>
#include <streams.h>
#include <hash.h>
#include <test/test_bitcoin.h>

#include <stdint.h>
#include <memory>

#include <boost/test/unit_test.hpp>

BOOST_FIXTURE_TEST_SUITE(serialize_tests, BasicTestingSetup)


struct  student
{
	std::string name;
	double midterm, final;
	std::vector<double> homework;

	ADD_SERIALIZE_METHODS;

	template <typename Stream, typename Operation>
	inline void SerializationOp(Stream& s, Operation ser_action) {
		READWRITE(name);
		READWRITE(midterm);
		READWRITE(final);
		READWRITE(homework);
	}
		
};

bool operator==(student const& lhs,  student const& rhs){
		return lhs.name == rhs.name &&  \
		       lhs.midterm ==  rhs.midterm && \
		       lhs.final  ==  rhs.final && \
		       lhs.homework == rhs.homework;
}

std::ostream& operator<<(std::ostream& os, student const& st){
		os << "name: " << st.name << '\n' 
		   << "midterm: " << st.midterm << '\n'
		   << "final: "   << st.final  << '\n'
		   << "homework: " ;
		for (auto e : st.homework) {
			os << e <<  ' ';
		}
		return os;
}


BOOST_AUTO_TEST_CASE(normal)
{
    student  s, t;
    s.name = "john";
    s.midterm = 77;
    s.final = 82;
    auto  v = std::vector<double> {83, 50, 10, 88, 65};
    s.homework = v;

    CDataStream ss(SER_DISK, 0);
    ss <<  s;
    ss >>  t; 

    BOOST_CHECK(t.name  == "john");
    BOOST_CHECK(t.midterm  == 77);
    BOOST_CHECK(t.final  == 82);
    BOOST_TEST(t.homework == v,  boost::test_tools::per_element()); 
    

    CDataStream sd(SER_DISK, 0);
    CDataStream sn(SER_NETWORK, PROTOCOL_VERSION);
    sd << s;
    sn << s;
    BOOST_CHECK(Hash(sd.begin(), sd.end()) == Hash(sn.begin(), sn.end()));
}

BOOST_AUTO_TEST_CASE(vector)
{
    auto vs = std::vector<student>(3);
    vs[0].name = "bob";
    vs[0].midterm = 90;
    vs[0].final = 76;
    vs[0].homework = std::vector<double> {85, 53, 12, 75, 55};

    vs[1].name = "jim";
    vs[1].midterm = 96;
    vs[1].final = 72;
    vs[1].homework = std::vector<double> {91, 46, 19, 70, 59};

    vs[2].name = "tom";
    vs[2].midterm = 85;
    vs[2].final = 57;
    vs[2].homework = std::vector<double> {91, 77, 45, 50, 35};


    CDataStream ss(SER_DISK, 0);
    auto vt = std::vector<student>(3);
    ss <<  vs;
    ss >>  vt; 

    BOOST_TEST(vs == vt,  boost::test_tools::per_element()); 
}

BOOST_AUTO_TEST_CASE(unique_ptr){
	auto hex = "0100000001b14bdcbc3e01bdaad36cc08e81e69c82e1060bc14e518db2b49aa43ad90ba26000000000490047304402203f16c6f40162ab686621ef3000b04e75418a0c0cb2d8aebeac894ae360ac1e780220ddc15ecdfc3507ac48e1681a33eb60996631bf6bf5bc0a0682c4db743ce7ca2b01ffffffff0140420f00000000001976a914660d4ef3a743e3e696ad990364e555c271ad504b88ac00000000";
	CDataStream stream(ParseHex(hex), SER_NETWORK, PROTOCOL_VERSION);
        //CTransaction tx(deserialize, stream);
	auto utx = std::unique_ptr<const CTransaction>(nullptr);
	::Unserialize(stream, utx);
	BOOST_TEST(utx->vin.size() == std::size_t(1));
	BOOST_TEST(utx->vout[0].nValue == 1000000);
}

BOOST_AUTO_TEST_SUITE_END()

需要在用戶的自定義類型內(nèi)部 添加 ADD_SERIALIZE_METHODS 調(diào)用, 宏展開后:

template<typename Stream>                                         \
    void Serialize(Stream& s) const {                                 \
        NCONST_PTR(this)->SerializationOp(s, CSerActionSerialize());  \
    }                                                                 \
    template<typename Stream>                                         \
    void Unserialize(Stream& s) {                                     \
        SerializationOp(s, CSerActionUnserialize());                  \
    }

這個宏為用戶自定義類型添加了兩個成員函數(shù): SerializeUnserialize, 它們內(nèi)部調(diào)用需要用戶自定義的模板成員函數(shù)SerializationOp , 在 SerializationOp 函數(shù)內(nèi)部, 主要使用 READWRITEREADWRITEMANY 宏,完成對自定義類型每個數(shù)據(jù)成員的序列化與反序列化。

#define READWRITE(obj)      (::SerReadWrite(s, (obj), ser_action))
#define READWRITEMANY(...)      (::SerReadWriteMany(s, ser_action, __VA_ARGS__))

struct CSerActionSerialize
{
    constexpr bool ForRead() const { return false; }
};
struct CSerActionUnserialize
{
    constexpr bool ForRead() const { return true; }
};

template<typename Stream, typename T>
inline void SerReadWrite(Stream& s, const T& obj, CSerActionSerialize ser_action)
{
    ::Serialize(s, obj);
}

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

template<typename Stream, typename... Args>
inline void SerReadWriteMany(Stream& s, CSerActionSerialize ser_action, Args&&... args)
{
    ::SerializeMany(s, std::forward<Args>(args)...);
}

template<typename Stream, typename... Args>
inline void SerReadWriteMany(Stream& s, CSerActionUnserialize ser_action, Args&... args)
{
    ::UnserializeMany(s, args...);
}

需要在用戶的自定義類型內(nèi)部 添加 ADD_SERIALIZE_METHODS 調(diào)用, 宏展開后:

template<typename Stream>  \

 void Serialize(Stream& s) const {  \

 NCONST_PTR(this)->SerializationOp(s, CSerActionSerialize()); \

 }  \

 template<typename Stream>  \

 void Unserialize(Stream& s) {  \

 SerializationOp(s, CSerActionUnserialize()); \

 }

這個宏為用戶自定義類型添加了兩個成員函數(shù): SerializeUnserialize, 它們內(nèi)部調(diào)用需要用戶自定義的模板成員函數(shù)SerializationOp , 在 SerializationOp 函數(shù)內(nèi)部, 主要使用 READWRITEREADWRITEMANY 宏,完成對自定義類型每個數(shù)據(jù)成員的序列化與反序列化。

#define READWRITE(obj)      (::SerReadWrite(s, (obj), ser_action))
#define READWRITEMANY(...)      (::SerReadWriteMany(s, ser_action, __VA_ARGS__))

struct CSerActionSerialize
{
    constexpr bool ForRead() const { return false; }
};
struct CSerActionUnserialize
{
    constexpr bool ForRead() const { return true; }
};

template<typename Stream, typename T>
inline void SerReadWrite(Stream& s, const T& obj, CSerActionSerialize ser_action)
{
    ::Serialize(s, obj);
}

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

template<typename Stream, typename... Args>
inline void SerReadWriteMany(Stream& s, CSerActionSerialize ser_action, Args&&... args)
{
    ::SerializeMany(s, std::forward<Args>(args)...);
}

template<typename Stream, typename... Args>
inline void SerReadWriteMany(Stream& s, CSerActionUnserialize ser_action, Args&... args)
{
    ::UnserializeMany(s, args...);
}

這里SerReadWrite 和 SerReadWriteMany 各自有兩個overload 實現(xiàn), 區(qū)別是末尾分別傳入了不同的類型CSerActionSerializeCSerActionUnserialize , 而且 形參 ser_action 根本沒有在內(nèi)部使用, 查閱了相關(guān)資料, 這里使用了c++ 泛型編程常用的一種模式:

tag dispatch 技術(shù)](https://akrzemi1.wordpress.com/examples/overloading-tag-dispatch/), 另一個解釋:[https://arne-mertz.de/2016/10/tag-dispatch/)(https://arne-mertz.de/2016/10/tag-dispatch/),

通過攜帶不同的類型,在編譯時選擇不同的overload 實現(xiàn), CSerActionSerialize 對應(yīng)序列化的實現(xiàn), CSerActionUnserialize 對應(yīng)反序列化的實現(xiàn)。

SerializeManySerializeMany是通過變長模板parameter pack 展開技術(shù)來實現(xiàn), 以 SerializeMany 為例子:

template<typename Stream>
void SerializeMany(Stream& s)
{
}

template<typename Stream, typename Arg>
void SerializeMany(Stream& s, Arg&& arg)
{
    ::Serialize(s, std::forward<Arg>(arg));
}

template<typename Stream, typename Arg, typename... Args>
void SerializeMany(Stream& s, Arg&& arg, Args&&... args)
{
    ::Serialize(s, std::forward<Arg>(arg));
    ::SerializeMany(s, std::forward<Args>(args)...);
}

SerializeMany有三個overload 實現(xiàn),假設(shè)從上倒下,分別編號為1, 2, 3; 當(dāng)我們傳入兩個以上的實參是,編譯器選擇版本3,版本3內(nèi)部從parameter pack 彈出一個參數(shù),然后傳給版本2調(diào)用,剩下的參數(shù)列表,傳給版本3,遞歸調(diào)用,直到parameter pack 為空時,選擇版本1。

迂回這么長, 最終序列化真正使用全局名稱空間的 Serialize 來完成, 反序列化通過調(diào)用Unserialize實現(xiàn)。

SerializeUnserialize 又有一堆的overload 實現(xiàn), Bitcoin 作者實現(xiàn)一些常見類型的模板特化,比如,std::string, 主要設(shè)計表達(dá)腳本的prevector , std::vector, std::pair, std::map, std::set, std::unique_ptr, std::share_ptr 。 c++ 的模板匹配根據(jù)參數(shù)列表的匹配程度選擇不同的實現(xiàn), 優(yōu)先精準(zhǔn)匹配,最后選擇類型T的成員函數(shù)實現(xiàn):

template<typename Stream, typename T>
inline void Serialize(Stream& os, const T& a)
{
    a.Serialize(os);
}

template<typename Stream, typename T>
inline void Unserialize(Stream& is, T& a)
{
    a.Unserialize(is);
}

在序列化string, map, set, vector, prevector 等可能包含多元素的集合類型時, 內(nèi)部會調(diào)用 ReadCompactSizeWriteCompactSize讀取寫入緊湊編碼的元素個數(shù):

template<typename Stream>
void WriteCompactSize(Stream& os, uint64_t nSize)
{
    if (nSize < 253)
    {
        ser_writedata8(os, nSize);
    }
    else if (nSize <= std::numeric_limits<unsigned short>::max())
    {
        ser_writedata8(os, 253);
        ser_writedata16(os, nSize);
    }
    else if (nSize <= std::numeric_limits<unsigned int>::max())
    {
        ser_writedata8(os, 254);
        ser_writedata32(os, nSize);
    }
    else
    {
        ser_writedata8(os, 255);
        ser_writedata64(os, nSize);
    }
    return;
}

template<typename Stream>
uint64_t ReadCompactSize(Stream& is)
{
    uint8_t chSize = ser_readdata8(is);
    uint64_t nSizeRet = 0;
    if (chSize < 253)
    {
        nSizeRet = chSize;
    }
    else if (chSize == 253)
    {
        nSizeRet = ser_readdata16(is);
        if (nSizeRet < 253)
            throw std::ios_base::failure("non-canonical ReadCompactSize()");
    }
    else if (chSize == 254)
    {
        nSizeRet = ser_readdata32(is);
        if (nSizeRet < 0x10000u)
            throw std::ios_base::failure("non-canonical ReadCompactSize()");
    }
    else
    {
        nSizeRet = ser_readdata64(is);
        if (nSizeRet < 0x100000000ULL)
            throw std::ios_base::failure("non-canonical ReadCompactSize()");
    }
    if (nSizeRet > (uint64_t)MAX_SIZE)
        throw std::ios_base::failure("ReadCompactSize(): size too large");
    return nSizeRet;
}

針對位寬1,2,4,8的基礎(chǔ)類型,SerializeUnserialize 最終調(diào)用ser_writedata*, ser_readdata8* 完成實現(xiàn)。

template<typename Stream> inline void Serialize(Stream& s, char a    ) { ser_writedata8(s, a); } // TODO Get rid of bare char
template<typename Stream> inline void Serialize(Stream& s, int8_t a  ) { ser_writedata8(s, a); }
template<typename Stream> inline void Serialize(Stream& s, uint8_t a ) { ser_writedata8(s, a); }
template<typename Stream> inline void Serialize(Stream& s, int16_t a ) { ser_writedata16(s, a); }
template<typename Stream> inline void Serialize(Stream& s, uint16_t a) { ser_writedata16(s, a); }
template<typename Stream> inline void Serialize(Stream& s, int32_t a ) { ser_writedata32(s, a); }
template<typename Stream> inline void Serialize(Stream& s, uint32_t a) { ser_writedata32(s, a); }
template<typename Stream> inline void Serialize(Stream& s, int64_t a ) { ser_writedata64(s, a); }
template<typename Stream> inline void Serialize(Stream& s, uint64_t a) { ser_writedata64(s, a); }
template<typename Stream> inline void Serialize(Stream& s, float a   ) { ser_writedata32(s, ser_float_to_uint32(a)); }
template<typename Stream> inline void Serialize(Stream& s, double a  ) { ser_writedata64(s, ser_double_to_uint64(a)); }

template<typename Stream> inline void Unserialize(Stream& s, char& a    ) { a = ser_readdata8(s); } // TODO Get rid of bare char
template<typename Stream> inline void Unserialize(Stream& s, int8_t& a  ) { a = ser_readdata8(s); }
template<typename Stream> inline void Unserialize(Stream& s, uint8_t& a ) { a = ser_readdata8(s); }
template<typename Stream> inline void Unserialize(Stream& s, int16_t& a ) { a = ser_readdata16(s); }
template<typename Stream> inline void Unserialize(Stream& s, uint16_t& a) { a = ser_readdata16(s); }
template<typename Stream> inline void Unserialize(Stream& s, int32_t& a ) { a = ser_readdata32(s); }
template<typename Stream> inline void Unserialize(Stream& s, uint32_t& a) { a = ser_readdata32(s); }
template<typename Stream> inline void Unserialize(Stream& s, int64_t& a ) { a = ser_readdata64(s); }
template<typename Stream> inline void Unserialize(Stream& s, uint64_t& a) { a = ser_readdata64(s); }
template<typename Stream> inline void Unserialize(Stream& s, float& a   ) { a = ser_uint32_to_float(ser_readdata32(s)); }
template<typename Stream> inline void Unserialize(Stream& s, double& a  ) { a = ser_uint64_to_double(ser_readdata64(s)); }

template<typename Stream> inline void Serialize(Stream& s, bool a)    { char f=a; ser_writedata8(s, f); }
template<typename Stream> inline void Unserialize(Stream& s, bool& a) { char f=ser_readdata8(s); a=f; }

另外代碼開始處的

struct deserialize_type {};
constexpr deserialize_type deserialize {};

作為tag 類型, tag 對象, 主要為多個實現(xiàn)簽名有以下形式:

template <typename Stream> 
T::T(deserialize_type, Stream& s)

的反序列化構(gòu)造器做分發(fā), 目前主要是CTransaction, CMutableTransaction 類型:

template <typename Stream>
    CTransaction(deserialize_type, Stream& s) : CTransaction(CMutableTransaction(deserialize, s)) {}
    
    template <typename Stream>
    CMutableTransaction(deserialize_type, Stream& s) {
        Unserialize(s);
    }

關(guān)于Bitcoin中如何使用序列化庫問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI