Michael.W深度剖析Bitcoin系统第2期-比特币的数据结构与协议 - Go语言中文社区

Michael.W深度剖析Bitcoin系统第2期-比特币的数据结构与协议


1 比特币中的数据结构

比特币中最具有标志性的数据结构就应该算是”哈希指针“。

这种指针不单单要标记出结构体的地址,同时还要记录该结构体的哈希值。这样可以通过该指针来判断该结构体是否被篡改。

往往一提到区块链,有计算机基础的小朋友一定首先会想到”链表“。那么,这个区块链和传统意义上的链表到底有什么区别?

简单的说,区块链可以理解为用哈希指针代替了普通指针的链表。

区块链中第一个区块是系统产生的创世区块genesis block),像一个大蛇头一样牵引着后面的小区块们。

2009年1月3日,在位于芬兰赫尔辛基的一个小型服务器上,比特币的缔造者中本聪挖出了比特币的第一个区块,就是我上面所说的创世区块,并获得了50个比特币的奖励。

中本聪在创世区块的铸币交易中永久性地留下一句意味深长的话:“The Times 03/Jan/2009 Chancellor on brink of second bailout for banks
在这里插入图片描述
这句话是2009年1月3日《泰晤士报》当天的头版文章标题。中本聪为什么要选择这样一句话?我的理解是:中本聪希望比特币的运行可以让消费者能够控制自己的钱,而不是经过银行允许才能转账和消费。这样消费者手中的货币就不会因为银行滥发随意的政策波动出现贬值。
在这里插入图片描述
言归正传,普通链表更改一个node的内容是不会影响到后面的内容,而区块链中修改区块中的数据内容会产生多米诺骨牌效应,导致后面的区块”找不到“这个修改内容后的区块。除非你把后面的区块依次都重新计算哈希值并修改。

所以我只需要保存最后一个区块的哈希指针,就可以验证前面的所有区块是否被篡改过。

比特币的区块链其实就是一个账本。账本从创世区块开始,一直记录到最新的区块。所有人的比特币交易都保存在区块链中的每个区块里。

那么在比特币网络中,是不是所有的节点都要保存这将近200个G的全账本呢?

并不是,有的节点可能只保存最近的几千个区块数据。当他需要之前的区块时,会从网络中的其他节点中调用。

但是其他节点可能是诚实的节点,也可能是恶意节点,那么我怎么知道别的节点返回给我的区块是真实的呢?很简单,计算一下对方发过来的区块的哈希值,看与他的子区块中存储的前区块哈希值是否一致即可。

在这里,我要郑重地介绍另一个对于比特币来说举足轻重的数据结构:Merkle Tree。我画了一张十分直白的图来表示这种数据结构:
在这里插入图片描述
图中的H()是对其下面连接的内容进行哈希运算。每个数据区块依次向上取哈希值之后,会在”树顶“上得到一个根哈希值,root hash

这个根哈希值有什么用呢?只要记住根哈希值,那么就可以知道所有的data blocks中的数据是否有被篡改过。

区块链中的区块将所有的交易都用Merkle Tree来记录。每个data block就是区块中的一笔交易(transaction)。

区块链中的区块分为区块头block header)和区块体block body) 。Merkle Tree的根哈希值就存储在区块头中 ,而区块体中则存有交易数据列表。

比特币网络中节点分为两种:全节点Full node)和轻节点Light node)。

全节点中保存着区块链上所有的区块头和区块体,而轻节点中只保留区块头(比如手机端的比特币钱包 )。比特币系统中大多数为轻节点,全节点数量不多

轻节点没有办法独立验证交易的合法性,并且不参与区块链的构造和维护

既然一个轻节点上不保存交易的数据,那么它是如何验证一笔交易是否是真实的呢?这要用到merkle proof

轻节点没有保存交易列表,只有一个merkle tree的根哈希值。轻节点向全节点发出申请,申请一个能证明该交易的merkle proof。当全节点接到轻节点的请求后,只需要将下图中三个红色的哈希值返给轻节点即可。

