深入分析 Bitcoin 比特币的原理和技术实现
Bitcoin 是一种虚拟电子货币,上周我写过一篇介绍性质的文章《通俗易懂讲解什么是 Bitcoin 虚拟货币》(http://ivarptr.blogspot.com/2011/05/bitcoin.html),当时主要参考了官方和民间的介绍材料,并没有深入了解 Bitcoin 的技术细节。这篇文章会进一步分析Bitcoin的原理和技术实现方法和细节,其中原理部分主要参考了 Bitcoin 的创始者Satoshi Nakamoto的论文 《》,Bitcoin 的,而技术分析部分主要由实践以及阅读源代码 所得。由于 Bitcoin 涉及的技术领域比较多,很多地方我还没法理解或没完全理解,所以如果发现这篇文章的错误,请反馈到这篇文章的原始出处(http://ivarptr.blogspot.com/2011/05/bitcoin_31.html),(转载此文请保留原始链接)。我会及时补充及更正。@ivarptr
要了解 Bitcoin 的技术原理,最好的方法是从 Bitcoin 应用程序开始(),运行之后会看到如图1所示的窗口:
图1:Bitcoin 程序窗口
窗口上侧的是你的“收款地址”(下面称为Bitcoin地址),这个地址是你第一次运行 Bitcoin 程序时自动创建的,你可以点击“New”按钮重新创建一个或者多个地址,其他人向你的任何一个地址发送Bitcoin币(以下简称 BTC)你都能接收到。Bitcoin地址实质上是一对公私钥(参考 )当中的公钥,转帐交易时使用公钥加密信息,只有持有相应私钥的人才能解密,这样就能完成一笔交易了。所以在 Bitcoin 网络中你并不需要在某个网站注册会员帐号,只要产生一对公私钥匙(产生成本是零)就可以跟他人交易了。
窗口中间的是“交易记录”,显示所有汇入和汇出记录,包括交易被多少人“见证”(后面会讲到)、时间、汇出或接收地址、金额等。第一次运行 Bitcoin 程序时这个列表是空白的。
窗口最底部的是Bitcoin程序此时跟多少人连接(connection)、下载了多少个数据块(block),以及交易记录的数量。
Bitcoin 程序实际上是一个P2P程序
成千上万个人运行 Bitcoin 程序并且通过互联网相连,就形成了一个庞大的“Bitcoin网络”。Bitcoin网络不存在服务器、中心节点、管理员,所有运行 Bitcoin 的节点的都平等的,所以跟 eMule、BT类似,Bitcoin也是一个P2P程序。Bitcoin 网络组成如图2所示(画得比较粗糙,见谅):
图2:Bitcoin 网络的组成示意图
图2当中绿色的表示Bitcoin应用程序,有些网站比如矿场(Mining Pool)和Bitcoin交易网站,其实内部也是运行着Bitcoin应用程序(可能是网站自己开发的程序,并不一定是官方所提供的那个,不过他们遵守共同的通信协议),网站只是在外围增加一些业务逻辑和界面让访客通过网页的形式访问。
跟eMule和BT不同的是,所有运行Bitcoin 程序的节点只会共享一个内容相同的“文件”(这个说法不严谨,不过好理解一些),这个文件会被分割成许多个小块(称为数据块,下面简称为 block),数据块会不断增长,大概每10分钟增加一个数据块,目前 Bitcoin 网络上这个大家所共享的“文件”大概由12万个数据块组成。只有当你的 Bitcoin 程序下载完所有这些数据块才进入正常的工作状态,所以首次运行 Bitcoin 你需要等待一段时间。
Bitcoin 共享的数据储存在哪里?
Bitcoin 共享的数据位于文件夹:
~/.bitcoin/ (Linux)
~/Library/Application Support/Bitcoin/ (MacOSX)
C:\Documents and Settings\YourUserName\Application data\Bitcoin\ (Windows XP)
C:\Users\YourUserName\Appdata\Roaming\Bitcoin\ (Windows vista / Windows 7)
进入这个文件夹会看到如下图所以的文件列表:
图3:Bitcoin的数据文件夹
主要文件的作用:
bitcoin.conf 这是应用程序配置文件,如果没有这个文件则表示程序是用默认设置,一个典型的 bitcoin.conf 文件内容参考这里:https://en.bitcoin.it/wiki/Running_Bitcoin ;
blkxxxx.dat 和blkindex.dat,储存所有block的数据库文件,前者是数据内容,后者是数据索引;
__db.xxx,数据库 Berkeley DB所使用到的文件;
database,数据库的日志文件;
wallet.dat,你的“钱包”文件。
对用户来说wallet.dat 最为重要的文件,里面存储着你所有的Bitcoin地址,所以这个文件相当于你的银行存折。假如这个文件丢失了或者损坏了,你的所有BTC也会随之消失,并且没法恢复。同样道理,假如这个文件被其他人所获得,那么你的所有 BTC 就归那个人所有,因为 wallet.dat 是没有密码保护的。所以及时安全地备份这个文件是有必要的。
目前Bitcoin 应用程序对wallet的实现还略显粗糙,缺少导出(备份)、导入(还原)、合并等功能。
现在网络上有一些托管Bitcoin账号的网站,它也可以为你生成Bitcoin地址,可以在里面进行汇入、汇出等操作,使得用户可以完全脱离Bitcoin程序,这样就不必担心wallet.dat文件丢失或者被盗了,不过前提是你是否完全信任那些网站。
Bitcoin 是怎样实现互联的?
由于Bitcoin网络不存在服务器或者中心节点,所以所有block都是从其他人所运行的Bitcoin程序处(下面称为节点)下载的,同时你已经下载的block也会共享给其他还没下载的节点。那么 Bitcoin是如何发现网络上其他节点的呢?
运行 Bitcoin 时它会首先连接一个指定的IRC(一种古老的网络聊天服务)服务器(irc.lfnet.org),然后加入 “#bitcoin”聊天频道并声明自己的IP地址,当 Bitcoin 程序查询当前聊天频道的用户时,就能获取网络上其他节点的IP地址了,然后bitcoin会自动连接一定数量的节点,可见IRC在Bitcoin网络中充当着媒介角色。假如IRC服务器当掉,那么第一次运行Bitcoin 程序时程序会调出内置的一批IP地址(称为“种子节点”)然后试图连接这些节点。Bitcoin 连接到某个节点之后还能通过那个节点获取更多其他节点,即所有节点同时也充当媒介角色,这样一个P2P网络就形成了。
Bitcoin 程序所有的功能都依赖于 Bitcoin P2P网络,有时运行 Bitcoin 程序会发现连接数(见图1窗口中底部的 connections)是“0”(正常情况下应该有50~200个连接),这有可能是因为你的网络受到限制而导致的,这种情况下 Bitcoin 是没法正常工作的。下面讲解如何解决网络问题:
1、检查网络防火墙或者路由器的端口打开状态:连接 IRC 服务器需要打开 tcp 6667传出端口,连接其他 节点需要打开 tcp 8333 传出端口,其他节点连接你需要打开 tcp 8333 传入端口。所以在防火墙或路由上一共需要增加3条规则:
tcp 6667 传出
tcp 8333 传入
tcp 8333传出
2、检查 IRC 服务器的ip地址解析状态,有时国外的域名会受到DNS污染,irc.lfnet.org 服务器的ip地址是:
173.246.103.92
193.107.204.22
193.107.204.81
92.243.23.21
把它们添加到 /etc/hosts 文件即可。
3、增加几个 Bitcoin 固定节点
Bitcoin网络上有一些相对固定的节点(比如一些交易网站和矿场网站服务器),在这里可以找到一份列表:https://en.bitcoin.it/wiki/Fallback_Nodes ,挑选几个并把他们添加到 bitcoin.conf 配置文件(文件的位置见图3)可以增加连接的成功几率,添加之后的配置文件如图4当中的addnode部分所示:
图4:添加几个固定的Bitcoin节点到配置文件
从这里可以看到 Bitcoin 网络并不是宣称中的“不可屏蔽”,相对其他P2P技术来说Bitcoin的并没有多大创新,某些地区或国家只需把IRC和种子节点干扰或者屏蔽一下就让普通人无法使用Bitcoin了。 说说Block吧 Bitcoin 所共享的数据就是一大堆 block,那么一个 block 里装有什么内容呢?见图5:
图5:一个Block的内容,以及Block链
一个block的主要内容是“最近的交易列表”和“上一个block的hash值”。交易列表记录着产生当前block这段时间内整个Bitcoin网络的所有交易记录,包括汇出地址、汇入地址、金额等。“上一个block的hash值”用于定位上一个block,有这个数据之后所有block就可以按顺序地形成一条链。一条完整的链记录着Bitcoin网络从诞生一刻开始直到未来所有的交易记录,这些记录会共享到每一个运行Bitcoin的节点。这里有个网站可以浏览每一个block和每一笔交易的具体内容:http://blockexplorer.com/ 。(题外话:在这个链当中的第一个block是比较特殊的,它是创始人Satoshi Nakamoto人为创造的,所以称为 Genesis Block)。
假如Bitcoin得到广泛应用,每个block中的交易列表会相当庞大,那么每个运行Bitcoin的节点会耗费大量的储存空间,到时可能更多人不再运行Bitcoin应用程序转而使用在线的Bitcoin银行网站。分析到这里不禁会想到:假设Bitcoin整个网络只剩下100万个节点,某个组织自己搭建一个200万个节点的“黑网络”,然后更改其中的数学公式,再连接到外面正常的Bitcoin网络,这样Bitcoin系统是不是会受到破坏呢?
每个block都会被计算出一个hash值(使用 ),以提供下一个block链接使用,而计算这个hash值的过程就是“挖金矿(mining)”过程,第一个计算出hash值的人就能获得50BTC的奖励(以后每21万个blocks奖金减半)。一般来说,现在的计算机要计算一个hash值已经非常快,所以为了竞争,Bitcoin对hash过程提出了一个要求:原始数据中留一个空缺让你填写任意内容,使得计算出的hash值前面有指定个数的数字“0”,问题可以用如下的式子理解:
代码:
sha256(“hello” + N)= “000000xxxxxxx”
其中N是让你填写的任意内容,xxxx的内容不限。
这就是专门用来为难CPU的,目前已知的解决方法只能将N从0开始一直递增到9999999.....逐个尝试看看计算出来的hash值的前面是否恰好有若干个0。使用这种方法除了可以让计算机耗费大量时间来计算hash值之外,还能通过增加或减少前导0的数目来调节难度,比如要计算出前面有4个0的平均时间为1小时,那么要计算5个0的平均时间可能会多10多倍。现在Bitcoin的计算难度正以指数形式不断增加(见统计图 ),所以已经不能单独个人挖矿了,只能通过矿场(mining pool)来挖取BTC。
讲到Mining Pool
Mining pool是用来集中散户力量共同挖矿,然后按工作量来分摊劳动所得的一种网站(比如 ),挖矿程序(Miner,比较常见的有 和 ,都是GPU挖矿程序)通过 协议 向矿场请求工作,矿场会把一个挖矿任务分割成许多份小工作,然后分派给 miner,miner 计算完之后向矿场提交劳动结果,这样就完成Pooled mining过程了。具体过程如下:
miner 调用矿场的 getwork(null)方法,获取一个工作任务,传输内容大致如下:
代码:
请求 --> {"method":"getwork", "id":"1", "params":null} 返回 <-- {"error":null, "id":"1", "result": {"midstate":"bbdb4f3078cc......", "data":"00000001106e6da2fdf......", "hash1":"000000000000000000......", "target":"fffffffffffff......"}}
Miner根据midstate,data,hash1,target再加上一个自己的递增的数字计算出符合要求的hash值,有结果之后再次调用矿场的getwork(data)方法以提交。具体的算法请参阅Bitcoin或者miner的源代码。矿场可能会因为这种工作方式而遭受类似DDos的攻击,假如有人写个恶意的miner不断获取工作而不真正干活,矿场有可能会减产。
矿场的服务端口通常是tcp 8332,要参加矿场工作你需要确认你的网络防火墙或者路由器打开了 tcp 8332端口的传出规则。
结论
Bitcoin比较吸引人的地方在于其游戏规则,不过通过仔细分析部分技术实现来看,发现仍有有待完善的地方。至于更多原理和技术细节,比如Bitcoin的 P2P协议、Bitcoin 的API,以后我会看是否需要或者是否有空再写。
原作者:Hippo notes
译者:ivarptr
微信扫描关注公众号,及时掌握新动向
2.本文版权归属原作所有,仅代表作者本人观点,不代表比特范的观点或立场
2.本文版权归属原作所有,仅代表作者本人观点,不代表比特范的观点或立场