区块链账号体系之UTXO模型

我的博客,欢迎大家捧场

生活类比

首先看这个名词UTXO (Unspent Transaction Output, “未花费事务输出”),老实说,第一次看到这个术语的时候,一时之间真是有些懵,如果说是未花费的余额还能理解,我钱包里有1000,花了200,还有800未花费,这是很符合通常的理解逻辑的,可这个未花费的“事务输出”是个什么意思,实际上,这与比特币中的交易事务结构是很有关系的。为了让大家更容易理解,我们暂且先不来解析这个交易数据结构,让我们进入到一个仓库,我们知道仓库的主要业务就是进和出,仓库也会把日常的进出流水账记录下来, 为了查询统计方便, 除了流水账通常还会汇总一 份库存表出来,举例如下:

image.png

以上图为例,这是从2017-8- 1倒2017-8- 5之间,仓库记录的出入流水账, 为了统计方便,仓库还汇总保存了一份每天的库存日报表,如下:

image.png

每天仓库在需要出库的时候,只要查看一下库存日报表就知道数量是否足够了,比如2017-8-3需要出库15支毛笔,此时查看库存表发现毛笔的库存量有30支,足够发出,于是就将库存表中的毛笔数量减掉15,并且将出库明细记录在流水账中。然而,这里有一个问题,库存日报表是另外编制保存的,那就有可能发生数据不一致的情况,比如2017-8-2时毛笔的库存本来是30却误写为20,这样导致后续的账务就都是错的了。因此在有些系统中,为了防止出现这样的不一致,索性不再另外保存库存表,而只是出一张视图统计(逻辑统计,并非实际去保存这样一个统计比特币中的交易事务过程与.上述的库存进出是很相像的,某个钱包地址中转入了一笔比特币,然后这个地址又向其他钱包地址转出了一笔比特币,这些不断发生的入和出跟仓库的进出是异曲同工的。然而,在比特币中并没有去保存份“库存表 ”每当“出库”的时候也并不是去“库存表”中进行扣除,而是直接消耗“入库记录”,也就是说在出库的时候就去找有没有之前的入库记录拿来扣除,比如2017-8-3时需要出库15支毛笔,此时系统就会去搜索之前的入库记录,发现有2017-8-1和2017-8-2分别有一笔数量为10和20的入库记录,为了满足15的发出数量,首先可以消耗掉10的这一笔,然后从20的这笔再消耗掉5支, 判断成功后, 系统会直接产生一条数量为10的出库记录和数量为5的出库记录,按这样的方法,将每一笔入和出都对应了起来。在比特币的交易事务结构中,“入”就是指金额转入,“出”就是指金额转出,为了让大家对这种金额转入与转出有一个更加通俗的理解,我们来看一幅 示意图:

image.png

上图展示 了比特币中的交 易事务结构,在比特币的交易事务数据中, 存储的就是这样的输入和输出,相当于仓库中的进出流水账,并且“输入”和“输出’彼此对应,或者更准确地说,“输入”就是指向之前的“ 输出”,我们解释一下图中发生的交易事务。

  • 001号交易为Coinbase交易,也就是挖矿交易,在这个交易中,“输入”部分没有对应的“输出”,而是由系统直接奖励发行比特币,矿工Alice得到了12.5个比特币的奖励,放在001号交易的“输出” 部分。此时,对于Alice来说, 拥有了这12.5个比特币的支配权,这12.5个比特币的输出可以作为下一笔交易的“输入”,顾名思义,这笔“输出”就称之为是Alice的未花费输出,也就是Alice的UTXO的意思。

  • 002号交易中,Alice转 账6比特币到Bob的地址,Alice找到 了自己的UTXO (如果Alice不止一笔UTXO,可以根据一定的规则去选用,比如将小金额的先花费掉)。由于只需要转账6比特币,可是UTXO中却有12.5个,因此需要找零6.5个到自己的地址中,由此产生了002号中的交易输出,注意,在002号交易输出中的Alice地址是可以和001号中的Alice地址不一样的,只要都是属于Alice自己的钱包地址就可以。

  • 003号交易中,Bob转账了2比特币到Lily的地址,过程与002号交易相同,就不再赘述了。相信大家看到这里,已经基本理解了所谓的UTXO是什么意思,我们再来总结一下。

1)比特币的交易中不是通过账户的增减来实现的,而是一笔笔关联的输入/输出交易事务。

2)每一笔的交易都要花费“输入”,然后产生“输出”,这个产生的“输出”就是所谓的“未花费过的交易输出”,也就是UTXO。每一笔交易事务都有一个唯一的编号,称为交易事务ID,这是通过哈希算法计算而来的,当需要引用某一笔交易事务中的“输出”时,主要提供交易事务ID和所处“输出”列表中的序号就可以了。

3)由于没有账户的概念,因此当“输入”部分的金额大于所需的“输出”时,必须给自己找零,这个找零也是作为交易的一部分包含在‘输出”中。有朋友会问:这个UTXO的意思是明白了,可是就这么一条条的“输入”和“输出怎么证明哪一条UTXO是属于谁的呢?

