# 第3章 Bitcoin SV

Bitcoin是一个开源项目,源代码使用开放(Open BSV License)授权,可免费下载使用。 开源不仅意味着可自由使用,还意味着比特币是由一个开源社区志愿者开发的。 最初这个社区只有中本聪,到2016年,比特币的源代码贡献者超过400个,大约十几位开发人员几乎全职工作,还有几十名兼职开发人员。任何人都可以为代码做出贡献 - 包括你!

当初中本聪是先开发了比特币软件,之后才完成了大名鼎鼎的satoshi_whitepaper白皮书。 中本聪考虑是发布白皮书之前先确保系统能够正常工作。比特币的第一个软件,就叫做“比特币(Bitcoin)”或者“Satoshi客户端”,到现在已经进行了大量的修改和改进了。为了与其他软件区别,这个软件后来演变成所谓的Bitcoin SV(比特币SV)。Bitcoin SV是比特币系统的参考实现,这意味着它是所有技术实现的权威参考。Bitcoin SV实现了比特币的所有方面,包括钱包,交易和区块验证引擎,以及P2P网络中的全节点。

警示 即使Bitcoin SV包含钱包的参考实现,但并不建议作为用户或应用程序的生产钱包。建议应用程序开发人员使用现代标准(如BIP-39和BIP-32)开发钱包(请参阅【5.1.4 种子和助记词(BIP-39)】 (opens new window)【5.1.3 分层确定性钱包(HD Wallets (BIP-32/BIP-44))】 (opens new window)章节)。BIP就是比特币改进提案(Bitcoin Improvement Proposal)。

下图为Bitcoin SV的架构。

图3-1

图3-1 比特币核心架构(来源Eric Lombrozo)

# 3.1 比特币开发环境

对于开发人员,需要使用所有工具,库和支持软件来配置开发环境,编写比特币应用程序。 这一章涉及的技术细节较深,我们将逐步介绍该过程。 如果你觉得过于繁琐(并且缺乏开发环境),建议你跳到下一章,技术性会浅显一些。

# 3.2 从源码编译比特币核心

Bitcoin SV的源代码可以下载ZIP格式,也可以从GitHub克隆官方的源代码库。 在GitHub比特币页面GitHub bitcoin page (opens new window)上,选择“下载ZIP”。 或者,使用git命令行在系统上创建源代码的本地副本。

在本例中,我们使用git命令来创建源代码的本地副本(“clone”):

% git clone https://github.com/bitcoin-sv/bitcoin-sv.git
正克隆到 'bitcoin-sv'...
remote: Enumerating objects: 2270, done.
remote: Counting objects: 100% (2270/2270), done.
remote: Compressing objects: 100% (839/839), done.
remote: Total 101508 (delta 1572), reused 1904 (delta 1425), pack-reused 99238
接收对象中: 100% (101508/101508), 72.56 MiB | 4.20 MiB/s, 完成.
处理 delta 中: 100% (78674/78674), 完成.
1
2
3
4
5
6
7
8

提示 Git是最广泛使用的分布式版本控制系统,是软件开发人员最重要的工具之一。 如果操作系统还未安装,需要先安装git命令行或git的图形界面。

当git克隆操作完成后,当前目录(如果是bitcoin)就有源代码存储库的完整本地副本。 在提示符下键入“cd bitcoin”,进入此目录:

$ cd bitcoin

# 3.2.1 选择Bitcoin SV版本

默认情况下,本地副本将与最新的代码同步,但是最新版本可能是不稳定的或者是Beta版。 所以在编译代码前,先查看发布标签tag,选择一个特定的版本。 这将使本地副本与关键字标签所标识的代码库的特定快照同步。 开发人员使用标签tag来标记特定版本号的代码。 首先,要找到可用的标签,我们使用git tag命令:

$ git tag
v0.1.5
v0.1.6test1
v0.10.0
...
v0.11.2
v0.11.2rc1
v0.12.0rc1
v0.12.0rc2
...
1
2
3
4
5
6
7
8
9
10

tag列表显示所有发布的比特币版本。根据惯例,用于测试的发布候选版本具有后缀“rc”。可以在生产系统上运行的稳定版本没有后缀。从上面的列表中,选择最高版本的版本,在编写时是v0.11.2。要使本地代码与此版本同步,请使用git checkout命令:

$ git checkout v0.11.2
HEAD is now at 7e27892... Merge pull request #6975
1
2

输入命令git status来确认检查到了选定的版本:

$ git status
HEAD detached at v0.11.2
nothing to commit, working directory clean
1
2
3

# 3.2.2 配置构建Bitcoin SV

源代码中包括帮助文档,可以在许多文件中找到。在提示符下输入“more README.md”,可以查看bitcoin目录中最主要的文档README.md的内容,使用空格键可翻页。在本章中,我们将在Linux上部署命令行比特币客户端,也称为bitcoind。在系统中查看编译bitcoind命令行客户端的说明,方法是输入“more doc / build-unix.md” 。doc目录中还有macOS和Windows的文档,分别为build-osx.md或build-windows.md。

