bCamp 是由國內區塊鏈頂級開發者、極客與投資人共同發起,旨在發現和培養國內頂級的區塊鏈技術合夥人,圍繞區塊鏈核心技術打造國內頂級的區塊鏈開發者社羣。

UTXO 與賬戶餘額模型

UTXO 與賬戶餘額模型

這篇文章將要介紹的就是 Bitcoin 以及衆多的加密貨幣,比如 Ethereum、NEO 和 Qtum 的底層結構究竟是什麼樣的。

目前的絕大多數區塊鏈項目不是使用_UTXO 模型_作爲底層的數據結構,就是使用_賬戶餘額模型_存儲交易相關的信息。

UTXO 與賬戶餘額模型

在這篇文章中,我們會分別展示兩種不同區塊鏈模型的實現方式以及優缺點,我們會以 Bitcoin 和 Ethereum 爲例分別介紹 UTXO 模型和賬戶餘額模型。

1

區塊與區塊鏈

在具體介紹 UTXO 模型和賬戶餘額模型之前,我們不得不首先介紹它們兩者、甚至所有區塊鏈應用中最重要的概念和數據結構,也就是_區塊(Block)_。區塊鏈其實就是由一個長度不斷增長的鏈表組成的,其中包含了很多記錄,也就是區塊。

UTXO 與賬戶餘額模型

在上述區塊鏈網絡中,綠色的區塊都被包含在主鏈中,所有黃色的區塊都是孤塊(Orphan Block),它們沒有被主鏈接受,在每一個區塊鏈網絡中只能有一條主鏈,也就是最長的有效鏈,也是當前區塊鏈網絡中所有節點達成的共識。

區塊

想要了解區塊到底是什麼,最簡單快捷的辦法就是分析它的數據結構,以 Bitcoin 中的區塊 #514095 爲例:

    {  \"hash\":\"00000000000000000018b0a6ae560fa33c469b6528bc9e0fb0c669319a186c33\",  \"confirmations\":1009,  \"strippedsize\":956228,  \"size\":1112639,  \"weight\":3981323,  \"height\":514095,  \"version\":536870912,  \"versionHex\":\"20000000\",  \"merkleroot\":\"5f8f8e053fd4c0c3175c10ac5189c15e6ba218909319850936fe54934dcbfeac\",  \"tx\":[    // ...  ],  \"time\":1521380124,  \"mediantime\":1521377506,  \"nonce\":3001236454,  \"bits\":\"17514a49\",  \"difficulty\":3462542391191.563,  \"chainwork\":\"0000000000000000000000000000000000000000014d2b41a340e60b72292430\",  \"previousblockhash\":\"000000000000000000481ab128418847dc25db4dafec464baa5a33e66490990b\",  \"nextblockhash\":\"0000000000000000000c74966205813839ad1c6d55d75f95c9c5f821db9c3510\"}

在這個 Block 的結構體中,

previousblockhashmerkleroot 是兩個最重要的字段;前者是一個哈希指針,它其實是前一個 Block 的哈希,通過 previousblockhash 我們能遞歸地找到全部的 Block,也就是整條主鏈,後者是一個 Merkle 樹的根,Merkle 樹中包含整個 Block 中的全部交易,通過保存 merkleroot,我們可以保證當前 Block 中任意交易都不會被修改。

Ethereum 的區塊鏈模型雖然與 Bitcoin 有非常大的不同,但是它的 Block 結構中也有着類似的信息:

    {
       \"jsonrpc\":\"2.0\",
       \"result\":{
          \"author\":\"0x00d8ae40d9a06d0e7a2877b62e32eb959afbe16d\",
          \"difficulty\":\"0x785042b0\",
          \"extraData\":\"0x414952412f7630\",
          \"gasLimit\":\"0x47b784\",
          \"gasUsed\":\"0x44218a\",
          \"hash\":\"0x4de91e4af8d135e061d50ddd6d0d6f4119cd0f7062ebe8ff2d79c5af0e8344b9\",
          \"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",
          \"miner\":\"0x00d8ae40d9a06d0e7a2877b62e32eb959afbe16d\",
          \"mixHash\":\"0xb8155224974967443d8b83e484402fb6e1e18ff69a8fc5acdda32f2bcc6dd443\",
          \"nonce\":\"0xad14fb6803147c7c\",
          \"number\":\"0x2000f1\",
          \"parentHash\":\"0x31919e2bf29306778f50bbc376bd490a7d056ddfd5b1f615752e79f32c7f1a38\",
          \"receiptsRoot\":\"0xa2a7af5e3b9e1bbb6252ba82a09302321b8f0eea7ec8e3bb977401e4f473e672\",
          \"sealFields\":[
             \"0xa0b8155224974967443d8b83e484402fb6e1e18ff69a8fc5acdda32f2bcc6dd443\",
             \"0x88ad14fb6803147c7c\"
          ],
          \"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",
          \"size\":\"0x276\",
          \"stateRoot\":\"0x87e7e54cf229003014f453d64f0344e2ba4fc7ee3b95c7dd2642cca389fa1efe\",
          \"timestamp\":\"0x5a10968a\",
          \"totalDifficulty\":\"0x1804de0c47ffe1\",
          \"transactions\":[...],
          \"transactionsRoot\":\"0xc2091b032961ca23cf8323ea827e8956fe6dda9e68d75bcfaa8b910035397e35\",
          \"uncles\":[]
       },

