1.4 一切源自比特币
1.4.1 比特币技术论文介绍
通常,在介绍一个比较重量级的人物的时候,我们常常会在他的名字前面加上很多定语,比如某著名歌唱家、慈善大使、两届××奖获得者等,然后最后才报出名字,为的就是让大家竖起耳朵听明白,这个牛人都能干些什么。而在介绍一个物件的时候,比如一辆汽车,我们就不会这么说了,因为能把人说睡着了,一个东西嘛,写个说明不就完了,一目了然。
那么,比特币技术论文就是这么一个说明书,当然了,人家这份说明书可是有正式名字的,人家的大名叫《Bitcoin: A Peer-to-Peer Electronic Cash System》,翻译过来叫《比特币:一种点对点的电子现金系统》。这篇技术论文通常称为比特币白皮书,因为它基本就是宣告了比特币的诞生。严格地说,是理论上宣告了比特币的诞生。这份文件是在2008年11月由一个叫Satoshi Nakamoto(中本聪)的人发布的。当然了,并不是发布在什么知名论坛或者学术期刊上,而是发布在一个小众的密码学讨论小组。在这份白皮书发布后的第二年,也就是2009年1月3日,比特币软件就正式启动运行了,也就是在这个时候,世界上第一个区块链数据诞生了,而这个由中本聪构造出来的第一个区块,也称为创世区块或者上帝区块,代表神话中创世元灵的意思。从此以后,比特币以及由比特币技术衍生出来的各种应用就一发不可收拾,开启了互联网应用的一个新纪元。
回到这个白皮书上来,注意看它的标题,有两个关键字:“点对点”和“电子现金”。有朋友说了,这俩词压根就没提什么区块链!别着急,看人看眼睛,读文读标题,咱们先来解释一下。“点对点”,就是指这个软件不需要一个特定的服务器,比如我们登录QQ就需要连接腾讯的QQ服务器,登录支付宝就需要连接阿里巴巴的支付宝服务器,倘若这些服务器关闭或者出个问题什么的,那就没法正常使用这些软件了。2015年5月,杭州电信光缆被施工队不慎挖断,直接导致通过这些光缆联网的支付宝服务器断网,影响了正常的运行。而点对点的网络结构,并不依赖于某一个或者某一群特定的服务器,相当于人人都是服务器,人人也都是使用者。再来看“电子现金”,顾名思义,现金嘛,就是钱或者货币的意思,也就是说这份白皮书,介绍的是一种数字货币系统,这个系统的运行不依赖于某些特定服务器,而是通过点对点网络(P2P)结构来运行的。相信有些读者朋友看到这里还是会有些懵,不要紧,毕竟咱们才刚看到标题嘛,有个概念就行了。
翻开白皮书正文可以发现,整个篇幅主要介绍了几个关键点。
(1)简介
提出了一个场景设想:如何不通过一个所谓的权威第三方结构(比如银行),来构建一个可信的交易网络呢?中本聪的语文还是不错的,先抛出个问题给你玩玩,然后吸引你继续看下去。
(2)交易
描述了一种通过密钥签名进行交易验证的方式,实际上就是计算机密码学在比特币中的应用。我们在银行转账交易用什么来证明自己呢?是通过账户和密码,必要的时候还可以通过身份证确认。而在比特币系统中没有银行这样一个角色,那靠什么来确定身份呢?只有靠现代计算机密码学技术。当然,密码学技术在比特币中的应用并不只是用来证明身份,是贯穿在各个环节的,可以说,密码学技术就是比特币系统的骨骼。
(3)时间戳服务器
这部分提到了区块以及通过时间戳运算连接成一条链的概念,这也是区块链概念的来源,同时在这里也说明了比特币数据的存储方式。
(4)工作量证明
这部分介绍了一种点对点网络中如何对各自的数据进行一致性确认的算法。为什么叫工作量证明呢?因为这种算法很消耗CPU的算力,等于人们干活一样,是要付出工作劳动的。
(5)网络
比特币软件是一种网络软件,而且是一个不依靠某个服务器来交换数据的网络软件。那么一个个节点之间,如何确认一笔笔交易数据呢?这部分介绍了交易确认的过程,这个实际上就是比特币网络的应用协议,跟日常使用的邮件收发协议、文件传输协议、超文本传输协议等,是一个层面上的。
(6)激励
激励就是奖励的意思,你干了活,得到一笔奖金,哇,好开心!就会继续努力干活,这就是激励。比特币软件的数据一致性确认是需要耗费CPU算力的,那凭什么有人愿意来耗费这些个算力,白干活吗?当然不是,系统会奖励给你比特币,还有别人交易的手续费。(有人会问,那我为什么没被奖励过啊?别急,在1.4.3节中会有详述。)
(7)回收硬盘空间
比特币系统从创世区块开始,大约每10分钟产生一个区块,也意味着区块链账本的“体积”会一直增长。事实上写作本书的时候,已经超过了120GB,只要比特币网络一直存在,数据就会一直增长。实际上,只有运行全功能节点的客户端才会一直保持完整的区块链数据,这些在1.4.2中会有详述。这里提出了一个思路,删除过老的一些交易数据,同时不破坏区块的随机哈希值,通过这种方法压缩区块数据。
(8)简化的支付确认
上述提到了,比特币客户端的数据量很大,这么一来,等于不管是用比特币系统干什么都要带上大量的数据,这岂不是很不方便,而且也会限制在其他一些终端(比如手机)上的使用。这部分提出了一个模型,这个模型主要是为比特币的支付服务的。在这个模型下实现的比特币支付功能并不需要携带那么庞大的数据,而只需要保留体积相对很小的区块头,具体细节可以查看3.1.3节。
(9)价值的组合与分割
这部分介绍的是比特币中的交易事务组成方式。①什么叫价值?在比特币系统中,价值就是比特币。②什么叫组合?比如我口袋里有5枚1元硬币,1张2元纸币,1张10元纸币,我要给你5块钱,怎么给呢?我可以给你5枚1元硬币,也可以给你3枚1元硬币加上1张2元纸币,这就是不同的组合。③什么叫分割?分割其实就是转出的意思,我通过不同的组合,构成了总计5元的金额,然后转出给你,这个过程就是价值的组合和重新分割。在这个例子中,还有一张10元的,假如我直接转了你10元,那会怎样?这就需要找零5元了,找零其实也是一种重新价值分割。
(10)隐私
作为一个货币系统,保密性也就是隐私毫无疑问是人人都会关心的。传统的体系,完全是依赖比如银行这个第三方的保护,大家相信银行,银行也设立了各种管理制度和方法来防止账户和交易信息的泄密。比特币系统则不同,它不依赖谁,每个人在比特币系统中也不用登记什么身份证、名称、性别等,就是一个地址,谁也不知道地址后面代表的是谁,而且,只要你需要,可以自己创建任意多个地址(你到银行去开任意多个户试试!),这使得比特币系统中的交易带有很大的匿名性和隐秘性。
(11)计算
这部分主要是站在概率统计的角度计算了一下攻击者成功的概率,以及经过多少个区块后还能攻击成功的概率,计算过程这里不赘述。
白皮书的内容就介绍到这里了。刚刚接触比特币、区块链这些概念的朋友,或许还是一头雾水吧!没关系,我们在下面的章节会有详细的解释。毕竟,能够只通过一份白皮书就完全明白比特币设计的人,或许只有中本聪这个“大神”了。
小提示
白皮书的原文可以在https://bitcoin.org/bitcoin.pdf查看,感兴趣的朋友可以阅读一下,英文不那么擅长的朋友,可以到巴比特网站(著名的区块链资讯与技术服务网站)上查看中文版,地址是http://www.8btc.com/wiki/bitcoin-a-peer-to-peer-electronic-cash-system。读明白了这份说明书,基本也就理解了比特币的原理,也就入了区块链这个“坑”(或者说这个“门”)了。
1.4.2 比特币核心程序:中本聪客户端
1.客户端程序介绍
我们知道,比特币其实就是一个软件,既然是软件,那还是百闻不如一见,看看到底长什么样。大家可以到https://bitcoin.org/en/download这个地址去下载客户端程序,可以看到,网站提供了多种操作系统的运行版本,选择自己需要的版本下载安装即可运行了,就能看到庐山真面目啦。
在具体介绍之前,咱们先说明一下,为什么这个程序叫比特币核心程序,难道还有非核心程序?我们在上述提供的下载页面上,可以看到比特币程序的名字叫Bitcoin core,这个翻译过来就是比特币核心的意思,这是最经典,也是中本聪一开始发布的那一支程序版本,这个版本也是使用人数最多的。可问题是,比特币程序是开源的,任何一个人或者组织都可以根据需要去修改源码发布出一个新的版本,事实上经过多年的发展,比特币程序已经出现了多个版本,比如Bitcoin Classic、Bitcoin XT以及Bitcoin Unlimited,这些不同的版本实际上都是比特币核心程序的分叉版本,本节使用的是比特币核心程序的客户端。
现在先安装一个比特币核心客户端,按照下载地址进入页面后,在这个页面可以看到针对不同操作系统的下载版本,读者朋友可以自行选择,无论哪个系统环境下,其功能都是一样的,见下图:
我们以Windows版本为例来说明,我们下载图中所示的0.14.1版。大家注意到没有,比特币发展了这么多年,到现在程序都还没进化到1.0版(通常一个软件的1.0版是首个正式版本),某种程度上也是因为比特币是一种实验性的软件吧,因此大家研究学习比特币可以带着一种玩的姿态,不要那么严肃,任何可能性都是有的,我们学习了解比特币是为了更好地应用它的设计思想,而不是去迷信它的神秘和权威。下载完成后,打开软件目录,可以看到有一个bin文件夹,其中有5个文件,如下图所示。
我们一一来说明一下:
(1)bitcoin-qt.exe
包含了比特币的核心节点以及一个钱包的前端功能,这是一个带有图形界面的客户端程序,运行后可以看到有如下提示:
按图中所示,需要选择一个比特币的区块链副本数据存储目录,目前整个区块链账本数据已经很大了,选择一个空间足够大的目录,然后点击OK按钮即可进入主界面了,我们看下主界面的样子:
如图所示,这便是比特币核心客户端了。等等,标题不是钱包吗,怎么又是核心客户端,到底运行的这个程序是什么?是的,这个客户端也叫“中本聪客户端”(satoshi client),它实现了比特币系统的所有方面,包括钱包功能,对整个交易数据也就是区块链账本完整副本的交易确认功能,以及点对点比特币对等网络中的一个完整网络节点。换句话说,这个客户端软件包含除了挖矿以外的其他所有比特币的功能模块,我们当然也可以分别去自己实现一个个的独立功能客户端,比如仅仅实现一个钱包功能,仅仅实现一个核心节点功能,只不过这个官方的客户端都集成在一起了。
通过这个界面,我们也能看到在底部显示有“正在连接到节点”以及“落后8年和16周”的字样,这是指运行中的核心客户端通过发现与连接网络中其他节点进行区块链账本数据的一致同步。如果是首次开始同步,需要花费不少时间,一百几十GB的数据下载真够喝一壶的。需要注意的是,所有的操作都要等到同步完成后才能进行。点击那个“落后8年和16周”的区域可以看到具体的同步进度信息:
图中可以看到有剩余的区块数、进度以及剩余时间等信息,耐心等待就是了。如果想查看一下当前客户端的版本以及网络连接等信息,可以点击“帮助”→“调试窗口”调出如下界面。
在“信息”标签页下可以看到软件版本、当前的网络连接数、数据目录等摘要信息。注意这里的“客户端版本”,比特币是一个分布式的点对点系统,不存在中心服务器来统一管理软件的版本升级,因此不同的节点有可能运行着不同版本的客户端,不同版本的客户端在一些功能支持上可能会有些差异,大家在操作时一定要注意自己的版本。在“信息”标签页旁边有个“控制台”,这可是个很有用的功能,在控制台可以通过命令来访问核心客户端,调取一些信息,进行一些操作,我们来看下控制台。
我们在控制台底部的输入框中输入了一个getinfo命令,回车确认后可以发现返回了一段信息,这是关于当前运行的核心客户端节点的一些摘要信息,比如version表示核心客户端版本,protocolversion表示协议版本,walletversion表示钱包版本,balance表示当前钱包中的比特币余额等。通过这个我们发现,比特币的核心客户端其实是充当了一个服务器的角色,通过控制台可以连接访问,通过界面也能看到提示:“欢迎使用Bitcoin Core的RPC控制台。”实际上比特币核心客户端就是在启动的同时启动了一个本地的RPC服务,以方便外部程序进行相应的数据操作和访问。
有朋友问,比特币一下子要同步这么多的数据,而我只是想看一看,有没有试用的版本呢?还真有,不过不叫试用版,而是测试网络。那么如何连接到测试网络呢?可以通过配置文件来进行配置。比特币的配置文件名为bitcoin.conf,可以在数据目录也就是钱包数据文件wallet.dat所在目录下创建一个文本文件,命名为bitcoin.conf即可,这就是bitcoin-qt默认读取的配置文件了。接下来我们就来配置一下以进入测试网络,只需在bitcoin.conf中写入如下配置项:
testnet=1
保存即可,然后重新启动bitcoin-qt.exe,我们可见如下画面:
我们发现颜色都变了,变成了淡绿色,标题上也有“测试网络”的字样,进入主界面后,界面样式基本还是那样:
进入到测试网络后的比特币客户端,其区块链数据会小一些,在功能操作上基本还是一样的。需要注意的是,配置文件中的配置项也是可以直接通过参数来传递的。假设想临时进入测试网络看看,那么就不需要去设置配置文件了,通过如下指令来运行即可:
bitcoin-qt -testnet
在控制台中执行上述指令后,同样会进入测试网络。有朋友会问,我一开始在运行bitcoin-qt时指定了一个数据目录,现在我想更换可以吗?当然是可以的,操作如下:
bitcoin-qt -datadir="D:\mybitcoin_data"
这样在启动bitcoin-qt的时候重新指定了一个自己创建的数据目录。当然了,不但可以重新指定数据目录,也可以重新指定配置文件,操作如下:
bitcoin-qt -conf="c:\mybitcoin.conf"
可以发现,另外指定的配置文件,其文件名可以是自定义的。需要注意的是,bitcoin-qt支持的所有参数中,除了-datadir与-conf只能通过命令参数指定外,其他参数都是既可以在命令参数中直接传递,也可以在配置文件中指定。
(2)bitcoind.exe
这个其实就可以看作不带界面的bitcoin-qt.exe,其中同样包含了比特币的核心节点,并且提供了RPC服务。比特币使用的是JSON-RPC协议,以便通过命令行交互的方式访问比特币系统的数据,比如访问区块链账本数据,进行钱包操作和系统管理等。
bitcoin-qt与bitcoind是互相兼容的,有同样的命令行参数,读取相同格式的配置文件,也读写相同的数据文件,使用的时候,这两个程序根据需要启动一个即可,同时启动也不会出错,但是同时启动两个bitcoin-qt或者两个bitcoind会出错,如下所示:
图中所示的大致意思是对数据文件的访问冲突了。
bitcoind默认读取的配置文件,在不同操作系统下路径也不尽相同,如下所示:
❑Windows:%APPDATA%\Bitcoin\
❑OS X:$HOME/Library/Application Support/Bitcoin/
❑Linux:$HOME/.bitcoin/
除了上述的默认配置路径外,与bitcoin-qt一样,也是可以在启动的时候通过传递参数来重新指定其他路径下的配置文件或者数据目录的:
bitcoind -datadir="c:\bitcoin_data" -conf="C:\mybitcoin.conf"
如上所示,启动时,使用-datadir指定了数据文件需要存储的目录,使用-conf指定了C盘目录下的一个配置文件,此时这个配置文件的名称是自定义的。bitcoind启动后可以通过bitcoin-cli进行访问,bitcoin-cli的使用在下一节介绍。
看到这里,有些朋友可能会有些疑问,比特币核心客户端运行后可以与其他节点进行互相的连接通信,那就得开放一个服务端口,而访问比特币节点信息又是通过RPC的方式,那相当于开启了一个RPC服务,这么说来,比特币网络中的每个节点其实相当于一个个服务器。确实如此,这些开启的服务端口说明如下。
❑8333,用于与其他节点进行通信的监听端口,节点之间的通信是通过bitcoin protocol进行的,通过这个端口才能进入比特币的P2P网络。
❑8332,这是提供JSON-RPC通信的端口,通过这个端口可以访问节点的数据。
❑如果是测试网络,分别是18333和18332。
以上端口是可以另外指定的,通过参数-port与-rpcport参数可以分别重新指定。
(3)bitcoin-cli.exe
bitcoin-cli允许你通过命令行发送RPC命令到bitcoind进行操作,比如bitcoin-cli help,因此这是一个命令行客户端,用来通过RPC方式访问bitcoind的RPC服务。我们可以通过命令行来查看当前的bitcoin-cli的版本:
bitcoin-cli -version
运行后会返回如下描述信息:Bitcoin Core RPC client version v0.14.2。通过返回的信息也能看到,bitcoin-cli就是一个RPC客户端工具,那么如何去连接核心客户端呢?首先bitcoin-cli与bitcoind是使用同样路径下的配置文件,因此在使用bitcoin-cli之前,我们需要先运行bitcoind,然后来执行bitcoin-cli命令:
bitcoin-cli getinfo
可以看到有如下格式的信息输出:
{ "version": 140100, "protocolversion": 70015, "walletversion": 130000, "balance": 0.00000000, "blocks": 48, "timeoffset": 0, "connections": 0, "proxy": "", "difficulty": 1, "testnet": false, "keypoololdest": 1503043764, "keypoolsize": 100,
"paytxfee": 0.00000000, "relayfee": 0.00001000, "errors": "" }
看到信息的返回,表明已经正常连接且可以访问了,如果想要停止bitcoind,则可以发送如下指令:
bitcoin-cli stop
bitcoind接收到停止命令,执行后退出运行服务。
我们再来看一个例子,在这个例子中,通过参数重新指定数据目录和配置文件:
bitcoind -datadir="c:\bitcoin_data" -conf="C:\bitcoin.conf"
此时,如果仍然要通过bitcoin-cli来访问这个运行的bitcoind,则需要运行如下命令:
bitcoin-cli -datadir="c:\bitcoin_data" -conf="c:\bitcoin.conf" getinfo
运行后返回了运行的bitcoind中的信息。
至此,我们可以发现,bitcoin-qt、bitcoind以及bitcoin-cli都能读取相同格式的配置文件,也拥有一样的命令参数。具体支持的各种参数很多,大家可以自行去查阅。另外,比特币中的很多功能调用都是通过RPC命令提供的,比如区块信息查询、交易事务查询、多重签名使用等,因此要了解完整功能调用的朋友可以去具体了解一下这些RPC命令的使用,笔者这里也推荐一些不错的网站方便大家学习使用:
❑https://blockchain.info:方便检索各项比特币网络的数据;
❑https://chainquery.com/bitcoin-api:基于网页的比特币RPC命令使用。
(4)bticoin-tx.exe
这是一个独立的工具程序,可以用来创建、解析以及编辑比特币中的交易事务。我们在通常使用比特币系统的时候,使用上述介绍的钱包功能也就足够了,但是如果需要单独查看或者创建一份交易事务数据,就可以使用这个工具了。既然是用于操作交易事务的,那么我们就来试一试。比特币的交易事务在本质上就是一段二进制数据,我们任意找一段过来,看看bitcoin-tx能解析成什么样子,为了方便,将二进制的交易事务数据转成十六进制的格式来显示,如下:
0100000001e0772cd81114d0993922a280e2b29209d6c6c5d2f22d807018d1ef0d55cfe4041c0 000006a473044022008650b496ea573a2d42efbcbfb49288ab3c7f9968a1fa6072155a028a4de b39e02201b2dd03307fcd1fbb2f9928a8904d50a84ae9d600986a3a8a125fe248b4faf1001210 354eb6c85025f3abecde8236e86aabf6b819a72154e69d39f7ae591a92436c166ffffffff01d9 38890c000000001976a914fe5d8413d80c3d3f9b975f45990cf432455b13ef88ac00000000
这就是一段交易事务的数据,接下来我们就来解析一下,将这段数据转换成容易阅读的格式,为了方便阅读,我们就转换为JSON格式,命令如下:
bitcoin-tx -json
0100000001e0772cd81114d0993922a280e2b29209d6c6c5d2f22d807018d1ef0d55cfe4041c0 000006a473044022008650b496ea573a2d42efbcbfb49288ab3c7f9968a1fa6072155a028a4de b39e02201b2dd03307fcd1fbb2f9928a8904d50a84ae9d600986a3a8a125fe248b4faf1001210 354eb6c85025f3abecde8236e86aabf6b819a72154e69d39f7ae591a92436c166ffffffff01d9 38890c000000001976a914fe5d8413d80c3d3f9b975f45990cf432455b13ef88ac00000000
执行后,可以得到如下的输出:
{ "txid": "2aff308e3e1a9b251ecb701762f6f2c1d28952fe6d0d94efc78880e8a62d2cbb", "hash": "2aff308e3e1a9b251ecb701762f6f2c1d28952fe6d0d94efc78880e8a62d2cbb", "version": 1, "locktime": 0, "vin": [ { "txid": "04e4cf550defd11870802df2d2c5c6d60992b2e280a2223999d01411d82c77e0", "vout": 28, "scriptSig": { "asm": "3044022008650b496ea573a2d42efbcbfb49288ab3c7f9968a1fa607 2155a028a4deb39e02201b2dd03307fcd1fbb2f9928a8904d50a84ae9d600986a3a8a125fe248b4faf10[ ALL] 0354eb6c85025f3abecde8236e86aabf6b819a72154e69d39f7ae591a92436c166", "hex": "473044022008650b496ea573a2d42efbcbfb49288ab3c7f9968a1fa 6072155a028a4deb39e02201b2dd03307fcd1fbb2f9928a8904d50a84ae9d600986a3a8a125fe248b4f af1001210354eb6c85025f3abecde8236e86aabf6b819a72154e69d39f7ae591a92436c166" }, "sequence": 4294967295 } ], "vout": [ { "value": 2.10319577, "n": 0, "scriptPubKey": { "asm": "OP_DUP OP_HASH160 fe5d8413d80c3d3f9b975f45990cf432455b13 ef OP_EQUALVERIFY OP_CHECKSIG", "hex": "76a914fe5d8413d80c3d3f9b975f45990cf432455b13ef88ac", "reqSigs": 1, "type": "pubkeyhash", "addresses": [ "1QBxfKsz2F7xwd66TwMj5wEoLxCQghy54c" ] } } ], "hex": "0100000001e0772cd81114d0993922a280e2b29209d6c6c5d2f22d807018d1ef0d55 cfe4041c0000006a473044022008650b496ea573a2d42efbcbfb49288ab3c7f9968a1fa6072155a028a4d eb39e02201b2dd03307fcd1fbb2f9928a8904d50a84ae9d600986a3a8a125fe248b4faf1001210354eb6c 85025f3abecde8236e86aabf6b819a72154e69d39f7ae591a92436c166ffffffff01d938890c000000001 976a914fe5d8413d80c3d3f9b975f45990cf432455b13ef88ac00000000" }
通过输出信息,我们可以很方便地看到其中包含的各个数据组成项,比如txid是指交易事务的哈希值,这个值与哈希数据项一样;vin是指交易事务中的输入部分;vout是指交易事务中的输出部分,具体每一项的含义这里暂且不多解释,第8章通过模拟比特币构建一个最简易的区块链系统,其中有具体的介绍。通过使用这个工具,除了能解析交易事务数据外,也能创建交易事务,读者朋友们可以去具体尝试一下。
(5)test_bitcoin.exe
这是用于比特币程序bitcoind的单元测试工具,与程序开发相关,除了这个,实际上还有一个用于bitcoin-qt的单元测试工具test_bitcoin-qt,这些工具普通用户一般用不到,这里不再展开详述。
2.客户端逻辑结构
通过上述介绍,我们了解了中本聪客户端程序的基本组成,为了让大家有一个更加清晰的理解,我们来看下中本聪客户端在逻辑结构上包含了哪些功能模块,见下图:
图中所示的4个功能模块,共同组成了称为全节点的比特币程序结构,其中“挖矿”部分标记了虚线,这是因为在中本聪客户端中没有包含挖矿功能,挖矿是另外独立的程序。钱包的功能我们已经比较了解了,主要用于管理用户的密钥以及提供转账操作等功能,属于比特币的前端功能。事实上,钱包功能是可以独立出来的,专门提供一个独立的钱包程序,这部分的阐述在下面章节中有详细描述。接下来,我们主要对“完整区块链”和“网络路由”部分进行说明。刚才说了,钱包只是一个前端功能,那么比特币的后端功能是什么呢?请看下文。
(1)完整区块链
中本聪客户端保留了完整的区块链账本数据,因此能够独立自主地校验所有交易,而不需借由任何外部的调用。当然,另外一些节点只保留了区块链的一部分(比如区块头),可以通过一种名为“简易支付验证”(SPV)的方式来完成支付验证,这样的节点被称为“SPV节点”。除了中本聪客户端外,一些挖矿节点也保有了区块链的完整数据副本,还有一些参与矿池挖矿的节点是轻量级节点,它们必须依赖矿池服务器维护的全节点进行工作。保有完整区块链数据的节点是非常重要的,比特币网络之所以能够成为一个可信任的去中心化网络,就是依赖于这些全节点,目前很多场合为了方便使用,提供了不少轻量级节点(如轻钱包等),但是这些轻量级节点的正常使用都是要通过全节点才能完成的,是一种依赖关系,如果网络中保有完整区块链数据的节点越来越少,那么比特币网络就会受到影响,无论性能、安全性等都会降低。
(2)网络路由
比特币网络是属于P2P网络架构,P2P也就是对等的意思,与此相对的是“客户端–服务器”架构,有一个提供服务功能的中心服务器,其他客户端通过调用服务器的功能来完成操作,比如我们通常使用的微信、支付宝、网银等,如果提供商的服务器关闭了,那也就完全没法使用这些软件了。在对等网中,每个节点共同提供网络服务,不存在任何所谓的中心服务器,因此在对等网络的网络架构中是没有层次的,大家都是平等的,每个节点在对外提供服务的同时也在使用网络中其他节点所提供的服务。我们来看下两者的区别示意图:
一目了然了,在“客户端–服务器”网络架构中,总是有一个中心的,一旦中心服务器出了问题,基本等于天塌了;而“对等”网络结构,相比中心化服务器这种单点故障结构有很强的抵抗能力,我们可以看到,“对等”结构中的节点都是可以与其他节点互连的,而且某个节点出问题也不影响其他节点之间通信,这种结构的好处显而易见。当然,无论哪种网络结构,底层的网络协议都是一样的,还是TCP/IP那一套。
比特币是属于区块链技术的首创应用,其特点就是去中心化或者说是分布式,由比特币节点组成的网络自然也就是属于“对等”网络了,那么既然没有一个服务器,大家彼此如何来认识对方呢,即如何发现其他的节点呢?这是需要通过一个协议的,首先节点会启动一个网络端口,通过这个网络端口与其他已知的节点建立连接。连接时,会发送一条包含认证内容的消息进行“握手”确认,比特币网络中是靠彼此共享节点信息来寻找其他节点的,当一个节点建立与其他节点的连接后,会发送一条包含自身IP地址的消息给相邻的节点,而邻居收到后会再次发送给自己的其他邻居,当然节点也不是只能被动地等别人来告诉自己,也可以自己发送请求给其他节点索取这些地址信息,如果与发现的节点之间能够成功连接,那么就会被记录下来,下次启动时就会自动去寻找上次成功连接过的节点。
简单地说,作为网络路由的功能,比特币节点在失去已有连接时会去发现新节点,同时自己也为其他节点提供连接信息,没有服务器的对等网络就是这么来认识陌生人的。
至此,大家对比特币的核心客户端就有了一个较为完整的理解了吧。
1.4.3 比特币的发行:挖矿
很多朋友在第一次看到“挖矿”这个词时都很疑惑,包括本人。比特币不是一个软件吗?通过软件来挖矿是什么意思?从字面上来看,应当是通过投入某种工作,然后能得到一个“宝贝”,也就是矿。当然了,“挖矿”自然不是我们通常认为的那个挖矿,它只是一套算法,在介绍算法过程前,我们先来了解下挖矿在比特币软件中主要都有哪些用途:
❑抢夺区块打包权
❑验证交易事务
❑奖励发行新币
❑广播新区块
我们知道,比特币是一个对等网络,每个节点都可以独立维护自己的数据副本,那么问题就来了,怎么来保证彼此之间的数据一致呢?既然没有一个中心服务器,自然也就没有一个传统意义上的权威数据来源了。这就得有一个约定的规则,大家共同按照这个规则来进行竞争,谁竞争成功了谁就有数据的打包权,也就是记账权,打包完成后广播给别人,别人只要验证一下有无问题即可,没有问题就存入到自己的数据文件中。这个思路不错,等于就是大家来竞争临时中心服务器的资格,那么比特币中实行了一种什么样的规则呢?那就是被称为工作量证明(Proof of Work, PoW)的一种算法,其实就是类似于掷骰子的一种游戏。比如大家约定掷出一个10位长度的数字,前面6位要都是0,后面的4位数得小于某个值,看谁先掷出符合要求的数字出来,谁就抢得了打包权(记账权)。我们来看下比特币中具体是怎么来掷这个骰子的。
1.难度值
首先,既然是大家都在竞争掷骰子,那掷出来的数字必然是要符合一个难度的,这个难度就是一个门槛,在比特币软件中,规定一个256位的整数:
x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
作为难度1的目标值。在比特币诞生初期,当时的全网算力,大约需要10分钟左右的运算能得到一个符合这个难度1要求的值,这也是我们常常说比特币网络每隔大约10分钟出一个区块的来源。我们在查询创世区块(也就是0号区块)的信息时,可以看到当时的难度就是1。那么,所谓符合这个难度为1的要求的值是什么意思呢?就是说通过工作量证明算法,也就是比特币中的挖矿算法来计算出一个结果,这个结果要小于这个难度目标值,我们来看下0号区块的难度信息:
"nonce": 2083236893, "bits": "1d00ffff",
"difficulty": 1,
这些信息可以通过比特币支持的JSON-RPC中的getblock命令方法获得,其中的difficulty就是指难度级别。0号区块的难度值是1, nonce是一个随机数,是挖矿计算得到的一个数字,这个等会儿再介绍,bits是用来存储难度的十六进制目标值的,这个难度目标值是存储在区块的头部的,在源码中被定义为一个4字节长度的字段,4字节也就是32位,要用来存储256位长度的难度目标值,因此这256位长度的值需要经过压缩处理后才能放到这个字段中。以这个难度1的目标值来说,我们查询区块信息后,看到的值是1d00ffff,那么,这个值是怎么压缩而来的呢?规则其实很简单,我们一共有4个字节来存储,这4个字节的最高位字节用来存储难度值的有效字节数。什么叫有效字节数?就是从第一个不全为0的字节开始的部分,比如难度1的值有效位是0x00FFFF……。等等,怎么前面有2个0呢?这是因为在压缩规则中,规定了如果难度值有效位的最高位为1(大于0x80),则需要在前面补上一个0x00,这里的最高位是F,也就是二进制的1111,因此是符合这个规则的。难度1的目标值中,有4个字节长度的0,减掉这些0的长度共32bit,剩余256-32=224,也就是28个字节,加上补的0x00,因此,有效位总计29个字节,29的十六进制是1D,另外3个字节中存储的是目标值有效位的最高3个字节,此时的目标值有效位前面已经加上了2个0,因此最高3个字节为0x00FFFF,合起来压缩后的值就是0x1D00FFFF。对于这样的一个压缩后的十六进制4字节难度目标值,前2位通常称为幂或者指数,后面6位称为系数。
那么,有朋友问了,压缩是可以,那还原出来呢?我们看个公式:目标值= 系数*2^(8*)指数-3))次方。
我们就以这个0x1D00FFFF为例来说明,系数是后面6位也就是00FFFF,指数是前面2位也就是1D,代入进去就是:0x00FFFF*2^(8*(0x1D-3)),计算后得到的值是:
0x00000000FFFF0000000000000000000000000000000000000000000000000000
有朋友可能疑惑了,不对吧,这个跟规定的那个难度1的值不一样了啊,精度少了很多,确实是的,存储在bits中的值是一个精度截断的近似值。
我们以200000号区块为例,查询一下难度值,得到如下:
"nonce": 4158183488, "bits": "1a05db8b", "difficulty": 2864140.507810974,
我们来看看这个difficulty的值是怎么来的,0号区块的难度是1,对应的目标值是0x1D00FFFF,200000号区块的难度目标值是0x1A05DB8B,将两者的目标值按照上述公式进行转换后相除便能得到这个2864140.507810974的难度值,我们发现,200000号区块的difficulty比0号区块的大许多,而bits的大小却比0号区块的小许多。这其实是表明了一个特点,随着全网算力越来越强,difficulty难度值就会越来越大,而bits表示的目标值会越来越小,这两者成反比,目标值越小就越难挖矿。
刚才也提到了,难度值并不是一成不变的,比特币差不多每两周会调整一下新的难度值,因为计算的算力是会变化的,为了维持差不多10分钟出一个区块的节奏,难度要跟随算力变化而调整,不得不说比特币的设计还是相当完整的。
新难度值的计算公式是这样的:新难度值= 当前难度值×(最近的2016个区块的实际出块时间/20160分钟)。2016个区块的意思是:假设按照理论的10分钟出一个块,2周也就是14天的时间,应该出2016个区块,可以看到实际上就是计算一下实际与理论上的时间差值,弥补上这个差值即可。
2.挖矿计算
我们了解了难度值的概念,现在来看看挖矿计算具体是怎样一个过程。首先,我们说了挖矿是要抢夺区块打包权,那就得收集需要打包进区块的那些交易事务,那这些数据从哪来呢?这里有个概念需要大家注意,打包就像是记账,是把发生的交易事务记录下来存档,但是无论什么时候打包、谁打包,在网络中发生的交易是持续不断的,就像企业仓库的进销存业务,无论记账员是一个月还是半个月记一次账,业务是持续进行的。在比特币系统中,每个人都会将通过钱包进行的转账交易数据广播到网络中,这些都是属于等待打包的未确认交易数据。这些数据都会放在一个内存池中,总之就是一个缓冲区,当然,这些数据都会被接受基本的验证,用以判断是否是不合法的或者是不符合格式的交易数据。
挖矿程序从内存池中获取用来打包区块的交易数据,接下来就要干活啦,我们来看一下挖矿的计算公式:
SHA256( SHA256(version + prev_hash + merkle_root + ntime + nbits + nonce ) ) < TARGET
SHA256是一种哈希算法,可以通过对一段数据进行计算后输出一个长度为256位的摘要信息。SHA256在比特币中使用很广泛,不但用于挖矿计算,也用于计算区块的哈希值和交易事务的哈希值,比特币对SHA256算法是情有独钟啊,我们看到在这个公式中,是对参数进行两次SHA256计算,如果计算出来的值小于那个TARGET(也就是难度目标值),那就算是挖矿成功了。那么,这些参数都是由哪些组成的呢?请看下表:
这些数据字段其实也是区块头的组成部分,将这些参数连接起来,参与SHA256的挖矿计算。在这些参数中,版本号是固定的值,前一个区块的哈希值也是固定的值,当前难度也是一个固定的值,那么要想改变这个公式的计算结果,能改动的参数就只有梅克尔根、区块时间戳和那个随机数了。
1)梅克尔根是通过交易事务计算出来的,挖矿程序从内存池中获取待打包的交易事务,然后计算出梅克尔根,获取交易事务本身也是有一些优先级规则的,比如根据手续费大小之类,这些细节就不赘述了。
2)区块时间戳是指UNIX时间戳,用于记录区块的产生时间,我们知道比特币系统是分布式的网络,没有固定的时间服务器,因此每个节点获得的时间戳都可能是不一样的,由此,比特币系统中设置了规则:①新产生区块的时间戳要大于之前11个区块的平均时间戳;②不超过当前网络时间2个小时。所以,后一个区块的时间戳比前一个区块的时间戳反而小也是可能的。
3)随机数是一个可自由取值的数值,取值范围是0~2的32次方。
我们可以看到,要通过这样的参数来计算出符合条件的值,基本上也就只能靠暴力计算匹配了,这种不断执行SHA256计算的过程很消耗算力,因此这个过程被形象地称为“挖矿”。简单地说,挖矿就是重复计算区块头的哈希值,不断修改该参数,直到与难度目标值匹配的一个过程。
一旦匹配成功,就可以广播一个新的区块,其他客户端会验证接收到的新区块是否合法,如果验证通过,就会写入到自己的区块链账本数据中。那么,挖矿的奖励在哪儿呢,不是说矿工成功出一个区块就能得到比特币作为奖励的吗?那么这里奖励在哪呢?这个奖励其实是作为一条交易事务包含在区块的交易事务中的,相当于系统给矿工转账了一笔比特币,这种交易事务由于特殊性,通常称为coinbase交易,这个交易一般是位于区块中的第一条,比特币系统也正是通过这种挖矿奖励的方式发行新的比特币,就像央行发行新钞一样。
这个奖励不是无限的,从2009年1月创建出第一个区块,每个区块奖励50个比特币,然后每21万个区块(大约4年)产量减半,到2012年11月减半为每个区块奖励25个比特币,然后在2016年7月减半为每个新区块奖励12.5个比特币。基于这个公式,比特币挖矿奖励逐步减少,直到2140年,所有的比特币(20999999.98)将全部发行完毕,到那个时候挖矿就只能收入一些交易手续费了。彼时,比特币网络是否还能保持运行,我们目前也只能持保留意见了。矿工在没有明显的激励情况下,是否还愿意通过挖矿承担区块打包的责任,现在也很难说。
比特币中的挖矿计算基本就是这个过程了,其实还是很简单的,本质上就是利用了SHA256计算,有朋友可能有疑问,那第一个区块也就是创世区块是怎么挖出来的?很简单,创世区块是硬编码直接写进去的,在比特币的源码中,通过CreateGenesisBlock这个方法写入,并且还留下了一句话:The Times 03/Jan/2009 Chancellor on brink of second bailout for banks。当时正是英国的财政大臣达林被迫考虑第二次出手缓解银行危机的时刻,这句话是泰晤士报当天的头版文章标题。
3.区块广播
矿工挖出区块后,就进行网络广播,传递给相邻的节点,节点接收到新的区块后会进行一系列的验证,比如区块数据格式是否正确;区块头的哈希值小于目标难度;区块时间戳是否在允许范围之内;区块中第一个交易(且只有第一个)是coinbase交易;区块中的交易事务是否有效等,总之就是一连串的检测,全部校验通过就把新的区块数据纳入到自己的区块链账本中。如果是挖矿节点接收到信息,就会立即停止当前的挖矿计算,转而进行下一区块的竞争。
比特币的挖矿过程说到这里,不知道有没有朋友会有个疑惑,那就是挖矿算法虽然能够提供工作量证明,表明矿工确实是投入了相当的算力的,但是却不能保证只能是一个矿工能挖到啊,如果在同一时间内多个矿工都计算出了符合条件的值,都拥有了打包权,那以谁的为准呢?比特币中的解决方案,竟然是那么简单,人家没用什么复杂的算法,就是让节点自己选择,最终传播最广、处于最长链中的区块将被保留,因此到底谁的区块会被保留下来,可能还真得看看运气了。
这里实际上隐含着FLP原理,先看下定义:在网络可靠,存在节点失效(即使只有一个)的最小化异步模型系统中,不存在一个可以解决一致性问题的确定性算法。这个其实也很好理解,来看个例子:三个人在不同房间投票,虽然三个人彼此之间是可以通电话沟通的,但是经常会有人时不时地睡着。比如,A投票0, B投票1, C收到了然后睡着了(类比节点失效了),则A和B永远无法在有限时间内和C共同获得最终的结果。看到这里,我们也就明白了挖矿的作用了,除了发行新的比特币外,主要就是维持网络共识,让每个节点对区块链的数据保持最终一致性。
4.挖矿方式
比特币的挖矿过程我们已经了解了,现在给大家介绍下挖矿方式。挖矿算法在执行过程中,为了抢夺区块打包权,就得拼命去算出那个符合难度目标的值,大家都在不断升级自己的算力,难度也就越来越大,挖矿程序本身不复杂,关键是这个过程非常依赖计算机的算力资源,可以说得算力者得天下,也因为这个原因,挖矿的方式在多年来不断进化,一切都围绕着为了得到更高的算力来进化。
我们先从硬件类型来说,早期的时候,还没多少人挖矿,难度值也还不大,使用普通的个人电脑就能进行挖矿了,这个时期的硬件设施主要是普通CPU挖矿。随着有更多的矿工加入,难度越来越大,使用普通CPU的算力,效率开始不够用,于是出现了GPU挖矿,利用显卡来进行挖矿计算,GPU对于SHA256的计算性能更高。曾经一段时间,市面上的显卡销量猛增,就是被买去搭建显卡挖矿的,2017年上半年,另外一种数字加密货币以太坊价格暴涨,也一度引发了市面上的“一卡难求”,显卡尤其是高端显卡的GPU计算在一些挖矿算法上的性能表现确实相当不错,然而,对于算力的追求是无止境的,接着又出现了配置FPGA(Field-Programmable Gate Array,现场可编程门阵列)和ASIC(Application Specific Integrated Circuit,特定应用集成电路)的挖矿装备。这两类是属于集成电路的装备了,尤其是ASIC,基本是目前顶级性能的矿机了,专门为了挖矿而设计,只为挖矿而生!
说完了挖矿的装备,我们再来说说挖矿节点的类型,最简单的挖矿节点类型就是solo挖矿,也就是个体矿工,自己搞个挖矿装备然后默默开挖,守株待兔般等待着挖矿成功。如今,在挖矿难度大幅度提升的时代,个人挖矿几乎是一点机会都没有,那么现在流行的挖矿节点是什么类型?那就是矿池,矿池通过挖矿协议协调众多的矿工,相当于大家联合起来,每个人都贡献自己的算力,形成一个整体,大大增强整个挖矿节点网络的算力,个人矿工也可以加入到矿池,他们的挖矿设备在挖矿时保持和矿池服务器的连接,和其他矿工共同分享挖矿任务,之后分享奖励。
1.4.4 比特币钱包:核心钱包与轻钱包
钱包,是属于比特币系统中的一个前端工具,其最基本的功能就是用来管理用户的比特币地址、发起转账交易、查看交易记录等,在这方面与我们生活中使用的钱包是类似的。一开始的比特币钱包是跟比特币核心客户端一起发布的,1.4.2节介绍比特币核心客户端的时候已经初步做了了解,这个钱包是比特币核心钱包,其使用过程必须要配合完整的区块链数据副本,因此一般也只适合在桌面端使用。
我们在使用比特币钱包的时候,经常会遇到一个名词:比特币地址。通过钱包转账就是将比特币从一个地址转移到另外一个地址,暂且不论这个转移的过程是什么样的,那这个地址到底是什么意思,它又是通过什么来产生的呢?我们先来看一组名词关键字:私钥、公钥和钱包地址。
私钥与公钥来自公开密钥算法的概念,我们常说比特币是一种加密数字货币,之所以这么说,是因为比特币的系统设计中巧妙地使用了现代加密算法,而其中一个运用就是生成比特币地址,比特币地址的生成与公开密钥算法密切相关。什么叫公开密钥算法呢?传统的加密算法,其加密和解密方法是对称的,比如凯撒密码,通过将字母移位来加密,比如字母a替换成c, b替换成d, d替换成f这样,本来是abc的单词就变成了cdf,然而这种加密算法一旦泄露,别人也就知道了解密算法,换句话说,只有一个密钥。针对这种问题,公开密钥算法就应运而生,而公开密钥算法属于一种不对称加密算法,拥有两个密钥:一个是私钥,一个是公钥。公钥可以公开给别人看到,私钥必须要妥善保存,使用私钥加密(通常习惯上将私钥加密称为“私钥签名”)的数据可以用公钥解密,而使用公钥加密的数据可以用私钥解密,两者是互相匹配的。目前使用比较广泛的公开密钥算法主要有RSA算法和椭圆曲线加密算法(ECC), RSA是利用了素数分解难度的原理,ECC是利用了椭圆曲线离散对数的计算难度,比特币中使用的是椭圆曲线加密算法。
接下来,我们就来看下比特币地址是怎么生成的,为了直观展示,我们看一幅示意图:
这便是比特币地址的生成过程,过程大致是这样的。
1)首先使用随机数发生器生成一个私钥,私钥在比特币中的作用非常重要,可以用来证明用户的身份,也可以签发交易事务。
2)私钥经过SECP256K1算法处理生成了公钥,SECP256K1是一种特定的椭圆曲线算法,需要注意的是,通过算法可以从私钥生成公钥,但是却无法反向从公钥生成私钥,这也是公钥为什么可以公开的原因。
3)公钥接下来先使用SHA256哈希算法计算,再使用RIPEMD160哈希算法计算,计算出公钥哈希。比特币的代码通过2次哈希来计算地址值,这样能进一步确保哈希后的数值唯一性,进一步降低不同数据进行哈希后相同的概率。与SHA256一样,RIPEMD160也是一种哈希算法。
4)将一个地址版本号连接到公钥哈希(比特币主网版本号为0x00),然后对其进行两次SHA256运算,将计算得到的结果取前面4字节作为公钥哈希的校验值。
5)将0x00版本号与公钥哈希以及校验值连接起来,然后进行BASE58编码转换,最终得到了比特币地址。
以上便是比特币地址的生成过程了,我们可以发现比特币的地址其实就是通过公钥转化而来的,将上图简化一下,就是下面这么一个过程:
所以,在比特币系统中,本质上并没有一个叫作“地址”的东西,因为“地址”是可以通过公钥转化而来的,可以理解为公钥的另外一种形式,而公钥又是可以通过私钥计算出来的,因此在比特币钱包中,真正需要妥善保存的是生成的私钥数据,这玩意可千万不能弄丢了,一旦丢失,那可比忘记银行卡密码还麻烦。比特币钱包的主要功能就是保管私钥。
比特币的核心钱包是跟核心客户端在一起的,可以完成创建钱包地址、收发比特币、加密钱包、备份钱包等功能,由于核心钱包是与核心客户端在一起使用的,因此在进行转账交易时,可以进行完整的交易验证,当然付出的代价就是必须得带上那么大量的账本数据,到2017年8月份这份数据已经超过了130GB,而且还在持续不断地增长中,因此并不方便用户的实际使用,实际上除了这一点不方便外,在私钥管理上也有麻烦的地方,通过官方的核心钱包可以无限制地创建自己所需数量的钱包地址,然而这些地址对应的私钥管理也就成了问题,如果不小心损坏了某一个私钥数据,那就找不回来了,基于这些问题,发展出了新的解决方案。
很多时候,我们在进行支付的时候,只是想通过一个支付验证,知道支付已经成功发起就可以了。对于完整的交易验证(需要在完整的账本数据上校验,比如是否包含足够的余额,是否双花等)可以交给核心节点,这样就可以将钱包功能部分剥离出来,由此产生了SPV钱包,事实上这个概念在比特币白皮书中就介绍过了,我们来看下它的原理是什么,SPV钱包的大致过程如下所示。
1)首先下载完整的区块头数据,注意是区块头,而不是所有的区块链数据,这样可以大大减少需要获取的账本数据量,区块头中包含有区块的梅克尔根,SPV方式主要就是靠它来实现的。
2)如果想要验证某笔支付交易,则计算出这笔交易事务的哈希值txHash。
3)找到txHash所在的区块,验证一下所在区块的区块头是否包含在账本数据中。
4)获得所在区块中计算梅克尔根所需要的哈希值。
5)计算出梅克尔根。
6)若计算结果与所在区块的梅克尔根相等,则支付交易是存在的。
7)根据该区块所处的高度位置,还可以确定该交易得到了多少个确认。
我们看到了,SPV原理的钱包就是使用了梅克尔树来验证支付是否已经发生,这也是为什么称之为简单支付验证的原因,不过我们也可以发现,支付验证所做的事情很少,仅仅能看到当前的支付交易是否被发起而已,并不能保证这笔交易事务最终会进入到主链中,也就是说还需要等待核心节点进行全面的交易验证并且矿工打包到区块后进入主链。在这个过程中是有可能发生失败的,所以SPV钱包虽然带来了便捷性但也牺牲了安全性。时至今日,已经出现了各种各样的比特币钱包,在bitcoin.org网站上我们可以一见端倪:
我们可以看到有各种类型的钱包可以使用,大家在选用自己的钱包时,务必了解清楚钱包的功能和来源,以免遭受损失。
接下来我们再来介绍一种管理多个私钥的钱包技术,即分层确定性钱包(Hierarchical Deterministic Wallets,有时也简称为HD Wallets),这个在比特币开发的BIP32中有专门的建议论述。简单地说,分层确定性钱包具有如下的特点。
1)用一个随机数来生成根私钥,这与任何一个比特币钱包生成私钥没有区别;
2)用一个确定的、不可逆的算法,基于根私钥生成任意数量的子私钥。
比如比特币中使用的SHA256就是一个确定不可逆的算法,可以很容易使用SHA256设计出一个HD模型:SHA256(seed+n),这个就算是类型1确定性钱包了。实际上,分层确定性钱包是确定性钱包的一种,目前分层确定性钱包有Type1、Type2,还有BIP32规范几种类型,这些都是为了实现同一目的而制定的不同实现方法,基本原理都是类似的。
所谓的分层,除了私钥由主私钥来生成逐层的私钥以外,公钥也一样,通过主公钥生成所有的子公钥。实际上,生成的密钥本身,都可以作为根来继续生成子密钥,这就是所谓的分层了。注意,这里通过公钥生成子公钥,不需要私钥的参与,无论是主私钥还是子私钥都不需要参与。我们来看下示意图,如下:
这个特性是非常有用的,在一定程度上,隔离了私钥和公钥,可以带来不少的便捷性,具体如下。
1)备份只需要备份主私钥就行了,新增地址无须再次备份私钥。
2)可以保证主私钥的冷存储,无论增加多少个地址,只需要主公钥就可以了。
3)方便审计,只需要提供主公钥或者某个分支的子公钥,就可以查看下级的数据而又保证不能被交易。
4)有了这棵树,还可以配合权限,设定不同层级的权限,能查看余额还是能交易等。当然啦,便捷性往往都是要牺牲安全性的,缺点很明显,这种钱包,由于私钥之间是具有固定关系的,不那么随机了,因此只要暴露任何一个私钥,再加上主公钥做关联分析,就很有可能使整个树状密钥结构都泄露。
1.4.5 比特币账户模型:UTXO
我们来认识一下比特币中交易事务的数据结构,首先看这个名词UTXO(Unspent Transaction Output, “未花费事务输出”),老实说,第一次看到这个术语的时候,一时之间真是有些懵,如果说是未花费的余额还能理解,我钱包里有1000,花了200,还有800未花费,这是很符合通常的理解逻辑的,可这个未花费的“事务输出”是个什么意思,实际上,这与比特币中的交易事务结构是很有关系的。
为了让大家更容易理解,我们暂且先不来解析这个交易数据结构,让我们进入到一个仓库,我们知道仓库的主要业务就是进和出,仓库也会把日常的进出流水账记录下来,为了查询统计方便,除了流水账通常还会汇总一份库存表出来,举例如下:
以上图为例,这是从2017-8-1到2017-8-5之间,仓库记录的出入流水账,为了统计方便,仓库还汇总保存了一份每天的库存日报表,如下:
每天仓库在需要出库的时候,只要查看一下库存日报表就知道数量是否足够了,比如2017-8-3需要出库15支毛笔,此时查看库存表发现毛笔的库存量有30支,足够发出,于是就将库存表中的毛笔数量减掉15,并且将出库明细记录在流水账中。然而,这里有一个问题,库存日报表是另外编制保存的,那就有可能发生数据不一致的情况,比如2017-8-2时毛笔的库存本来是30却误写为20,这样导致后续的账务就都是错的了。因此在有些系统中,为了防止出现这样的不一致,索性不再另外保存库存表,而只是出一张视图统计(逻辑统计,并非实际去保存这样一个统计表)。
比特币中的交易事务过程与上述的库存进出是很相像的,某个钱包地址中转入了一笔比特币,然后这个地址又向其他钱包地址转出了一笔比特币,这些不断发生的入和出跟仓库的进出是异曲同工的。然而,在比特币中并没有去保存一份“库存表”,每当“出库”的时候也并不是去“库存表”中进行扣除,而是直接消耗“入库记录”,也就是说在出库的时候就去找有没有之前的入库记录拿来扣除,比如2017-8-3时需要出库15支毛笔,此时系统就会去搜索之前的入库记录,发现有2017-8-1和2017-8-2分别有一笔数量为10和20的入库记录,为了满足15的发出数量,首先可以消耗掉10的这一笔,然后从20的这一笔再消耗掉5支,判断成功后,系统会直接产生一条数量为10的出库记录和数量为5的出库记录,按这样的方法,将每一笔入和出都对应了起来。
在比特币的交易事务结构中,“入”就是指金额转入,“出”就是指金额转出,为了让大家对这种金额转入与转出有一个更加通俗的理解,我们来看一幅示意图:
上图展示了比特币中的交易事务结构,在比特币的交易事务数据中,存储的就是这样的输入和输出,相当于仓库中的进出流水账,并且“输入”和“输出”彼此对应,或者更准确地说,“输入”就是指向之前的“输出”,我们解释一下图中发生的交易事务。
1)001号交易为Coinbase交易,也就是挖矿交易,在这个交易中,“输入”部分没有对应的“输出”,而是由系统直接奖励发行比特币,矿工Alice得到了12.5个比特币的奖励,放在001号交易的“输出”部分。此时,对于Alice来说,拥有了这12.5个比特币的支配权,这12.5个比特币的输出可以作为下一笔交易的“输入”,顾名思义,这笔“输出”就称之为是Alice的未花费输出,也就是Alice的UTXO的意思。
2)002号交易中,Alice转账6比特币到Bob的地址,Alice找到了自己的UTXO(如果Alice不止一笔UTXO,可以根据一定的规则去选用,比如将小金额的先花费掉)。由于只需要转账6比特币,可是UTXO中却有12.5个,因此需要找零6.5个到自己的地址中,由此产生了002号中的交易输出,注意,在002号交易输出中的Alice地址是可以和001号中的Alice地址不一样的,只要都是属于Alice自己的钱包地址就可以。
3)003号交易中,Bob转账了2比特币到Lily的地址,过程与002号交易相同,就不再赘述了。
相信大家看到这里,已经基本理解了所谓的UTXO是什么意思,我们再来总结一下。
1)比特币的交易中不是通过账户的增减来实现的,而是一笔笔关联的输入/输出交易事务。
2)每一笔的交易都要花费“输入”,然后产生“输出”,这个产生的“输出”就是所谓的“未花费过的交易输出”,也就是UTXO。每一笔交易事务都有一个唯一的编号,称为交易事务ID,这是通过哈希算法计算而来的,当需要引用某一笔交易事务中的“输出”时,主要提供交易事务ID和所处“输出”列表中的序号就可以了。
3)由于没有账户的概念,因此当“输入”部分的金额大于所需的“输出”时,必须给自己找零,这个找零也是作为交易的一部分包含在“输出”中。
有朋友会问:这个UTXO的意思是明白了,可是就这么一条条的“输入”和“输出”,怎么证明哪一条UTXO是属于谁的呢?
在比特币中,是使用输入脚本和输出脚本程序实现的,有时候也称为“锁定脚本”和“解锁脚本”。简单地说,就是通过“锁定脚本”,利用私钥签名解锁自己的某一条UTXO(也就是之前的“输出”),然后使用对方的公钥锁定新的“输出”,成功后,这笔新的“输出”就成为了对方的UTXO。同样,对方也可以使用“锁定脚本”和“解锁脚本”来实现转账。这个脚本程序其实本质上就可以看成是比特币中的数字合约,这也是为什么比特币被称为可编程数字货币的原因,它的转入/转出或者说输入/输出是通过脚本程序的组合来自动实现的,实现过程中还使用到了私钥和公钥,也就是公开密钥算法,所以比特币还称为可编程加密数字货币。
1.4.6 动手编译比特币源码
如果有人一直在跟你说有个煎饼多好吃,芝麻有多香,鸡蛋有多金黄,你肯定希望去看一看;如果有人一直在跟你说有首歌曲多动人,旋律有多美,歌词有多感人,你肯定希望去听一听……是的,我们说了那么多的概念、技术名词,界面也看过了,可是这么一个软件到底是怎么编译出来的呢?无论你是不是程序员,都可以感受一下这个过程,看看这个设计巧妙的软件是怎样通过源代码生成可执行程序的。
比特币的源码是公开的,并且维护在GitHub网站上:https://github.com/bitcoin/bitcoin,目前该源码由比特币基金会进行维护。版权类型是MIT,这是一个很松散的版权协议,每一个对比特币源码感兴趣的人都可以自由地去复制、修改,以进行学习研究。
打开网页后,可以看到有详细的程序源码以及附带的文档说明,我们就从这里下载源码进行编译。在说明编译步骤之前,先介绍些概要前提吧,烹调大餐前得先看个菜谱不是。首先,比特币的源码是使用C++语言开发的,因此想要深入研究源码的朋友们,最好要有不错的C++基础;其次,源码中使用了很多其他的开源库,比如libssl-dev、libevent-dev、libboost-all-dev等,因此编译的时候也需要先安装这些第三方的依赖;另外,比特币源码在Linux系统上进行编译最方便,很多依赖库都是先天开发在Linux平台的,当然其他系统上也可以进行编译。
好了,接下来,我们就开始这道大餐吧!
1.准备操作系统环境
这里我们使用Ubuntu 16.04 LTS桌面版,关于Ubuntu的安装就不在这里赘述啦,物理安装或者用虚拟机加载安装都可以,装好系统后,首先使用如下命令更新一下系统的软件源:
sudo apt-get update
2.获得源码
先来看下获得源码的命令:
sudo apt-get install git
mkdir ~/bitcoinsource
git clone https://github.com/bitcoin/bitcoin.git "~/bitcoinsource"
1)第1条命令是安装git命令工具,这个git工具是用来从GitHub上下载源码的,事实上,使用git工具不但可以下载源码,也可以在本机创建自己的版本库;
2)第2条命令是在当前用户的目录下创建一个文件夹,用以保存即将下载的比特币源码,读者朋友具体操作时,可以自行决定路径和文件夹名称;
3)第3条命令就是从GitHub上下载比特币的源码到创建的bitcoinsource目录中。这里有个问题需要注意,如果在git clone过程中终止了,当再次进行clone时会出错,一般会有这样的提示:
git clone:GnuTLS recv error(-9):A TLS packet with unexpected length was received
出错的原因是因为git clone并不支持断续下载,删除目录后重新创建一个新目录再clone就可以了。
除了上述的git clone命令方法外,实际上,我们可以在GitHub上直接下载源码压缩包,下载下来的文件名一般为bitcoin-master.zip,然后解压缩即可:
unzip bitcoin-master.zip
解压缩后,将当前工作目录cd到bitcoin-master中,至此就可以开始着手编译了。
3.安装依赖库
工欲善其事必先利其器,比特币源码中使用了很多第三方的功能库,这些都是必需的依赖,正所谓一个好汉三个帮,一个篱笆三个桩,没有这些可以自由方便使用的库,使用C++开发比特币软件就要复杂不少。
比如,以下3行命令主要安装C++编译器和make工具:
sudo apt-get install make sudo apt-get install gcc sudo apt-get install g++
比如,以下命令主要是安装依赖库:
sudo apt-get install build-essential sudo apt-get install libtool sudo apt-get install autotools-dev sudo apt-get install autoconf sudo apt-get install pkg-config sudo apt-get install libssl-dev sudo apt-get install libevent-dev sudo apt-get install libboost-all-dev sudo apt-get install libminiupnpc-dev sudo apt-get install libqt4-dev sudo apt-get install libprotobuf-de sudo apt-get install protobuf-compiler sudo apt-get install libqrencode-dev
libevent-dev是一个网络库,实现网络通信功能;libssl-dev是一个密码算法库,提供了随机数生成,椭圆曲线密码算法等功能;libboost-all-dev是一个C++工具库,提供各种C++调用的基础功能库,如多线程调用以及一些有用的数据结构等;libqt4-dev是一个跨平台的C++库,用于实现跨平台运行的软件界面,这些都是比特币源码中需要用到的功能依赖库。值得一提的是,这些依赖库也都是开源的,也就是说,比特币源码不但本身是自由开源的,使用的其他依赖库也是自由开源的,这样就方便了那些希望对比特币源码进行深入研究的朋友,可以对每一个实现细节细嚼慢咽,尽情去学习和研究。
这两行命令主要安装比特币需要用到的数据存储驱动,其使用的类型是Berkeley DB,是一种开源的文件数据库。
sudo apt-get install libdb-dev sudo apt-get install libdb++-dev
到这里为止,就万事俱备只欠东风啦,该准备的材料都准备好了。
4.编译准备
这两个步骤是使用make工具进行编译的准备工作。
./autogen.sh ./configure
需要注意的是,在执行./configure的时候,有可能会看到这样的提示,如下:
configure: error: Found Berkeley DB other than 4.8, required for portable wallets (--with-incompatible-bdb to ignore or --disable-wallet to disable wallet functionality)
看提示是configure命令执行时出的问题,大概的意思是发现Berkeldy DB的版本高于4.8,我们在安装Berkeley DB的时候,命令下载安装的是最新版本,这个其实就是个警告而已,没什么影响,提示中也给出了解决方法,在configure的命令后面加上一个参数就可以了:
./configure —with-incompatible-bdb
执行完毕就可以了,接下来的工作就简单啦,直接make编译安装即可。
5.编译安装
make sudo make install
执行完毕后,就大功告成啦,接下来就可以运行比特币客户端程序啦。我们可以运行带界面的程序试试,经过这个步骤,在源码目录src/qt/下生成了可执行程序,同时安装到了/usr/local/bin目录下。
6.运行测试
输入以下命令:
bitcoin-qt
激动人心的时刻就来临啦!我们可以看到比特币的界面显示出来了,当然了,也可以去尝试运行bitcoind程序。至此,在Ubuntu操作系统上编译比特币源码就结束了。限于篇幅,在其他操作系统比如Mac、Windows上的编译过程就不再赘述了,读者朋友如果感兴趣,也可以参考比特币源码中doc文件夹下面的build-osx.md和build-windows.md的文件说明,分别是尝试在Windows和MacOS系统上的编译。
7.使用IDE管理源码
按理说到这里也没什么可说的了,编译完成了,运行也可以了,不过有没有觉得哪里不太爽呢?对了,缺少一个IDE(Integrated Development Environment,集成开发管理),这么多的文件,用文本编辑器一个个看,要看花眼了。好,接下来我们就安装一个IDE工具来管理这些源码,比特币系统是使用C++开发的,图形界面部分使用的又是QT组件,那就选择Qt Creator吧,本身也开源,而且跨平台,对C++的编译支持也非常好。由于上述的源码编译是在Ubuntu下进行的,因此,我们仍然在Ubuntu下进行安装设置,还是按照步骤来一步步说明吧。
(1)准备Qt Creator
可以直接到Qt Creator官网下载,Qt分为商业版和开源版本,我们使用开源版本即可,下载后得到一个文件qt-opensource-linux-x64-5.6.2.run,读者朋友自己下载的时候,还可以选择在线安装版和离线安装版,这里下载的是离线安装版,进入到文件所在的目录,执行如下命令:
chmod +x qt-opensource-linux-x64-5.6.2.run ./qt-opensource-linux-x64-5.6.2.run
第1行命令是给安装文件赋上一个执行权限。
第2行命令是执行安装。
安装完毕后,可以打开Qt Creator,见到如下界面:
(2)导入源码项目
在Qt Creator的菜单栏,点击“文件”→“新建文件或项目”命令,会弹出一个向导窗体,选择其中的Import Project,并选中右侧的“导入现有项目”,如下图所示:
接下来就是选择我们的比特币源码所在目录,也就是需要导入的项目。
图中的“项目名称”可以任意起名,“位置”就是比特币源码所在的目录。选择完毕后继续。
这个界面主要是用于选择一个源码版本控制系统,可以根据自己需要选用,这里只是演示,因此不做选择,直接就可以完成操作,源码导入完毕后,在Qt Creator中的展现如下:
可以看到,在左侧已经列出了源码的文件列表,src目录下是所有的代码文件,可以看到,根据不同的代码功能,划分了不同的目录,具体细节这里就不赘述了。到了这一步,可以运行一下试一试,点击运行按钮。咦,弹出了什么?
这是要选择一个执行程序,比如bitcoin-qt、bitcoind等,我们通过这个对话框选择bitcoin-qt,如下图所示:
注意,这里选择的执行程序是在src/qt/目录下,该目录下的执行程序是通过源码编译直接生成的。
选择后,点击运行,可以看到熟悉的界面又出来了,这样我们就使用Qt Creator将比特币的源码管理起来了,通过IDE工具查看源码要方便许多,感兴趣的朋友也可以尝试着修改其中的界面文件或者源码文件,体会一把编译调试的乐趣。
小提示
① 我们使用的Qt Creator引入的源码目录,是之前已经经过了一系列步骤编译过的,因此依赖库都已经具备了,执行程序也已经生成了,Qt Creator就像一个外壳,只是做了一个导入集成。
② 比特币是一个一直在发展的开源项目,在参照以上步骤进行操作的时候,一定要注意选择的版本是否一致或者兼容,本书选用的操作系统是Ubuntu 16.04 LTS桌面版,下载的比特币源码版本是v0.14,使用的Qt Creator是4.0.3。