Pre:
UTXO 与账户余额模型,学习记录。
内容有点多,分成两部分来学习。
目前的绝大多数区块链项目不是使用 UTXO模型
作为底层的数据结构,就是使用账户余额模型
存储交易相关的信息。
在这篇文章中,我们会分别展示两种不同区块链模型的实现方式以及优缺点,
我们会以 Bitcoin
和 Ethereum
为例分别介绍 UTXO 模型和账户余额模型。
先看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 模型
——假设所有的资产都是一个个的硬币,那我们记下所有硬币的「流动轨迹」,知道当前没花的硬币都在哪里,那么我们无需给每个人创建账户,也能知道每个人有多少钱。
上图中所有绿色的交易输出
才是 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 之和,这两者之间的差值就是矿工费:
sum(inputs.value) = sum(outputs.value) + fee
基于 UTXO 的交易模型,与我们在日常生活中使用纸币的场景是非常相似的,每一张纸币都是不可分割的整体,当我们想要使用现金购买商品或者服务时,往往都会获得找零。
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 就是基于这一核心思想的,通过哈希指针
连接不同交易的输入和输出,保证所有交易的合法性。
Refs:
这篇写得确实好,语言简单而清晰👍🏻👍🏻👍🏻