0%

【分布式共识四】POW共识算法

下面我来说说Bitcoin是如何通过Pow算法解决拜占庭将军问题的。

比特币

2008年,中本聪介绍了一个点对点的电子现金系统--比特币。比特币的基石是拜占庭共识协议。比特币怎样实现了拜占庭共识协议将在接下来的章节中介绍。不过首先要先介绍一下比特币

交易

比特币协议在数字货币中提供了一种交易方法,在这个方法中每个人可以对货币所有权,交易顺序达成共识。货币所有权是通过公钥来决定。整个网络需要对货币数量与货币所有权之间的关系达成共识。货币所有权可以通过对转账交易(从一个账户转移给另一个账户)签名进行转移。整个网络需要能个解决同一笔钱不能花费两次的难题。由于没有一个中心化的权威机构能够校验交易,因此需要在没有受信任第三方机构的情况下解决这个问题。就是说需要用去中心化的方法解决这个问题。

解决方案:公开向网络广播每笔交易,网络中的节点对先到达的交易是有效的达成共识。每个节点检查先到达的交易的输出之前是否被花费。难点是:由于网络通信不是及时的(异步网络),所以导致所有节点收到的交易并不是完全一致的。在这个情况下找到一个确认哪一个交易是最先到达的共识是困难的。为了对交易顺序达成共识,交易被打上时间戳存放在含有工作量证明的区块中。

这个方法为交易顺序共识提供了解决方案:区块包含上一个区块的Hash,最新交易。

Proof-of-Work 工作量证明

为了实现对交易打时间戳,Hash交易数据。比特币用了工作量证明方法。网络中的每个节点从事于解决一个适度困难的密码难题。难题的解决方法是:把区块中的所有数据做SHA256哈希运算,并且得到哈希值小于给定的目标值。区块中还包含一个Nonce值,通过递增Nonce来寻找正确的哈希值。这个密码谜题被设计成,每隔10Mins会找到一个谜题答案。

一旦正确的哈希值被找到,节点就会向网络中广播这个哈希值。这个哈希值可以很容易的被网络中的其他节点验证,节点可以对收到区块后对区块中的数据进行SHA256运算哈希值。

花费的CPU就是工作量证明。要修改一个区块需要重做这个区块以及这个区块之后所有区块的工作量证明。这就意味比特网络更倾向于最诚实的链,只要网络中大多数节点是诚实的。

img

Pow的过程可以被看做是一个投票的过程。每个新增的区块累积了以前的历史交易(区块是串联成链的),每个节点都会继续对以有最多投票数量的链继续投票。

攻击比特币网络

现在考虑一下网络中可以存在的一种攻击。一个恶意的节点试图双花之前已花费的交易。攻击者需要重做包含这个交易的区块,以及这个区块之后的所有的区块,创建一个比目前诚实区块链更长的区块链。只有网络中的大多数节点都转向攻击者创建的区块链,攻击者的攻击才算成功了。考虑交易T包含在区块b1中。每个后续区块b2,b3,b4,.........bn会降低交易T被修改的可能性,因为修改这些后续的区块需要更多的算力。中本聪用概率理论证明,六个区块后攻击者追赶上最长链的可能性降低到0.0002428%。在过四个或更多区块后这个可能行会降到0.0000012%。每新增一个区块bn,攻击的可能性就会以指数形式下降,很快整个攻击的可能性就会低到可以忽略的程度。在实际中,比特币交易会在六个区块后被确认,因为在这种情况下,攻击者追赶上的可能性已经非常低了,可以认为这个交易是有效的,不再会被修改。

工作量证明Pow 实现拜占庭共识

现在我们看一下比特币的工作量证明是如何解决计算机网络中的拜占庭将军问题的。比特币网络是就交易的顺序,没有中心化机构的情况下达成共识的,同样拜占庭将军也是做得同样的事情。拜占庭将军需要攻击城堡,所有将军需要对任何将军可能提出的攻击时间达成共识。

方案一:被所有将军都接受到的攻击计划,被认为是正式的攻击计划。问题是:两个或多个将军有可能同时发出不同的攻击计划。

这个问题模型被工作量证明简化了,比特币工作量证明系统中,不会追踪交易顺序,取而代之是在将军之间达成共识。每个将军基于工作量证明,解决一个难度适当的Hash难题,每个难题有足够的难度,仅当在所有的将军同时工作时,平均10Mins会找到一个难题的答案(solution)。当一个将军找到问题的答案,它会把这个答案连同攻击计划在网络中广播。一旦收到Solution,每个将军调整难题为在广播中收到的攻击时间,攻击计划。然后将军继续解决下一个工作量证明。这样接下来每个solution会依次在第一个solution后串联成链。如果有将军还在继续在对另一个不同的攻击方案进行工作量证明计算,它会切换到这个最长的链上。这个最长链上积累了最多的CPU算力。

