区块链三大基石技术之二-哈希

哈希函数就像一个冲压机器,不管多长的信息,它都输出一个固定大小的字符串。更神奇的是,输入不同输出就不同,哪怕两本红楼梦,只有一个字不同,冲压结果也将大相径庭。

什么是哈希

#

简单认识哈希

<!--StartFragment-->

以比特币在用的SHA256哈希函数为例,我们看看不同的输入,得到的输出有什么不同。我们用Python得到hello world的哈希值:

import hashlib
# 要计算SHA-256哈希的原始数据
data = "hello world"
# 创建SHA-256哈希对象
sha256_hash = hashlib.sha256()
# 更新哈希对象,传入数据(需要编码为字节)
sha256_hash.update(data.encode('utf-8'))
# 哈希结果
print(sha256_hash.hexdigest())
# 输出
b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9

给hello world加一个感叹号,哈希结果为:

7c0bf498f86df605bc1716e74f375c62708510829f22078d5abb04124833cead

给50个连续的hello world!超长字符串求哈希,结果为:

b62cbce3594235e3551370ce4d55951f20476a0f1aa9bc6e2712eec9a3cce16e

结果表明,三次哈希结果长度相同,肉眼看上去毫无关系。这就是哈希这个冲压机器的特点。就算输入几百万几千万字符,输出依然是64个字母或数字。

直觉上,既然不管输入什么,输出都是64位字符串,那多个输入总会对应同一个输出的吧?

是的,这就是哈希碰撞,不过SHA256出现哈希碰撞的概率极低,应用中忽略不计。有些反直觉,毕竟随意长度的字符串太多了,而64个字符串不管怎么样都是有限的。数学上可以证明概率极低,但是学习区块链这个证明可以跳过,不妨碍区块链的安全性。

哈希的这个特性,使其成为验证信息完整性和检测任何更改的优秀工具。

譬如:大多数软件都会在官网展示其程序安装文件的SHA256结果。这样,不管你从哪个代理商购买的软件,或者担心下载过程中被拦截植入病毒,只需要在安装前求文件的SHA256结果,跟官网对比下。一样的结果证明程序文件未被更改过,可放心安装。

常见的哈希函数

常见的哈希函数有MD5、SHA1、SHA256和SHA3。MD5和SHA1的安全性较低,安全领域不建议使用。目前,比特币使用了SHA256,以太坊使用了SHA3。

这些哈希函数的计算过程有以下通用的几个主要步骤:

  1. 填充数据

    进行哈希计算的原始数据长度是参差不齐的,需要按照一定要求补齐长度。通常在数据末尾添加一个1位,接着添加若干0位,直到数据长度取模(如512位)运算等于特定值(如448位)。最后,添加一个固定的(如64位)的表示原始数据长度的结尾,这步操作是为了后续分块的时候,没有不足一块的残余。

  2. 分块处理

    将整个原始数据划分为固定长度的块,如512位,这也是为什么要填充数据的一个原因。对每个块进行几十次甚至上百次的数学运算,如位操作、模运算、压缩等...

  3. 拼接或压缩输出固定长度

    在所有数据块处理完成后,输出最终的哈希值,通常是将状态值连接起来形成固定长度的输出。SHA3的输出长度是可变的。

细节上差异很大,一般而言数学计算步骤较多,安全性较高;生成长度越长,防碰撞的能力越强。根据实际需要,在计算效率和安全性上做平衡。

哈希在比特币中的应用

#

区块链是去中心化的,每个全节点保存了全部的交易信息,不同节点之间对比数据差异,保证数据一致性,就必须使用哈希这一工具。\

如果没有哈希,想象下对比两个巨量的数据集,逐一对比,既需要漫长的时间,也消耗巨量的网络带宽。可以这么说,没有哈希,区块链无法成为现实

比特币充分利用了哈希,区块之间,后一个区块会将前一个区块的哈希值纳入区块哈希计算中,这样将不同的区块链接起来,形成了区块链;在一个区块内,将所有的交易信息进行哈希,方便节点之间校验区块数据,只需要计算出默克尔树,默克尔树一致则区块内交易信息完全一致。

哈希在区块之间的应用-区块链