在这里插入图片描述
轻节点拿到这三个哈希值就可以求出root hash与自己区块头中的对应值对比。如果一致,则证明该笔交易是合法交易。

观察以上merkle proof的验证过程可以发现,其中是存在安全隐患的。

如果我要恶意使用钱包,伪造一笔交易,我会和一个全节点联合作恶。让他返回给钱包一个修改后的哈希值(比如第一层的H())。让返回的这个值跟我伪造的这笔交易的哈希值结合再求哈希值与第二层原来Merkle Tree中的哈希值(第二层绿色H())一致就可以。这样就可以骗过轻节点钱包的验证机制。

这就是人为制造哈希封装。这种修改成功的机会存在,但是实现概率上太小了。

首先,你要保证是你的全节点给这个钱包返回merkle proof,而不是其他全节点。因为比特币网络中有成百上千个全节点,没有人能保证你的答复是那个轻节点(钱包)收到的第一个答复。

其次,还要制造256位的哈希碰撞,成功的几率小的不能再小,可以认为可行性为0。

比特币没有运用Sorted Merkle Tree,因为比特币不需要做交易不存在证明。而这个Sorted Merkle Tree为最底层的交易是按哈希值从小到大排列来求root hash的树状结构。

指针是数据结构中的常客,那是不是所有的数据结构都可以用区块链特有的哈希指针来替代传统的指针呢?

答:只要是不带环的数据结构,都可以用哈希指针来代替普通指针。在带环的数据结构中改用哈希指针,如下图所示:
在这里插入图片描述
环状数据结构中,所有的区块都需要找到一个区块作为依赖,即存储它的哈希值。但是环状数据结构中没有一个区块是可以被事先确定下来的(按照“先有鸡还是先有蛋”问题理解),即没有创世区块。所以不能用哈希指针实现。

2 比特币的协议

央行也可以发行数字货币,只需要央行用自己的私钥对它发行的数字货币进行签名。

当我们再收到这个数字货币的时候,只需要用央行的公钥对其验证即可。(这其中只涉及密码学技术,而未使用区块链技术 )

上面设计模式存在一个最大的弊端,就是无法抵御"双花"攻击,double spending attack

数字货币就是一个文件,我可以无限复制这个文件,来实现"双花"攻击。目前,数字货币的主要挑战就是如何防范"双花"攻击。

那央行是如何防范"双花"攻击的呢?

央行可以自己设立个数据库,在里面记载上每张数字货币(数字货币上有编号)都属于哪个人。在我收到A给我数字货币时,我只要去央行的数据库中查看一下该数字货币编号下的持有人是不是A,就可以来验证这笔钱的有效性。

上面我描述的是一个中心化数字货币的设计思想,该可行性已经在实际中得到验证。

一个去中心化的数字货币要解决两个问题:1.谁来发行货币? 2.如何验证交易的合法性?

2.1 比特币网络是如何验证比特币的真伪

其实这个问题就在探讨比特币网络对一笔交易的有效性验证方式。

比特币网络中,一笔交易包括输入和输出两个部分:

如果A要转钱给B:

  • 输出:要给出B(接受方)的公钥的哈希。

  • 输入:给出用A自己的私钥对该交易进行的签名,以及A的公钥。
    在这里插入图片描述
    图中模拟的交易流程很清楚,加入矿工A挖到了10BTC,然后分别转给了B和C各5BTC。B又向C转了2BTC,向D转了3BTC。此时B的地址中应该只有5-2=3BTC。

假如说B是个贪得无厌的人,自己已经没有比特币了,但是他又偏要发起一笔交易给E,转10BTC。

全节点收到B发出的交易申请后,从区块链末尾向前回溯到第三个区块时发现B的钱已经被花光了,所以认为该笔交易是不合法的。即这笔交易不会被全节点接受,所以B发起的这次交易是不会被记录到区块链中的。

