比特幣源碼閱讀筆記【基礎篇】

比特幣源碼閱讀筆記【基礎篇】

出差坐火車ing,正好利用這段時間學習一波比特幣源代碼,比特幣源碼的主要語言是C++,測試代碼語言主要是Python。

一、區塊鏈數據結構和數字簽名算法

1. 數據結構 Merkle樹

區塊鏈, 顧名思義是由一個個區塊按一定規則組成的。何謂區塊,我們可以用命令行工具bitcoin-cli或者區塊鏈瀏覽器(blockchain.info等網站)瀏覽區塊詳細信息, 例如:

{
    "size": 43560,
    "version": 2,
    "previousblockhash": "00000000000000027e7ba6fe7bad39faf3b5a83daed765f05f7d1b71a1632249",
    "merkleroot": "5e049f4030e0ab2debb92378f53c0a6e09548aea083f3ab25e1d94ea1155e29d",
    "time": 1388185038,
    "difficulty": 1180923195.25802612,
    "nonce": 4215469401,
    "tx": ["257e7497fb8bc68421eb2c7b699dbab234831600e7352f0d9e6522c7cf3f6c77", # [...many more transactions omitted...]
        "05cfd38f6ae6aa83674cc99e4d75a1458c165b7ab84725eda41d018a09176634"
    ]
}

區塊包含的信息分幾個部分:1.元信息,如version,time,size等 2. 挖礦信息,如僅用一次的隨機數nonce, 難度係數difficulty 3. 交易數據 4. 交易摘要,merklerootperviousblockhash

比特幣採用Merkle樹進行交易摘要。 Merkle樹的基礎是哈希函數和非對稱數字簽名,比特幣選擇的哈希函數是兩層SHA256。交易本身不存儲在Merkle樹中,Merkle樹保存交易的哈希值。例如,一筆交易A的哈希值計算過程HA = SHA256(SHA256(A))
, 兩筆交易A和B的哈希值計算過程爲H(A,B) = SHA256(SHA256(HA+HB)))。交易數據兩兩計算,最終組成一個哈希二叉樹,如下圖所示
WX20180224-010301@2x.png-61.3kB

這兩個字段,merkleroot表示整條鏈的交易摘要哈希值,perviousblockhash表示該區塊在鏈中的位置;
下圖是一個的樣例:
WX20180224-004307@2x.png-337.5kB

到此爲止,我們已經知道,比特幣區塊鏈的基礎數據結構是Merkle樹。Merkle樹是哈希指針形式的二叉樹,每個節點包含其子節點的哈希值,一旦子節點的結構或內容發生變動,該節點的哈希值必然發生改變,從而,兩個Merkle樹最上層節點的哈希值相同,我們就認爲兩個Merkle樹是完全一致的。

Merkle樹計算核心函數,見consensus/merkle.cpp