平均一个小时后,这个链上会有六个区块。每个将军可以判断是否有足够多的将军工作在含有相同初始攻击计划的最长链上。链会在一小时累积到六个区块,说明大多数将军对相同的攻击计划进行工作量证明计算(CPU投票)。因此将军对攻击时间达成共识。

结论

在没有中心化权威机构存在的P2P网络上,比特币共识协议功能上等同于一个受信任的中心化机构。这个协议解决了拜占庭将军问题中缺少中心化权威机构的难题。帮组将军在攻击时间上达成共识。而且,它缓解了多个攻击计划同时提交的可能性,同时也降低了攻击的可能性。因此比特币共识协议现代拜占庭将军中的问题。

拜占庭将军问题和Bitcoin 对比
拜占庭将军问题 BitCoin
攻击时间上达成共识 目标 对合法的交易达成共识
将军分布在城堡周围 分布情况 节点分布在网络中
忠诚的将军和副官 好的 可信任的节点
叛徒 坏的 作恶节点
篡改消息(干扰忠诚的将军达成共识) 破坏 向区块中加入无效非法的交易
怎么才能知道那个消息是真的 难点 怎么才能知道那个交易是合法的
暂无 解决方案 Pow
暂无 共识 BlockChain + Pow

Pow 是如何运行的

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
32
33
34
35
36
37
38
39
#!/bin/python

import sys

import time

import hashlib

from struct import unpack, pack

timestamp = str(time.time()) # Work timestamp

message = "This is a random message." # Plaintext message

nonce = 0

guess = 99999999999999999999

payload = timestamp + message

throttle = 100000000

target = 2**64 / throttle

payloadHash = hashlib.sha256(payload).digest()

start = time.time()

while guess > target:

​ nonce += 1

​ guess,=unpack('>Q',hashlib.sha256(hashlib.sha256(pack('>Q',nonce)+payloadHash).digest()).digest()[0:8])

​ print(guess);

end = time.time()

print "%s:%s:%s:%s:%s:%s:%s" % (timestamp, message, nonce, guess, payload, target, end-start)
  • timestamp 开始产生区块的时间戳

  • message 类似比特别中的交易,这里只做演示用字符串替代

  • payload is a combination of the things that you will encrypt.

  • nonce 会从0到N递增,直到找到target为止

  • guess 将会保存谜底,一开始初始化为无穷大

  • throttle 相当于比特币中的难度

  • target 8个字节的整数最大值 (2^64)除以难度(throttle )

Timestamp,message,payload是你要发送到网络中的东西。它可以是区块数据

Nonce,guess,throttle target是用来进行工作量证明运算。Pow最重要的是难于生成易于验证。

1
2
3
4
5
while guess > target:

​ nonce += 1

​ guess,=unpack('>Q',hashlib.sha256(hashlib.sha256(pack('>Q',nonce)+payloadHash).digest()).digest()[0:8])

这三行就是Pow算法主要内容。这是一个简单的循环。它使用SHA256对数据进行两轮哈希。前八个字节作为我们的谜底。

img

比特币的除数(难度)2016个区块后调整一次 增加或下降。上图显示随着难度的增加正确谜底的可能值范围缩小,也就是越难于发现Guess

nonce += 1

Nonce表示CPU的工作量,在本例子nonce 表示发现一个低于target的guess的累计工作量。因为每一个guess都有相同的可能性会低于target,它和nonce生成的方式是无关的。所以nonce从0开始递增比生成随机数成本更低。当区块提交到网络中,nonce会被用来证明区块的正确性。

guess,=unpack('>Q',hashlib.sha256(hashlib.sha256(pack('>Q',nonce)+payloadHash).digest()).digest()[0:8])

Guess是把nonce和payload经过两轮SHA256哈希之后值的前8个字节。因为target的范围是0..2^8,所以guess不可能超过target.在每次循环后,nonce是唯一会改变的值。

向网络中提交nonce是安全的。因为每个旷工的payload是独一无二的。如果旷工Alice使用了旷工Bob提交的nonce,Alice需要提供相同的payload,由于Alice不能在payload把Bob的公钥自己的公钥,因为这会改变两轮SHA256哈希的输出。改变后的输出就一定满足小于target这个条件。由于Alice的payload不同于Bob的payload,所以Bob的nonce对Alice就不适用。