比特币收款地址是根据用户的公钥推算出来的。

比特币系统中不存在账户地址查询的功能。如果我要给你转钱,那么你必须将你的收款地址或公钥提供给我。所以在线下交易中,比特币地址可以生成二维码来让付款方进行扫码支付。

如果A要给B转账,A不但要对交易签名,同时还要在交易中给出自己的公钥。目的是为了让全网络中的所以节点可以验证这笔交易的有效性!

验证一笔交易的有效性的核心流程,就是要验证这笔交易的输入部分的公钥与该输入部分在区块链中对应的输出部分的公钥哈希是匹配的。

这样就避免了黑客利用其他的公私钥伪造的交易。因为使用其他的公私钥创建的伪交易的输入公钥与之前对应区块的输出中的公钥哈希是对应不上。这也是比特币对于交易验证设计的最精妙的地方!

这里我要提一嘴比特币脚本——BitCoin Script

我在上面所说的交易验证的过程,其实就是通过执行比特币脚本来完成的。

具体实现就是把当前交易的输入脚本和之前它钱的来源的输出脚本拼在一起(合成一段程序)看能否顺利执行。如果能执行,才是合法的。关于比特币脚本的细节,我会找时间在后面的文章中详细介绍。

2.2 比特币区块头

区块头中包含的内容:

  • Version:版本号
  • Hash of previous block header:父区块头的哈希值
  • Merkle root hash: 用来保证本区块体中的交易数据没有被篡改
  • Target(难度) :也称 nBits,与比特币挖矿有关
  • Nonce:挖矿的过程就是调节Nonce值使得 H(区块头)<= Target

2.3 比特币的共识协议

每个全节点都在自己独立地记账本(写区块链),那么是如何保持网络内的所有账本的一致性呢?

其实这个问题就是在讨论比特币系统是如何达成分布式共识distributed consensus)的。

可能小伙伴对分布式系统没什么概念,那么我先举一个简单的例子:全局的分布式哈希表(distributed hash table)。

多台机器共同维护着一个全局的哈希表。那怎么样才叫达成分布式共识呢?如果机器A修改了一个键对应的值,那么机器B要能读取到修改后键对应的值。

分布式系统共识有很多不可能原则,最著名的是FLP impossibility result:

一个网络时延无上限的异步(asynchronous)系统中,如果只要一个成员出现问题,那么这个系统就永远无法达成共识。(FLP为三个作者的名字首字母)

还有一个CAP理论

Consistency:一致性
Availability:可用性
Partition tolerance:分区容忍性
一个分布式系统中,以上三个性质最多只能满足其中两个,永远无法三者兼顾。

分布式共识中有一个著名的Paxos协议。这个协议比较复杂,我就不概括其细节了。Paxos协议可能在系统中永远无法达成共识。理论上在真实系统中,这种可能性是存在的,只是概率比较小。

那么比特币中的共识协议Consensus in BitCoin)到底是什么样的?

首先比特币共识协议有个基础,就是要求:比特币网络内大部分节点是善意节点

比特币最开始的一种共识设计为:一个全节点打包好一个区块,然后广播给网络中其他的全节点验证。之后让所有节点投票,如果得票超过半数就认可这个区块的有效性。

这种设计思路在后来的联盟链中被实现。但是这种基于投票机制的共识要解决第一个问题就是membership,即谁有权利来投票。

我之前说过,比特币中创建一个账户是非常容易的,在本地生成一个公私钥对就可以创建一个账户。

这些账户在网络中是不可见的。只有当发生交易的时候,这些账户才对网络可见。那么我用一个超级计算机每天无限制地生成公私钥对,然后分别做小额交易,这样就可以让账户在比特币网络中可见(即这个账户就可以投票了)。只要我产生的账户超过网络可见账户的半数以上,我就可以左右投票走势了,即可以自行控制所有全节点的记账了。

这种攻击手段叫做女巫攻击sybil attack