transactionsRoot`parentHash 分別對應着 Bitcoin 中 merkleroot 和previousblockhash`,這兩者在整個區塊鏈網絡中是非常重要的。

哈希指針

Block 結構體中的哈希指針在區塊鏈中有兩個作用,它不僅能夠連接不同的區塊,還能夠對 Block 進行驗證,保證 Block 中的數據不會被其他惡意節點篡改。

UTXO 與賬戶餘額模型

除了第一個 Block,每一個 Block 中的 prev_hash 都是前一個 Block 的哈希,如果某一個節點想要修改主鏈上 Block 的交易,就會改變當前 Block 的哈希,後面的 Block 就沒有辦法通過 prev_hash 找到前面的鏈,所以當前節點篡改交易的行爲就會被其他節點發現。

Merkle Tree

另一個字段 merkleroot 其實就是一個 Merkle 樹 的根節點,它其實是一種使用哈希指針連接的數據結構;雖然 Merkle 樹有葉節點和非葉節點,但是它只有葉節點會存儲數據,所有的非葉結點都是用於驗證數據完整性的哈希。

UTXO 與賬戶餘額模型

每一個 Block 中的全部交易都是存儲在這個 Merkle 樹中並將 merkleroot 保存在 Block 的結構體中,保證當前 Block 中任意交易的篡改都能被立刻發現。

小結

prev_hashmerkleroot 分別通過『指針』的方式保證所有的 Block 和交易都是連接起來的,最終保證 Block 和交易不會被惡意節點或攻擊者篡改,幾乎全部的區塊鏈項目都會使用類似方式連接不同的 Block 和交易,這可以說是區塊鏈項目的基礎設施和標配了。

2

UTXO 模型

作爲最早出現的加密貨幣,Bitcoin 就採用了 UTXO 模型作爲其底層存儲的數據結構,其全稱爲_Unspent Transaction output_,也就是未被使用的交易輸出。

在 Bitcoin 以及其他使用 UTXO 模型的加密貨幣中,某一個『賬戶』中的餘額並不是由一個數字表示的,而是由當前區塊鏈網絡中所有跟當前『賬戶』有關的 UTXO 組成的。

UTXO 與賬戶餘額模型