/* This implements a constant-space merkle root/path calculator, limited to 2^32 leaves. */
static void MerkleComputation(const std::vector<uint256>& leaves, uint256* proot, bool* pmutated, uint32_t branchpos, std::vector<uint256>* pbranch) {
    if (pbranch) pbranch->clear();
    if (leaves.size() == 0) {
        if (pmutated) *pmutated = false;
        if (proot) *proot = uint256();
        return;
    }
    bool mutated = false;
    // count is the number of leaves processed so far.
    uint32_t count = 0;
    // inner is an array of eagerly computed subtree hashes, indexed by tree
    // level (0 being the leaves).
    // For example, when count is 25 (11001 in binary), inner[4] is the hash of
    // the first 16 leaves, inner[3] of the next 8 leaves, and inner[0] equal to
    // the last leaf. The other inner entries are undefined.
    uint256 inner[32];
    // Which position in inner is a hash that depends on the matching leaf.
    int matchlevel = -1;
    // First process all leaves into 'inner' values.
    while (count < leaves.size()) {
        uint256 h = leaves[count];
        bool matchh = count == branchpos;
        count++;
        int level;
        // For each of the lower bits in count that are 0, do 1 step. Each
        // corresponds to an inner value that existed before processing the
        // current leaf, and each needs a hash to combine it.
        for (level = 0; !(count & (((uint32_t)1) << level)); level++) {
            if (pbranch) {
                if (matchh) {
                    pbranch->push_back(inner[level]);
                } else if (matchlevel == level) {
                    pbranch->push_back(h);
                    matchh = true;
                }
            }
            mutated |= (inner[level] == h);
            CHash256().Write(inner[level].begin(), 32).Write(h.begin(), 32).Finalize(h.begin());
        }
        // Store the resulting hash at inner position level.
        inner[level] = h;
        if (matchh) {
            matchlevel = level;
        }
    }
    // Do a final 'sweep' over the rightmost branch of the tree to process
    // odd levels, and reduce everything to a single top value.
    // Level is the level (counted from the bottom) up to which we've sweeped.
    int level = 0;
    // As long as bit number level in count is zero, skip it. It means there
    // is nothing left at this level.
    while (!(count & (((uint32_t)1) << level))) {
        level++;
    }
    uint256 h = inner[level];
    bool matchh = matchlevel == level;
    while (count != (((uint32_t)1) << level)) {
        // If we reach this point, h is an inner value that is not the top.
        // We combine it with itself (Bitcoin's special rule for odd levels in
        // the tree) to produce a higher level one.
        if (pbranch && matchh) {
            pbranch->push_back(h);
        }
        CHash256().Write(h.begin(), 32).Write(h.begin(), 32).Finalize(h.begin());
        // Increment count to the value it would have if two entries at this
        // level had existed.
        count += (((uint32_t)1) << level);
        level++;
        // And propagate the result upwards accordingly.
        while (!(count & (((uint32_t)1) << level))) {
            if (pbranch) {
                if (matchh) {
                    pbranch->push_back(inner[level]);
                } else if (matchlevel == level) {
                    pbranch->push_back(h);
                    matchh = true;
                }
            }
            CHash256().Write(inner[level].begin(), 32).Write(h.begin(), 32).Finalize(h.begin());
            level++;
        }
    }
    // Return result.
    if (pmutated) *pmutated = mutated;
    if (proot) *proot = h;
}

2. 數字簽名算法 橢圓曲線算法

2.1 橢圓曲線算法和公私鑰

日常工作中,我們每天登錄服務器,對公私鑰系統已經非常熟悉了,公私鑰並不是新鮮的技術。我們可以用ssh-keygen命令生成一對公鑰和私鑰;當擁有私鑰時,可以用ssh-keygen -y -f生成配對的公鑰,但擁有公鑰無法生成配對的私鑰。這背後的原理就是橢圓曲線數字簽名算法(Elliptic Curve Digital Signature Algorithm, 簡寫爲ECDSA)。比特幣利用橢圓曲線生成密鑰對,其中公鑰作爲交易接收方的地址,對外傳播。

在平面直角座標系中,一條橢圓曲線的形狀如下圖。比特幣的源碼中,橢圓曲線定義在src/secp256k1目錄。比特幣選擇的橢圓曲線方程爲y2 mod p = (x3 + 7) mod p,其中p是一個非常大的質數,p = 2256 - 232 - 29 - 28 - 27 - 26 - 24 - 1。
橢圓曲線示意圖.png-69.5kB

給定私鑰k, 如何根據橢圓曲線生成公鑰K呢?公式爲K = k * G。其中,G 代表一種變換,從點k=(x,y)進行一次變換生成k1=(x1, y1),先在點k出求正切線,正切線與橢圓曲線的交點…
下圖用幾何形式展示了變換G。
橢圓曲線2.png-129.2kB

2.2 SHA-256

哈希函數在比特幣中用途極爲廣泛,包括:比特幣地址,交易腳本地址,挖礦工作量證明。在比特幣系統中,利用公鑰生成比特幣地址的算法是SHA256和 RIPEMD160。
由公鑰生成比特幣地址的總體過程如下圖所示
公鑰-比特幣地址.png-158.2kB
給定公鑰K,先進行SHA256哈希,再對SHA256的結果進行RIPEMD160哈希,得到的值A就是比特幣地址, 即A = RIPEMD160(SHA256(K))。A值進一步壓縮, 我們就得到了比特幣地址, 形如1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy。比特幣選擇的壓縮算法是Base58Check

SHA-256計算過程,如下圖
sha-256-in-depth.png-512.5kB

我們來看一下比特幣源碼中SHA256哈希的實現
哈希函數類定義如下, 主體是狀態s,由8個32位無符號整數組成。緩衝buf用於批量讀入和計算數據。

/** A hasher class for SHA-256. */
class CSHA256
{
private:
    uint32_t s[8];
    unsigned char buf[64];
    uint64_t bytes;

public:
    static const size_t OUTPUT_SIZE = 32;

