1. 程式人生 > >比特幣中的默克爾樹Merkle

比特幣中的默克爾樹Merkle

簡介

Merkle Tree,通常也被稱作Hash Tree,顧名思義,就是儲存hash值的一棵樹。Merkle樹的葉子是資料塊(例如,檔案或者檔案的集合)的hash值。非葉節點是其對應子節點串聯字串的hash。
默克爾樹

比特幣中對應的默克爾樹

  • 比特幣中區塊的定義結構
    CBlock
//
// Nodes collect new transactions into a block, hash them into a hash tree,
// and scan through nonce values to make the block's hash satisfy proof-of-work
// requirements.  When they solve the proof-of-work, they broadcast the block
// to everyone and the block is added to the block chain. The first transaction // in the block is a special one that creates a new coin owned by the creator // of the block. // // Blocks are appended to blk0001.dat files on disk. Their location on disk // is indexed by CBlockIndex objects in memory. // class CBlock { public
: // header int nVersion; uint256 hashPrevBlock; uint256 hashMerkleRoot; unsigned int nTime; unsigned int nBits; // 記錄本區塊難度 unsigned int nNonce; // network and disk vector<CTransaction> vtx; // memory only mutable vector<uint256> vMerkleTree; // 對應的方法省略,後面用到會單獨講解
};
  • 構建塊中交易對應的默克爾樹
    對應的程式碼如下:
uint256 CBlock::BuildMerkleTree() const
    {
        vMerkleTree.clear();
        foreach(const CTransaction& tx, vtx)
            vMerkleTree.push_back(tx.GetHash());
        int j = 0;
        for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
        {
            for (int i = 0; i < nSize; i += 2)
            {
                int i2 = min(i+1, nSize-1);
                vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]),  END(vMerkleTree[j+i]),
                                           BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2])));
            }
            j += nSize;
        }
        return (vMerkleTree.empty() ? 0 : vMerkleTree.back());
    }

舉一個例子,假設對應的block中有A,B,C,D四個交易記錄(這些交易記錄儲存在block中的vector vtx)
假設塊中對應的交易vtx中內容有A,B,C,D四個交易記錄

則使用上述構建默克爾樹的方法BuildMerkleTree,則使得block中對應的默克爾樹結構體vector vMerkleTree如下圖所示:
對上述交易構建的默克爾樹結果
默克爾樹對應的樹形圖
* 根據交易在塊中的索引查詢其對應的默克爾樹分支

獲取對應的默克爾樹的分支在CBlock中對應的方法GetMerkleBranch程式碼如下:

    vector<uint256> CBolck::GetMerkleBranch(int nIndex) const
    {
        if (vMerkleTree.empty())
            BuildMerkleTree();
        vector<uint256> vMerkleBranch;
        int j = 0;
        for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2)
        {
            int i = min(nIndex^1, nSize-1);
            vMerkleBranch.push_back(vMerkleTree[j+i]);
            nIndex >>= 1;
            j += nSize;
        }
        return vMerkleBranch;
    }

那麼對應於上面的例子,如果我想得到在交易列表vtx中A對應的默克爾樹分支,則對應函式GetMerkleBranch(0)對應的返回結果列表如下所示:交易索引0對應的默克爾樹分支
紅色節點對應的就是節點A的默克爾樹分支

  • 驗證對應的交易是否在默克爾樹中
    驗證某個節點是否在默克爾樹中,在CBlock中對應的方法程式碼如下:
 static uint256 CBlock::CheckMerkleBranch(uint256 hash, const vector<uint256>& vMerkleBranch, int nIndex)
    {
        if (nIndex == -1)
            return 0;
        foreach(const uint256& otherside, vMerkleBranch)
        {
            if (nIndex & 1)
                hash = Hash(BEGIN(otherside), END(otherside), BEGIN(hash), END(hash));
            else
                hash = Hash(BEGIN(hash), END(hash), BEGIN(otherside), END(otherside));
            nIndex >>= 1;
        }
        return hash;
    }

驗證CheckMerkleBranch方法返回的結果是否和CBlock中對應的默克爾樹中對應根的值一樣,即如下面程式碼所示:

if (CBlock::CheckMerkleBranch(GetHash(), vMerkleBranch, nIndex) != pBlock->hashMerkleRoot)
            return 0;

對應上面的例子對應的驗證過程如下圖所示:
驗證第一步是得到節點E
驗證第二步是得到節點G

比特幣交易對應的資料結構

交易對應的類圖
對應的程式碼如下:

//
// The basic transaction that is broadcasted on the network and contained in
// blocks.  A transaction can contain multiple inputs and outputs.
//
class CTransaction
{
public:
    int nVersion;
    vector<CTxIn> vin;
    vector<CTxOut> vout;
    int nLockTime;
// 方法省略
};
//
// A transaction with a merkle branch linking it to the block chain
//
class CMerkleTx : public CTransaction
{
public:
    uint256 hashBlock;
    vector<uint256> vMerkleBranch;
    int nIndex;

    // memory only
    mutable bool fMerkleVerified;
// 方法省略
};

從類圖或程式碼中可以看到CMerkleTx 中有一個屬性是vMerkleBranch,表示的是對應的每一個默克爾交易都對應一個默克爾樹分支,因此對應默克爾交易可以很快的驗證這個交易是否在某個CBlock中。