比特币系统如何确认交易?

96
浮云狒
2018.04.04 09:50* 字数 2988

一个货币系统,必须要能够在较短的时间内确认交易,并且确保不可篡改,这是最基本的要求,今天我来梳理一下这个交易确认的过程,仅供大家参考,其中很多理解还是借助了汤强的文章,在此谢过,也声明我这个不是原创,相当于读后学习笔记。

首先来说说比特币系统的记账方式,或者直白点说是记录交易的方式。

先讲一个概念:UTXO,指未花费的交易输出

比特币的记账模式跟传统的中心化记账模式有一个比较难理解的区别:比特币没有账户的概念。

具体来说,比特币的记账系统,是通过记录和确认每一笔交易本身来记账,是交易本身,是交易的行为本身,也就是A向B转账这个过程,而不是记录A和B的余额变动,而A及B的最终余额是通过扫描交易数据计算出来的。

而传统的记账系统(银行),是通过记录和保存每个账户的余额变动数据来验证交易的,也就是说,每一次交易由中心记账系统把不同账户(一般是2个),的余额进行相应的调整并保存,完成了一个交易的记录。A向B转100万,那么先把A的余额减少100万,同时把B的余额增加100万,会计上说的,有借必有贷,借贷必相等。

为什么会这样?为什么比特币系统,不采取实时更新账户余额的方式来记账?主要有两个原因:

1.节省算力和储存空间。因为如果采取余额模式,为了避免双重支付,即使你的账户没有发生变化,比特币系统里任何一个客户端(钱包)发生一笔交易和变动,所有的客户端都有同步数据,否则,若B没有同步更新整个系统中的所有账户余额信息,在A给B转账时,B无法知道A的账上是否有足够的余额,甚至有可能,A把同一个比特币同时转给了B和C,而B和C由于都没有更新A的余额信息,都不知道A的账上其实没有那么多钱。

而传统的支付系统不一样,每一次交易,只有付款方和收款方的余额变动,顶多再加上中心化的服务器数据发生变动,所以只要同步很少的数据(这个是相对于区块链来说),事实上由于所有记账都是中心服务器来完成,其实不需要额外同步,只需要在完成记录的同时储存数据就可以了。所以在数据储存上中心化的系统是有优势的,如果比特币也采用这种余额思路,那么必定会失败(虽然现在也还不好说一定能成功)。

2.如果采取记录账户余额的方式,那么在不同节点更新存在时间差的时候,很容易出现双重支付,简单来说,A把同1各比特币转给B和C,由于BC的地理位置不同,他们附近的节点不同,有可能都确认为交易,虽然随着时间的推移,比特币系统会达成竞争共识,只确认一个交易,但,BC两人都提供了商品给A,最后,肯定有一个人蒙受损失。

那比特币系统是怎么做的呢:那就是只确认交易本身,直记录交易输入和输出:每笔比特币交易都有输入和输出,别人付给你的钱是“交易输入”(对你来说),你收到的钱如果被你支付出去了,叫做“交易输出”,你收到但是还没有支付出去的钱叫做“未花费交易输出”(UTXO),这是一种待支付的状态。

矿工们把每一次交易本身的数据(输入和输出)打包到区块之中,通过竞争计算,确保不可串改,实际上如果得到6个区块的确认,那么全球的算力加起来都没法串改。

如果需要确认余额,那么就用客户端(钱包)扫描某一个账户的输入、输出数据,就可以算出这个账户的余额,相比更新所有账户的余额数据来说,简直太轻松,不到1秒钟就可以搞定。

说完了比特币系统记账的方式,下面讲讲具体支付过程中,双方如何确认交易已经完成了?还是先讲一个关键概念:

SPV:简单支付验证。我们在用比特币交易的时候,必须确定交易成功,才会提供相应的服务或者商品,也就是一手交钱一手交货,确认收到钱,才交货。为什么说是“简单验证”呢?因为这种验证方法,不需要下载所有的交易数据,只需要下载区块头数据就可以验证。

那什么是区块头呢?

