比特币:一个点对点的电子现金系统
作者:中本聪,https://bitcoin.org/bitcoin.pdf 翻译:happypeter ,English Version: html。
摘要
一个真正的点对点的电子现金系统可以让互联网上的支付直接从一方到达另一方,而不需要中间经过金融机构。数字签名可以提供部分解决方案,但是如果还是需要一个可以信任的第三方机构来来避免双花问题,那么这个系统最重要的优势也就消失了。我们提出了一种通过点对点网络解决双花的方案。网络会不断对交易运算哈希,来形成一条不断延长的基于哈希的工作量证明链,通过这样的形式来给交易加上时间戳的。如果有人想修改交易记录,那么他就必须重新进行哈希运算得到工作量证明。最长的那条链不仅可以用来证明各种事情发生的先后顺序,同时也可以证明自己是来自一个最大的算力池。所以只要掌握大部分算力的节点群体没有协同起来攻击比特币网络,那么最长的链就会由这个群体生成,因为它会比攻击者群体跑的更快。网络本身的几乎不需要考虑组织结构,节点们只需要尽最大的努力广播信息就行了。每个节点都可以随时的加入和退出网络,当重返网络的时候,只要相信那条最长的链就可以了。
1. 介绍
互联网上的商家目前在处理电子支付的时候都必须要依赖于第三方的金融机构。大多数时候,这种方式还是可行的,但是始终要受到这种基于信任的模型的拖累。因为避免不了仲裁争议,金融机构无法实现绝对不可逆的交易,调解争议的成本造成了交易成本的增加,导致实际中交易的最小数额不可能太小,这样我们有些很小额的日常交易其实就不能做了,另外一个更大的损失就是,系统没有办法去为那些提供不可逆服务的机构去提供不可逆支付。由于可能存在交易被逆转,对于信任的需求就变得愈发强烈。商家必须非常提防顾客,让他们填写一些本来不是那么必要的信息。一定比例的欺诈行为被认为是不可避免的。如果采用付现金的方式,这些成本和交易不确定性是可以避免的,但是还没有一种机制可以让我们摆脱对他人的信任,通过一条通信通道来直接进行支付。
我们真正需要的是一个不基于信任而基于加密证明的电子支付系统,这样有意愿的交易双方就可以不依赖可信的第三方而直接进行交易了。不可逆转的交易可以防止卖方被欺诈,常规的第三方托管机制则可以让买方得到保护。本论文中,我们提出了解决双花问题的解决方案,采用点对点分布式的时间戳服务器去生成用来保证交易时间先后顺序的算力证明。只要所有诚实的节点加起来的运算能力大于协同起来的攻击者节点的运算能力,那么整个系统就是安全的。
2. 交易
我们把一枚电子货币定义成一条数字签名链。币的拥有者如果需要把这枚币给别人,那么他就拿出上一次交易的哈希加上收款人的公钥,再对这两个东西的结合体做一个数字签名,然后把三者都添加到币的结尾处。收款人可以通过验证签名来验证付款人是否真的拥有这个币。
当然,问题是收款的人没有办法验证这个币的之前的持有人中,有没有人把这个币花了两次。通常的解决方案是引入一个可信的中央权威,或是铸币厂,来进行每次交易的双花检查。每次交易之后,这枚币都必须回到铸币厂,然后发放一枚新币,只有直接从铸币厂发出的币才被认为是没有被双花过的。这种解决方案的问题是整个货币系统的命运都依赖于运营铸币厂的那个公司,所有的交易都要经他们的手,这个就和银行没有什么区别了。
我们需要一种方式让收款人知道以前的持有人没有签署过任何更早的交易。大家规定最早的那个交易才是算数的,忽略后面的任何试图进行双花的操作。如何去确认以前确实不存在一个特定交易呢?没有别的办法,只能是公布历史上所有的交易。在基于铸币厂的模型中,铸币厂是知道所有的交易的,所以可以判断出哪次交易首先到达。为了在不依赖于第三方的情况下达成这个效果,各个交易都必须公布给所有人,同时系统要保证可以让大家认可同一个交易顺序。收款人需要有东西可以证明交易发生的时候,大多数的节点都认可这个交易是大家收到的第一个。
3. 时间戳服务器
我们提出的方案要开始于构建一个时间戳服务器。时间戳服务器的工作原理是对一个需要被打时间戳的包含多项记录的区块运算哈希,然后把这个哈希作为时间戳广播出去。这就好比把这个时间戳登到报纸上或者发到论坛里了,让大家去见证它的出现的时刻。时间戳可以证明用来生成它相关的数据在那个时刻已经存在了,不然也不可能运算出这个哈希来。同时,每次生成新时间戳的哈希运算过程中,输入数据都包含着之前的时间戳,这样就形成了一个链条,每个新的时间戳都可以强化印证以前的时间戳的出现的顺序。
4. 工作量证明
我们真正需要的是一个类似 Adam Back 的哈希现金的工作量证明系统,而不是报纸或者论坛,来达成点对点的分布式时间戳服务器。工作量证明机制就是去找一个值,当对这个值进行类似 SHA-256 这样的哈希运算的时候,得到的哈希值满足以特定数量的零打头。随着零的数量的增加,找到这个值所需要的工作量会呈指数增长,但是要验证这个值只需要一次哈希运算。
我们给区块数据添加一个随机数,每次运算区块的哈希的时候都把它加一,这样经过不断尝试就能找到一个合格的随机数的值,保证区块哈希以所需位数的零打头,这就是为时间戳服务器构建的工作量证明机制了。当我们花费了 CPU 算力去让区块满足了工作量证明机制的要求,那么这个区块就很难被改动了,因为改动区块必须要重新运算获得合格的随机数。随着新区块不断的添加的这个区块的后面,形成一条链,那么修改区块的工作量中还会包含重新生成之后的所有区块。
工作量证明机制同时解决了如何计票的问题。如果用一个 IP 地址代表一票,那么就很容易捣鬼,因为有人可以很轻易的获得很多 IP 。而工作量证明机制的思路是一个 CPU 一票。这样最长的那条链就可以代表多数票,因为它是用最多的算力生成出来的。如果诚实的节点控制了大多数的 CPU 算力,那么诚实的链就会比任何竞争链都要延长的更快。要想修改一个过去的区块,攻击者需要完成这个以及后面所有区块的工作量证明过程所需的工作量,然后赶上并超过所有诚实节点的工作量。稍后我们会展示,运算速度比较慢的攻击者追上所有诚实节点的概率会随着后续区块的增多而呈指数减小。
因为硬件速度和节点数量的变化,工作量证明的难度是会不断调整的。调整通过不断改变每小时平均能够生成的区块数来实现。如果生成的太快,那么难度就会增加。
5. 网络
网络运行的步骤如下:
- 1 新交易被广播给所有节点
- 2 节点把交易收集到一个区块中
- 3 节点开始计算这个区块的工作量证明
- 4 当一个节点找到工作量证明后,它就会把区块广播给所有节点
- 5 只有当区块中的交易都是有效的并且是没有被花费的,其他的节点才会接受这个区块
- 6 接受的表现形式是把这个区块的哈希作为前哈希来制作下一个区块
所以节点都始终认为最长的那条链是正确的,并基于这条链来运算。如果两个节点同时广播出了不同版本的下一个区块,那么某些节点可能会先收到其中一个,而其他节点却先收到另一个。这样,节点会基于它先收到的节点来运算,但是也会保存另外一个分支,因为这个分支也很有可能成为最长链。当下一个工作量证明被找到后,到底哪条分支比较长就明确了,发现自己站错队的节点会切换到最长的这个分支上。
新的交易不一定非要到达网络上的所有节点。只要足够多的节点收到了这个交易,那么它不久后就可以被收入到区块中。区块广播的时候也是有容错能力的,不必担心个别的信息丢失。如果节点没有收到区块,那么当它收到下一个区块的时候,就会发现自己少了一个区块,然后会再次请求。
6. 激励
按照约定,区块中的第一个交易是比较特殊的,里面会生成一个归区块创建者所有的币。这个激励措施让节点有动力去支持网络。同时这也是发行新币的方式,因为网络上不会通过中央权威发行新币。我们稳定的去增加币的数量,很像去花费资源挖矿来增加黄金的流通量,只不过我们这里花费的资源是 CPU 时间和电力。
交易手续费是另外一种激励。如果一笔交易的输出值小于它的输入值,那么其中的差额就是手续费。手续费会被添加到交易所在区块的激励总额之中。一旦既定数量的币已经进入流通,那么奖励将全部由交易手续费来完成,整个系统将不会有通货膨胀。
这样的激励形式也很有可能让节点保持诚实。如果一个贪婪的攻击者手里真的掌握了比全部诚实节点还要多的 CPU 算力,那他可以有两种选择。一是可以选择欺骗他人来把自己已经花掉的币再偷回来,另外就是可以用算力来生产新币。他会发现遵守规则会获利更多,因为规则可以让他获得的币比其他所有人加起来还要多。而如果破坏规则了,那么他自己手里的币也就不值钱了。
7. 回收硬盘空间
当一个币的最近一个交易被埋在足够多的区块之下,就可以扔掉之前花费这个币的交易来节省硬盘空间了。区块中的交易是通过默克尔树的形式来生成哈希的,这使得扔掉交易之后区块的哈希也不会变,因为只有默克尔根会被包含在区块哈希中。把默克尔树的一些分支砍掉,删掉一些内部哈希,这样就能压缩老区块。
一个不带交易的区块头大概是80字节。假定每十分钟生成一个区块,每年生成的数据总量就是:80字节 * 6 * 24 * 365 = 4.2M 。就2008年来看,计算机系统一般都是 2G 的内存。根据摩尔定律,每年会增加 1.2G ,这样算的话,即使把所有的区块头都保存到内存里也不成问题。
8. 简化的支付验证
不运行全节点也可以去验证支付。通过请求网络上的各个节点,用户可以获取到他认为最长的那条工作量证明链。用户只需要去保存最长链的区块头,并且拿到连接交易和给它加上时间戳的区块的默克尔分支。用户自己并不能去检查交易,但是通过把交易定位到链上的一个特定位置,就可以看到的确有节点接受了这个交易,而之后的区块则可以证明这个交易是被全网接受的。
这样,只要诚实的节点控制网络,那么验证就是可信的,但是在攻击者掌握较多算力的情况下,这种验证方式是比较脆弱的。全节点可以自己验证交易,而使用简化验证这种方法的用户就很可能被掌握更多算力的攻击者伪造的交易所迷惑。防范这个问题的策略是去接收节点发过来的警告。当节点发现无效区块的时候,它会提醒用户的软件去下载完整的区块和被警告的交易,然后自己确认一下问题是否真的存在。需要频繁交易的业务场景还是最好运行自己的节点,这样可以保证更独立的安全性和更快的验证速度。
9. 价值的组合与分割
尽管可以对币作单个的处理,但是如果转账的时候把每一分钱都单独做个交易,就显得太傻了。为了实现价值的合并和分割,交易中可以有多个输入和输出。一般来讲,如果之前的有一个交易数额够用的话,输入就是一个,否则就会有几个输入,以便凑够数额。输出最多就是两个:一个用来支付,如果需要找零,才会另外有一个输出指向发出方。
需要指出的是,这种扇形展开的形式,也就是一个交易会依赖多个交易,同时那些交易又依赖更多的交易,并不会造成问题,因为永远没有必要去抽出一个交易的完整且独立的历史。
10. 隐私
传统的银行模式达成隐私的方式是限制公众访问交易双方以及可信第三方的信息。我们这里因为需要对公众宣布所有的交易,这种方法就不可行了。但是通过保证公钥持有者匿名,依然可以切断信息流从而获得隐私。公众可以看到发送方转了多大的数额给接收方,但是不能确认双方的真实身份。这跟股票交易所发布信息的隐私水平类似,可以让公众知道每个交易的时间和金额,但是不说出是谁在交易。
为了再增加一层安全,每次交易的时候都应该更换密钥对,这样就可以避免所有的信息都指向同一个人。对于有多个输入的交易,有些指向还是避免不了的,这样就必然会被识别出这些交易的输入都是属于同一个人的。这里的最大的危险是,如果一旦一个密钥的拥有者被曝光了,这种指向性会暴露这个人的其他密钥对应的交易。
11. 计算
我们来思考一下攻击者试图去生成另一条链,并让他的链的延长速度快于诚实链的情况。即使攻击者做到了这一点,也不意味着可以对系统做任意的修改,例如凭空生成新币,或者把从来不属于自己的币转给自己。节点不能用无效的交易来进行支付,诚实的节点也绝不会接受包含无效交易的区块。攻击者唯一能做的就是通过修改自己的交易来把最近花掉的钱弄回来。
诚实链和攻击链的竞赛可以用二叉树随机漫步来描述。成功事件是诚实链延长了一个区块,让自己的优势加1,失败事件是攻击者的链延长了一个区块,把差距减1。
落后特定距离的攻击者追上来的概率很类似赌徒破产问题。假定赌徒已经欠下了一些钱,但是拥有无限的透支信用,可以无限次的去赌来把钱还上。通过如下方法可以计算出赌徒能还上钱或者是攻击者可以追上诚实链的概率:
假定 p>q ,概率会随着需要追赶的区块数量的增加而呈指数减小。由于他每次赢的机会都是偏小的,如果开始的时候它没能特别走运而迅速翻身,后面随着他落后的更远,能够追上的机会就几乎不存在了。
现在我们来思考交易接收方需要等多久才能足够确信发送方不能更改交易了。我们假定发送方是一个攻击者,他想要让接收方相信他已经支付,然后等一会儿却又会把钱弄了回来。虽然收款人早晚会知道自己上当了,不过攻击者希望那时已经太晚了。
接收方生成一对新的钥匙,并要求发送方收到公钥后立即签署交易进行转账。否则就可能形成这样的情况:发送方先偷偷运算几个区块出来,如果够幸运他很有可能让自己的链比大家的链领先足够多的区块,然后他才签署一个把钱转给接收方交易并广播。但是当交易发出后,发送方继续在自己私藏的那条链上运算,而这条链上的交易中不包含给接收者的那次转账。
接收者会等待交易被区块收录,并且后续已经延长了 z 个区块。他不知道攻击者的那条链上已经延长了几个区块了,假定生成每个区块需要一个平均期待时间,攻击者的延长数量满足泊松分布,期望值是:
为了得到攻击者仍然能够追上的概率,我们将攻击者每个延长数量对应的泊松密度,乘以在该数量下依然能够追赶上的概率:
重新整理,避免对无限数列求和:
转换为 C 语言程序:
#include <math.h>
double AttackerSuccessProbability(double q, int z)
{
double p = 1.0 - q;
double lambda = z * (q / p);
double sum = 1.0;
int i, k;
for (k = 0; k <= z; k++)
{
double poisson = exp(-lambda);
for (i = 1; i <= k; i++)
poisson *= lambda / i;
sum -= poisson * (1 - pow(q / p, z - k));
}
return sum;
}
运算部分结果,可以看到随着 z 的增加,概率呈指数下降:
q=0.1
z=0 P=1.0000000
z=1 P=0.2045873
z=2 P=0.0509779
z=3 P=0.0131722
z=4 P=0.0034552
z=5 P=0.0009137
z=6 P=0.0002428
z=7 P=0.0000647
z=8 P=0.0000173
z=9 P=0.0000046
z=10 P=0.0000012
q=0.3
z=0 P=1.0000000
z=5 P=0.1773523
z=10 P=0.0416605
z=15 P=0.0101008
z=20 P=0.0024804
z=25 P=0.0006132
z=30 P=0.0001522
z=35 P=0.0000379
z=40 P=0.0000095
z=45 P=0.0000024
z=50 P=0.0000006
要保证 P 小于 0.1% …
P < 0.001
q=0.10 z=5
q=0.15 z=8
q=0.20 z=11
q=0.25 z=15
q=0.30 z=24
q=0.35 z=41
q=0.40 z=89
q=0.45 z=340
12. 总结
我们提出了一个不依赖于信任的电子交易系统。首先引入了通过数字签名实现的常见加密货币框架,这个框架可以保证明确的所有权,但是不能防止双花。为此,我们提出了一个通过工作量证明来记录交易历史的一个点对点网络,只要大部分的 CPU 算力被诚实节点掌握,就能使得攻击者实际中难以改变交易记录。网络自身没有任何结构限制,这种简约保证了它的容错能力。各个节点同时工作但是不需要进行任何的协调。节点也不需要标明身份,因为信息发送没有明确的目的地,只需要尽可能广泛的传播即可。节点可以随意的进出网络,当重新加入网络后,只要接受工作量证明链作为离开的这段时间内所发生的事情即可。节点通过 CPU 算力来投票,如果表示接受一些有效区块,就会继续延长它们,反之如果拒绝延长,就表示认为这些区块是无效的。任何必要的规则和激励都可以通过这种共识机制来推行。
参考文献
- [1] 戴伟,b-money , http://www.weidai.com/bmoney.txt ,1998年。
- [2] H. Massias , X.S. Avila , and J.-J. Quisquater ,《基于最小化信任的安全时间戳服务的设计》, 1999年五月,Benelux 第二十届信息理论座谈会。
- [3] S. Haber, W.S. Stornetta, 《如何给数字文件加时间戳》,《密码学期刊》第3卷2号, 99-111页, 1991年。
- [4] D. Bayer , S. Haber , W.S. Stornetta,《提升数字时间戳的性能和可信度》,《计算机和通信安全》第二章,329-334页,1993年。
- [5] S. Haber , W.S. Stornetta ,《数位字符串的安全命名》,《第四届 ACM 计算机和通信安全大会论文集》28-35页,1997年四月。
- [6] A. Back, 《哈希现金 – 一种 DOS 防范措施》 http://www.hashcash.org/papers/hashcash.pdf ,2002年。
- [7] R.C. Merkle, 《公钥加密系统协议》, Proc ,1980 安全和隐私座谈会, IEEE 计算机学会,122-133页,1980年四月。
- [8] W. Feller, 《概率理论及其应用简介》 1957年。