如果我们已知字符串 A 的哈希值是 H(A),那么我们没有可行的办法算出 A 是什么。注意,这里说的是 “不可行” 而不是 “不可能”。 比如下面的例子中,知道输出哈希值是可以算出输入的。
假如我们掷骰子🎲,输出就是骰子上数字的哈希值。那么在知道输出的哈希值情况下,我们能否知道骰子上的数字呢?因为哈希函数是具有确定性的,相同输入的哈希值一定相同,所以我们只需计算 1-6 的哈希值是什么,然后对比就能知道骰子上的数字是什么了。
当然,我们能够根据哈希值猜出骰子的数字,是因为输入值只有 6 种可能性。如果我们的输入值来自一个分散的集合,那么想要通过输出推导出输入的唯一方法可能就是“暴力破解法”了。暴力破解就是,任意选择一个输入,计算其哈希值,与现有哈希值对比是否一致,不断重复这一过程,直到找到一个输入的哈希值与现有哈希值一致。
那么暴力破解法是否可行呢?假设我们现在处理的是128位的哈希值。
最好的情况:第一次尝试就找到了答案,但这种情况可以说是几乎不可能的,比中大乐透还难。
最坏的情况:在尝试 2^128 -1 次后得到了答案,也就是试过了所有可能的输入才找到。
平均的情况: 在平均情况下,我们要尝试 2^128 / 2 = 2^127 次之后才能找到答案。2^127 = 1.7 X 10^38 , 也可以说是个天文数字了。
所以,在已知哈希值的情况下, 尽管可以通过暴力破解的方法找到输入的字符串是什么,但这会花费很长长长长长的时间,所以不用担心。
这一特性对加密货币来说至关重要(特别是在挖矿过程中)。先定义下什么是谜题友好。
谜题友好: 如果对于任意 n 位输出值 y, 假定 k 选自高阶最小熵分布,如果无法找到一个可行的办法,在比 2^n 小很多的时间内找到 x , 保证 H(k|x) = y 成立,那么我们称哈希函数 H 具有谜题友好的特性。
再来回顾下谜题友好的定义。假设有一个 n 位输出值 y, 从高阶分布中选取一个任意值 k, 那么没有一个可行的办法,比 2^n 小很多的时间内找到 x ,使得 H(k| x) = y。
这里说的还是 “不可行”,而不是 “不可能”。
整个的比特币采矿的过程就基于解谜。
下面是几个典型的加密哈希函数:
-
MD5:产生 128 位哈希。
-
SHA-1:产生 160 位哈希。
-
SHA-256:产生 256 位哈希。也是比特币中使用的哈希函数。
-
Keccak-256:产生 256 位哈希,在 Ethereum 中使用。
如果想要理解区块链是怎样工作的,就必须要理解其中 3 种重要的数据结构 哈希指针 , 区块链 和 梅克尔树。
与普通指针不同的是,哈希指针的值是通过数据计算出来的且指向数据所在位置,所以哈希指针可以告诉我们数据存储位置及数据的哈希值。通过哈希指针,我们可以很容易判断出数据是否有被篡改。
哈希指针在区块链中极为重要。区块链的结构就是由创世区块开始,之后的每个区块通过哈希指针进行连接。每一个区块中都包含了前一个区块的哈希指针,这样后面区块不仅可以查找到前面所有区块,也可以验证前面区块数据有没有被更改,从而保证了区块链不易篡改的特性。
哈希指针在区块链中第二个用处就是构建Merkle Tree(梅克尔树),下文会详细讲。
区块链是一个基于哈希指针构建的一个有序的,反向链接的交易块链表,也就是说在区块链中每个区块都通过哈希指针连接到前一个区块上。大致结构如下图:
区块链也常被看做一个垂直的堆栈,区块在栈顶一次追加,第一个区块也就是整个堆栈的基础。所以也常常用“高度”(height) 这个词来描述某个区块到第一个区块的距离。
区块链中的每一个区块都有一个对区块头部进行 SHA256 加密哈希函数计算得出的哈希值作为标识。由于每个区块需要连接到前一个区块,所以每个区块头部专门有一个字段用来存储前一个区块(也叫父区块)的哈希值。这样每个区块都连接到了他们的父区块,从而创建了区块链。
尽管每一个区块只能由一个父区块,但却可能短时间内拥有多个子区块。也就是说可能存在多个区块头部中存储的父区块的哈希值是一样的。这种情况一般发生在不同的区块在同一时间被不同的矿工找到。这样就会造成区块链的分叉,如下图:
不过区块链的分叉只是暂时,会根据“最长链原则”来解决分叉。不是本文重点不再赘述。
刚刚第二节提到哈希指针可以保证区块链不易被篡改,下面来分析下原因。
假设有一个黑客想要篡改上图中 区块 2 的数据。由于哈希函数具有抗篡改能力,很小的改动,输出的哈希值会大不一样。所以如果改动区块2上的数据,那么 区块3 自身的哈希会发生变化。 而 区块3 的头部中存储了 区块2 的哈希值,所以 对区块2 的改动必然会影响到 区块3,以此类推,区块3后面的区块也会受到影响。所以一个区块的修改会级联影响到它之后的所有区块,而要修改之后的所有区块需要强大的算力,可以说是不可能的,这也就保证了区块链的不变性。
仔细看下区块链中每个区块的结构。
在区块的头部中,有存储一个梅克尔树根的 hash 值。所以先来了解下什么是梅克尔树。
上图就是梅克尔树的样子。
梅克尔树在区块链中用于组织和记录存储在区块中的交易,以便高效的验证某个交易是否存在在区块中。梅克尔树是通过不断的递归计算节点的哈希值直到只有一个hash值来构建的。
当梅克尔树中有N个数据时,最多只需要2*log2(N)计算就可以验证某个特定数据是否存在,所以梅克尔树是相当高效的。
梅克尔树是自底向上构建的。举个例子,假设我们现在有 A, B, C,D四笔交易需要存储记录在区块中,来看下是如何构建梅克尔树的。 首先要用 A,B,C,D 来构建树的叶子节点,将他们二次哈希后的哈希值存储在是叶子节点,就是上图中的 HA,HB,HC,HD。