    CSHA256();
    CSHA256& Write(const unsigned char* data, size_t len);
    void Finalize(unsigned char hash[OUTPUT_SIZE]);
    CSHA256& Reset();
};

SHA256的主體代碼,數據data每64字節讀入一次,寫入buff,經過Transform處理,得到s值。

CSHA256& CSHA256::Write(const unsigned char* data, size_t len)
{
    const unsigned char* end = data + len;
    size_t bufsize = bytes % 64;
    if (bufsize && bufsize + len >= 64) {
        // Fill the buffer, and process it.
        memcpy(buf + bufsize, data, 64 - bufsize);
        bytes += 64 - bufsize;
        data += 64 - bufsize;
        Transform(s, buf, 1);
        bufsize = 0;
    }
    if (end - data >= 64) {
        size_t blocks = (end - data) / 64;
        Transform(s, data, blocks);
        data += 64 * blocks;
        bytes += 64 * blocks;
    }
    if (end > data) {
        // Fill the buffer with what remains.
        memcpy(buf + bufsize, data, end - data);
        bytes += end - data;
    }
    return *this;
}

這裏定義了一些基本運算,Ch, Maj, Sigma0, Sigma1, sigma0, sigma1,很直白,無需贅言。

uint32_t inline Ch(uint32_t x, uint32_t y, uint32_t z) { return z ^ (x & (y ^ z)); }
uint32_t inline Maj(uint32_t x, uint32_t y, uint32_t z) { return (x & y) | (z & (x | y)); }
uint32_t inline Sigma0(uint32_t x) { return (x >> 2 | x << 30) ^ (x >> 13 | x << 19) ^ (x >> 22 | x << 10); }
uint32_t inline Sigma1(uint32_t x) { return (x >> 6 | x << 26) ^ (x >> 11 | x << 21) ^ (x >> 25 | x << 7); }
uint32_t inline sigma0(uint32_t x) { return (x >> 7 | x << 25) ^ (x >> 18 | x << 14) ^ (x >> 3); }
uint32_t inline sigma1(uint32_t x) { return (x >> 17 | x << 15) ^ (x >> 19 | x << 13) ^ (x >> 10); }

初始化8個32位的無符號整數,作爲初始狀態。

/** Initialize SHA-256 state. */
void inline Initialize(uint32_t* s)
{
    s[0] = 0x6a09e667ul;
    s[1] = 0xbb67ae85ul;
    s[2] = 0x3c6ef372ul;
    s[3] = 0xa54ff53aul;
    s[4] = 0x510e527ful;
    s[5] = 0x9b05688cul;
    s[6] = 0x1f83d9abul;
    s[7] = 0x5be0cd19ul;
}
/** One round of SHA-256. */
void inline Round(uint32_t a, uint32_t b, uint32_t c, uint32_t& d, uint32_t e, uint32_t f, uint32_t g, uint32_t& h, uint32_t k, uint32_t w)
{
    uint32_t t1 = h + Sigma1(e) + Ch(e, f, g) + k + w;
    uint32_t t2 = Sigma0(a) + Maj(a, b, c);
    d += t1;
    h = t1 + t2;
}

每一輪SHA256計算過程如上所示,同樣很直白,無需贅言。

下面的Transform函數比較有內容,函數輸入一個64字節的chunk和狀態s,每次從chunk讀入4字節,根據當前狀態s,做16次Round計算;完成後再做三輪16次Round;最後,8個無符號整數都加上上述64次Round的結果,更新狀態s。經過上述計算,我們就得到了數據chunk的SHA256哈希值。

數據讀寫的代碼,見/src/crypto/common.h