仔细查看文档第一部分提到的依赖库,在开始构建比特币客户端之前这些库必须存在。如果缺少这些库,构建过程会提示出错,并失败。如果构建失败提示是缺少这些依赖库,那么就必须再安装它,然后从刚才提示出错的地方重新开始构建。这些依赖库都安装之后,可以使用autogen.sh脚本生成一组脚本来启动构建过程。

注意 Bitcoin Core 构建过程已经从0.9开始更改为使用autogen / configure / make系统。 0.9之前的旧版本使用简单的Makefile,与以下示例的方法略有不同。 建议按照版本的说明进行操作。 在0.9中引入的autogen / configure / make用于之后的版本构建过程,下面演示的示例就是这个过程。

$ ./autogen.sh
...
glibtoolize: copying file 'build-aux/m4/libtool.m4'
glibtoolize: copying file 'build-aux/m4/ltoptions.m4'
glibtoolize: copying file 'build-aux/m4/ltsugar.m4'
glibtoolize: copying file 'build-aux/m4/ltversion.m4'
...
configure.ac:10: installing 'build-aux/compile'
configure.ac:5: installing 'build-aux/config.guess'
configure.ac:5: installing 'build-aux/config.sub'
configure.ac:9: installing 'build-aux/install-sh'
configure.ac:9: installing 'build-aux/missing'
Makefile.am: installing 'build-aux/depcomp'
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14

autogen.sh脚本创建一组自动配置脚本,它会询问系统以发现正确的设置,并确保依赖库已经完整。 其中最重要的是配置脚本,它提供了许多自定义构建过程的可选选项。输入“./configure --help”查看各种选项:

$ ./configure --help
`configure' configures Bitcoin Core 0.11.2 to adapt to many kinds of systems.

Usage: ./configure [OPTION]... [VAR=VALUE]...

...
Optional Features:
  --disable-option-checking  ignore unrecognized --enable/--with options
  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]

  --enable-wallet         enable wallet (default is yes)

  --with-gui[=no|qt4|qt5|auto]
...

帮助文档中可以看到,配置脚本允许使用--enable-FEATURE和--disable-FEATURE标志来启用或禁用bitcoind的某些功能,使用该命令是把FEATURE替换为功能名称。 在本章中,我们将构建所有默认功能的bitcoind客户端。 我们不会使用配置标志调整功能,但是需要了解客户端是能够实现可选功能的。如果您处于学术环境中,计算机实验室的限制可能需要把应用程序安装在当前账户的主目录中(例如,使用--prefix = $ HOME)。

以下这些选项,可以覆盖configure脚本的默认行为:

--prefix=$HOME

这将覆盖生成的可执行文件的默认安装位置(默认是/ usr / local /)。 使用$HOME将所有内容放在主目录,也可以放在其他路径。

--disable-wallet

这用于禁用钱包功能。

--with-incompatible-bdb

如果您正在构建钱包,允许使用不兼容的Berkeley DB库版本。

--with-gui=no

不构建图形用户界面,图形界面是需要Qt库的。 这只生成服务器和命令行客户端。

接下来,运行configure脚本来自动发现所有必需的库,并创建一个自定义的构建脚本:

$ ./configure
checking build system type... x86_64-unknown-linux-gnu
checking host system type... x86_64-unknown-linux-gnu
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
...
[many pages of configuration tests follow]
...
$
1
2
3
4
5
6
7
8
9
10
11
12

如果一切顺利,configure命令运行完成后,会自动一个创建可定制的构建脚本。可以使用构建脚本编译bitcoind。如果有缺失的库或是错误,configure命令将会提示出错。出现错误,一般都是缺少库或是有不兼容的库。这需要重新查看文档,确认是否安装所有依赖库。然后再次运行configure,看看错误是否消失。

# 3.2.3 构建Bitcoin SV可执行文件

下一步,开始编译源代码,这个过程根据CPU处理能力和内存大小,但一般可能需要1个小时完成。编译的过程中,应该过几秒或是几分钟看一下输出结果。如果出现了问题,就会看到错误。如果中断了,编译的过程可以在任何时候恢复。输入make命令就可以开始编译了:

$ make
Making all in src
  CXX      crypto/libbitcoinconsensus_la-hmac_sha512.lo
  CXX      crypto/libbitcoinconsensus_la-ripemd160.lo
  CXX      crypto/libbitcoinconsensus_la-sha1.lo
  CXX      crypto/libbitcoinconsensus_la-sha256.lo
  CXX      crypto/libbitcoinconsensus_la-sha512.lo
  CXX      libbitcoinconsensus_la-hash.lo
  CXX      primitives/libbitcoinconsensus_la-transaction.lo
  CXX      libbitcoinconsensus_la-pubkey.lo
  CXX      script/libbitcoinconsensus_la-bitcoinconsensus.lo
  CXX      script/libbitcoinconsensus_la-interpreter.lo

[... many more compilation messages follow ...]

$
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

如果CPU是多核,那么需要设置并行编译作业需要的核数,比如输入make -j 2会使用两个核。如果一切顺利,bitcoind就编译完成了。可以使用make check命令运行单元测试包,检查链接的库没有发生中断。最后一步就是sudo make install 命令,安装 bitcoind 可执行文件,可能会提示您输入用户密码,因为这一步需要管理员权限:

$ make check && sudo make install
Password:
Making install in src
 ../build-aux/install-sh -c -d '/usr/local/lib'
libtool: install: /usr/bin/install -c bitcoind /usr/local/bin/bitcoind
libtool: install: /usr/bin/install -c bitcoin-cli /usr/local/bin/bitcoin-cli
libtool: install: /usr/bin/install -c bitcoin-tx /usr/local/bin/bitcoin-tx
...
$
1
2
3
4
5
6
7
8
9

bitcoind 默认的安装位置是/usr/local/bin。查询下面这两个可执行文件的路径,可以确认bitcoin是否安装成功。

$ which bitcoind
/usr/local/bin/bitcoind

$ which bitcoin-cli
/usr/local/bin/bitcoin-cli
1
2
3
4
5

# 3.3 运行Bitcoin SV节点

比特币的点对点网络由网络中的“节点”组成,主要由志愿者和一些构建比特币应用程序的商业机构运行。 那些运行的比特币节点具有直接和权威的比特币区块链视图,并且具有所有交易的本地副本,自己可以独立验证。 自己运行节点,就不必依赖任何第三方来验证交易。 此外,自己运行比特币节点,也可以使比特币网络更加健壮,为比特币网络做出贡献。

但是,运行节点需要足够资源来处理所有比特币交易,而且不能停机断网。 如果选择索引所有交易并保留完整区块链副本,还需要足够的磁盘空间和RAM。 2018年初,全索引节点需要2 GB的RAM和160 GB的磁盘空间,才能满足空间增长需求(参考 https://blockchain.info/charts/blocks-size)。 比特币节点还传输和接收比特币交易和区块,消耗互联网带宽。 如果互联网连接有带宽上限或按流量计费,建议就不要运行比特币全节点,或者用限制其带宽的方式运行(请参阅例3-2)。

提示 Bitcoin Core默认情况下保留区块链的完整副本,包括从2009年比特币成立以来发生的每一笔交易。此数据集的大小为几百GB,下载可能需要几天或几周,具体取决于 CPU和互联网带宽。完整区块链数据集被下载完成之前,Bitcoin Core将无法处理交易或更新帐户余额。 因此确保足够的磁盘空间,带宽和时间来完成初始同步。 也可以配置Bitcoin Core通过丢弃旧区块来减少区块链的大小(请参阅例3-2),但是在丢弃数据之前仍将下载整个数据集。

尽管有这些资源需求,但仍有成千上万的志愿者运行比特币节点。 有些节点在简单的系统上运行,就像树莓派Raspberry Pi(35美元的计算机,一副扑克牌大小)。 许多志愿者在租用的服务器类Linux系统上运行比特币节点。 虚拟专用服务器(VPS)或云计算服务器实例也可用于运行比特币节点。 这些服务器每月租金大约25至50美元。

为什么要运行节点? 以下是一些最常见的原因:

  • 如果是开发比特币软件,需要比特币节点进行可编程接口(API)访问网络和区块链。

  • 如果是开发根据比特币共识规则验证交易的应用程序。 比特币软件公司通常运行多个节点。

  • 如果只是想支持比特币。 那么运行节点可以使网络更加健壮,提供更多的钱包,更多的用户和更多的交易。

  • 如果不想依赖任何第三方来处理或验证自己的交易。

如果阅读本书并对开发比特币软件感兴趣,那么也应该运行自己的节点。

# 3.3.1 配置Bitcoin SV节点

Bitcoin Core每次启动都会在查找数据文件目录下的配置文件(文件名一般是bitcoin.conf)。这一部分先了解配置文件的不同选项,建立配置文件。要找到配置文件的位置,在“terminal”中运行bitcoind -printtoconsole命令,命令输出的前几行就能看到。

$ bitcoind -printtoconsole
Bitcoin version v0.15.0
Using the 'standard' SHA256 implementation
Using data directory /home/ubuntu/.bitcoin/
Using config file /home/ubuntu/.bitcoin/bitcoin.conf
...
[a lot more debug output]
...
1
2
3
4
5
6
7
8

找到配置文件的位置后,可以使用Ctrl-C退出程序。一般来说,配置文件位于当前用户的主目录的.bitcoin数据目录下。系统不会自动创建这个文件的,需要手动从下面例3的内容复制粘贴过来。也可以使用自己的默认编辑器手动创建进行修改。

Bitcoin Core还提供了100多个配置选项,可以修改网络节点的行为,区块链的存储以及操作的其他方面。

要查看这些选项的列表,运行bitcoind --help命令:

 $ bitcoind --help
Bitcoin Core Daemon version v0.15.0

Usage:
  bitcoind [options]                     Start Bitcoin Core Daemon

Options:

  -?
       Print this help message and exit

  -version
       Print version and exit

  -alertnotify=<cmd>
       Execute command when a relevant alert is received or we see a really
       long fork (%s in cmd is replaced by message)
...
[many more options]
...

  -rpcthreads=<n>
       Set the number of threads to service RPC calls (default: 4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

以下是您可以在配置文件中设置的一些最重要的选项,也可以作为bitcoind的命令行参数:

alertnotify

运行指定的命令或脚本,通常通过电子邮件将紧急警报发送给该节点的所有者。

conf

配置文件的替代位置。 只作为bitcoind的命令行参数有意义,不能在配置文件中起作用。

datadir

选择存放区块链数据的目录和文件系统。 默认情况下,在账户主目录的.bitcoin子目录。 确保该文件系统最少有几GB的可用空间。

prune

启用修剪模式,通过删除旧区块,将磁盘空间要求降低到几百MB。供空间有限的节点使用。

txindex

维护所有交易的索引。 这意味着可以通过ID以编程方式在区块链的完整副本中检索任何交易。

dbcache

设置UTXO cache大小。默认是300MB。配置高的系统上可以增加一些,配置低的话就减少一些,可以减少内存消耗,但是需要更多的磁盘IO开销。

maxconnections

设置最连接多少个节点。 从默认值减少该值将减少带宽消耗。 如果网络是带宽受限或者按照流量计费,可以设置这个参数。

maxmempool

将交易内存池限制在几兆字节。 使用它来减少节点的内存使用。

maxreceivebuffer/maxsendbuffer

将每个连接的内存缓冲区限制为多少个KB。 在内存受限节点上使用。

minrelaytxfee

设置发送的交易的的最低费用。 低于此值,交易被视为不规范的,会被交易池拒绝,也不会被发送。

交易数据库索引和txindex选项

默认情况下,Bitcoin Core会创建一个数据库,这个数据库只包含与用户钱包有关的交易。 如果您想要使用诸如getrawtransaction(参见3.4.2)之类的命令解读任何交易,则需要修改配置文件中的txindex选项设置txindex = 1来构建完整的交易索引。 如果不是一开始设置此选项,后期再想开启完全索引,需要使用-reindex选项重新启动bitcoind,并等待它重建索引。

下面的配置文件显示了前面提到的选项与完全索引节点组合起来的示例,可以作为比特币应用程序的API后端运行。

例3-1完整索引节点的例子

alertnotify=myemailscript.sh "Alert: %s"
datadir=/lotsofspace/bitcoin
txindex=1
1
2
3

例3-2是小型服务器资源不足的配置示例。

alertnotify=myemailscript.sh "Alert: %s"
maxconnections=15
prune=5000
dbcache=150
maxmempool=150
maxreceivebuffer=2500
maxsendbuffer=500
1
2
3
4
5
6
7

配置文件修改完成之后,可以使用printtoconsole选项测试 bitcoind,查看输出的结果:

$ bitcoind -printtoconsole

Bitcoin version v0.15.0
InitParameterInteraction: parameter interaction: -whitelistforcerelay=1 -> setting -whitelistrelay=1
Assuming ancestors of block 0000000000000000003b9ce759c2a087d52abc4266f8f4ebd6d768b89defa50a have valid signatures.
Using the 'standard' SHA256 implementation
Default data directory /home/ubuntu/.bitcoin
Using data directory /lotsofspace/.bitcoin
Using config file /home/ubuntu/.bitcoin/bitcoin.conf
Using at most 125 automatic connections (1048576 file descriptors available)
Using 16 MiB out of 32/2 requested for signature cache, able to store 524288 elements
Using 16 MiB out of 32/2 requested for script execution cache, able to store 524288 elements
Using 2 threads for script verification
HTTP: creating work queue of depth 16
No rpcpassword set - using random cookie authentication
Generated RPC authentication cookie /lotsofspace/.bitcoin/.cookie
HTTP: starting 4 worker threads
init message: Verifying wallet(s)...
Using BerkeleyDB version Berkeley DB 4.8.30: (April  9, 2010)
Using wallet wallet.dat
CDBEnv::Open: LogDir=/lotsofspace/.bitcoin/database ErrorFile=/lotsofspace/.bitcoin/db.log
scheduler thread start
Cache configuration:
* Using 250.0MiB for block index database
* Using 8.0MiB for chain state database
* Using 1742.0MiB for in-memory UTXO set (plus up to 286.1MiB of unused mempool space)
init message: Loading block index...
Opening LevelDB in /lotsofspace/.bitcoin/blocks/index
Opened LevelDB successfully

[... more startup messages ...]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

一确认配置显示正常,可以按Ctrl-C中断进程。

如果不想在前台运行Bitcoin Core,可以使用守护进程选项启动:bitcoind -daemon,在后台运行。

要监视比特币节点的进度和运行状态,使用命令bitcoin-cli getblockchaininfo:

$ bitcoin-cli getblockchaininfo
{
  "chain": "main",
  "blocks": 0,
  "headers": 83999,
  "bestblockhash": "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
  "difficulty": 1,
  "mediantime": 1231006505,
  "verificationprogress": 3.783041623201835e-09,
  "chainwork": "0000000000000000000000000000000000000000000000000000000100010001",
  "pruned": false,
  [...]
}
1
2
3
4
5
6
7
8
9
10
11
12
13

上面输出内容显示了这个节点当前更新的数据是区块高度为0,区块头为83999,意思是只获取到了最佳链的区块头,完整的区块数据之后才会开始下载。

配置选项确定好之后,应该将bitcoin命令添加到操作系统中的启动脚本中,让它随系统重启自动启动。 可以在contrib / init下的bitcoin目录查看README.md文件,找到不同操作系统对应的示例启动脚本。

# 3.4 通过命令行使用Bitcoin SV的JSON-RPC API接口

Bitcoin Core客户端实现了JSON-RPC接口,这个接口也可以通过命令行工具bitcoin-cli访问。命令行可以使用API进行编程,方便进行交互方式测试。开始前,使用help命令查看可用的比特币RPC命令列表:

$ bitcoin-cli help
addmultisigaddress nrequired ["key",...] ( "account" )
addnode "node" "add|remove|onetry"
backupwallet "destination"
createmultisig nrequired ["key",...]
createrawtransaction [{"txid":"id","vout":n},...] {"address":amount,...}
decoderawtransaction "hexstring"
...
...
verifymessage "bitcoinaddress" "signature" "message"
walletlock
walletpassphrase "passphrase" timeout
walletpassphrasechange "oldpassphrase" "newpassphrase"
1
2
3
4
5
6
7
8
9
10
11
12
13

这些命令中的每一个可能都有多个参数。 要获得更多帮助,查看详细说明和参数信息,可以在help之后添加命令名称。 例如,要查看getblockhash RPC命令使用详情:

$ bitcoin-cli help getblockhash
getblockhash height

Returns hash of block in best-block-chain at height provided.

Arguments:
1. height         (numeric, required) The height index

Result:
"hash"         (string) The block hash

Examples:
> bitcoin-cli getblockhash 1000
> curl --user myusername --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getblockhash", "params": [1000] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/
1
2
3
4
5
6
7
8
9
10
11
12
13
14

在帮助信息的最后,可以看到RPC命令的两个示例,使用bitcoin-cli或HTTP客户端的curl。 这些例子演示如何调用命令。 复制第一个示例并查看结果:

$ bitcoin-cli getblockhash 1000
00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09
1
2

输出结果是一个区块哈希值,下面的章节中对此有更详细的描述。 这个命令的输出应该与你的输出一致,这才表明你自己的Bitcoin Core节点运行正常,可以接受命令,能够正确返回关于区块1000的信息。

在下一节中,将演示一些非常有用的RPC命令及其预期输出。

# 3.4.1 获得Bitcoin SV客户端状态的信息

Bitcoin Core通过JSON-RPC提供了不同模块的状态信息。其中最重要的命令有getblockchaininfo, getmempoolinfo, getnetworkinfo and getwalletinfo。

getblockchaininfo RPC命令之前介绍过了。getnetworkinfo显示比特币网络节点状态的基本信息。用bitcoin-cli运行这个命令示例如下:

$ bitcoin-cli getnetworkinfo
  "version": 150000,
  "subversion": "/Satoshi:0.15.0/",
  "protocolversion": 70015,
  "localservices": "000000000000000d",
  "localrelay": true,
  "timeoffset": 0,
  "networkactive": true,
  "connections": 8,
  "networks": [
    ...
    detailed information about all networks (ipv4, ipv6 or onion)
    ...
  ],
  "relayfee": 0.00001000,
  "incrementalfee": 0.00001000,
  "localaddresses": [
  ],
  "warnings": ""
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

数据返回的是JavaScript Object Notation (JSON)格式,这种格式很容易被所有编程语言“识别”,同时也方便人阅读。在上面数据中,我们看到比特币软件客户端版本号(150000)和比特币协议版本号(70015)。 还看到当前的连接数(8)。 后面还看到比特币网络和客户端设置的相关信息。

提示 比特币客户端“赶上”当前的blockchain高度需要一些时间,因为它要从其他比特币客户端下载区块。 可以使用getblockchaininfo检查进度,查看已已同步的区块的数量。

# 3.4.2 查看和解码交易

命令:getrawtransaction,decodeawtransaction

在买咖啡的故事中,Alice从Bob咖啡厅买了一杯咖啡。 她的交易ID(txid)0627052b6f28912f2703066a912ea577f2ce4da4caa5a5fbd8a57286c345c2f2,已经被记录在区块链上。 使用API把交易ID作为参数可以进行检索和检查该交易:

$ bitcoin-cli getrawtransaction 0627052b6f28912f2703066a912ea577f2ce4da4caa5a↵
5fbd8a57286c345c2f2

0100000001186f9f998a5aa6f048e51dd8419a14d8a0f1a8a2836dd734d2804fe65fa35779000↵
000008b483045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4↵
ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813014↵
10484ecc0d46f1918b30928fa0e4ed99f16a0fb4fde0735e7ade8416ab9fe423cc54123363767↵
89d172787ec3457eee41c04f4938de5cc17b4a10fa336a8d752adfffffffff0260e3160000000↵
0001976a914ab68025513c3dbd2f7b92a94e0581f5d50f654e788acd0ef8000000000001976a9↵
147f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a888ac00000000
1
2
3
4
5
6
7
8
9
10

提示 交易ID在交易被确认之前是没有意义的。 在区块链中缺少交易哈希并不意味着交易未被处理。 这被称为“交易延展性”,因为区块中的交易被确认之前是可以修改交易哈希的。 交易被确认后,txid是就是不可改变的和权威的。

命令getrawtransaction以十六进制形式返回序列化交易。 为了解码,使用decodeawtransaction命令,将十六进制数据作为参数。 可以复制getrawtransaction返回的十六进制,并将其作为参数粘贴到decodeawtransaction中:

$ bitcoin-cli decoderawtransaction 0100000001186f9f998a5aa6f048e51dd8419a14d8↵
a0f1a8a2836dd734d2804fe65fa35779000000008b483045022100884d142d86652a3f47ba474↵
6ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298↵
cad530a863ea8f53982c09db8f6e381301410484ecc0d46f1918b30928fa0e4ed99f16a0fb4fd↵
e0735e7ade8416ab9fe423cc5412336376789d172787ec3457eee41c04f4938de5cc17b4a10fa↵
336a8d752adfffffffff0260e31600000000001976a914ab68025513c3dbd2f7b92a94e0581f5↵
d50f654e788acd0ef8000000000001976a9147f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8↵
88ac00000000
1
2
3
4
5
6
7
8
{
  "txid": "0627052b6f28912f2703066a912ea577f2ce4da4caa5a5fbd8a57286c345c2f2",
  "size": 258,
  "version": 1,
  "locktime": 0,
  "vin": [
    {
      "txid": "7957a35fe64f80d234d76d83a2...8149a41d81de548f0a65a8a999f6f18",
      "vout": 0,
      "scriptSig": {
        "asm":"3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1decc...",
        "hex":"483045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1de..."
      },
      "sequence": 4294967295
    }
  ],
  "vout": [
    {
      "value": 0.01500000,
      "n": 0,
      "scriptPubKey": {
        "asm": "OP_DUP OP_HASH160 ab68...5f654e7 OP_EQUALVERIFY OP_CHECKSIG",
        "hex": "76a914ab68025513c3dbd2f7b92a94e0581f5d50f654e788ac",
        "reqSigs": 1,
        "type": "pubkeyhash",
        "addresses": [
          "1GdK9UzpHBzqzX2A9JFP3Di4weBwqgmoQA"
        ]
      }
    },
    {
      "value": 0.08450000,
      "n": 1,
      "scriptPubKey": {
        "asm": "OP_DUP OP_HASH160 7f9b1a...025a8 OP_EQUALVERIFY OP_CHECKSIG",
        "hex": "76a9147f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a888ac",
        "reqSigs": 1,
        "type": "pubkeyhash",
        "addresses": [
          "1Cdid9KFAaatwczBwBttQcwXYCpvK8h7FK"
        ]
      }
    }
  ]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

交易解码展示这笔交易的所有成分,包括交易的输入及输出。在这个例子中,可以看到这笔给我们新地址存入15mBTC的交易使用了一个输入并且产生两个输出。这笔交易的输入是前一笔确认交易的输出(以7957a35fe开头的 vin txid)。两个输出则是15mBTC转账金额和返回的找零金额。

可以使用相同命令(例如 gettransaction )检查本次交易txid前一笔交易,进一步探索区块链。通过从一笔交易跳到另外一笔交易,我们可以追溯一连串的交易,因为币一定是从一个拥有者的地址传送到另一个拥有者的地址。

# 3.4.3 探索区块

命令: getblock 、 getblockhash

探索区块类似于探索交易。不同之处是区块可以由区块高度或区块哈希引用。首先,我们找一个区块的高度。在买咖啡故事中,我们看到Alice的交易已被包含在277316区块中。

使用getblockhash命令,该命令用区块高度作为参数,并返回该区块的区块哈希值:

$ bitcoin-cli getblockhash 277316
0000000000000001b6b9a13b095e96db41c4a928b97ef2d944a9b31b2cc7bdc4
1
2

既然知道要找的交易在这个区块中,我们可以使用getblock命令,把区块哈希值作为参数:

$ bitcoin-cli getblock 0000000000000001b6b9a13b095e96db41c4a928b97ef2d944a9b31b2cc7bdc4

{
  "hash": "0000000000000001b6b9a13b095e96db41c4a928b97ef2d944a9b31b2cc7bdc4",
  "confirmations": 37371,
  "size": 218629,
  "height": 277316,
  "version": 2,
  "merkleroot": "c91c008c26e50763e9f548bb8b2fc323735f73577effbc55502c51eb4cc7cf2e",
  "tx": [
    "d5ada064c6417ca25c4308bd158c34b77e1c0eca2a73cda16c737e7424afba2f",
    "b268b45c59b39d759614757718b9918caf0ba9d97c56f3b91956ff877c503fbe",
    "04905ff987ddd4cfe603b03cfb7ca50ee81d89d1f8f5f265c38f763eea4a21fd",
    "32467aab5d04f51940075055c2f20bbd1195727c961431bf0aff8443f9710f81",
    "561c5216944e21fa29dd12aaa1a45e3397f9c0d888359cb05e1f79fe73da37bd",
[... hundreds of transactions ...]
    "78b300b2a1d2d9449b58db7bc71c3884d6e0579617e0da4991b9734cef7ab23a",
    "6c87130ec283ab4c2c493b190c20de4b28ff3caf72d16ffa1ce3e96f2069aca9",
    "6f423dbc3636ef193fd8898dfdf7621dcade1bbe509e963ffbff91f696d81a62",
    "802ba8b2adabc5796a9471f25b02ae6aeee2439c679a5c33c4bbcee97e081196",
    "eaaf6a048588d9ad4d1c092539bd571dd8af30635c152a3b0e8b611e67d1a1af",
    "e67abc6bd5e2cac169821afc51b207127f42b92a841e976f9b752157879ba8bd",
    "d38985a6a1bfd35037cb7776b2dc86797abbb7a06630f5d03df2785d50d5a2ac",
    "45ea0a3f6016d2bb90ab92c34a7aac9767671a8a84b9bcce6c019e60197c134b",
    "c098445d748ced5f178ef2ff96f2758cbec9eb32cb0fc65db313bcac1d3bc98f"
  ],
  "time": 1388185914,
  "mediantime": 1388183675,
  "nonce": 924591752,
  "bits": "1903a30c",
  "difficulty": 1180923195.258026,
  "chainwork": "000000000000000000000000000000000000000000000934695e92aaf53afa1a",
  "previousblockhash": "0000000000000002a7bbd25a417c0374cc55261021e8a9ca74442b01284f0569",
  "nextblockhash": "000000000000000010236c269dd6ed714dd5db39d36b33959079d78dfd431ba7"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

该区块包含419笔交易,列出的第64笔交易(0627052b ...)是Alice的咖啡付款。高度条目告诉我们这是区块链中的第277316区块。

# 3.4.4 使用Bitcoin Core的编程接口

bitcoin-cli 助手工具对于探索Bitcoin Core API和测试功能非常有用。 应用编程接口的全部要点是以编程方式访问软件功能。 在本节中,我们将演示从另一个程序访问Bitcoin Core的过程。

Bitcoin Core的API是一个JSON-RPC接口。 JSON代表JavaScript Object Notation,它可以很方便地呈现出人类和程序都可以轻松阅读的数据格式。 RPC代表远程过程调用,这意味着我们通过网络协议调用远程(位于Bitcoin Core节点)的程序(函数)。 这里的网络协议是HTTP或HTTPS(加密连接)。

当我们使用bitcoin-cli助手获取命令的帮助时,它给了我们一个使用curl的例子,这是一个常用的JSON-RPC调用命令行HTTP客户端:

$ curl --user myusername --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "getinfo", "params": [] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/
1

此命令显示curl向本地主机(127.0.0.1)提交HTTP请求,连接到默认比特币端口(8332),并使用text / plain编码向getinfo方法提交jsonrpc请求。

你会注意到curl随同请求会发出一个凭证。Bitcoin Core每次启动时会创建一个随机口令,放在数据目录下的.cookie文件中。bitcoin-cli助手会读取数据目录下的这个口令文件。同样你也可以复制这个口令发送给curl(或更高级别的Bitcoin Core RPC封装)。你还可以使用Bitcoin Core源码目录下的./share/rpcauth/rpcauth.py助手脚本创建一个静态口令。

如果您在自己的程序中部署JSON-RPC调用,可以使用通用的HTTP库构建调用,类似于前面的curl示例所示。

然而,大多数编程语言中都有“包装”在Bitcoin Core API中的库,这样使用起来更简单。 使用python-bitcoinlib库可以简化API访问。 记住,这需要一个正在运行的Bitcoin Core实例,用于JSON-RPC调用。

下面的例子中的Python脚本使用简单的getblockchaininfo调用,从Bitcoin Core返回的数据中显示了区块参数。

例3-3通过Bitcoin Core的JSON-RPC API运行getblockchaininfo

from bitcoin.rpc import RawProxy

# Create a connection to local Bitcoin Core node
p = RawProxy()

# Run the getblockchaininfo command, store the resulting data in info
info = p.getblockchaininfo()

# Retrieve the 'blocks' element from the info
print(info['blocks'])
1
2
3
4
5
6
7
8
9
10

运行结果如下:

$ python rpc_example.py
394075
1
2

它告诉我们,我们的本地Bitcoin Core节点存储的区块链中有394075个区块。 虽然这个结果很简单,但它演示了使用库作为Bitcoin Core的JSON-RPC API的简化接口的基本用法。

接下来,我们使用getrawtransaction和decodetransaction调用来检索Alice咖啡付款的详细信息。 在下面的例子中,我们检索Alice的交易并列出交易的输出。 对于每个输出,我们显示收款地址和金额。 回忆一下,Alice的交易有一个输出支付Bob的咖啡馆,另一个输出是给Alice自己的找零。

例3-4检索交易并遍历输出

from bitcoin.rpc import RawProxy

p = RawProxy()

# Alice's transaction ID
txid = "0627052b6f28912f2703066a912ea577f2ce4da4caa5a5fbd8a57286c345c2f2"

# First, retrieve the raw transaction in hex
raw_tx = p.getrawtransaction(txid)

# Decode the transaction hex into a JSON object
decoded_tx = p.decoderawtransaction(raw_tx)

# Retrieve each of the outputs from the transaction
for output in decoded_tx['vout']:
    print(output['scriptPubKey']['addresses'], output['value'])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

运行结果如下:

$ python rpc_transaction.py
([u'1GdK9UzpHBzqzX2A9JFP3Di4weBwqgmoQA'], Decimal('0.01500000'))
([u'1Cdid9KFAaatwczBwBttQcwXYCpvK8h7FK'], Decimal('0.08450000'))
1
2
3

上述两个例子都比较简单。 你真的不需要一个程序来运行它们; 你可以很容易地使用bitcoin-cli 助手。 不过,下一个示例需要数百个RPC调用,并更清楚地说明了使用编程接口的方便。

在例5中,我们首先检索区块277316,然后通过引用每个交易ID来检索所有的419个交易。 接下来,我们遍历每个交易的输出累加起来。

例3-5 检索区块并累加所有交易的输出

rpc_block.py文件中的代码如下:

from bitcoin.rpc import RawProxy

p = RawProxy()

# The block height where Alice's transaction was recorded
blockheight = 277316

# Get the block hash of block with height 277316
blockhash = p.getblockhash(blockheight)

# Retrieve the block by its hash
block = p.getblock(blockhash)

# Element tx contains the list of all transaction IDs in the block
transactions = block['tx']

block_value = 0

# Iterate through each transaction ID in the block
for txid in transactions:
    tx_value = 0
    # Retrieve the raw transaction by ID
    raw_tx = p.getrawtransaction(txid)
    # Decode the transaction
    decoded_tx = p.decoderawtransaction(raw_tx)
    # Iterate through each output in the transaction
    for output in decoded_tx['vout']:
        # Add up the value of each output
        tx_value = tx_value + output['value']

    # Add the value of this transaction to the total
    block_value = block_value + tx_value

print("Total value in block: ", block_value)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

运行结果如下:

$ python rpc_block.py

('Total value in block: ', Decimal('10322.07722534'))
1
2
3

我们的示例代码计算出,此区块中交易的总价值为10,322.07722534 个BTC(包括25 BTC矿工费奖励和0.0909 BTC交易费)。 可以将这个结果与使用区块浏览器搜索区块哈希或高度的结果进行比较。 有些区块浏览器不包括矿工费奖励和交易费。 看看是否有不同。

# 3.5 其他可选的客户端、库、工具包

比特币生态中还有许多可选的客户端,库,工具包,甚至还有全节点的实现。这些工具都由各自的编程语言实现,为程序员提供其首选语言的本地接口。

以下按照编程语言分类列出了推荐的一些库,客户端和工具包:

# 3.5.1 C/C++

Bitcoin Core (opens new window)

比特币的参考实现

libbitcoin (opens new window)

跨平台C++开发工具包,节点和共识库

bitcoin explorer (opens new window)

Libbitcoin的命令行工具

picocoin (opens new window)

Jeff Garzik提供的比特币c语言轻量级客户端库

# 3.5.2 JavaScript

bcoin (opens new window)

带API的模块化可扩展的全节点实现

Bitcore (opens new window)

Bitpay提供的全节点,API和库

BitcoinJS (opens new window)

用于node.js和浏览器的纯JavaScript比特币库

# 3.5.3 Java

bitcoinj (opens new window)

Java全节点客户端库

Bits of Proof (BOP) (opens new window)

比特币JAVA企业级实现

# 3.5.4 PHP

bitwasp /bitcoin (opens new window)

PHP比特币库和相关项目

# 3.5.5 Python

python-bitcoinlib (opens new window)

Peter Todd提供的Python比特币库,共识库和节点

pycoin (opens new window)

Richard Kiss提供的Python比特币库

pybitcointools (opens new window)

Vitalik Buterin提供的Python比特币库

# 3.5.6 Ruby

bitcoin-client (opens new window)

Ruby封装的JSON-RPC API

# 3.5.7 Go

btcd (opens new window)

Go语言全节点比特币客户端

# 3.5.8 Rust

rust-bitcoin (opens new window)

用于序列化,解析和API调用的Rust比特币库

# 3.5.9 C#

NBitcoin (opens new window)

.NET框架的综合比特币库

# 3.5.10 Objective-C

CoreBitcoin (opens new window)

ObjC和Swift比特币工具包

还有许多其他编程语言的库,在不断开发中。