比特币区块哈希算法

February 07, 2018

本文主要介绍比特币区块链上的每个区块的地址是如何产生的。

区块头部(Header)主要包括:

  • Version:区块版本号,4 Bytes
  • hashPrevBlock:上一区块地址,32 Bytes
  • hashMerkleRoot:默克尔树,一种用来表达链上历史交易记录的数据结构,32 Bytes。
  • Time:时间戳,4 Bytes
  • Bits:网络难度,4 Bytes
  • Nonce:随机数,也就是 PoW 要计算的数,4 Bytes。

比特币使用两次 hash 来计算:SHA256(SHA256(Block_Header))

以高度为 1 的区块为例,它的地址是 00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048

$ curl https://blockchain.info/rawblock/00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048

{
    "hash":"00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048",
    "ver":1,
    "prev_block":"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
    "mrkl_root":"0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098",
    "time":1231469665,
    "bits":486604799,
    "fee":0,
    "nonce":2573394689,
    "n_tx":1,
    "size":215,
    "block_index":14850,
    "main_chain":true,
    "height":1,
......

首先把 ver, prev_block, mrkl_root, time, bits, nonce 拼接起来,得到 header_hex

在拼接之前需要先把 ver, time, bits, nonce 转换为十六进制字符串,然后将这六个属性由大端序转换为小端序,最后才拼接成一个字符串。

大端序和小端序是计算机硬件存储地址排列的两个通用规则,简单来说高位字节在前就是大端序,这和人类的认知一致;低位字节在前就是小端序,这是因为电路计算都是从低位开始的,方便计算机计算,这和人类计算方式也是一致的。具体可以参考阮一峰的《理解字节序》

# Source: https://arminli.com

import hashlib, struct

ver = 1
prev_block = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
mrkl_root = "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098"
time = 1231469665
bits = 486604799
nonce = 2573394689

def deal_str(big_endian):
    # [::-1] 改变字节序
    return big_endian.decode("hex")[::-1]

def deal_int(n):
    # < 代表小端序
    # L 代表 unsigned long
    return struct.pack("<L", n)

header_bin = deal_int(ver) + deal_str(prev_block) + deal_str(mrkl_root) + deal_int(time) + deal_int(bits) + deal_int(nonce)

其中 deal_int(time) + deal_int(bits) + deal_int(nonce) 也可以通过 stack.pack("<LLL", time, bits, nonce) 实现。

然后做两次 sha256 计算,这一步也就是挖矿做的工作:找到一个 nonce 使得这个结果满足某个条件(比如前八位都是 0)。

hash = hashlib.sha256(hashlib.sha256(header_bin).digest()).digest()

最后将结果转回大端序,即为当前区块的地址。

cur_block = hash[::-1].encode('hex')
print(cur_block)
> 00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048

Reference


Profile picture

Written by Armin Li , a venture capitalist. [Weibo] [Subscribe]