/** Perform a number of SHA-256 transformations, processing 64-byte chunks. */
void Transform(uint32_t* s, const unsigned char* chunk, size_t blocks)
{
    while (blocks--) {
        uint32_t a = s[0], b = s[1], c = s[2], d = s[3], e = s[4], f = s[5], g = s[6], h = s[7];
        uint32_t w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15;
        // 步驟一:
        Round(a, b, c, d, e, f, g, h, 0x428a2f98, w0 = ReadBE32(chunk + 0));
        Round(h, a, b, c, d, e, f, g, 0x71374491, w1 = ReadBE32(chunk + 4));
        // 中間省略...
        Round(c, d, e, f, g, h, a, b, 0x9bdc06a7, w14 = ReadBE32(chunk + 56));
        Round(b, c, d, e, f, g, h, a, 0xc19bf174, w15 = ReadBE32(chunk + 60));
        // 步驟二:
        Round(a, b, c, d, e, f, g, h, 0xe49b69c1, w0 += sigma1(w14) + w9 + sigma0(w1));
        Round(h, a, b, c, d, e, f, g, 0xefbe4786, w1 += sigma1(w15) + w10 + sigma0(w2));
        // 中間省略
        Round(c, d, e, f, g, h, a, b, 0x06ca6351, w14 += sigma1(w12) + w7 + sigma0(w15));
        Round(b, c, d, e, f, g, h, a, 0x14292967, w15 += sigma1(w13) + w8 + sigma0(w0));
        // 步驟三:
        Round(a, b, c, d, e, f, g, h, 0x27b70a85, w0 += sigma1(w14) + w9 + sigma0(w1));
        Round(h, a, b, c, d, e, f, g, 0x2e1b2138, w1 += sigma1(w15) + w10 + sigma0(w2));
        // 中間省略
        Round(c, d, e, f, g, h, a, b, 0xf40e3585, w14 += sigma1(w12) + w7 + sigma0(w15));
        Round(b, c, d, e, f, g, h, a, 0x106aa070, w15 += sigma1(w13) + w8 + sigma0(w0));
        // 步驟四:
        Round(a, b, c, d, e, f, g, h, 0x19a4c116, w0 += sigma1(w14) + w9 + sigma0(w1));
        Round(h, a, b, c, d, e, f, g, 0x1e376c08, w1 += sigma1(w15) + w10 + sigma0(w2));
        // 中間省略
        Round(c, d, e, f, g, h, a, b, 0xbef9a3f7, w14 + sigma1(w12) + w7 + sigma0(w15));
        Round(b, c, d, e, f, g, h, a, 0xc67178f2, w15 + sigma1(w13) + w8 + sigma0(w0));

        s[0] += a;
        s[1] += b;
        s[2] += c;
        s[3] += d;
        s[4] += e;
        s[5] += f;
        s[6] += g;
        s[7] += h;
        chunk += 64;
    }
}

以上就是SHA256哈希算法的實現;下一步,我們分析交易結構。

二、交易

一般的交易系統設計:賬戶,餘額。比特幣的交易系統, 不記錄餘額,記錄交易內容。例如,一般的交易系統,記錄Alice的餘額爲10 RMB,Bob的餘額爲5RMB,當Alice發起交易,給Bob支付5RMB時,支付系統後臺檢查Alice的餘額,驗證通過後,對Alice的餘額減5RMB,再對Bob的餘額加5RMB。
而區塊鏈處理的思路是,不記錄Alice和Bob的賬戶餘額,記錄歷史上發生過的所有交易記錄,以未消費交易輸出(UTXO)形式分佈式地保存在區塊鏈上,區塊鏈網絡節點共同驗證UTXO是否合法。
構建比特幣交易的基礎是交易輸出, 交易輸出是比特幣貨幣系統中不可拆分的塊(chunk),記錄在區塊鏈上,被比特幣網絡認證;比特幣網絡節點記錄所有可獲得且可消費的交易輸出,稱作未消費交易輸出(unspent transaction outputs), 簡寫成UTXO
區塊鏈上的一筆交易,代表着一組UTXO集合狀態轉換到另外一組UTXO集合狀態。當產生新的“比特幣”時,UTXO集合大小增加。UTXO是比特幣交易的基礎。“賬戶餘額”的概念由比特幣錢包應用創造,錢包應用掃描區塊鏈,得到錢包擁有者的私鑰可以消費的UTXO的總和,即“賬戶餘額”。大多數錢包應用維護了一個數據庫,用於存儲該錢包私鑰可以消費的UTXO集合。

2.1 交易格式

實際比特幣的交易記錄是二進制格式,轉換成可讀格式後,一筆交易的內容包含三個部分:元數據,輸入,輸出; 如下所示

{
    "version": 1,
    "locktime": 0,
    "vin": [{
        "txid": "7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18",
        "vout": 0,
        "scriptSig": "3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204 b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813[ALL] 0484ecc0d46f1918b30928fa0e4ed99f16a0fb4fde0735e7ade8416ab9fe423cc5412336376789d1 72787ec3457eee41c04f4938de5cc17b4a10fa336a8d752adf",
        "sequence": 4294967295
    }],
    "vout": [{
        "value": 0.01500000,
        "scriptPubKey": "OP_DUP OP_HASH160 ab68025513c3dbd2f7b92a94e0581f5d50f654e7 OP_EQUALVERIFY OP_CHECKSIG "
    }, {
        "value": 0.08450000,
        "scriptPubKey": "OP_DUP OP_HASH160 7f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8 OP_EQUALVERIFY OP_CHECKSIG"
    }]
}