由于这种设计存在安全漏洞,后来对齐进行了改进:每个节点都可以自己组织区块,然后进行算力比拼。第一个获取到达标的nonce值的节点就获得了记账权。当其他节点收到这个区块后,要先验证一下区块头中的nBits是否跟全网内的难度值一致,然后检查选出的nonce是否满足H(区块头)<= Target,最后再校验区块头中其他内容和区块体中的交易。比如:每笔交易的有效性(签名,是否被双花等等)。

上面的过程是比特币共识协议的一部分——工作量证明POW)。

那POW如何抵御女巫攻击?尽管你可以在你的服务器上创建10000个账号,但是你服务器的算力是固定的且无法增加的。只要算力不够,就永远无法拿到记账权,从而无法干扰比特币的正常运作。

争夺记账权的节点,称为矿工

真正保护比特币系统安全的是**“最长合法链”共识协议**,longest valid chain

该协议规定:只有接在链的最后面的区块才是合法区块

一个节点接到广播过来的新区快,如果这个区块不是接到区块链的最末端,那么该区块就是非法区块,不会被该节点承认。

下面看我画的这个例子:
在这里插入图片描述
这种是一种攻击手段,称为分叉攻击forking attack)。通过向区块链中间插入一个新的区块,来造成区块链上交易数据的回滚。

还有一个区块链分叉的例子:如果A和B两个节点在相近的时间内都算出了nonce值,然后分别将自己包装的区块广播到网络中。由于是分布式系统存在网络时延,可能一部分节点先接收到了节点A打包的区块,一部分节点先接收到了节点B打包的区块。 之后这些节点会分别按照自己接收的区块继续记账,这种分叉的等长的临时区块会在系统中存在一段时间,直到某一链分叉在记账权上决出胜负
在这里插入图片描述
上图中上下两条链由于上链最先挖出区块,根据“最长合法链”共识协议成为合法链。比特币网络中的最长合法链共识是为了保证各个节点上的账本的一致性

被遗弃掉的分叉区块称为孤块orphan block)。孤块中的铸币交易(矿工挖矿都是从该交易中得到奖励)是无法被比特币网络验证到的,也就是说挖孤块的这个矿工白挖矿了。

比特币节点如何被认为是已经接受了一个区块?
答:如果该节点沿着该区块继续扩展,那么就认为该节点已经承认了接受了这个区块。

2.4 比特币交易

比特币交易有两类:

  • coinbase transaction(唯一一种产生新币的途径,可认为是发行货币)
  • transaction(普通交易)

比特币协议中规定(初始出块奖励为50BTC),每21万个区块后(每个区块约10分钟,大致4年减半),出块奖励减半。

那有的人会认为,现在出块奖励已经衰减了两次,那么现在挖矿岂不是不赚钱了?
答:并不是。当出块奖励为50BTC的时候,比特币并不值钱。尽管现在的出块奖励为12.5BTC,但是比特币的价格已经翻了好几倍。所以不能用奖励币的多少来判断价值。

2.5 比特币中的“披萨日”

比特币第一次与现实世界交易就发生在“披萨日”这一天。

软件设计师的拉斯洛 · 韩内奇在2010年5月22日用10000BTC换了一个披萨饼。于是这天被定为“比特币披萨日”。每年这一天,比特币爱好者们都会聚在一起吃披萨庆祝。

hash rate:一个挖矿机每秒进行求解哈希计算的次数,其单位为 Hash/s。哈希率越高,获取记账权的概率越大。

ps:
本人热爱图灵,热爱中本聪,热爱V神,热爱一切被梨花照过的姑娘。
以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。
同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下!
如果需要转发,麻烦注明作者。十分感谢!
后现代泼痞浪漫主义奠基人
公众号名称:后现代泼痞浪漫主义奠基人

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/michael_wgy_/article/details/88937619
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

0 条评论

请先 登录 后评论

官方社群

GO教程

推荐文章

猜你喜欢