上圖中所有綠色的交易輸出纔是 UTXO,紅色的交易輸出已經被當前『賬戶』使用了,所以在計算當前賬戶的餘額時只會考慮綠色的交易輸出,也就是 UTXO。

    {
       \"addr\":\"14uhqGYDEhqwfdoP59QdLWdt4ha5CHttwQ\",
       \"n\":1,
       \"script\":\"76a9142ae017a5bd24a3f935897085253e503fbfd66f4e88ac\",
       \"spent\":false,
       \"tx_index\":335926477,
       \"type\":0,
       \"value\":21680000}

上述的 UTXO 中包含了很多信息,例如:包含當前 UTXO 屬於的交易索引 tx_index、交易接收方的地址 addr、交易的數額 value

交易

UTXO 其實就是交易的一部分,基於 UTXO 模型的交易由輸入和輸出兩個部分組成:

    {
       \"txid\":\"5be7a9e47f56c98e5297a44df52da0475f448ece98bb51489103cdf70653092f\",
       \"hash\":\"5be7a9e47f56c98e5297a44df52da0475f448ece98bb51489103cdf70653092f\",
       \"version\":1,
       \"size\":224,
       \"vsize\":224,
       \"locktime\":0,
       \"vin\": [...],
       \"vout\": [...],
       \"hex\":\"0100000001a90b4101e6cbb75e1ff885b6358264627581e9f96db9ae609acec98d72422067000000006b483045022100c42c89eb2b10aeefe27caea63f562837b20290f0a095bda39bec37f2651af56b02204ee4260e81e31947d9297e7e9e027a231f5a7ae5e21015aabfdbdb9c6bbcc76e0121025e6e9ba5111117d49cfca477b9a0a5fba1dfcd18ef91724bc963f709c52128c4ffffffff02a037a0000000000017a91477df4f8c95e3d35a414d7946362460d3844c2c3187e6f6030b000000001976a914aba7915d5964406e8a02c3202f1f8a4a63e95c1388ac00000000\",
       \"blockhash\":\"0000000000000000000c23ca00756364067ce5e815deb5982969df476bfc0b5c\",
       \"confirmations\":5,
       \"time\":1521981077,
       \"blocktime\":1521981077  
    }

交易對象中的大多數其它字段並沒有什麼意義,只是對當前的交易進行了一些描述,讓我們能夠更好的理解當前交易的相關信息,例如:上述交易中的 sizevsize 字段可以從交易其他部分計算出來。

在每一筆合法的交易中,所有的輸入的 value 之和必須大於所有輸出的 value 之和,這兩者之間的差值就是礦工費:

    sum(inputs.value) = sum(outputs.value) + fee

基於 UTXO 的交易模型,與我們在日常生活中使用紙幣的場景是非常相似的,每一張紙幣都是不可分割的整體,當我們想要使用現金購買商品或者服務時,往往都會獲得找零

    inputs = price + change + fee

每一個 UTXO 和紙幣一樣,只可能有兩種狀態,要麼是沒有被花費的,要麼就是已經被花費,所有權變成了其他人或者地址,成爲其他地址的 UTXO。

UTXO 與賬戶餘額模型

在基於 UTXO 的區塊鏈網絡中,除了找零(Change)非常常見之外,將多個 UTXO 整合(Consolidate)成一個 UTXO 的操作也比較常見,在 Bitcoin 的網絡中,無論當前的 UTXO 中有多少錢,每一個 UTXO 的大小都是差不多的,所以在進行大額轉賬時,往往需要多個 UTXO 作爲輸入,這樣會明顯的增加交易的礦工費。

輸入和簽名

UTXO 模型中的每一筆交易都是由多個交易輸入組成的,這些輸入其實就是 UTXO + 簽名:

    {
       \"vin\":[
          {
             \"txid\":\"672042728dc9ce9a60aeb96df9e9817562648235b685f81f5eb7cbe601410ba9\",
             \"vout\":0,
             \"scriptSig\":{
                \"asm\":\"3045022100c42c89eb2b10aeefe27caea63f562837b20290f0a095bda39bec37f2651af56b02204ee4260e81e31947d9297e7e9e027a231f5a7ae5e21015aabfdbdb9c6bbcc76e[ALL] 025e6e9ba5111117d49cfca477b9a0a5fba1dfcd18ef91724bc963f709c52128c4\",
                \"hex\":\"483045022100c42c89eb2b10aeefe27caea63f562837b20290f0a095bda39bec37f2651af56b02204ee4260e81e31947d9297e7e9e027a231f5a7ae5e21015aabfdbdb9c6bbcc76e0121025e6e9ba5111117d49cfca477b9a0a5fba1dfcd18ef91724bc963f709c52128c4\"
             },
             \"sequence\":4294967295
          }
       ]}