值得注意的是,每個輸入包含了一項scirptSig, 上例中是3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204 b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813[ALL] 0484ecc0d46f1918b30928fa0e4ed99f16a0fb4fde0735e7ade8416ab9fe423cc5412336376789d1 72787ec3457eee41c04f4938de5cc17b4a10fa336a8d752adf, 每個輸出的元素都包含了一項scriptPubKey, 上例中是ab68025513c3dbd2f7b92a94e0581f5d50f654e7, 這個值代表了交易腳本的哈希值。(稍後詳細描述)

比特幣交易結構,見primitives/transaction.h

/**
 * Basic transaction serialization format:
 * - int32_t nVersion
 * - std::vector<CTxIn> vin
 * - std::vector<CTxOut> vout
 * - uint32_t nLockTime
 *
 * Extended transaction serialization format:
 * - int32_t nVersion
 * - unsigned char dummy = 0x00
 * - unsigned char flags (!= 0)
 * - std::vector<CTxIn> vin
 * - std::vector<CTxOut> vout
 * - if (flags & 1):
 *   - CTxWitness wit;
 * - uint32_t nLockTime
 */

可以看到,基本交易序列化格式的信息包括:版本號,輸入列表,輸出列表,鎖定時間;此外,擴展的交易序列化格式還包括擴容方案隔離見證的一些信息。關於比特幣擴容和隔離見證此處不展開討論,準備單獨寫一篇筆記。對比特幣擴容方案感興趣的讀者,建議閱讀BIP-141, BIP-143和BIP-147。

交易輸入定義,見src/primitives/transaction.h

/** An input of a transaction.  It contains the location of the previous
 * transaction's output that it claims and a signature that matches the
 * output's public key.
 */
class CTxIn
{
public:
    COutPoint prevout;
    CScript scriptSig;
    uint32_t nSequence;
    CScriptWitness scriptWitness; //! Only serialized through CTransaction

    /* Setting nSequence to this value for every input in a transaction
     * disables nLockTime. */
    static const uint32_t SEQUENCE_FINAL = 0xffffffff;

    /* Below flags apply in the context of BIP 68*/
    /* If this flag set, CTxIn::nSequence is NOT interpreted as a
     * relative lock-time. */
    static const uint32_t SEQUENCE_LOCKTIME_DISABLE_FLAG = (1 << 31);

    /* If CTxIn::nSequence encodes a relative lock-time and this flag
     * is set, the relative lock-time has units of 512 seconds,
     * otherwise it specifies blocks with a granularity of 1. */
    static const uint32_t SEQUENCE_LOCKTIME_TYPE_FLAG = (1 << 22);

    /* If CTxIn::nSequence encodes a relative lock-time, this mask is
     * applied to extract that lock-time from the sequence field. */
    static const uint32_t SEQUENCE_LOCKTIME_MASK = 0x0000ffff;

    /* In order to use the same number of bits to encode roughly the
     * same wall-clock duration, and because blocks are naturally
     * limited to occur every 600s on average, the minimum granularity
     * for time-based relative lock-time is fixed at 512 seconds.
     * Converting from CTxIn::nSequence to seconds is performed by
     * multiplying by 512 = 2^9, or equivalently shifting up by
     * 9 bits. */
    static const int SEQUENCE_LOCKTIME_GRANULARITY = 9;

    CTxIn()
    {
        nSequence = SEQUENCE_FINAL;
    }

    explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=SEQUENCE_FINAL);
    CTxIn(uint256 hashPrevTx, uint32_t nOut, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=SEQUENCE_FINAL);

    ADD_SERIALIZE_METHODS;

    template <typename Stream, typename Operation>
    inline void SerializationOp(Stream& s, Operation ser_action) {
        READWRITE(prevout);
        READWRITE(scriptSig);
        READWRITE(nSequence);
    }

    friend bool operator==(const CTxIn& a, const CTxIn& b)
    {
        return (a.prevout   == b.prevout &&
                a.scriptSig == b.scriptSig &&
                a.nSequence == b.nSequence);
    }

    friend bool operator!=(const CTxIn& a, const CTxIn& b)
    {
        return !(a == b);
    }

    std::string ToString() const;
};

