如何防止一个比特币被使用两次?
一个比特币被使用两次,这个问题称之为叫“双花”问题,这是基于点对点的分布式网络系统特有的问题,如果是传统的基于服务器的结构,是不会有双花问题的,比如我们在银行转账一笔钱,账户里的余额就被扣除了,如果一开始是1000,转了1000,那就剩下0元了,我们是不可能再一次去转出1000的,因为银行的服务器会立即扣除转账金额。(个别情况下系统出问题了,导致可以重复花费,这个另论,这种情况的概率是极低的,低到绝大部分人在活着的时候可能都不会碰到一次?)。
然而在比特币网络中,这个问题是一个需要被正视的问题,当有人假设小明在比特币网络中发送一笔交易后,这笔交易会被广播出去,等待挖矿节点也就是矿工来打包进区块,由于没有一个指定的服务器用来进行唯一的验证,不同的节点接收到这笔交易的时间是会有时差的。当小王接收到这笔交易的时候,他知道小明的账户地址下是要消耗掉对应的金额的,可是小张还没收到这笔交易,甚至小张由于某些问题,一直不能正常的联网,一直就没收到小明的这笔交易,他完全不知道小明的账户地址余额发生了这个变化。
这里补充一个背景介绍,在比特币节点网络中,每个节点都是独立的存储着同样的数据账本的,在保持同步的情况下,每个节点都有一模一样的账本数据,因此他们是可以独立的去验证每个地址之下应该有的余额以及发生的交易的。当然,有些节点并不需要携带完整的数据账本,比如一些轻钱包,这里我们先不谈,对于比特币网络来说,拥有完整账本数据的完全节点才是支撑整个网络的骨干核心。
回到刚才的问题,比特币中怎么来防止这样的双花问题呢?
首先我们来看一下比特币中的交易模型,为了让大家容易理解,举例说明吧,就不贴具体的代码和数据结构了,如下所示:
通过上图我们可以看到,比特币中的转账交易就是入和出环环相扣,彼此衔接对应的,对于一个账户地址来说,比如小明,其具备的能花费的余额并不是直接的一个值,而是一个结构,比特币中称之为UTXO(Unspent Transaction Output),翻译过来叫未花费交易输出,初次听来,很是有些拗口,其实也很容易理解,类比于商贸企业中的仓库进出,有入有出,企业仓库的入对应于供应商仓库的出,而企业自己的销售出又是客户仓库入的来源,通过这个例子对比,我们可以发现,实际上“入”就是来源于上游的“出”,如果将这个“入”去掉,实际上企业自己能“出”的来源就是供应商的“出”,这个供应商的“出”随着企业每一次的销售,会被消耗掉,这也是为什么这里的名词叫“为花费输出”的意思,这里的“输出”指的就是供应商对应自己的“出”。
在这样的结构中,当一笔交易数据广播到网络中后,其他节点会验证合法性,比如是否有足够的“出”等等,由于这种的数据的关联关系,很难通过简单的欺骗来实现双花。那么,如果小明同时发送相同的交易到别人,希望利用时间差来通过双花校验呢,这个时候,由于时间差的原因,可能其他人在那一刻的验证都是通过的,比如小明给小王发送1000的转账,同时给小张也发送了1000的转账,小王和小张在还没有得到更多确认之前,在那一时刻是校验都没问题,这个时候会怎么样呢?在这个时候,比特币的区块链数据会发生临时分叉,最终看小王和小张接收到的交易数据所在的区块,哪个会最终进入到难度最大的链中去,一旦其中一个胜出后,其他的交易数据就无效了。为了保证确认的一致性,因此会需要等待多个区块的确认,收款方会看交易事务是不是进入到一个可靠的难度的链中,比如至少等待6个区块的确认,这样从概率上就能基本保证没问题了。
当然,双花的发生可能性还是存在的,比如某矿工节点具备很强大的算力,那是可以发动51%攻击的,限于篇幅,这个问题在这里暂时不展开论述,事实上目前比特币网络中虽然存在51%攻击的概率,但是由于利益和成本等原因,也不会那么容易去发生的。