第5章比特币核心技术 5.1libbitcoinexplorer工具 在介绍本节内容之前,先介绍一款比特币的命令行工具libbitcoinexplorer。它是一款基于C++层面,可扩展、多线程、模块化的执行工具。与比特币的命令行相似,但提供了相较于比特币更为丰富的功能。通过使用libbitcoinexplorer,可以看到比特币中地址及交易等信息在各个生命周期阶段编码的样子,对于学习和理解比特币原理有很大帮助。这款工具在github上是开源的,感兴趣的读者可以登录https://github.com/libbitcoin/libbitcoinexplorer#macintosh查看它的源代码,包括其各个系统版本下的编译安装方式。 在这里,以Mac OS为例,介绍一下Mac环境下的安装方式。建议从github上复制源代码,自己动手进行编译,这样可以将bx的所有功能打包进来。 (1) 检查Clang/LLVM的版本。 $ clang++ version (2) 返回如下信息。 Apple LLVM version 9.1.0 (clang902.0.39.1) Target: x86_64appledarwin17.5.0 Thread model: posix InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin (3) 如果提示更新xcode版本,则执行如下命令。 $ xcodeselect install (4) 安装Homebrew。 $ ruby e "$(curl fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" (5) 安装依赖库和wget。 $ brew install autoconf automake libtool pkgconfig wget (6) 安装boost(要求1.57.0及以上版本)。 $ brew install boost (7)安装ZeroMQ(要求4.2.0及以上版本)。 $ brew install zeromq (8) 下载安装脚本并进入目录赋予install.h执行权限。 $ git clone https://github.com/libbitcoin/libbitcoinexplorer.git $ chmod +x install.sh (9) 执行安装脚本,其中prefix与build参数为自己指定的输出目录,编译好的可执行文件都在prefix文件夹中。 $ ./install.sh disableshared prefix=/Users/me/Desktop/libbitcoinexplore/myprefix builddir=/Users/me/Desktop/libbitcoinexplore/build buildicu buildpng buildqrencode buildboost withicu withpng withqrencode 执行完毕后,编译好的libbitcoinexplorer程序就会在prefix参数指定的目录中的bin文件夹下,用户可以使用命令行进入到这个目录下调用bx程序来执行,如./bx help。libbitcoinexplorer的命令及操作指南都可以在Github给出的文档中查找。 5.2密钥与地址 在比特币系统中,是没有账户的概念的,所有比特币的归属权都是通过用户的私钥、比特币的地址和数字签名算法来确定的。用户的私钥实际上并不存储在网络上,而是存储在用户本的wallet.dat文件中,由用户自己保管。用户可以通过钱包软件生成和管理自己的地址,这些操作都是独立的,无须连接到比特币的网络。比特币的去中心化信任和控件、资产的归属权和安全模型,均是基于密码学算法来实现的。 5.2.1Base58Check 在输入比特币地址的时候,如果比特币地址是错误的,BitcoinCore钱包会有提示的: 出现背景色,如图5.1所示。 图5.1比特币转账地址输入错误提示 这是因为,在比特币系统中比特币地址加入了校验信息,用户验证转账时所输入的地址的合法性。这个检验地址合法性的过程,就是Base58Check。Base58Check的编码过程如图5.2所示。 (1) 将原始数据前边加上前缀版本号。 (2) 将生成后的数据进行两次SHA256运算,即SHA256(SHA256(版本信息+原始数据))。 (3) 取两次SHA256运算后的前4字节作为校验码,拼接在数据后边。 (4) 对拼接完的数据进行Base58编码。 图5.2Base58Check编码过程 Base58Check编码中,版本前缀使数据的格式非常容易辨认,让人们能更加直观地看出数据是何种属性及如何使用它们。例如,比特币的地址以1或3开头的,而Base58Check编码的私钥WIF是以5开头的。比特币系统中常见的版本前缀和它们对应的Base58编码如表5.1所示。 表5.1Base58Check版本前缀和Base58编码后的结果 种类 版本前缀Hex Base58编码 Bitcoin Address 0x00 1 PaytoScriptHash Address 0x05 3 Bitcoin Testnet Address 0x6F m或 n Private Key WIF 0x80 5,K 或 L BIP 39 Encrypted Private Key0x0142 0x0142 6P BIP32 Extended Public Key 0x0488B21E xpub 5.2.2比特币中的密钥与地址 比特币系统是采用椭圆曲线签名算法来生成公钥与私钥的。其中,公钥是用来生成地址的,生成的地址用于接收比特币; 私钥是用来生成支付其对应的公钥地址上拥有的比特币所必需的数字签名,来确定比特币的所有权。由于公钥是公开的,收款用户乃至全网用户都可以通过公钥地址来验证签名的正确性、比特币价值的所有权。所以,私钥是整个比特币价值流通过程中的关键,一旦私钥丢失或损坏,则对应公钥地址上的比特币也将丢失,并且再也无法找回。 1. 比特币私钥 比特币系统采用的椭圆曲线参数为secp256k1标准所定义的一条标准曲线。secp256k1标准中私钥的长度为256位,即32字节。也就是说比特币系统一共有2256个私钥,对应的有2256个公钥和地址。如果把沙子看成是1mm的立方体,而整个地球都是由沙子构成的话,那么整个地球上沙子将一共有270粒,远远没有比特币的地址多。所以,用户生成两个同样的比特币地址的概率,就好比是去年在三亚海滩度假时踩过的沙子,今年在北京进入到自己眼里了。因此,比特币的用户不用担心有人生成了跟自己一样的地址,并可以用私钥花掉自己的比特币。 本质上来讲,私钥就是一个随机数,即可以是0~2256之间的任意数字,比特币系统一般通过调用操作系统底层的随机数发生器来随机生成私钥。 在比特币核心客户端上使用getnewaddress命令来生成一个新的密钥对。出于对安全的考虑,这个参数只返回一个比特币的地址,不显示私钥信息。但是,可以通过dumpprivkey命令导出对应的私钥。整个过程如下所示。 $ bitcoincli getnewaddress 3A9y51qdu97FhWRvKBmrcWN2jUB9QDf57f $ bitcoincli dumpprivkey 3A9y51qdu97FhWRvKBmrcWN2jUB9QDf57f L2mJjmHaiedJHXkJq2JSYzetn35zgLB7j8BkVka8Gio6qvhjuns3 可以看到,L2mJjmHaiedJHXkJq2JSYzetn35zgLB7j8BkVka8Gio6qvhjuns3就是地址3A9y51qdu97FhWRvKBmrcWN2jUB9QDf57f对应的私钥。这种私钥的格式显示为Base58编码,被称为钱包导入格式(Wallet Import Format,WIF),主要是为了提高可读性和加入校验,防止输入错误。 表5.2展示了比特币系统中私钥存在的7种常见编码格式。 表5.2私钥存在的7种编码格式 编 码 格 式 使 用 网 络 前缀版本号 开 头 符 号 描述 Hex 无 — 无 64个十六进制数字 WIF 主网络 80 5 Base58Check编码: 包含128位的版本前缀和32位的校验码,公钥使用未压缩格式 WIFcompressed 主网络 80 K或L 编码前加后缀0x01,公钥使用压缩格式 WIF 测试网络 EF 9 Base58Check编码: 包含128位的版本前缀和32位的校验码,公钥使用未压缩格式 WIFcompressed 测试网络 EF C 编码前加后缀0x01,公钥使用压缩格式 BIP32私钥 主网络 0488ADE4 xprv 主网络BIP32类型私钥 BIP32私钥 测试网络 04358394 tprv 测试网BIP32类型私钥 其中,WIF格式是最经常看到的私钥格式,WIF格式的私钥生成算法过程如图5.3所示。 图5.3WIF格式私钥生成过程 下面,我们使用libbitcoinexplorer工具来讲解一下比特币私钥在生命周期的各个步骤中,从最初的256位随机数到我们在BitcoinCore中看到的WIF格式的私钥,是如何编码变换的。 (1) 生成一个256位的随机数,即私钥。 $ bx ecnew ac55f88ab5d5818b11e9e2796a01baf7 629872b98745aa3711534ca964920651f8359d2a7000343f41c6c11bb59b9463 (2) 在生成的随机数前加上网络标识,0x80表示mainnet主网络,0xef表示testnet测试网络。在这里,我们以主网络为例,在前边加上0x80。 80629872b98745aa3711534ca964920651f8359d2a7000343f41c6c11bb59b9463 (3) 如果使用压缩公钥,在步骤(2)的末尾追加0x01; 若不使用压缩公钥,则不在末尾追加0x01,在这里以压缩格式为例,数据末尾再加上0x01。 80629872b98745aa3711534ca964920651f8359d2a7000343f41c6c11bb59b946301 (4) 对步骤(3)的结果使用两次SHA256哈希算法。 $ bx sha256 80629872b98745aa3711534ca964920651f8359d2a7000343f41c6c11bb59b946301 341427ac8b5f8c7b592c6ccc3b73b3d4ee662410c6d75aa781c09de99007e0d1 $ bx sha256 341427ac8b5f8c7b592c6ccc3b73b3d4ee662410c6d75aa781c09de99007e0d1 f27643d9e982807d80ae64bc2d545fb3da70fb694fcda671f1f0682de0e3a274 (5) 取步骤(4)的结果前4字节,即0xf27643d9作为校验值,追加到步骤(3)结果的末尾。 80629872b98745aa3711534ca964920651f8359d2a7000343f41c6c11bb59b946301f27643d9 (6) 对步骤(5)的结果进行Base58编码。 $ bx base58encode 80629872b98745aa3711534ca964920651f8359d2a7000343f41c6c11bb59b946301 f27643d9 KzXNH2g5C8Eu6b8M7s5n1MXcLSfjxVT8CmvbeYDKjAyw2XdMGBdr 最终返回的结果KzXNH2g5C8Eu6b8M7s5n1MXcLSfjxVT8CmvbeYDKjAyw2XdMGBdr就是我们在BitcoinCore中所见到的WIF格式的私钥。 2. 比特币公钥 在比特币系统中公钥存在的形式有两种: 非压缩格式公钥和压缩格式公钥。下面我们以上文中的私钥为例,计算它对应的公钥及两种存在形式。私钥如下。 629872b98745aa3711534ca964920651f8359d2a7000343f41c6c11bb59b9463 1) 非压缩格式公钥 使用libbitcoinexplorer中的bx ectopublic u命令,参数为私钥,可生成对应的非压缩格式的公钥。 $ bx ectopublic u 629872b98745aa3711534ca964920651f8359d2a7000343f41c6c11bb59b9463 0417c4c04516045dc5a9912ce3901f5f004a0e9c2df5521c4eaca0134658ac510cbb7366fb45238b13ae65a27f4da7055c79d349cc91836e6a43f2163b0fa20ee1 在返回的结果中,前8位0x04是代表本条数据是非压缩格式的公钥,接下来256位是x,最后256位是y。例子中的公钥解析后如下。 x=17c4c04516045dc5a9912ce3901f5f004a0e9c2df5521c4eaca0134658ac510c y=bb7366fb45238b13ae65a27f4da7055c79d349cc91836e6a43f2163b0fa20ee1 2) 压缩格式公钥 带宽和存储对于比特币来说都是非常宝贵的资源,所以应该尽可能地对数据进行精简、压缩。对于非压缩格式的公钥来说,算上前缀共520位。比特币交易中是含有公钥信息的,如果有大量的交易时,在传输及写入数据都有大量的数据。 比特币的密钥对是通过参数为secp256k1标准所定义的一条标准椭圆曲线算法生成的。其椭圆曲线方程y2=x3+7mod p是公开固定的,所以完全可以通过x计算出y。 使用libbitcoinexplorer中的bx ectopublic命令,参数为私钥,可生成对应的压缩格式的公钥。 $ bx ectopublic 629872b98745aa3711534ca964920651f8359d2a7000343f41c6c11bb59b9463 0317c4c04516045dc5a9912ce3901f5f004a0e9c2df5521c4eaca0134658ac510c 在返回的结果中,可以看到除了前缀0x03之外,剩下的就是在非压缩格式公钥中的x。前缀0x03代表公钥中的y是负数,前缀0x02,代表公钥中的y是正数。压缩格式公钥的具体压缩方法如图5.4所示。 图5.4压缩格式公钥的压缩方法 3. 比特币地址 比特币的地址看起来是很没有规律的一串字符,如在blockchain.info中可以查到创世区块中的挖矿地址,也就是传说中“中本聪”的地址如下。 1A1zP1eP5Qgefi2DMPTfTL5SLmv7DivfNa 比特币的地址是由密钥对中的公钥计算而得的。具体的计算过程如图5.5所示。 图5.5从公钥到比特币地址 以上文非压缩格式中公钥为例,使用libbitcoinexplore详细分析一下公钥是如何转变成一个比特币的地址的。 (1) 生成公钥,在这里直接用上文中讲解非压缩格式公钥时的数据。 0417c4c04516045dc5a9912ce3901f5f004a0e9c2df5521c4eaca0134658ac510cbb7366fb45238b13ae65a2 7f4da7055c79d349cc91836e6a43f2163b0fa20ee1 (2) 对生成的公钥执行SHA256算法。 $ bx sha256 0417c4c04516045dc5a9912ce3901f5f004a0e9c2df5521c4eaca0134658ac510cbb7366fb45 238b13ae65a27f4da7055c79d349cc91836e6a43f2163b0fa20ee1 0cf8f4af5e32b237689f0c52a1526ef9583b05e7fc7df7ae0ac22f4c9b8b82d8 (3) 对步骤(2)中的结果执行RIPEMD160 哈希算法。 $ bx ripemd160 0cf8f4af5e32b237689f0c52a1526ef9583b05e7fc7df7ae0ac22f4c9b8b82d8 0fd8a9bb80ec366ad2fe64052b6168831b146f3c (4) 在步骤(3)中的结果前加上网络识别的版本号,如0x00代表是Main Network,0x6f代表是Test Network,0x34代表是Namecoin Net。在这里我们选用比特币正式运行的主网络,所以加上前缀0x00。 000fd8a9bb80ec366ad2fe64052b6168831b146f3c (5) 对步骤(4)中的结果再执行两次SHA256算法。 $ bx sha256 000fd8a9bb80ec366ad2fe64052b6168831b146f3c 3312e1c9f947074135e6f72b0a7b2ef9f0e244186c23cc361e97d0309476277f $ bx sha256 3312e1c9f947074135e6f72b0a7b2ef9f0e244186c23cc361e97d0309476277f 9910d379b4712175d61a3d6ae21c91bb3c0ae555d1e70f4afa8bfbe95abab485 (6) 取步骤(5)结果中的前4字节作为校验码。 9910d379 (7) 将步骤(6)中的校验码作为后缀拼接到步骤(4)的结果末尾。 000fd8a9bb80ec366ad2fe64052b6168831b146f3c9910d379 (8) 对步骤(7)中的结果进行Base58Check编码,得到对应的比特币地址。 $ bx base58encode 000fd8a9bb80ec366ad2fe64052b6168831b146f3c5abab485 12SngtVcKJ2fWpoMQhkVPweunhPymaNV44 综上所述,由私钥47181e60b4e24263dbf8b5a616ea35e1463db834b288315e2a3fa7c79451aeff生成的一个比特币地址为12SngtVcKJ2fWpoMQhkVPweunhPymaNV44。同理,使用计算地址的算法对上文中的压缩格式公钥进行同样的操作,会得到一个比特币地址1MfGsiQtiJDTZ7mCWgr14UkdWAfM86tQea。经过对比,可以发现两次生成的地址是不一样的,所以一个比特币的私钥可以对应两个不一样的比特币地址。 表5.3展示了比特币系统中地址存在的4种常见编码格式。 表5.3比特币地址存在的4种编码格式 编码格式 使用网络 前缀版本号 开头符号 示例 P2PKH地址 主网络 00 1 17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem P2SH地址 主网络 05 3 3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX P2PKH地址 测试网络 6F m或n mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn P2SH地址 测试网络 C4 2 2MzQwSSnBHWHqSAqtTVQ6v47XtaisrJa1Vc 4. 比特币私钥、公钥、地址在转账过程中的作用 在比特币系统中,地址可以看作是一个银行的账户,对应的私钥看作是账户的密码。那一个比特币地址的钱如何花出去呢?用户需要首先填写自己的一份转账账单,在自己的转账账单填写完毕后,使用自己的私钥对账单进行签名,得到签名信息后将签名信息附在账单上。 其他用户在收到这份账单的时候,会首先验证这个公钥经过地址生成的算法后是否为这个地址,即是否为对应的银行账户。如果通过的话,则再使用这个公钥验证这个账单的签名信息。如果签名信息也通过的话,则可以证明消费者的身份,可以将其账户下的资产花出去。 在比特币系统中,从一个随机数到一个地址,整个转换流程如图5.6所示。 图5.6比特币系统随机数到地址的转换流程 从整个流程中可知,私钥可以计算出公钥,公钥可以计算出地址,但反过来任何一步都不可行。私钥、公钥、地址的关系如图5.7所示。 图5.7私钥、公钥和地址三者的关系 5.3交易 在比特币系统中,比特币的交易是整个系统中最为重要的部分。根据比特币的设计原理可知,比特币系统中任何一部分的设计,都是为了保证价值的流转。要确保价值的正确流转,就要保证交易能够正确生成,能够在比特币网络中进行广播,能够通过其他节点的验证,并且最终加入整个比特币的全球账本中。 在本节,将介绍比特币交易的生命周期,并详细说明比特币交易数据是如何被创建,如何被广播验证,如何成为全球账本的一部分,解析交易在不同的阶段的各种形式、包含哪些信息、是怎样的一种数据结构。 5.3.1交易的生命周期 在比特币系统中,用户Bob向用户Alice转账一笔比特币,就是一笔交易。比特币的一笔交易起始于它被创建的那一刻,结束于它被记入区块链总账。如图5.8所示,比特币的生命周期共分为5个阶段。 图5.8比特币交易的生命周期 1. 创建交易 比特币的交易可以被任何人在线上或线下创建,可以是一个账户的主人,也可以是其他人,这时的交易就相当于一张支票,比如Bob要向Alice进行转账,这张支票的意思就是Bob要付款给Alice一部分比特币。但是如果想让这张支票生效,那么支票上就必须要有Bob的签字,同时Bob的账户上还必须拥有比支票上额度多且合法的比特币,这笔交易才能成为一笔合法交易。在比特币系统中,交易的创建就是构造一条交易的数据,之后由资金的所有者进行数字签名的过程,这条交易数据包含了比特币在网络中进行价值流通的所有信息。最终,如果这笔交易是一笔可以被比特币网络接受的有效交易,那么就可以进入比特币的网络中。 2. 广播交易 比特币的交易被创建后,将会被发送到至少一个与比特币正式网络相连接的节点,节点会验证该交易是否是一笔合法的交易,如果交易有效,则该节点就会将这笔交易发送给与自己相连接的其他比特币节点,其他节点收到之后将会做同样的操作。以此类推,在几秒钟之内,一笔有效的交易就会呈指数级扩散一样在全网传播开来,直到所有的节点都收到它。 3. 交易验证和打包 交易在传播的过程中,为了能高效、灵活地将信息传送至所有区块,避免垃圾信息的传播,每个节点在收到交易后都会对交易进行验证,需要验证的信息主要包括交易的语法和数据结果是否正确,交易的总价值是否为0~2100万,交易的输入是否合法,交易的解锁脚本是否可以被所引用输出的锁定脚本验证等。这些检验可以在BitcoinCore源代码中的AcceptToMemoryPool、CheckTransaction和CheckInputs函数中获取更详细的阐述。 如果没有通过验证,则节点将会丢弃不合法的数据,从而避免其在比特币网络上进行传播。通常来讲,一笔异常交易所能达到的节点不会超过1个。这样做还可以防止拒绝服务攻击或其他针对比特币系统的恶意攻击。 所有具有挖矿功能的节点,都会通过工作量证明的机制来争取将最近的交易打包成区块。所谓的工作量证明就是一轮算力竞赛,谁先算出来正确的目标结果,谁就可以优先将自己制造的区块广播给其他区块。其他的节点收到区块后,如果这个区块合法有效,则会停止这一轮的算力竞赛,将该区块接入至自己的账本中,并将区块中的交易从自己的交易池中去除,开始下一轮的算力竞赛。 4. 交易确认 其他节点在收到区块后,会对区块进行检验,检验内容包括本轮算力竞赛目标结果是否正确、区块中的交易是否都为合法交易等。如果全部正确,则将该区块接入到自己区块链的账本末尾,并将该区块广播至与自己相连接的节点。 5. 交易记录 为了防止分叉和解决双花问题,这个交易所在的区块之后又接上一个新的区块,称为对这笔交易的一个区块的确认,当一笔交易被6个区块所确认之后,这笔交易便永远地被记录在区块链账本中,不可更改。 5.3.2基于UTXO的交易模型 首先,从比特币的区块链浏览器blockchain.info上查找出一笔交易,如图5.9所示,是一笔被记录在第300000个区块内的一条交易记录。交易的来源是3个地址,交易的去向是2个地址。 图5.9区块链浏览器中显示的交易 在比特币系统中,没有账户的概念,而是基于UTXO(Unspent Transaction Outputs)模型,所有的交易都是由输入和输出构成。比特币的交易本质就是一条包含发送方的输入、接收方的输出以及其他与资产价值转移相关的数据结构。下面看一下官方给出的比特币交易数据结构,如表5.4所示。 表5.4比特币交易的数据结构 字段 描述 大小 版本号 交易数据结构的版本号,目前是1 4字节 输入数量 交易中输入的数量 1~9字节 var_int类型 输入列表 交易中输入数组,可以是1个也可以是多个 不确定 输出数量 交易中输出的数量 1~9字节 var_int类型 输出列表 交易中输出数组,可以是1个也可以是多个 不确定 锁定时间 多意字段,为0时表示立即生效; <500000000时代表区块高度,表示该区块高度之前锁定; >500000000时代表UNIX时间戳,表示该时刻前锁定 4字节 打一个比方来描述比特币交易中的输入和输出。例如,Alice口袋中有1张10元、2张5元、6张1元的钞票,如果Alice需要向Bob支付5元钱,Alice可以有多种支出方式,可以是5张1元的,也可是1张5元的,同样也可以付1张10元的,Bob再找零5元。这与比特币系统中的输入输出相似,输入标志着价值的发送方,输出标识着价值的接收方,只要输入之和不少于输出之和,便可任意组合。输入之和与输出之和的差为交易的手续费。 在比特币系统中,除了挖矿所得的奖励之外,每一笔输入都可以在账本中找到与之对应的上一笔交易的输出,形成一条条交易链,使得每一笔交易都可以查找对应的来源。比特币交易流转的示意图如图5.10所示。 图5.10比特币交易流转示意图 一笔比特币交易可以有任意的数值,但必须是该用户可用的UTXO中创建出来的。在每一笔交易中,被消费掉的UTXO即为输入,新创建的UTXO为输出。每一笔交易所消耗的比特币,都可以在区块链中查到其来源。而对于一般的比特币交易,用户在创建新的UTXO时,因为地址为收款方的公钥所得来的,所以收款方需要使用对应的私钥进行签名和公钥验证,才能向其他用户证明这笔钱归他所有,才能消费这笔比特币。由此,可以得出比特币交易链式结构,如图5.11所示。 图5.11比特币交易链式结构图 1. 交易输出 每一笔比特币交易创造的输入、输出都会记录在比特币的账本中,比特币交易的本质就是用户消耗自己可用的UTXO,创造新的UTXO,将新创建的UTXO注册到收款人的比特币地址上,并且能被收款人用于新的支付。 一笔交易输出中主要包含两部分,一部分是一定量的比特币,另一部分是锁定脚本,其中锁定脚本就好比是收款用户的一把锁,付款用户用这把锁将对应的比特币“锁住”,只有收款用户有对应的钥匙,所以只有收款用户可以“解锁”花掉。交易输出的具体结构如表5.5所示。 表5.5比特币交易输出结构 字段 大小/比特 描述 比特币数量 8 比特币的值,以聪为单位 锁定脚本大小 1~9 后边锁定脚本的长度,以字节为单位 var_int类型 锁定脚本 不定长 定义支付这笔比特币所需要的条件 一般来说,交易的输出一共有两种,分别是标准输出(Standard TxOut)和挖矿交易输出(Coinbase TxOut)。结合整个交易来看,交易输出的结构如图5.12所示。 图5.12交易输出的结构 2. 交易输入 交易输入一般是指向一个UTXO的指针,因为一笔交易的输入也是交易链中上一笔的某一笔输出,以交易哈希ID和UTXO的索引号n作为参考,代表以该ID所交易的第n个输出。另外,若想支付这笔交易,则需要“锁住”这笔交易对应的“钥匙”,即解锁脚本。只有解锁脚本和锁定脚本组合起来,满足支付条件,才可以被比特币网络所接收。交易输入的具体结构如表5.6所示。 表5.6比特币交易输入结构 字段 大小/比特 描述 交易哈希 32 上一笔交易的ID 输出索引号 4 上一笔交易中的输出索引号 续表 字段 大小/比特 描述 解锁脚本大小 1~9 后边解锁脚本的长度,以字节为单位 var_int类型 解锁脚本 不定长 满足花费这笔比特币的解锁条件 序列号 4 目前未被使用的字段,设置为0xFFFFFFFF 一般来说,交易输入的类型一共有三种,标准交易输入(Standard TxIn)、花费挖矿交易输入(Spend Coinbase TxOut)和挖矿交易输入(Coinbase)。结合整个交易来看,交易输入的结构如图5.13所示。 图5.13交易输入的结构 3. 交易费 在交易的数据结构中,我们发现并没有交易费这个字段,但是在上文中我们一再强调一笔合法的交易,所有的输入总价值必须不小于所有的输出总价值。交易费的计算公式为 交易费=所有输入之和 - 所有输出之和 在比特币系统中,交易费是可以由用户来设置的,用户可以通过设置配置文件中的payfee参数或者在控制台调用settxfee命令来设置交易费。需要注意的是,这里设置的交易费率与这笔交易包含多少比特币没有关系,而是与这笔交易的大小(字节数)有关系,即设置的是发送的交易每千字节需要的手续费。 5.4脚本 5.4.1比特币脚本结构 比特币的脚本有两种,锁定脚本和解锁脚本。 锁定脚本(scriptPubKey)位于一个可用的UTXO上,是花费这个UTXO的一个“障碍”,相当于提出了一个问题,并且明确了要花费这个UTXO的条件。一般来讲,锁定脚本是在双方进行交易时,由付款方创建的,内容包含收款方的比特币地址信息。 解锁脚本(scriptSig)是一个解决锁定脚本中提出问题的答案,位于交易的输入中,只有这个答案能够解决锁定脚本中的问题,才可以花费这个UTXO。锁定脚本是在双方进行交易时,由付款方创建,解决可用的UTXO中的问题。钱包中可用的UTXO中的问题由上一笔交易创建,包含这个钱包中的地址,所以一般解锁脚本中包含这个地址的公钥及使用对应私钥生成的签名信息。 比特币系统中最常见的交易类型(P2PKH:对公钥哈希的付款)的脚本结构如图5.14所示。 图5.14P2PKH类型交易脚本结构 比特币的矿工在收到一笔交易时,会先使用堆栈执行这个交易所有输入中的解锁脚本,如果执行完解锁脚本没有报错,则会将解锁脚本执行完所得的数据复制。接下来,矿工将会执行锁定脚本,如果成功地执行且返回的结果为true,则解锁脚本满足花费该UTXO的条件,矿工就会使交易生效,并将对应的可用的UTXO标记为“已使用”。 5.4.2比特币交易脚本类型 在比特币系统中一共定义了五种类型的标准交易脚本,除了P2PKH外,还有P2PK、MS、P2SH和OP_RETURN。本节将介绍这些交易类型的脚本格式。 1. P2PKH P2PKH(paytopublickeyhash)交易是比特币网络中最常见的交易,绝大多数交易都是这种类型。在P2PKH类型交易里,其锁定脚本的主要内容是一个公钥的Hash,解锁脚本包括该公钥和对应私钥的签名,验证的主要思想即验证公钥是否正确,再使用公钥验证签名是否正确。 P2PKH型交易锁定脚本的格式如下: OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG P2PKH型交易解锁脚本的格式如下: < sig> < Pubk> 2. P2PK 相较于P2PKH型的交易,P2PK(paytopublickey)型交易更为容易,这种交易类型主要用在挖矿交易中。在P2PK型交易中,锁定脚本中包含公钥本身,解锁脚本中的主要内容就是公钥对应私钥的签名。验证的主要思想也很简单,就是用公钥验证数字签名。 P2PK型交易锁定脚本的格式如下: OP_CHECKSIG P2PK型交易解锁脚本的格式如下: < Signature from Private Key A> 3. MS MS(multiple signature)型交易为多重签名交易,即允许多个公钥签署一笔交易,以实现多方管理资产的目的。MS交易中设定了一个条件,在锁定脚本中记录了N个公钥,在解锁脚本中至少得有M个签名验证通过才算有效交易,这也被称为M~N交易,其中M为签名的数量,N为公钥的数量。例如,一个2~3交易表达的意思就是,这笔交易必须3个人中至少有2个人签署了,才能成为一笔合法交易。这种类型交易的验证思想就是锁定脚本中N个公钥中,与它们对应的私钥中,是否有任意M个进行了正确的签名。MS型交易锁定脚本格式如下: M ... N CHECKMULTISIG 以2~3交易为例,MS型交易锁定脚本格式如下: 2 3 CHECKMULTISIG 以2~3交易为例,MS型交易解锁脚本格式如下: 最终组成的交易脚本程序如下: 2 3 CHECKMULTISIG 但是,操作符OP_ CHECKMULTISIG从其诞生开始就自带一个bug,理论上来讲,执行OP_ CHECKMULTISIG应该从堆栈中弹出M+N+2个参数。例如本例中,参数依次入栈,执行OP_ CHECKMULTISIG会先弹出3,再弹出3个参数,之后弹出2,再弹出2个参数,共7个参数。但实际上是操作符OP_ CHECKMULTISIG会弹出M+N+3个参数,弹出最后一个参数时栈已为空,就会报出异常错误。所以在创建这个解锁脚本时,应该在最前边随便再加一个参数,默认写一个0,这个bug并不影响后边签名的验证过程,已经被大家默认接受,所以最终的交易脚本程序应该如下: 0 2 3 CHECKMULTISIG 4. P2SH P2SH(paytoscripthash)型交易是2012年引入的一种新型、强大的交易类型,它能大大简化复杂交易类型的脚本程序。以MS类型的交易为例,虽然MS交易类型有诸多好处,可以实现多方管理资产,但其锁定脚本的长度会随着M和N的增加不断增长,这样一来,锁定脚本就会变得很长,会带来以下缺点: (1) 锁定脚本过长,会为发起付款的客户带来昂贵的交易费。 (2) 交易完成后,这个UTXO会一直在用户的内存中,直到这笔UTXO被花费,如果这个UTXO的锁定脚本过长的话,会占用更多用户的内存。 P2SH型交易正是为了解决这些问题而提出的,它是这样处理的: (1) 将原锁定脚本进行Hash,新的锁定脚本中只包括验证Hash部分。 (2) 花费这个UTXO时,解锁脚本中应包含原锁定脚本。 (3) 矿工验证时,会先验证原锁定脚本的Hash值是否与新锁定脚本中存储的Hash值相等,再验证签名的正确性。 如此一来,锁定脚本中就只存储了原解锁脚本的Hash,长度缩短了很多。再次以2~3交易为例,设脚本Temp如下: 2 3 CHECKMULTISIG 锁定脚本如下: OP_HASH160 <160bits hash of Temp> OP_EQUAL 解锁脚本如下: 0 Temp P2SH型交易更重要的一个用途是可以锁定脚本的160哈希值,按照Base58格式进行编码,编译成为一个地址,由于在比特币主网络中,P2SH地址采用0x05作为前缀,所以主网中P2SH地址都是以3为开头的。 5. OP_RETURN 由于比特币自身具有的去中心化和时间戳账本的特点,所以比特币的价值和应用领域远远地超出了支付的范畴,许多开发者试图充分去利用比特币的脚本优势,将其应用到其他方面,如电子公证服务、证券认证和智能合约等领域。很多优秀开发者都在这方面付出了努力,利用脚本开发出新的符合比特币规则的交易,最早的创新应用是将文件的Hash值作为电子指纹存放在交易中,证明该文件的存在性。但是这种做法的反对者们认为,这样会使账本变得更加臃肿,进一步消耗磁盘容量。更有甚者,创建出来的交易的新UTXO是伪交易型UTXO,不能用于创建新的交易,会使这些UTXO一直存储在用户的内存中。 从Bitcoin0.9的版本开始,比特币社区对这些用户进行了妥协,增加了OP_RETURN操作符。使用OP_RETURN操作符输出的记录,只会被记录在区块链上,并不会存储在用户的UTXO内存池中。RETURN样式的脚本如下,其中data部分被限制为80字节数据。 OP_RETURN 5.4.3比特币脚本执行 在本节中,以比特币系统中最常见的交易类型(P2PKH)为例,分析矿工在收到一笔新交易后,是如何执行比特币脚本程序,验证这笔交易的有效性的。 以程序员使用比特币买比萨为例,程序员发起一笔向比萨店付款的交易,这个交易的输出中锁定脚本如下。 OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG 如果比萨店想花费这笔交易,需要在输入中创建对应的解锁脚本。 < Pizza’s Signature> < Pizza’s Public Key> 矿工在收到交易后,会将二者结合起来,组成一个脚本程序。 < Pizza’s Signature> < Pizza’s Public Key> OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG 矿工执行脚本程序,验证交易有效性的过程如图5.15所示。 图5.15矿工执行P2PKH脚本交易有效性 5.5本章小结 本章首先介绍了一种比特币的命令行操作工具libbitcoinexplorer,该工具可以查看比特币地址和交易各个生命周期的格式及转换过程,方便读者更好地理解比特币的几大基本核心组件地址、交易和脚本,并从原理上讲解了比特币中地址、交易和脚本的数据结构,进行了实战分析。此外,本章还介绍了比特币钱包的分类,以及比特币社区关于比特币钱包的技术标准。 参考文献 [1]Satoshi Nakamoto. Bitcoin: A PeertoPeer Electronic Cash System[OL]. Available on: https://bitcoin.org/bitcoin.pdf, 2008. [2]Andreas M Antonopoulos.精通比特币[OL]. Available on: http://book.8btc.com/books/1/master_bitcoin/_book/, 2018.