区块链:哈希算法与一致性哈希算法

本篇主要介绍区块链中常用到的哈希算法。

1 哈希算法

1.1 定义及特性

  哈希算法是指通过哈希函数(Hash Function)对任意长度的输入数据(比如文件、消息、数字等)进行转换,生成一个固定长度的哈希值(Hash Value)的过程。
  在区块链中,哈希算法常用于区块验证及安全性保证。为了保证安全,哈希算法必须满足以下三个条件:

  • 抗冲突(collision-resistance): 即不同的输入不能产生相同的输出。为了保证哈希算法的抗冲突性,一般会采用以下几种方式:将输入信息尽可能均匀地映射到输入空间、引入随机性、提升计算复杂度等。好的哈希算法设计能够大大降低哈希碰撞的概率,提高数据的安全性和完整性。
  • 信息隐藏性(information hiding): 即无法通过哈希函数的输出反推其输入。
  • 可隐匿性(puzzle friendly): 即任何微小的输入变化都会使得输出哈希值的分布发生不可预测的改变,可以保证攻击者无法通过改变输入数据的一部分来预测输出哈希值的变化情况。
1.2 常用哈希算法

  密码学中常用的哈希算法有MD5、SHA1、SHA2、SHA256、SHA512SHA3、RIPEMD160。这里仅以MD5和SHA1为例展示其Python代码,如下:

import hashlib

message='今天是2023年7月13号,今年天气很热!'

#MD5
md5=hashlib.md5()
md5.update(message.encode('utf-8'))
md5_mess=md5.hexdigest()
print("MD5加密后的内容为:{}".format(md5_mess))

#SHA1
sha1=hashlib.sha1()
sha1.update(message.encode('utf-8'))
sha1_mess=sha1.hexdigest()
print("SHA1加密后的内容为:{}".format(sha1_mess))

其结果如下:

MD5加密后的内容为:77815d973ce48613428d52956a1f6979
SHA1加密后的内容为:572379a7baa62fd26e39bb4bbaf511497dbf838c

2 一致性哈希算法

  一致性哈希算法(Consistent Hashing,CH)是哈希算法的一种扩展,主要是为了解决分布式系统中的数据分布和负载均衡问题。

2.1 算法原理

  一致性哈希算法的工作原理如下:

  • 设置一个地址空间范围为 0 ∼ ( 2 32 − 1 ) 0 sim (2^{32}-1) 0(2321)的哈希环;
  • 使用节点的特征值(一般使用节点ip地址)计算哈希值,并将该哈希值映射到哈希环上的某点;
  • 对数据key使用相同的哈希函数计算出哈希值,同样将其映射到哈希环上;
  • 从数据映射的位置开始顺时针查找,其所遇到的第一个存储节点,即为这个key值所对应的存储节点地址。

  但当哈希环结构上的存储节点较少时,存储节点在哈希环上分配随机性较高,导致存储节点所承担的负载并不均匀。为了避免这种现象的发生,引入虚节点。其思想是将哈希环的不同位置上放置一个节点的多个拷贝(即虚拟节点,使用真实节点对应虚拟节点的特征来计算哈希值得到虚拟节点在哈希环上的位置)。加入虚拟节点后,key值的映射关系需要经过两步:

  • 计算出key值和虚拟节点的映射关系;
  • 根据虚拟节点和存储节点的映射关系找到key值对应的真实存储节点;
2.2 案例

  使用python代码模拟使用一致性哈希算法解决分布式数据分布的问题。案例代码如下:

import hashlib

class ConsistentHashing:
    def __init__(self, nodes=None, replica_count=10):
        self.replica_count = replica_count #虚拟节点的数量
        self.ring = {} #记录哈希环地址及其对应的真实节点
        self.sorted_keys = []
        if nodes:
            for node in nodes:
                self.add_node(node)
                
    #新节点加入
    def add_node(self, node):
        for i in range(self.replica_count):
            key = self.get_key(node, i)
            self.ring[key] = node
            self.sorted_keys.append(key)
 
        self.sorted_keys.sort()
        
    #节点退出
    def remove_node(self, node):
        for i in range(self.replica_count):
            key = self.get_key(node, i)
            del self.ring[key]
            self.sorted_keys.remove(key)
            
    #获取数据内容的保存地址
    def get_node(self, data_key):
        if not self.ring:
            return None

        hashed_key = self.hash_key(data_key)
        for key in self.sorted_keys:
            if hashed_key <= key:
                return self.ring[key]

        return self.ring[self.sorted_keys[0]]
    
    #根据真实节点构造虚拟节点的特征值,并用此特征值计算虚拟节点在哈希环上的地址
    def get_key(self, node, replica_index):
        return self.hash_key(f"{node}#{replica_index}")

    #这里使用的哈希函数为SHA1
    def hash_key(self, key):
        hashed_key = hashlib.sha1(key.encode()).digest()
        return int.from_bytes(hashed_key, byteorder='big')


nodes = ['node1', 'node2', 'node3', 'node4']
CH = ConsistentHashing(nodes=nodes, replica_count=5)

data_keys = ['data1', 'data2', 'data3', 'data4']
for key in data_keys:
    node = CH.get_node(key)
    print(f"'{key}' belongs to '{node}'")

其中一种可能的运行结果如下:

‘data1’ belongs to ‘node1’
‘data2’ belongs to ‘node3’
‘data3’ belongs to ‘node1’
‘data4’ belongs to ‘node2’

参考资料

  1. 《一致性哈希算法的对比研究》
  2. 《白话区块链》