上述 JSON 其實就是 Bitcoin 交易 #338309214 的輸入,這裏的 prev_out 就來自於另一筆交易 #338283541 的輸出,通過不停的回溯,最終我們會找到當前交易涉及的 Coinbase,也就是當前 UTXO 相關 Bitcoin 被挖出來的 Block 的首筆交易。

通過 txidvout 兩個字段,我們能夠在區塊鏈網絡中定位到唯一一個 UTXO,這個 UTXO 加上持有當前 UTXO 的地址對交易的簽名構成了一個交易輸入。

輸出

每一個交易都可能會有多個輸出,也就是 vout 數組,每一個 vout 都可以指向不同的地址,其中也有當前輸出包含的值 value,在這裏也就是 Bitcoin 的單位:

    {
       \"vout\":[
          {
             \"value\":0.10500000,
             \"n\":0,
             \"scriptPubKey\":{
                \"asm\":\"OP_HASH160 77df4f8c95e3d35a414d7946362460d3844c2c31 OP_EQUAL\",
                \"hex\":\"a91477df4f8c95e3d35a414d7946362460d3844c2c3187\",
                \"reqSigs\":1,
                \"type\":\"scripthash\",
                \"addresses\":[
                   \"3CcqrGq4oQcfx3u75ijj4tDiqf4HJvhoeP\"
                ]
             }
          },
          {
             \"value\":1.84809190,
             \"n\":1,
             \"scriptPubKey\":{
                \"asm\":\"OP_DUP OP_HASH160 aba7915d5964406e8a02c3202f1f8a4a63e95c13 OP_EQUALVERIFY OP_CHECKSIG\",
                \"hex\":\"76a914aba7915d5964406e8a02c3202f1f8a4a63e95c1388ac\",
                \"reqSigs\":1,
                \"type\":\"pubkeyhash\",
                \"addresses\":[
                   \"1GedHcxdxq2tab98hqAmREUK9BBYHKznof\"
                ]
             }
          }
       ]}

每一個未被使用的 vout 就是一個 UTXO (Unspent Transaction Output),我們可以通過其中的 addresses 字段找到持有當前輸出的地址。

小結

UTXO 模型通過鏈式的方式組織所有交易的輸入和輸出,每一個交易的輸出最終都能追尋到一個 Coinbase,也就是當前 Bitcoin 被挖出時的區塊的第一筆交易。

UTXO 與賬戶餘額模型

由於在 UTXO 中沒有賬戶的概念,所以並行地處理交易不會出現任何問題,同時不可變的賬本能夠讓我們在 Bitcoin 節點快速更新時,也能分析某一時刻整個網絡中數據的快照。

雖然 UTXO 模型的不可變賬本條目帶來一些好處,但是當我們需要計算某個地址中的餘額時,需要遍歷整個網絡中的全部相關區塊,同時,並行的處理交易雖然可行,不過並行的創建交易卻會出現很多問題,例如多筆交易使用了同一個 UTXO,導致雙花,最終只有一筆交易能夠被網絡確認。

UTXO 模型確實能夠解決區塊鏈世界中的各種問題,它的核心思想就是保證已經寫入的數據不可變,鏈式的 UTXO 就是基於這一核心思想的,通過哈希指針連接不同交易的輸入和輸出,保證所有交易的合法性。

3

賬戶餘額模型

與 UTXO 模型不同的就是賬戶餘額模型,它跟現實世界中的銀行賬戶非常相似,Ethereum 就使用了賬戶餘額模型存儲區塊鏈中的數據。

UTXO 與賬戶餘額模型

相比於 UTXO 模型,賬戶餘額模型更加容易實現和理解,如果忽略很多 Ethereum 的實現細節,那麼在整個網絡中只存在三種對象:賬戶、交易和區塊。在這裏,我們會介紹該模型中的三個最重要的概念,雖然 Block 並不是賬戶餘額模型中獨有的概念,但是我們也會介紹它在 Ethereum 中有什麼特殊之處。

賬戶

