4.2 计算谜题
4.2.1 谜题(一)
设计师从靶场得到灵感,开始设计“运气”。
(1)射手应该是计算机程序,计算机擅长“计算”,箭就是一个“计算结果”。
(2)“射中”即为在某个范围内的“计算结果”。
(3)可以用第二个设计靶场的方法来设计“运气”,涉及计算的“运气”当然是随机数。
(4)计算机一样,算法一样,总得有一个地方不一样才能产生不一样的随机数。还好,每个人记账的账页内容不一样,交易的排序可能不一样,也可能一样,但至少有一个交易不一样,即奖金交易的账号不一样。
(5)由第2章可知,对刚记完账的当前账页求哈希值,由于账页内容不一样,哈希值就会不一样,这就能得到满足要求的“运气”了。
有了上述分析,就可以这样设计“运气”:对于当前的编号,使用同一个哈希函数,账页内容的哈希值小者胜。
4.2.2 谜题(二)
有个记账员为获得更多奖金,让程序员丈夫帮忙破解谜题。程序员通过一番研究,终于找到了提升“运气”的方法:
(1)在账页中留一个空格,分别试填1、2、3、…、100,计算出该账页的100个哈希值;
(2)在这100个哈希值中找出最小的,并把对应的“试填值”正式填入空格中;
(3)提交这个账页。
显然,他提交的这个账页获胜的概率大得多,他将这个“试填值”称为“幸运数”。
俗话说得好:“魔高一尺、道高一丈”。设计师为了应对这种情况,调整了“谜题”:
(1)数据范围的表示,通常我们以后面有多少个0来表示数据范围,如小于100,但有个问题是长度不固定,如,小于100是三位,小于1000是四位,小于10000是五位……如果要求长度固定,怎么办?可用前面多少个0来表示,如十进制,固定长度是六位,则可选000099作为范围,表示不超过99,类似的十六进制00…00FFF…FFF也是一个范围表示,前面0的个数称为“难度系数”,0的个数越多,范围就越小。
(2)计算数据块的Hash值(指纹)即为打靶,靶子大小固定为上述的一个数据范围,范围越小,命中的难度就越大。
(3)将获胜规则,由“哈希值最小”改为“最先中靶”,符合公平射箭比赛的方法二。这里有两层意思:一是要“中靶”,不管是9环还是10环,中了就行;二是要“最先”,大家都以自己“看到的最先”进行投票,这等价于采用“多数人认为的最先”(越先出现,越有更多人看到)。这往往对后续区块的投票产生影响,即第1章所说的“改弦易辙”。
接下来解决如何“射中”的问题:
在程序员丈夫所做的账页中加上一个“调节钮”,即通过for(从0起至机器表示的最大整数)去找“幸运数”Nonce,使计算的Hash值到达范围。
思考题:Hash值是逐步变小的吗?
4.2.3 谜题(三)
在上述情况下,统一型号的计算机、固定的靶子、产生账页的速度,从平均意义上讲是匀速的,设为V1。
假定统一升级了,使其性能更高,那么产生账页的速度就会加快,但从平均意义上讲仍是匀速的,设为V2。
有时,希望产生账页的速度是匀速的,至少在平均意义上是匀速的。例如,发行货币时要控制发币速度,即每页每天以固定的速度发行一定数量的货币。
设计思路:当速度变快时,提高难度,“难度系数”加大,即范围中前面0的个数加多,反之,则降低难度。就像射击场的经营者经常对靶子的大小进行调整一样,货币的发行者也要控制发币的速度,以控制奖金的发放速度。
(1)在账页中加上时间戳,以便计算产生账页的速度;
(2)若V2 >V1,则提高“难度系数”,反之,则降低“难度系数”。
有两种调整“难度系数”的方式,一是及时调整,即每次产生账页时都去计算要不要调整,二是在一定的时间周期内调整一次,“时间周期”通常以账页的个数来表示,例如,比特币链的理想速度为每10分钟产生一块,调整的时间周期为两周,对应2016块,即每2016块后调节一次。一般用第二种方式,第一种实际上是第二种的特例。
每个时间周期内的平均速度为:V1,V2,V3,V4,…,虽然它们不相等,表现为上下波动,但是从平均意义上讲,是匀速前进的,如一年大约产生多少个账页。
上面是从原理角度来说明的,实际上,公有链并不追求平均速度,一方面,它只是根据前一个周期内的平均速度(而不是整个历史的平均速度)来计算下一个周期的难度系数;另一方面,速度对应难度系数(由前面0的个数来表示),但二者之间并不是线性关系。
以谜题难度来控制记账速度,说明公有链天生就不是追求速度的。
谜题变为:求出一个“幸运数”,使得账页的Hash值满足当时“难度系数”的要求。
求解谜题非常困难,几乎得用暴力破解,而验证谜题却非常简单,就像平时你猜谜语,猜时很难,但当你看到答案时,会发出“啊”的一声。