交易輸出定義,見src/primitives/transaction.h

/** An output of a transaction.  It contains the public key that the next input
 * must be able to sign with to claim it.
 */
class CTxOut
{
public:
    CAmount nValue;
    CScript scriptPubKey;

    CTxOut()
    {
        SetNull();
    }

    CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn);

    ADD_SERIALIZE_METHODS;

    template <typename Stream, typename Operation>
    inline void SerializationOp(Stream& s, Operation ser_action) {
        READWRITE(nValue);
        READWRITE(scriptPubKey);
    }

    void SetNull()
    {
        nValue = -1;
        scriptPubKey.clear();
    }

    bool IsNull() const
    {
        return (nValue == -1);
    }

    friend bool operator==(const CTxOut& a, const CTxOut& b)
    {
        return (a.nValue       == b.nValue &&
                a.scriptPubKey == b.scriptPubKey);
    }

    friend bool operator!=(const CTxOut& a, const CTxOut& b)
    {
        return !(a == b);
    }

    std::string ToString() const;
};

2.2 交易腳本

比特幣腳本語言是專爲比特幣而設計的簡化版本的程序語言。比特幣腳本語言有意設計成非圖靈完備語言,計算表達能力受到了一定的限制。最主要的有三點:1.數據存儲在棧中,不支持定義變量;2. 限制比特幣腳本的可用內存和執行時間;3. 比特幣腳本中不支持循環,避免死循環導致浪費比特幣礦工計算資源。
比特幣腳本指令集合,總共有256個操作,其中15個禁用,75個保留。主要的操作爲:1. 算數操作,如加減法 2. 分支判斷 if/then 3. 邏輯和數據處理 與或非,拋出異常和捕獲異常,提前返回等 4. 加密算法,包括哈希函數,簽名認證,多重簽名認證等。 值得注意的是,比特幣腳本語言具有強大的加密算法庫,支持在一條操作中對多重簽名進行驗證,即OP_CHECKMULTISIG操作。多重簽名意味着,指定n個公鑰,指定一個閾值參數t,表示如果操作想要正常執行,至少匹配n個公鑰中的的t個簽名。

2.3 未消費交易輸出, UTXO

終於到了“幣”的環節, “幣”在比特幣的源碼世界裏,定義如下,見src/coins.h

/**
 * A UTXO entry.
 *
 * Serialized format:
 * - VARINT((coinbase ? 1 : 0) | (height << 1))
 * - the non-spent CTxOut (via CTxOutCompressor)
 */
class Coin
{
public:
    //! unspent transaction output
    CTxOut out;

    //! whether containing transaction was a coinbase
    unsigned int fCoinBase : 1;

    //! at which height this containing transaction was included in the active block chain
    uint32_t nHeight : 31;

    //! construct a Coin from a CTxOut and height/coinbase information.
    Coin(CTxOut&& outIn, int nHeightIn, bool fCoinBaseIn) : out(std::move(outIn)), fCoinBase(fCoinBaseIn), nHeight(nHeightIn) {}
    Coin(const CTxOut& outIn, int nHeightIn, bool fCoinBaseIn) : out(outIn), fCoinBase(fCoinBaseIn),nHeight(nHeightIn) {}

    void Clear() {
        out.SetNull();
        fCoinBase = false;
        nHeight = 0;
    }

    //! empty constructor
    Coin() : fCoinBase(false), nHeight(0) { }

    bool IsCoinBase() const {
        return fCoinBase;
    }

    template<typename Stream>
    void Serialize(Stream &s) const {
        assert(!IsSpent());
        uint32_t code = nHeight * 2 + fCoinBase;
        ::Serialize(s, VARINT(code));
        ::Serialize(s, CTxOutCompressor(REF(out)));
    }

    template<typename Stream>
    void Unserialize(Stream &s) {
        uint32_t code = 0;
        ::Unserialize(s, VARINT(code));
        nHeight = code >> 1;
        fCoinBase = code & 1;
        ::Unserialize(s, REF(CTxOutCompressor(out)));
    }

    bool IsSpent() const {
        return out.IsNull();
    }

    size_t DynamicMemoryUsage() const {
        return memusage::DynamicUsage(out.scriptPubKey);
    }
};

參考資料

  • 普林斯頓大學公開課 Coursera
  • Mastering Bitcoin
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章