比特币有关
在现代社会中,一种大量流通的货币一般都有非常高级的加密技术,例如人民币上就有水印、荧光光圈等一系列技术来提高伪造的难度。数字货币在本质上是网络上的数据,非常容易受到攻击,所以需要一套完善的加密体系。
就像我们在银行有账户密码一样,在比特币中我们也有一些相似的东西来决定一个账户的所有权。具体来说有三个与账户相关的概念:私钥、公钥和比特币地址。
他们之间的关系如上图所示,私钥可以通过椭圆加密算法生成公钥,公钥可以通过哈希函数生成比特币地址。反过来看,有了比特币地址是无法生成公钥的,有了公钥也无法生成私钥,这是由后面要介绍的加密算法所实现的。
掌握私钥就能生成相应的公钥和比特币地址,相当于掌握了整个账户,所以我们一定要保管好自己的私钥。
椭圆加密算法
从私钥到公钥所用的是椭圆加密算法,它有一个简单的数学形式,即
其中 K
代表公钥,k
代表私钥,而 G
代表椭圆曲线上面的一点,这个乘法不是自然数的乘法,而是椭圆乘法。
椭圆曲线的加法如下所示:
得到
\(x_3 = \lambda^2-x_1-x_2\)
\(y_3 = \lambda(x_3-x_1)+y_1\)
通过计算 G+G 算出 2G,继而算出 4G,最终通过大约 log2(k)
次算出 k * G,由于每次的计算量不大,所以总体计算量也不大。
要根据 K
和 G
得到 k
的问题叫做离散对数问题,计算复杂度为根号 p
,其中 p
为整个群的大小,(如果 n
位二进制那么复杂度就是 2n/2)这个复杂度是非常可怕的,几乎没有可能求解出来。换句话说,根据公钥反向计算私钥基本上是不可能的。
哈希算法
哈希函数有很多种,一般有如下定义:
- 输入是任意长度的字符串。
- 输出是固定长度的数字(例如比特币一般用
256
位)。 - 计算非常简单。
从安全角度来看,它有下面的特征:
- 没有碰撞:对于
x
和y
,如果x
不等于y
,那么我们可以保证H(x)
不等于H(y)
。这条性质是概率上的保证,因为我们的输入集合是一个无限的集合,而输出是一个只有 2 的 256 次方的集合,根据抽屉原理必然存在x1
与x2
不相等但是H(x1)
等于H(x2)
的情况,但是 Hash 函数的设计会让这个概率非常非常小,小到几乎不可能发生。 - 隐藏性质:从
x
可以生成H(x)
,但是几乎无法从H(x)
生成x
,这条性质保证我们无法由比特币地址得到公钥。
交易与数字签名
一个方框代表一笔交易,例如中间的方块代表的是由 owner1
支付给 owner2
的一次交易,我们可以看到在方框下方有一个签名,正是这个签名让这次交易有效。
这个签名签署用到的是 owner1
的私钥和与这次交易的相关信息(包括前一次交易,owner2
公钥,金额,时间戳等信息),其中:
其中 sig
代表签名,sign
是签名函数,sk
代表私钥,message
代表交易相关信息。
而签名完成后,其他人要有办法验证这笔交易是否有效,所以它们要用 owner1
的公钥,交易相关信息以及签名完成验证。用 api
的形式来表达就是
这里的 pk
代表公钥,isValid
是一个布尔变量,其值为真或假。
从上面的两个 api
我们可以看到数字签名有一系列好的性质,一个性质是我们在签名的时候用到私钥,但是验证时只用公钥,这保证了私钥的安全性。第二个是签名与每次交易对应,我们不能把签名迁移到另一次交易上,只要私钥不泄露别人就无法伪造签名。
哈希指针
首先我们要引入一个叫哈希指针的概念。一般来说一个普通指针可以告诉你数据存储的位置,哈希指针除了能够告诉你数据存储的位置,由于它还存储了对应数据的哈希值,还能告诉你对应数据是否被篡改过。
在区块链中,如果我们假设一个区块被篡改了:
那么如果我们对这个区块的数据计算哈希值,就会跟后面一个区块中存储的哈希值对不上,就会发生验证的问题。那么如果要掩盖这个问题,就必须把再后面一个区块的哈希值给改掉,这是后面的也出现哈希值改变的问题,这里就需要一直改到最近的一个区块。有一个特殊的哈希指针指向最近的区块,由于这个哈希指针无法被黑客篡改,所以黑客没有办法通过篡改区块的方式对区块链进行攻击。
默克尔树
其实所有带有指针的数据结构都可以把普通指针换为哈希指针,例如我们可以把二叉树的指针也换成哈希指针,然后就可以得到默克尔树。
默克尔树底部的树叶是我们的交易数据,而每一个父节点是两个子节点哈希值之和的哈希值。
树这种数据结构为我们快速验证一个交易是否存在提供了便利,在比特币中我们为每个交易存储了一个默克尔路径,比如我们要验证 K
这个交易,我们只用存储下图中蓝色的哈希值就能把根节点给重建出来,验证是否存在这笔交易。而不需要整棵树的数据,这大大节约了空间和时间,在比特币的 SPV
轻钱包认证中默克尔树起到非常重要的作用。
由默克尔树和区块链组成的区块链的架构
真实的比特币系统中,外层是一个区块链,而每个区块内部则是用一个默克尔树来存储交易。外层的区块链保证了结构的简单,为后面将要介绍的挖矿与共识打下了基础,而默克尔树则为快速验证交易提供了基础。
挖矿
作为一种货币,中本聪在设计之初就对比特币的分发做了周详的设计,总共 2100 万枚比特币将按照一个递减的速率的分发,具体是每四年速率减缓一次。最初发现每个区块会奖励 50 个比特币,到了2017年,发现新区块的奖励已经降低到了 12.5 个比特币。
新区块是如何被发现的呢?这就涉及到了比特币的 “挖矿”,所有的“矿工” 会竞争解决一个问题来获得记账权并获得相应的新币奖励。
它们解决的问题是一个与哈希加密算法有关的数学难题,具体来说就是对于一个字符串,寻找一个后缀来使哈希值小于某一特定值。
对于字符串,寻找后缀使得哈希值小于某一阈值是随机的。
如果得到的哈希值是完全随机的(近似均匀抽样),那么我们可以通过设置一个阈值,让竞争者找到一个后缀,使其对应的哈希值小于这个值。我们可以通过调整这个值的大小来改变期望的计算次数,我们的哈希函数得到的结果的最大值是 2 的 256 次方,所以如果我们把阈值设为 2 的 240 次方,那得到小于这个值的概率是 1 除以 2 的 16 次方。我们可以通过调整阈值来控制难度,阈值越小,难度越高。在比特币系统中,难度会随着前面发现区块的时间动态调整,使得发现每个区块的平均时间为 10 分钟。
双重支付
比如使用饭卡的支付,如果系统响应减慢,完全可以以五块的余额做出十块的东西。方法就是在刚用五块钱,当系统没有确认支付,你刷卡显示的钱还是五块钱。可以利用这个时间差进行双重支付。
我们可以看上面这幅图,A 支付给了 B,这项交易被写入了区块,假设这个区块的名字是 M,然后 A 马上开始从 M 的前一个区块开始挖新的区块,构成一条新的链条,在这个新的链条中 A 把比特币支付给了自己的地址,如果这条新的链条的长度超过了原来的链条,那么所有人会接受这条最长链条,抛弃原来的链条(这是因为在比特币中所有人都遵循将最长链条视为有效链条的规则)。如此一来,M 及 M 以后的区块交易都会变得无效,A 转出去的比特币又变回来了。
通过“挖矿”我们可以给予人们动机去当诚实的节点。
我们先从数学角度分析一下这个问题:
\(p = 诚实节点发现下一节点的概率\)
$q = 欺诈节点发现下一节点的概率 $
\(qz = 欺诈节点从落后 z 个节点到追上诚实节点的概率\)
我们假设在 A 支付给 B 后就马上开始准备新的链条 ,如果在 z 个区块后 B 确认了交易,那么在这段时间内 A 生成区块的数量应该是一个均值为 z * (q/p) 的泊松分布,那么 A 能够完成双重支付攻击的概率就是:
对泊松分布的解释:
生成区块数量的期望是z*(q/p)
那么如果 q > p ,几乎一定会追上诚实节点,这也是 51%算力攻击这种说法的来源(更详细的介绍51%算力攻击)
如果 q < p 的话,坏人能够追上的概率随着 z 的增加呈现出指数型衰减 。由于比特币的交易需要 6 次确认, 并且在现实中已经几乎没有机构能够垄断算力,所以双重支付攻击基本是不可能实现的。
从另一方面来看,就算有人有能力能够追上诚实节点并发动 51%算力攻击,他可以选择当一个诚实的节点,从挖到的新区块的奖励和比特币价值的增长中获得收益。也可以选择对系统进行攻击,损害整个社区使比特币失去价值。如果作为诚实节点的经济激励大于作恶,他就会有经济动机去保持诚实。
从上面的分析来看,工作量证明通过用算力投票的方式让节点自发地选择做诚实的节点从而让双重支付几乎不会发生,这样的机制非常巧妙,在比特币后很多新币也发明了如 POS , DPOS 等新的共识机制来解决双重支付的问题。
内容系:蓝桥云课 《比特币基础概念入门》 整理笔记