本节书摘来自华章计算机《区块链开发指南》一书中的第1章,第1.1节,作者:申屠青春 主编 宋 波 张 鹏 汪晓明 季宙栋 左川民 编著更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1.1 交易和交易链
交易是签过名的数据结构,该数据结构会在区块链网络中广播,并被收集到区块中。它会引用以前的交易,从该交易中发送特定数量的比特币到一个或多个公钥中(即比特币地址),并且交易未被加密(比特币体系中没有加密任何数据)。多个交易可组成一个区块(block),这些区块同样也会在区块链网络中传播,一个区块会引用上一个区块,简而言之,区块链就是由区块(block)用某种方式组织起来的链条(chain)。区块链包括成千上万个区块,而一个区块内又包含一个或多个交易,上下关联的交易组成了一个交易链,一个交易链内部可能又包含了多个交易,下面的章节将会对这些知识点进行详细解释。
1.1.1 比特币地址
比特币地址是一个由数字和字母组成的字符串,可以与任何想给你比特币的人分享。由公钥(一个同样由数字和字母组成的字符串)生成的比特币地址以数字“1”开头。下面是一个比特币地址的例子:
1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy
在交易中,比特币地址通常是以收款方的形式出现。如果把比特币交易比作一张支票,那么比特币地址就是收款人,也就是我们要写入“收款人”一栏的内容。一张支票的收款人可能是某个银行账户,也可能是某个公司、机构,甚至是现金支票。支票不需要指定一个特定的账户,而是可以用一个普通的名字作为收款人,这使得它成为一种相当灵活的支付工具。与此类似,比特币地址的使用也使比特币交易变得很灵活。比特币地址可以代表一对公钥和私钥的所有者,也可以代表其他东西。
比特币地址是由公钥经过单向的Hash函数生成的。用户通常所见到的比特币地址是经过“Base58Check”编码的,这种编码使用了58个字符(一种Base58数字系统)和校验码,提高了可读性、避免了歧义,并有效地防止了在地址转录和输入中产生错误。Base58Check编码也被用于比特币的其他地方,例如私钥、加密的密钥和脚本Hash中,用来提高可读性和录入的正确性。图1-1描述了如何从公钥生成比特币地址。
1.1.2 交易的本质
交易实质上就是包含一组输入列表和输出列表的数据结构,也可称之为转账记录,这其中就包括了交易金额、来源和收款人等信息,表1-1就是一个比特币交易的数据格式。
图1-1 比特币地址生成流程
下面以一个具体的例子来说明一个区块链上的交易构成。假设有一个带有一个交易及一个输出的交易A,其中的输入列表和输出列表如下所示:
Input:
Previous tx: f5d8ee39a430901c91a5917b9f2dc19d6d1a0e9cea205b009ca73dd04470b9a6
Index: 0
scriptSig: 304502206e21798a42fae0e854281abd38bacd1aeed3ee3738d9e1446618c4571d10
90db022100e2ac980643b0b82c0e88ffdfec6b64e3e6ba35e7ba5fdd7d5d6cc8d25c6b241501
Output:
Value: 5000000000
scriptPubKey: OP_DUP OP_HASH160 404371705fa9bd789a2fcd52d2c580b65d35549d
OP_EQUALVERIFY OP_CHECKSIG
上文表示,交易A的输入0从交易f5d8ee39a430901c91a5917b9f2dc19d6d1a0e9cea205b009ca73dd04470b9a6的0号输出中导入了50个比特币,然后该输出发送50个比特币到一个比特币地址的公钥Hash值(404371705fa9bd789a2fcd52d2c580b65d35549d,该公钥Hash值是十六进制表示,而非正常的base58表示)。
如果接收者想花掉这笔钱,那么他首先得创建自己的交易B,再引用该交易A的0号输出作为交易B的输入。
1.1.3 输入和输出
输入是对其他交易输出的引用,一个交易中通常列有多个输入。所有被引用的输出值相加,得出的总和值会在该交易A的输出中用到。Previous tx是以前交易的Hash值,Index是被引用交易的特定输出号,ScriptSig是一个脚本的前一半(脚本将在后文中详细讨论)。
脚本包含两个部分,一个签名和一个公钥,公钥属于交易输出的收款人,并且表明交易创建者允许收款人获得的输出金额;另一个部分是ECDSA签名,是通过对交易的Hash值进行ECDSA签名而得到的。签名和公钥一起,证明原地址的真正所有者创建了该支付交易。
输出中包含了发送比特币的指令,金额(Value)是以聪(Satoshi, 1BTC = 100?000?000聪)为单位的数值。ScriptPubKey是脚本的另一半(这点将在后文中详细讨论),可以有多个输出,它们共享了输入金额。一个交易中的每一个输出都只能被后来的交易当成输入引用一次。如果你不想丢币,那就需要把所有输入值的总和值发送到一个输出地址。如果输入是50BTC,但你仅想发送25BTC,那么比特币将创建2个25BTC的输出:一个发往目标地址,另一个则回到你的地址(称之为“找零”,详见1.1.5节)。在交易过程中,会产生一笔交易费,作为交易费支付的任何比特币都不能被赎回,生成这个区块的矿工将获得这笔交易费。
为了验证某个交易的输入已经被授权,可以收集被引用的输出中的所有金额。比特币体系使用了一个类似于Forth的脚本系统,其目的是验证从某地址发出的比特币是否真正属于该地址的拥有人,输入的scriptSig和被引用的输出scriptPubKey会按顺序运行。如果scriptPubKey返回真,则输入被授权,证明是地址拥有人发出了比特币。通过脚本系统,发送者可以创建非常复杂的发送条件,人们为了收到金额,首先必须满足这些条件。举个例子,可以创建一个能被任何人赎回而无需授权的输出,也可以创建一个需要10个不同签名的输入,或者无需公钥仅由密码赎回的输出。
1.1.4 交易类型
根据目标地址的不同,可以把交易分为如下几种类型。
(1)支付到公钥Hash
scriptPubKey: OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG
scriptSig:
一个比特币地址只是一个Hash值,因而发送者无法在scriptPubKey中提供完整的公钥,当要赎回比特币时,接收者需要同时提供签名scriptSig和公钥scriptPubKey,脚本系统会验证公钥的Hash值与scriptPubKey中的Hash值是否匹配,同时还会检查公钥和签名是否匹配。检查过程见4.2.5节。
(2)支付到脚本Hash
该类交易非常有意义,未来应该会在某些场合频繁使用。该类交易的接受地址不是通常意义上的地址,而是一个多签地址,以3开头。比如,三对公钥对可以生成一个多签地址。需要在生成过程中指定n of 3中的n,n的范围是[1, 3],若n = 1,则仅需要一个私钥签名即可花费该地址的币,若n = 3,则需要三把私钥依次签名才可以。
地址以3开头,可以实现多方管理资产,极大地提高安全性,也可以轻松实现基于比特币原生的三方交易担保支付。一个m-of-n的模式如下:
m {pubkey}...{pubkey} n OP_CHECKMULTISIG
其中,m和n需要满足:1 ≤ n ≤ 20,m ≤ n。
m和n可以是1 of 1、1 of 2、2 of 3等组合,通常选择n = 3。
1 of 3,最大程度私钥冗余。防丢私钥损失,3把私钥中任意一把即可签名发币,即使丢失2把也可以保障不受损失。
2 of 3,提高私钥冗余度的同时解决单点信任问题。3把私钥中的任意2把私钥可签名发币,对于三方不完全信任的情形,即中介交易,非常适用。
3 of 3,最大程度解决资金信任问题,无私钥冗余。必须3把私钥全部签名才能发币,适用于多方共同管理的重要资产,但是任何一方遗失私钥均会造成严重损失。
多签地址的交易构造、签名、发送过程与普通交易类似。
(3)挖矿交易
挖矿(coinbase)交易用于凭空产生比特币。挖矿交易只有一个输入,该输入有一个“coinbase”参数,没有scriptSig,“coinbase”中的数据可以是任意内容,它不会被使用。比特币把压缩的当前目标Hash值和任意精确度的“extraNonce”都存储在这儿,区块头中的Nonce每次一溢出,它们就会增长。extraNonce有助于扩大工作量证明函数的范围,矿工很容易修改Nonce(4字节)、时间戳和extraNonce(2~100字节)。
挖矿交易的输出金额在一段时间内是固定值,初始是50个比特币,每21万个区块后减半,目前已经经历了两次减半,因而是12.5个比特币。输出地址可以是任何地址,一般是矿工或矿池的比特币地址。
Nonce溢出是指在对一个块进行散列时,Nonce从0开始,每计算一次Hash都要增长一次,因而有可能会出现超过数值范围的情况,这时,extraNonce就要相应增长以存储Nonce。
1.1.5 找零地址
在实际的区块链交易中,假设A拥有一个比特币地址,里面包含着还没有花费过的10个比特币。B也有一个比特币地址,里面一分钱也没有。当A想向B支付10个比特币时,A地址里的未花费输出变为零,而B的则会变为10 BTC。如果A想支付的金额与所拥有的相同,自然不会存在需要找零钱的问题。不过当手头的金额比要支付的大时,找零自然也是天经地义的事情。
假设A的地址上有35个比特币(如图1-2所示),当A想向B支付8个比特币时,如图1-2所示,只需要使用包含着20个比特币的那一笔未消费支出,并设置好要支持的金额即可,剩下的12个比特币则会返回给A,以便A在将来可以继续使用。
图1-2 找零示意图
这样就有了一个找零机制,实际上,比特币在交易时会把消费时所用的地址(消费地址)的余额设置为零。当需要支付的金额小于可用余额时,在交易信息中必须告诉比特币网络零钱将要被发送至哪个地址,即“找零地址”。找零地址可能是也可能不是原先的发送地址。除此之外,发送地址所留下的剩余款项将由网络作为交易费支付给矿工。在上面的例子里,A可以选择将找回的零钱发送到一个新创建的找零地址上,或者将原先发送的地址设置为找零地址,并将零钱返回。虽然将发送地址作为找零地址对A而言是方便了管理,不过这也可能会造成A的隐私性降低,在一定程度上还可能会影响到B的匿名性。
根据设计,每一笔比特币交易将在一个称为“区块链”的全球性的公共总账上永久可见,这就意味着任何人随时都可以在上面进行跟踪查询。通过将某个比特币地址与其使用者关联起来,好事者都可以据此绘制关于这个人与他人之间的资金转移的关系图。但如果是将找回的零钱发送至一个新创建的地址,那么就可以让这种追踪变得更加困难。
要理解这一点,可以参考图1-3。假设从地址A发送比特币到地址B后,零钱返回地址为A,则区块链会揭示地址A向地址B支付了一笔钱。同样的道理,如果有两个或两个以上地址参与其中,任何涉及这个接收零钱的找零地址都会揭示A作为支付方的交易。假如某个控制着的任何接收地址或付款地址的人其身份是众所周知的,那么其他有过交易往来的各方的身份也有可能被推断出来。
现在想象一下,地址A发起付款到地址B,但此时将找零地址更改为新生成的地址C,如图1-4所示。如果没有进一步的信息,那么其他人所能知道的,只是有一个交易拆分了地址A的余额至地址B和C。而地址B或C的主人可能是也可能不是A。由于新地址C的加入,让整个交易的真相变得更加扑朔离迷:哪一个地址代表着被支付方,哪一个地址代表着找回的零钱呢?
![image](https://yqfile.alicdn.com/bb1a5efdd14dc82c6bb4e66283593697c331cf7f.png)
图1-3 两种找零方案 图1-4 重新生成找零地址
当所有各方都将零钱发送至新创建的地址时,要想将个人身份与地址相关联,就必须收集更多的信息,并耗费更多的资源。