Ethereum 其實就是一個巨大的狀態機,其中的狀態都是由多個賬戶組成的,每一個賬戶都包含四個字段 (nonce, ether_balance, contact_code, storage)

UTXO 與賬戶餘額模型

在 Ethereum 中有兩種類型的賬戶,一種是被私鑰控制的賬戶,它沒有任何的代碼,與 Bitcoin 地址基本有完全相同的功能,能夠向網絡中發送已簽名的交易;另一種是被合約代碼控制的賬戶,能夠在每一次收到消息時,執行保存在 contract_code 中的代碼,所有的合約在網絡中都能夠響應其他賬戶的請求和消息並提供一些服務。

所有賬戶的 nonce 都必須從 0 開始遞增,當前賬戶每使用 nonce 簽發並廣播一筆交易之後,都會將其 +1;UTXO 模型決定了來自同一地址的多筆金額相同的交易完全不同,從原理上避免了重放攻擊,因爲賬戶餘額模型中不存在 UTXO,交易僅僅是賬戶 ether_balance 的變動,所以在這裏引入 nonce 來解決重放攻擊的問題。

合約賬戶

被私鑰控制的賬戶與 Bitcoin 中地址其實差不多,所以沒有什麼可以解釋的,這裏簡單介紹一些合約賬戶。目前 Ethereum 網絡上最多的合約賬戶應該就是 ERC20 的合約了,我們平時經常說的 Token 就是 Ethereum 上的合約,這些合約其實也是 Ethereum 賬戶:

    contract ERC20Interface {
        function totalSupply() public constant returns (uint);
        function balanceOf(address tokenOwner) public constant returns (uint balance);
        function allowance(address tokenOwner, address spender) public constant returns (uint remaining);
        function transfer(address to, uint tokens) public returns (bool success);
        function approve(address spender, uint tokens) public returns (bool success);
        function transferFrom(address from, address to, uint tokens) public returns (bool success);

        event Transfer(address indexed from, address indexed to, uint tokens);
        event Approval(address indexed tokenOwner, address indexed spender, uint tokens);}

Token 的轉賬其實就是合約的調用,所有的賬戶餘額都是存儲在這個合約的 balances 中:

    mapping(address => uint256) balances;

每一筆 Token 的轉賬都會改變這個 mapping 中對應地址的值併發出 Transfer 事件,這也就是 Token 的實現原理;部署 Token 的智能合約其實非常簡單,很多加密貨幣項目其實都只是一個 ERC20 的 Token,發行的成本幾乎爲 0。

交易