简单回顾一下,区块链对交易的记录,是把一段时间内的交易数据,打包到一个区块中,然后随着时间的推移,区块不断累积叠加,环环相扣,形成区块链,区块链越长,越难修改,一般来说经过6个区块以后,全球的计算机加起来都无法修改。

每个区块头信息代表着一个区块,就像是区块的身份证一样,你看到我的身份证就知道我是谁,就可以在脑海中浮现出我的身高、样貌,甚至职业、爱好等信息,在区块链世界里,系统通过区块头,可以识别出区块信息的方方面面。

区块头的核心是默克尔树根(Markle Tree Root)。那这个默克尔树根是怎么来的呢?假设这个区块里面包含了n个交易信息(n为偶数),那么每个交易都有一个哈希值(Hn),那么我把这些哈希值排列成一排,从第一个开始,相邻的两个哈希值再进行一次哈希计算,那么就能得到n/2个哈希值,然后再重复刚才的过程,就能得到n/4个哈希值,重复k次这样的哈希运算,就得到n/(2^k)个哈希值,一直计算下去,最后会得到1个哈希值,这个哈希值就是默克尔树根(root)。下图可以直观的表示这个过程:


如果n不是偶数也没有关系,比如k+1(k为偶数),把多出来的那个交易撂在一边,等计算完k个交易的默克尔树根后,在用这个默克尔树根的哈希值跟这个落单交易的哈希值进行一次哈希,就能得到这n个交易的默克尔树根了。

那么现实中需要重复多少次这个过程才能计算出默克尔树根呢?稍微观察一下上面这个图就知道,每一次进行相邻哈希值的打包哈希计算,得到的哈希值数量就减少一半,这是数量是以2为底数的指数级减少,减少的速度相当快。

假设有2048个交易,进行一次打包哈希计算,得到1024个哈希值

  进行二次打包哈希计算,得到512个哈希值

  进行三次打包哈希计算,得到256个哈希值

                                    ……

进行11次打包,得到1个哈希值。

也就是说,只要要进行11次打包哈希计算,就能得到默克尔树根,

利用对数表(高中数学有的,我也是复习才发现的),我们可以查到,假设有65536次交易,需要进行16次打包哈希计算,如果有1048576次交易,需要进行20次打包哈希计算,而每次哈希计算大约需要0.01秒,那么计算20次需要0.2秒,还是很快的。

说了那么多,铺垫,重点来了,如果客户给我转了1个比特币,我怎么确认这笔交易被比特币网络确认并无法串改了呢?通俗的说,就是如何确认收到钱了?分4步:

第一:我的钱包软件计算出本次交易的哈希值。

第二:我从最近的全节点(包含所有的交易数据),下载全部区块头信息,大约40M。

第三:全节点反馈给我几个(这个数量跟上面说的要得到默克尔树根,需要进行打包哈希运算的次数是一样的)本次交易所处的分支上关键的节点哈希值,我将本次交易的哈希值依次与这几个哈希值打包哈希,得到了本次交易所在区块的默克尔树根。

假设这个区块中由16次交易,本交易的哈希值是Hk,那么全节点反馈给我的就是下图中蓝色的几个交易哈希值。


第四:把我计算出来的默克尔树根,跟之前下载的区块头信息比对,如果一致,就证明本次交易被本区块记录了。

说到这里,你应该能理解,通过默克尔树根比对的方法,比特币钱包可以在较短时间里面确认交易的真实性。

但是不得不说,比特币的这个系统还是有很大缺陷的,前面说过,大约10分钟生成一个区块,那么也就是说,如果我的这笔交易是本区块的第一笔交易,我需要10分钟才能拿到这个区块的默克尔树根,来确认交易,如果想要更加安全一些,经过6个区块的认证,我需要一个小时的时间,如果遇到网络拥堵,时间会更长,那么小额的交易基本不可能利用比特币来交易,就像我要打车去机场,如果我付一笔钱,要一个小时司机才肯让我上车,就完全没有实际意义,这也是去中心化所付出的代价,希望随着区块链技术的发展,这个问题能够解决,或者说,我们在去中心化和效率之间找到一个相对平衡点。听说现在BM团队开发的EOS就是奔着这个高效交易的方向去的,我们拭目以待。

日记本
Web note ad 2