在比特币中,是使用输入脚本和输出脚本程序实现的,有时候也称为“锁定脚本”和“解锁脚本”。简单地说,就是通过“锁定脚本”,利用私钥签名解锁自己的某一条UTXO (也就是之前的“输出”),然后使用对方的公钥锁定新的“输出”,成功后,这笔新的“输出”就成为了对方的UTXO。同样,对方也可以使用“锁定脚本”和“解锁脚本”来实现转账。这个脚本程序其实本质上就可以看成是比特币中的数字合约,这也是为什么比特币被称为可编程数字货币的原因,它的转入/转出或者说输入/输出是通过脚本程序的组合来自动实现的,实现过程中还使用到了私钥和公钥,也就是公开密钥算法,所以比特币还称为可编程加密数字货币。

具体模型

比特币的区块链由一个个区块串联构成,而每个区块又包含一个或多个交易。
任何一个交易,它总是由若干个输入(Input)和若干个输出(Output)构成,一个Input指向的是前面区块的某个Output,只有Coinbase交易(矿工奖励的铸币交易)没有输入,只有凭空输出。所以,任何交易,总是可以由Input溯源到Coinbase交易。

这些交易的Input和Output总是可以串联起来:

┌─────────────┐     ┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│Block #1     │     │Block #2     │     │Block #3     │     │Block #4     │
│┌──┬────┬───┐│     │┌──┬────┬───┐│     │┌──┬────┬───┐│     │┌──┬────┬───┐│
││CB│50.0│OUT├┼──┐  ││CB│50.0│OUT├┼──┐  ││CB│50.0│OUT├┼──┐  ││CB│50.0│OUT││
│└──┴────┴───┘│  │  │└──┴────┴───┘│  │  │└──┴────┴───┘│  │  │└──┴────┴───┘│
│             │  │  │┌──┬────┬───┐│  │  │┌──┬────┬───┐│  │  │┌──┬────┬───┐│
│             │  │  ││  │8.70│OUT├┼──┼──>│IN│    │   ││  └──>│IN│25.0│OUT││
│             │  └──>│IN├────┼───┤│  │  │├──┤58.7│OUT││     │├──┼────┼───┤│
│             │     ││  │41.3│OUT├┼─┐└──>│IN│    │   ││  ┌──>│IN│66.3│OUT││
│             │     │└──┴────┴───┘│ │   │└──┴────┴───┘│  │  │└──┴────┴───┘│
└─────────────┘     └─────────────┘ │   └─────────────┘  │  └─────────────┘
                                    └────────────────────┘

还没有被下一个交易花费的Output被称为UTXO:Unspent TX Output,即未花费交易输出。给定任何一个区块,计算当前所有的UXTO金额之和,等同于自创世区块到给定区块的挖矿奖励之和。

因此,比特币的交易模型和我们平时使用的银行账号有所不同,它并没有账户这个说法,只有UTXO。想要确定某个人拥有的比特币,并无法通过某个账户查到,必须知道此人控制的所有UTXO金额之和。

在钱包程序中,钱包管理的是一组私钥,对应的是一组公钥和地址。钱包程序必须从创世区块开始扫描每一笔交易,如果:

  1. 遇到某笔交易的某个Output是钱包管理的地址之一,则钱包余额增加;
  2. 遇到某笔交易的某个Input是钱包管理的地址之一,则钱包余额减少。

钱包的当前余额总是钱包地址关联的所有UTXO金额之和。

如果刚装了一个新钱包,导入了一组私钥,在钱包扫描完整个比特币区块之前,是无法得知当前管理的地址余额的。那么,给定一个地址,要查询该地址的余额,难道要从头扫描几百GB的区块链数据?

当然不是。

要做到瞬时查询,我们知道,使用关系数据库的主键进行查询,由于用了索引,速度极快。

因此,对区块链进行查询之前,首先要扫描整个区块链,重建一个类似关系数据库的地址-余额映射表。这个表的结构如下:

address balance lastUpdatedAtBlock
address-1 50.0 0

一开始,这是一个空表。每当扫描一个区块的所有交易后,某些地址的余额增加,另一些地址的余额减少,两者之差恰好为区块奖励:

address balance lastUpdatedAtBlock
address-1 50.0 0
address-2 40.0 3
address-3 50.0 3
address-4 10.0 3

这样,扫描完所有区块后,我们就得到了整个区块链所有地址的完整余额记录,查询的时候,并不是从区块链查询,而是从本地数据库查询。大多数钱包程序使用LevelDB来存储这些信息,手机钱包程序则是请求服务器,由服务器查询数据库后返回结果。

如果我们把MySQL这样的数据库看作可修改的,那么区块链就是不可修改,只能追加的只读数据库。但是,MySQL这样的数据库虽然其状态是可修改的,但它的状态改变却是由修改语句(INSERT/UPDATE/DELETE)引起的。把MySQL的binlog日志完整地记录下来,再进行重放,即可在另一台机器上完整地重建整个数据库。把区块链看作不可修改的binlog日志,我们只要把每个区块的所有交易重放一遍,即可重建一个地址-余额的数据库。

可见,比特币的区块链记录的是修改日志,而不是当前状态。

小结

比特币区块链使用UTXO模型,它没有账户这个概念;

重建整个地址-余额数据库需要扫描整个区块链,并按每个交易依次更新记录,即可得到当前状态。

你可能感兴趣的:(区块链账号体系之UTXO模型)