0%

StudyRecord-UTXO与账户余额模型(一)

Pre:

UTXO 与账户余额模型,学习记录。
内容有点多,分成两部分来学习。

目前的绝大多数区块链项目不是使用 UTXO模型作为底层的数据结构,就是使用账户余额模型存储交易相关的信息。
20220809113059
在这篇文章中,我们会分别展示两种不同区块链模型的实现方式以及优缺点,
我们会以 BitcoinEthereum 为例分别介绍 UTXO 模型和账户余额模型。

先看UTXO模型,刚好有一些疑问

这篇文章写得非常好,后续要反复多读几遍🤡🤡🤡

区块与区块链:

在具体介绍 UTXO 模型和账户余额模型之前,我们不得不首先介绍它们两者、甚至所有区块链应用中最重要的概念和数据结构,也就是区块(Block)
区块链其实就是由一个长度不断增长的链表组成的,其中包含了很多记录,也就是区块。

20220809140256
在上述区块链网络中,绿色的区块都被包含在主链中,所有黄色的区块都是孤块(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 的结构体中,previousblockhashmerkleroot 是两个最重要的字段;
前者是一个哈希指针,它其实是前一个 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
}

parentHashtransactionsRoot 分别对应着 Bitcoin 中 previousblockhashmerkleroot,这两者在整个区块链网络中是非常重要的。

哈希指针:

Block 结构体中的哈希指针在区块链中有两个作用,它不仅能够连接不同的区块,还能够对 Block 进行验证,保证 Block 中的数据不会被其他恶意节点篡改。

20220809140617
除了第一个 Block,每一个 Block 中的 prev_hash 都是前一个 Block 的哈希,如果某一个节点想要修改主链上 Block 的交易,就会改变当前 Block 的哈希,后面的 Block 就没有办法通过 prev_hash 找到前面的链,所以当前节点篡改交易的行为就会被其他节点发现。

Merkle Tree:

另一个字段 merkleroot 其实就是一个 Merkle 树 的根节点,它其实是一种使用哈希指针连接的数据结构;
虽然 Merkle 树有叶节点非叶节点,但是它只有叶节点会存储数据,所有的非叶结点都是用于验证数据完整性的哈希。

20220809140715

每一个 Block 中的全部交易都是存储在这个 Merkle 树中并将 merkleroot 保存在 Block 的结构体中,保证当前 Block 中任意交易的篡改都能被立刻发现。

小结:

prev_hashmerkleroot 分别通过『指针』的方式保证所有的 Block 和交易都是连接起来的,最终保证 Block 和交易不会被恶意节点或攻击者篡改,几乎全部的区块链项目都会使用类似方式连接不同的 Block 和交易,这可以说是区块链项目的基础设施和标配了。

UTXO 模型:

作为最早出现的加密货币,Bitcoin 就采用了 UTXO 模型作为其底层存储的数据结构,其全称为 Unspent Transaction output,也就是未被使用的交易输出

在 Bitcoin 以及其他使用 UTXO 模型的加密货币中,某一个『账户』中的余额并不是由一个数字表示的,而是由当前区块链网络中所有跟当前『账户』有关的 UTXO 组成的

专业的定义:UTXO 模型是资产在用户之间移动的有向图

大白话定义:UTXO 模型——假设所有的资产都是一个个的硬币,那我们记下所有硬币的「流动轨迹」,知道当前没花的硬币都在哪里,那么我们无需给每个人创建账户,也能知道每个人有多少钱。

20220809140957
上图中所有绿色的交易输出才是 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。

20220809141452
在基于 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 的首笔交易。

通过 txidvout 两个字段,我们能够在区块链网络中定位到唯一一个 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 被挖出时的区块的第一笔交易。

20220809141725
由于在 UTXO 中没有账户的概念,所以并行地处理交易不会出现任何问题,同时不可变的账本能够让我们在 Bitcoin 节点快速更新时,也能分析某一时刻整个网络中数据的快照。

虽然 UTXO 模型的不可变账本条目带来一些好处,但是当我们需要计算某个地址中的余额时,需要遍历整个网络中的全部相关区块,同时,并行的处理交易虽然可行,不过并行的创建交易却会出现很多问题,例如多笔交易使用了同一个 UTXO,导致双花,最终只有一笔交易能够被网络确认。

UTXO 模型确实能够解决区块链世界中的各种问题,它的核心思想就是保证已经写入的数据不可变,链式的 UTXO 就是基于这一核心思想的,通过哈希指针连接不同交易的输入和输出,保证所有交易的合法性。

Refs:

这篇写得确实好,语言简单而清晰👍🏻👍🏻👍🏻