UTXO 与账户余额模型
UTXO 與賬戶余額模型
從寫上一篇介紹區塊鏈共識算法的文章?分布式一致性與共識算法?到現在已經過去了三個多月的時間;雖然整個行業內有非常多的變化,但是區塊鏈技術,尤其是底層技術卻沒有太多的改變。這篇文章將要介紹的就是 Bitcoin 以及眾多的加密貨幣,比如 Ethereum、NEO 和 Qtum 的底層結構究竟是什么樣的。
目前的絕大多數區塊鏈項目不是使用 UTXO 模型作為底層的數據結構,就是使用賬戶余額模型存儲交易相關的信息。
在這篇文章中,我們會分別展示兩種不同區塊鏈模型的實現方式以及優缺點,我們會以 Bitcoin 和 Ethereum 為例分別介紹 UTXO 模型和賬戶余額模型。
區塊與區塊鏈
在具體介紹 UTXO 模型和賬戶余額模型之前,我們不得不首先介紹它們兩者、甚至所有區塊鏈應用中最重要的概念和數據結構,也就是區塊(Block)。區塊鏈其實就是由一個長度不斷增長的鏈表組成的,其中包含了很多記錄,也就是區塊。
在上述區塊鏈網絡中,綠色的區塊都被包含在主鏈中,所有黃色的區塊都是孤塊(Orphan Block),它們沒有被主鏈接受,在每一個區塊鏈網絡中只能有一條主鏈,也就是最長的有效鏈,也是當前區塊鏈網絡中所有節點達成的共識。
區塊
想要了解區塊到底是什么,最簡單快捷的辦法就是分析它的數據結構,以 Bitcoin 中的區塊 #514095 為例:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | { "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 的結構體中,previousblockhash 和 merkleroot 是兩個最重要的字段;前者是一個哈希指針,它其實是前一個 Block 的哈希,通過 previousblockhash 我們能遞歸地找到全部的 Block,也就是整條主鏈,后者是一個 Merkle 樹的根,Merkle 樹中包含整個 Block 中的全部交易,通過保存 merkleroot,我們可以保證當前 Block 中任意交易都不會被修改。
Ethereum 的區塊鏈模型雖然與 Bitcoin 有非常大的不同,但是它的 Block 結構中也有著類似的信息:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | { ???"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":[] ???}, ???"id":1 } |
parentHash 和 transactionsRoot 分別對應著 Bitcoin 中 previousblockhash 和 merkleroot,這兩者在整個區塊鏈網絡中是非常重要的。
哈希指針
Block 結構體中的哈希指針在區塊鏈中有兩個作用,它不僅能夠連接不同的區塊,還能夠對 Block 進行驗證,保證 Block 中的數據不會被其他惡意節點篡改。
除了第一個 Block,每一個 Block 中的 prev_hash 都是前一個 Block 的哈希,如果某一個節點想要修改主鏈上 Block 的交易,就會改變當前 Block 的哈希,后面的 Block 就沒有辦法通過 prev_hash 找到前面的鏈,所以當前節點篡改交易的行為就會被其他節點發現。
Merkle Tree
另一個字段 merkleroot 其實就是一個 Merkle 樹 的根節點,它其實是一種使用哈希指針連接的數據結構;雖然 Merkle 樹有葉節點和非葉節點,但是它只有葉節點會存儲數據,所有的非葉結點都是用于驗證數據完整性的哈希。
每一個 Block 中的全部交易都是存儲在這個 Merkle 樹中并將 merkleroot 保存在 Block 的結構體中,保證當前 Block 中任意交易的篡改都能被立刻發現。
小結
prev_hash 和 merkleroot 分別通過『指針』的方式保證所有的 Block 和交易都是連接起來的,最終保證 Block 和交易不會被惡意節點或攻擊者篡改,幾乎全部的區塊鏈項目都會使用類似方式連接不同的 Block 和交易,這可以說是區塊鏈項目的基礎設施和標配了。
UTXO 模型
作為最早出現的加密貨幣,Bitcoin 就采用了 UTXO 模型作為其底層存儲的數據結構,其全稱為 Unspent Transaction output,也就是未被使用的交易輸出。
在 Bitcoin 以及其他使用 UTXO 模型的加密貨幣中,某一個『賬戶』中的余額并不是由一個數字表示的,而是由當前區塊鏈網絡中所有跟當前『賬戶』有關的 UTXO 組成的。
上圖中所有綠色的交易輸出才是 UTXO,紅色的交易輸出已經被當前『賬戶』使用了,所以在計算當前賬戶的余額時只會考慮綠色的交易輸出,也就是 UTXO。
| 1 2 3 4 5 6 7 8 9 | { ???"addr":"14uhqGYDEhqwfdoP59QdLWdt4ha5CHttwQ", ???"n":1, ???"script":"76a9142ae017a5bd24a3f935897085253e503fbfd66f4e88ac", ???"spent":false, ???"tx_index":335926477, ???"type":0, ???"value":21680000 } |
上述的 UTXO 中包含了很多信息,例如:包含當前 UTXO 屬于的交易索引 tx_index、交易接收方的地址 addr、交易的數額 value。
交易
UTXO 其實就是交易的一部分,基于 UTXO 模型的交易由輸入和輸出兩個部分組成:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | { ???"txid":"5be7a9e47f56c98e5297a44df52da0475f448ece98bb51489103cdf70653092f", ???"hash":"5be7a9e47f56c98e5297a44df52da0475f448ece98bb51489103cdf70653092f", ???"version":1, ???"size":224, ???"vsize":224, ???"locktime":0, ???"vin":?[...], ???"vout":?[...], ???"hex":"0100000001a90b4101e6cbb75e1ff885b6358264627581e9f96db9ae609acec98d72422067000000006b483045022100c42c89eb2b10aeefe27caea63f562837b20290f0a095bda39bec37f2651af56b02204ee4260e81e31947d9297e7e9e027a231f5a7ae5e21015aabfdbdb9c6bbcc76e0121025e6e9ba5111117d49cfca477b9a0a5fba1dfcd18ef91724bc963f709c52128c4ffffffff02a037a0000000000017a91477df4f8c95e3d35a414d7946362460d3844c2c3187e6f6030b000000001976a914aba7915d5964406e8a02c3202f1f8a4a63e95c1388ac00000000", ???"blockhash":"0000000000000000000c23ca00756364067ce5e815deb5982969df476bfc0b5c", ???"confirmations":5, ???"time":1521981077, ???"blocktime":1521981077 } |
交易對象中的大多數其它字段并沒有什么意義,只是對當前的交易進行了一些描述,讓我們能夠更好的理解當前交易的相關信息,例如:上述交易中的 size 和 vsize 字段可以從交易其他部分計算出來。
在每一筆合法的交易中,所有的輸入的 value 之和必須小于所有輸出的 value 之和,這兩者之間的差值就是礦工費:
| 1 | sum(inputs.value)?=?sum(outputs.value)?+?fee |
基于 UTXO 的交易模型,與我們在日常生活中使用紙幣的場景是非常相似的,每一張紙幣都是不可分割的整體,當我們想要使用現金購買商品或者服務時,往往都會獲得找零。
| 1 | inputs?=?price?+?change?+?fee |
每一個 UTXO 和紙幣一樣,只可能有兩種狀態,要么是沒有被花費的,要么就是已經被花費,所有權變成了其他人或者地址,成為其他地址的 UTXO。
在基于 UTXO 的區塊鏈網絡中,除了找零(Change)非常常見之外,將多個 UTXO 整合(Consolidate)成一個 UTXO 的操作也比較常見,在 Bitcoin 的網絡中,無論當前的 UTXO 中有多少錢,每一個 UTXO 的大小都是差不多的,所以在進行大額轉賬時,往往需要多個 UTXO 作為輸入,這樣會明顯的增加交易的礦工費。
輸入和簽名
UTXO 模型中的每一筆交易都是由多個交易輸入組成的,這些輸入其實就是 UTXO + 簽名:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | { ???"vin":[ ??????{ ?????????"txid":"672042728dc9ce9a60aeb96df9e9817562648235b685f81f5eb7cbe601410ba9", ?????????"vout":0, ?????????"scriptSig":{ ????????????"asm":"3045022100c42c89eb2b10aeefe27caea63f562837b20290f0a095bda39bec37f2651af56b02204ee4260e81e31947d9297e7e9e027a231f5a7ae5e21015aabfdbdb9c6bbcc76e[ALL]?025e6e9ba5111117d49cfca477b9a0a5fba1dfcd18ef91724bc963f709c52128c4", ????????????"hex":"483045022100c42c89eb2b10aeefe27caea63f562837b20290f0a095bda39bec37f2651af56b02204ee4260e81e31947d9297e7e9e027a231f5a7ae5e21015aabfdbdb9c6bbcc76e0121025e6e9ba5111117d49cfca477b9a0a5fba1dfcd18ef91724bc963f709c52128c4" ?????????}, ?????????"sequence":4294967295 ??????} ???] } |
上述 JSON 其實就是 Bitcoin 交易?#338309214?的輸入,這里的 prev_out 就來自于另一筆交易 #338283541 的輸出,通過不停的回溯,最終我們會找到當前交易涉及的 Coinbase,也就是當前 UTXO 相關 Bitcoin 被挖出來的 Block 的首筆交易。
通過 txid 和 vout 兩個字段,我們能夠在區塊鏈網絡中定位到唯一一個 UTXO,這個 UTXO 加上持有當前 UTXO 的地址對交易的簽名構成了一個交易輸入。
輸出
每一個交易都可能會有多個輸出,也就是 vout 數組,每一個 vout 都可以指向不同的地址,其中也有當前輸出包含的值 value,在這里也就是 Bitcoin 的單位:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | { ???"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 中沒有賬戶的概念,所以并行地處理交易不會出現任何問題,同時不可變的賬本能夠讓我們在 Bitcoin 節點快速更新時,也能分析某一時刻整個網絡中數據的快照。
雖然 UTXO 模型的不可變賬本條目帶來一些好處,但是當我們需要計算某個地址中的余額時,需要遍歷整個網絡中的全部相關區塊,同時,并行的處理交易雖然可行,不過并行的創建交易卻會出現很多問題,例如多筆交易使用了同一個 UTXO,導致雙花,最終只有一筆交易能夠被網絡確認。
UTXO 模型確實能夠解決區塊鏈世界中的各種問題,它的核心思想就是保證已經寫入的數據不可變,鏈式的 UTXO 就是基于這一核心思想的,通過哈希指針連接不同交易的輸入和輸出,保證所有交易的合法性。
賬戶余額模型
與 UTXO 模型不同的就是賬戶余額模型,它跟現實世界中的銀行賬戶非常相似,Ethereum 就使用了賬戶余額模型存儲區塊鏈中的數據。
相比于 UTXO 模型,賬戶余額模型更加容易實現和理解,如果忽略很多 Ethereum 的實現細節,那么在整個網絡中只存在三種對象:賬戶、交易和區塊。在這里,我們會介紹該模型中的三個最重要的概念,雖然 Block 并不是賬戶余額模型中獨有的概念,但是我們也會介紹它在 Ethereum 中有什么特殊之處。
賬戶
Ethereum 其實就是一個巨大的狀態機,其中的狀態都是由多個賬戶組成的,每一個賬戶都包含四個字段 (nonce, ether_balance, contact_code, storage):
在 Ethereum 中有兩種類型的賬戶,一種是被私鑰控制的賬戶,它沒有任何的代碼,與 Bitcoin 地址基本有完全相同的功能,能夠向網絡中發送已簽名的交易;另一種是被合約代碼控制的賬戶,能夠在每一次收到消息時,執行保存在 contract_code 中的代碼,所有的合約在網絡中都能夠響應其他賬戶的請求和消息并提供一些服務。
所有賬戶的 nonce 都必須從 0 開始遞增,當前賬戶每使用 nonce 簽發并廣播一筆交易之后,都會將其 +1;UTXO 模型決定了來自同一地址的多筆金額相同的交易完全不同,從原理上避免了重放攻擊,因為賬戶余額模型中不存在 UTXO,交易僅僅是賬戶 ether_balance 的變動,所以在這里引入 nonce 來解決重放攻擊的問題。
合約賬戶
被私鑰控制的賬戶與 Bitcoin 中地址其實差不多,所以沒有什么可以解釋的,這里簡單介紹一些合約賬戶。目前 Ethereum 網絡上最多的合約賬戶應該就是 ERC20 的合約了,我們平時經常說的 Token 就是 Ethereum 上的合約,這些合約其實也是 Ethereum 賬戶:
| 1 2 3 4 5 6 7 8 9 10 11 | 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 結構,只有 from 和 to 兩個地址:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | { ???"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 的差值,而是使用 gas 和 gasPrice 來表示,為了防止重放攻擊,這里也引入了 nonce 字段。
(nonce, from, to, value, input) 是一個 Transaction 包含的最重要的幾個字段,通過 nonce 防止重放攻擊,from 和 to 分別表示了當前交易的發出者和接受者,value 是當前交易包含的 Ether,input 中包含了合約調用相關的二進制信息。
每當一個 Transaction 被 Ethereum 主網挖到后,from 和 to 賬戶的 Ether 余額就會變動,Ethereum 就像一個狀態機,它接受一個又一個的 Transaction 并不停改變自己的狀態。
小結
賬戶余額模型是一種非常容易理解的區塊鏈應用模型,它與我們生活中的賬戶模型非常相似,只是為了保證賬戶的安全,使用了簽名以及 nonce 的機制阻止惡意的攻擊。這種基于賬戶余額模型的應用包含了一個包含所有賬戶余額的全局狀態,在進行轉賬時,需要由節點對賬戶的余額進行驗證,判斷當前賬戶是否有足夠的 Ether 進行轉賬。
總結
無論是 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.
關于圖片和轉載
本作品采用知識共享署名 4.0 國際許可協議進行許可。 轉載時請注明原文鏈接,圖片在使用時請保留圖片中的全部內容,可適當縮放并在引用處附上圖片所在的文章鏈接,圖片使用 Sketch 進行繪制。
關于評論和留言
如果對本文 UTXO 與賬戶余額模型 的內容有疑問,請在下面的評論系統中留言,謝謝。
原文鏈接:UTXO 與賬戶余額模型 · 面向信仰編程
Follow:?Draveness · GitHub
總結
以上是生活随笔為你收集整理的UTXO 与账户余额模型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 比特币交易原理分析
- 下一篇: 用Java编写第一个区块链(二)