比特币每个区块都有一个区块哈希值,表示这个区块的唯一性。它的计算逻辑如下:

  • 构建区块头

    区块哈希是通过对区块头进行哈希计算得到的。区块头包含以下字段:

    版本号(version) 前一个区块的哈希(previous block hash) 默克尔根(merkle root) 时间戳(time) 难度目标(bits) 随机数(nonce)

  • 序列化区块头

    将区块头的各个字段按照特定顺序进行序列化,通常是以小端字节序(little-endian)排列。

  • 两次哈希计算生成区块哈希值

使用 SHA-256 算法对序列化后的区块头进行两次哈希计算

因为每个区块哈希计算,都使用了前一个区块哈希值,所以区块之间就有了向前的唯一指向,形成一条链。我们可以取区块头信息和前一个区块哈希值做校验,判断当前区块的合法性。

我们以区块#856960为例子,自己计算区块哈希值,看是否和比特币主网浏览器查到的哈希一致:

import hashlib
import struct

def sha256d(data):
    """计算双重SHA-256哈希"""
    return hashlib.sha256(hashlib.sha256(data).digest()).digest()

def create_block_header(version, prev_hash, merkle_root, timestamp, bits, nonce):    
    """创建区块头"""
    header = (
        struct.pack("&lt;L", version) +  # 版本号
        bytes.fromhex(prev_hash)[::-1] +  # 前一个区块的哈希(小端序)
        bytes.fromhex(merkle_root)[::-1] +  # 默克尔根(小端序)
        struct.pack("&lt;L", timestamp) +  # 时间戳
        struct.pack("&lt;L", bits) +  # 难度目标 
        struct.pack("&lt;L", nonce)  # 随机数   
    )
    return header

def validate_block_hash(version, prev_hash, merkle_root, timestamp, bits, nonce, expected_hash): 
    """验证区块哈希的合法性"""
    block_header = create_block_header(version, prev_hash, merkle_root, timestamp, bits, nonce)
    block_hash = sha256d(block_header)
    print("计算哈希值为:"+block_hash[::-1].hex())
    print("主网哈希值为:"+expected_hash)
    return block_hash[::-1].hex() == expected_hash  # 转换为小端序并比较

# block id #856960 的信息 取自比特币主网 prev_hash为前一个区块#856959的哈希值
version = 537452544
prev_hash = "00000000000000000000fdcb6d2e07cc883ab259e8e6c7f9d7fbcbe47ea44274"
merkle_root = "5a39abc218ee9d95b393ecdbc407725fe2577d506208c5c5da4ef8d0d6578828"
timestamp = 1723766311
bits = 386088310
nonce = 2189834309
expected_hash = "00000000000000000002521858364734ccd35e3cdcf443f89a630395bc89e8b9"

# 验证哈希
is_valid = validate_block_hash(version, prev_hash, merkle_root, timestamp, bits, nonce, expected_hash)
print("区块哈希合法性:", is_valid)

验证结果为:

计算哈希值为:00000000000000000002521858364734ccd35e3cdcf443f89a630395bc89e8b9
主网哈希值为:00000000000000000002521858364734ccd35e3cdcf443f89a630395bc89e8b9
区块哈希合法性: True

哈希在区块内的应用-默克尔树

 比特币中区块的默克尔根(Merkle Root)是通过对区块内所有交易的哈希值进行一系列哈希计算得到的。以下是计算默克尔根的步骤:

  • 收集交易哈希:

从区块中提取所有交易,计算每个交易的 SHA-256 哈希值。

  • 构建哈希树:

将所有交易哈希放入一个列表。若交易数量为奇数,重复最后一个哈希以确保每一层都是偶数个哈希。

  • 逐层计算哈希值:

将相邻的哈希值组合在一起(连接),然后对组合后的字符串进行 SHA-256 哈希。继续这个过程,直到只剩下一个哈希值,这个值就是默克尔根。

每个节点可以校验区块内的交易信息,计算默克尔树的合法性。节点之间校验区块内交易信息时,可以仅对比默克尔树即可,非常高效。

点赞 1
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
seashell
seashell
区块链+金融,个人公众号SeashellMovingOn