关于区块链技术已经写了两篇了,今天来说说挖矿的事儿
说起挖矿这两字给人的第一印象是一帮黑不溜秋的矿工拿着锄头和探照灯,在漆黑的地下挖掘,在无数声叮叮当当的敲打声后,突然有个矿工拿起一枚通透的宝石或者黄灿灿的金子叫喊到,“挖到了,挖到了”,这是电影或者小说中的情节,真实情况是,在区块链的网络世界,“挖矿”这俩字用的并不准确。
不过“挖矿”这俩字如今已经深入人心,我也只好顺着往下说。
“挖矿”其实是对比特币等数字货币发行的一种通俗称呼,这个行为背后的真正含义是—网络上的无数台电脑对某一个特定的问题求解,哪台计算机先解出了这个问题,在全网所有计算机的见证下,那台求出解的计算机就会得到网络奖励的比特币。
那这个问题是什么?
是对某个区块的区块头求一个特殊的hash值,这个特殊的hash值要满足小于预先设定的目标值
这句话怎么理解呢?
方便起见,先举个简单的例子,区块链世界中用到一种叫做SHA256的算法,它的特点是,给定任何一段语句,在经过该算法后都能得到一个hash值(详情请见《聊聊钱包,私钥,公钥,地址》)。
比如输入“power overwhelming”,经过SHA256算法,得到一个32字节的hash值:
‘8fe12dec3dc8433bfe04ca1e4381feaa58d30c5ece06d37756d88face23aa3e1’
由于sha256算法的特殊性,使得对这段语句修改或增减任何一个字符,输出的hash值都会改变:
比如在原句后加一个“1”,输入“power overwhelming1”,得到一个新的32字节的hash值:
‘d9e940764a410ecf7eae1f27a63248070ab45292e41de1cdfc89433ba2c20ac7’
现在设个目标,通过修改语句尾巴上的数字,使得输出十六进制hash值的首位为0(相当于256位的二进制hash的前四位都是0),下面通过一段简单的代码,来实现对语句尾部迭代不同数字,并统计满足要求的hash值
代码如下
执行程序,分别输入语句“power overwhelming”和数字“4”,数字“4”表示目标hash值的十六进制首位为0
以上运行结果显示,
– 目标hash值是:
0x1000000000000000000000000000000000000000000000000000000000000000
– 一共计算了32次,满足条件的hash值出现了两次,平均每算16次出现一个,这是因为一位十六进制代表的是0-16(0-f)的任意一位,所以满足要求的尾部数字的出现概率为1/16。
如果再次执行程序,输入“show me the money”,数字“4”改成“8”,表示目标hash的前两位都是0(或者256位二进制的前8位都是0)
– 目标hash值是:
0x0100000000000000000000000000000000000000000000000000000000000000
– 一共计算了1024次,满足条件的hash值出现了5次,数据量不够大,如果足够大的话,出现的概率应为1/256。
所以,规律就很明显了,hash值的0位要求的越多,则满足目标的计算次数越多,比如从比特币区块链第477016块的数据来看,其hash值是:
00000000000000000097cf400e2634e1b42cb0b55bbd25476a3e97a5e0d481d5
转换成二进制后,其要求的0位高达72个,也就是说每计算4.7×10^21次,大概会出现一次正确的hash值,这种靠更改尾部数值反复迭代计算,以得到正确的hash值的做法就叫做工作量证明机制-POW-proof of work,尾部不断迭代的数字叫做nonce值。
那真正的区块头hash如何计算的呢?
真正的区块头hash值计算中,会用到以下6个数据:
其中前五个都是已知的值,分别是版本号,前一个区块的hash值,从创始区块到现在的时间戳,根merkle值,难度标记。
merkle值是将区块中等待打包的所有交易递归产生的一个值-详情请见《一本四十七万页的账本》,网络上不同的矿机可能由于各种原因,使得等待打包的区块内的交易记录和时间戳不完全一样。
而bits难度标记是整个比特币网络自行调整的一个参数,它有两个作用,第一,是保证无论全网的hash运算速度多快,每两个区块产生的间隔始终保持在10分钟左右,第二,该参数对应的是某等待打包的区块的目标hash值,回到第477016号区块,其难度目标为18015dcc,头两位是幂,后六位是系数,经过下列公式展开,可以得到目标HASH值。
公式: Target hash=coefficient*2^(8*(exponent-3)
coefficient=0x015dcc
exponent=0x18
Target hash=
0x0000000000000000015dcc000000000000000000000000000000000000000000
随后对区块头进行nonce值迭代,找到小于目标值的hash
公式如下:
hash=SHA256(SHA256(区块头))
区块头=十六进制小端结尾(版本信息+上一个区块头+根merkle+时间戳+难度目标+nonce值)
不过,因为该十六进制的目标hash一共有17个0,转换成二进制后,0位高达72个,每计算4.7×10^21次,大约会有一个正确的hash值,可谓是丧心病狂了。不过目前比特币全网的hash算力为—6200Ph/s,也就是每秒可以进6.2x10^18次hash计算,于是乎每个区块的产生时间大约是750秒,折合12.5分钟。
普通笔记本的算力大概是1*10^8次/s,这个数据可能多,也可能少,不过无所谓,因为按照目前如此变态的hash算力要求,用笔记本找出一个正确hash值大概要150万年,到那时候,估计iphone都出银河系版了,所以就不在笔记本上尝试了嘛。
但是,一旦找到正确的nonce,算出小于目标的hash值后,所有人就能用一套相同的参数来验证该计算是否正确,比如对于第477016号区块,其区块头信息如下
根据公式hash=SHA256(SHA256(区块头)),写python代码如下:
运行后,分别输入:
版本信息
上一个区块头
根merkle
时间戳
难度目标
nonce值
得到一个hash值,该值正等于第477016号区块的hash值
00000000000000000097cf400e2634e1b42cb0b55bbd25476a3e97a5e0d481d5
一旦某台挖矿机算出了正确的hash值,也就意味着该区块的区块头大包成功,于是所有的矿机开始进行下一个区块的计算,重复上面的过程。
正因为计算量如此巨大,才会有所谓的专业矿机出现,他们都装备了专业的GPU,这点倒是和大数据运算很像,事实也确实差不多,如此海量的同质化数据,用GPU运算才能更快更高效,你可以把他们想象成一帮只会做数学题的小朋友,复杂的都不会,只会1+1,一个个数字的往公式里套求解,但人家数量巨大,行动统一,最大的特点就是贼耗电,这点大概和真的挖矿机也很类似吧。
Kindly Reminder
文章里的代码都是可以运行的,大家要是有python编译器,可以拿回去自己玩
hash-nonce
import hashlib
text =raw_input(“please enter a sentence:”)
# iterate nonce from 0 to xxx
difficult=raw_input(“please enter a 0-32 number:”)
bit=int(difficult)
count=0
list=[]
target=2**(256-bit)
hextar=hex(target)
for nonce in xrange(4*(2**bit)):
# add the nonce to the end of the text
input = text + str(nonce)# calculate the SHA-256 hash of the input (text+nonce)
hash = hashlib.sha256(input).hexdigest()
# show the input and hash result
print input, ‘=>’, hash
if long(hash, 16)<target:
count=count+1
list.append(hash)print “There are %d HASH meet target”%(count)
print “The target HASH is:”, hextar
print “Following HASH are meet target:”
for a in list:
print a
hash-cal
import hashlib
#ask for input
ver=raw_input(“please enter 6 digi of version number:”)
phash=raw_input(“please enter hash of previous block:”)
rmerkle=raw_input(“please enter root merkle value:”)
t=raw_input(“please enter time in digital:”)
bits=raw_input(“please enter hex bits number in hex:”)
nonce=raw_input(“please enter digital nonce value:”)
#convert time and nonce to int
t=int(t)
n=int(nonce)
#convert all input to hex format and small end
sver=”0x”+str(ver)
hexver=sver[2::].decode(‘hex’)[::-1].encode(‘hex’)
hexphash=phash.decode(‘hex’)[::-1].encode(‘hex’)
hexrmerkle=rmerkle.decode(‘hex’)[::-1].encode(‘hex’)
hextime=hex(t)[2::].decode(‘hex’)[::-1].encode(‘hex’)
hexbits=bits.decode(‘hex’)[::-1].encode(‘hex’)
hexnonce=hex(n)[2::].decode(‘hex’)[::-1].encode(‘hex’)
print “hexver is:”, hexver
print “hexphash is:”, hexphash
print “hexrmerkle is:”, hexrmerkle
print “hextime is:”, hextime
print “hexbits is:”, hexbits
print “hexnonce is:”, hexnonce
#calculate hash value
header_hex=(hexver+hexphash+hexrmerkle+hextime+hexbits+hexnonce)
header_bin=header_hex.decode(‘hex’)
hash=hashlib.sha256(hashlib.sha256(header_bin).digest()).digest()
new_hash=hash[::-1].encode(‘hex_codec’)
print “hash is:”, new_hash
版权说明
本文首发自微信公众号:“肉摩陀”
无需申请版权许可即可转载,转载时请注明作者
微信号: papa117_
苹果用户请看下图