由於沒有 Bitcoin 複雜的 UTXO 模型,Ethereum 的交易模型也簡單,交易中沒有輸入和輸出的 TransactionIO 結構,只有 fromto 兩個地址:

    {
       \"blockHash\":\"0xb4a992ff99a487db8421f516be998920f06dfe5d355d88e3b7f22b7422e6340d\",
       \"blockNumber\":\"0x24f85c\",
       \"chainId\":null,
       \"condition\":null,
       \"creates\":null,
       \"from\":\"0x8b56adcf332ff80a1f1bf433975dcb28b730d110\",
       \"to\":\"0xe94b04a0fed112f3664e45adb2b8915693dd5ff3\",
       \"value\":\"0x10d43fb8311ca800\",
       \"gas\":\"0x2062a\",
       \"gasPrice\":\"0x560aab7c5\",
       \"hash\":\"0xfea448d11cfa863c8b3c38d3b65649e66c1957f9ac16638e3a0edff45a6b3d84\",
       \"input\":\"0x0f2c9329000000000000000000000000fbb1b73c4f0bda4f67dca266ce6ef42f520fbb98000000000000000000000000e592b0d8baa2cb677034389b76a71b0d1823e0d1\",
       \"nonce\":\"0x3fe\",
       \"publicKey\":\"0x765b0f012e49f6a4cc5c917fb176984b24814bdaf5f9464db1a7f9ffcc730cb678f69e49f78aa9de8249cce138bbc25cf8842374d8d09089dff7f1ef6906f4fb\",
       \"r\":\"0x4275d35821dec971f6d58c2adae077ffcdfa3ec74af542a2d29ab4e5239d8b25\",
       \"raw\":\"0xf8b48203fe850560aab7c58302062a94e94b04a0fed112f3664e45adb2b8915693dd5ff38810d43fb8311ca800b8440f2c9329000000000000000000000000fbb1b73c4f0bda4f67dca266ce6ef42f520fbb98000000000000000000000000e592b0d8baa2cb677034389b76a71b0d1823e0d11ca04275d35821dec971f6d58c2adae077ffcdfa3ec74af542a2d29ab4e5239d8b25a036221b525c758c45e60f964eec698ae33208dfa74bea7f77dff002ceec418b0a\",
       \"s\":\"0x36221b525c758c45e60f964eec698ae33208dfa74bea7f77dff002ceec418b0a\",
       \"standardV\":\"0x1\",
       \"transactionIndex\":\"0x8\",
       \"v\":\"0x1c\"}

交易的手續費也不再是交易輸入輸出 value 的差值,而是使用 gasgasPrice 來表示,爲了防止重放攻擊,這裏也引入了 nonce 字段。

UTXO 與賬戶餘額模型

(nonce, from, to, value, input) 是一個 Transaction 包含的最重要的幾個字段,通過 nonce 防止重放攻擊,fromto 分別表示了當前交易的發出者和接受者,value 是當前交易包含的 Etherinput 中包含了合約調用相關的二進制信息。

每當一個 Transaction 被 Ethereum 主網挖到後,fromto 賬戶的 Ether 餘額就會變動,Ethereum 就像一個狀態機,它接受一個又一個的 Transaction 並不停改變自己的狀態。

UTXO 與賬戶餘額模型

小結

賬戶餘額模型是一種非常容易理解的區塊鏈應用模型,它與我們生活中的賬戶模型非常相似,只是爲了保證賬戶的安全,使用了簽名以及 nonce 的機制阻止惡意的攻擊。這種基於賬戶餘額模型的應用包含了一個包含所有賬戶餘額的全局狀態,在進行轉賬時,需要由節點對賬戶的餘額進行驗證,判斷當前賬戶是否有足夠的 Ether 進行轉賬。

4

總結

無論是 UTXO 模型還是賬戶餘額模型,都能夠很好地解決區塊鏈世界中的『安全』問題,保證交易的合法,從原理上杜絕一些可能的攻擊行爲,實現原理的不同其實也只是由於出發點不同,在設計時權衡了利弊;UTXO 模型相比於賬戶餘額模型有以下的兩個優點:

  • 如果用戶啓用了新的地址用於轉賬和交易,新地址與原地址之間的關係很難被追蹤,更好地保證用戶的隱私;

  • UTXO 模型理論上來說可以並行地利用不同的 UTXO 簽發多筆交易,並廣播到網絡中;

而賬戶餘額模型也有它的優點:

  • 非常容易理解和編碼實現;

  • 每一筆交易都只需要有一個簽名,交易的輸入和輸出都是地址,能夠節省存儲空間;

  • 由於在區塊鏈層級沒有『幣的來源』這一概念,很難實現對來源的追蹤和回溯;

  • 因爲創建交易時不需要對過去的 UTXO 進行簽名,可以從任何一個時間點開始同步區塊的狀態,利於編寫輕量級客戶端;

總而言之,軟件開發到最後就是進行權衡的過程,選擇一些優勢必然會在另外一些領域上處於劣勢,如何在不同的需求進行權衡是開發區塊鏈應用以及所有應用都需要考慮的事情。

Reference

  • White Paper · Ethereum

  • Rationale for and tradeoffs in adopting a UTXO-style model

  • What are the pros and cons of Ethereum balances vs. UTXOs?

  • Design Rationale · Ethereum Wiki

  • ERC20 Token Standard

  • How to issue your own token on Ethereum in less than 20 minutes.

UTXO 與賬戶餘額模型

內容來源:Draveness.GitHub

原文鏈接:

https://draveness.me/utxo-account-models

UTXO 與賬戶餘額模型

_
_

以下是我們的知識星球,歡迎有興趣的小夥伴加入我們,共同成長 !

_
_

UTXO 與賬戶餘額模型