购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.2 交易的本质与执行

我们来看一下区块链1.0(比特币时代)在维基百科上的官方定义:区块链是一个基于比特币协议的、不需要许可的分布式数据库,它维护了一个持续增长的不可被篡改和修改的数据记录列表,即使对于数据库节点的运营者们也是如此。事实上,区块链1.0(尤指比特币)就是用区块和链条的方式将交易记录组织起来的不可篡改的分布式数据库,那么“交易”指的是什么呢?下文将对此进行介绍。

2.2.1 比特币地址

比特币地址是一个由数字和字母组成的字符串,可以与任何想给你比特币的人分享。由公钥(一个同样由数字和字母组成的字符串)生成的比特币地址以数字“1”开头。下面是一个比特币地址的例子:

1J7mdg5rbQyUHENYdx39WVWK7fsLpEoXZy

在交易中,比特币地址通常作为收款方出现。如果把比特币交易比作一张支票,比特币地址就是收款人,也就是要写入收款人一栏的内容。一张支票的收款人可能是某个银行账户,也可能是某个公司、机构。支票不需要指定一个特定的账户,而是用一个普通的名字作为收款人,这使它成为一种相当灵活的支付工具。与此类似,比特币地址的使用也使比特币交易变得很灵活。比特币地址可以代表一对公钥和私钥的所有者,也可以代表其他事物。

比特币地址与公钥不同。比特币地址是由公钥经过单向的哈希函数生成的。通常用户见到的比特币地址是经过Base58Check编码的(第3章会介绍这种编码算法),这种编码使用了58个字符(一种Base58数字系统)和校验码,提高了可读性,避免了歧义并有效防止了在地址转录和输入中产生的错误。Base58Check编码也被用于比特币的其他地方,例如比特币地址、私钥、加密的密钥和脚本哈希中,用来提高可读性和录入的正确性。图2-6和图2-7所示为从公钥生成比特币地址的流程。

图2-6 从公钥生成比特币地址的流程(概览)

图2-7 比特币地址的生成过程(细节)

2.2.2 交易的本质

交易本质上就是带有一组输入列表和输出列表的数据结构,其中就包括了交易的额度及来源,表2-1所示为比特币交易的数据格式。

表2-1 比特币交易数据格式

只有一个输入及一个输出的简单交易如图2-8所示。

图2-8 一个简单的交易

这个交易的0号输出中导入了50个比特币,输出则发送了50个比特币到一个比特币地址(这里用十六进制表示:404371705fa9bd789a2fcd52d2c580b65d35549d,而非用正常的Base58表示)。如果接收者想花掉这些钱,他需要首先创建自己的交易B,再引用交易A的0号输出作为B交易的输入。

2.2.3 bitcoin的脚本系统

1. 脚本系统

比特币在交易中使用脚本系统,与Forth(一种编译语言)一样,脚本是简单的、基于堆栈的,并且从左向右处理,它特意被设计成非图灵完整,没有loop语句。

一个脚本本质上是众多指令的列表,这些指令记录在每个交易中,交易的接收者想花掉发送给他的比特币,这些指令就是描述接收者如何获得这些比特币的。一个典型的发送比特币到目标地址D的脚本要求接收者提供以下两个条件,才能花掉发给他的比特币:

(1)一个公钥,当进行哈希生成比特币地址时,生成的地址是嵌入在脚本中的目标地址D。

(2)一个签名,证明接收者保存与上述公钥相对应的私钥。

脚本可以灵活改变花掉比特币的条件,举个例子,脚本系统可能会同时要求两个私钥、几个私钥或无须任何私钥等。

如果联合脚本中未导致失败并且堆栈顶元素为真(非零),表明交易有效。原先发送币的一方控制脚本运行,以便比特币在下一个交易中使用。想花掉币的另一方必须把以前记录的运行为真的脚本放到输入区。

堆栈保存着字节向量,当用作数字时,字节向量被解释成小尾序的变长整数,最重要的位决定整数的正负号。这样0x81代表−1,0x80是0的另外一种表示方式(称之为负0)。正0用一个NULL长度向量表示。字节向量可以解析为布尔值,这里False表示0,True表示非0。

我们先讨论单输入单输出的比特币交易,因为这样描述起来更方便且不影响对脚本的理解,以下面一个交易哈希值9c50cee8d50e273100987bb12ec46208cb04a1d5b68c9bea84fd4a04854b5eb1为例介绍。

这是一个单输入单输出交易,我们要关注的数据如图2-9所示。

图2-9 单输入单输出交易

假设Alice是转账发送者,Bob是接受者。那么输入交易表明Alice要动用的比特币的来源,输出交易表明Alice要转账的数额和转账对象——Bob。

2. 交易是如何通过脚本系统完成的

Alice转账给Bob的时候,输出交易中给出了Bob的钱包地址(等价于公钥哈希);当Bob想要转账给Carol的时候,他要证明自己拥有这个钱包地址对应的私钥,所以在输入交易中给出了自己的公钥以及使用私钥对交易的签名。看下面的实例:

交易a:

9c50cee8d50e273100987bb12ec46208cb04a1d5b68c9bea84fd4a04854b5eb1

交易b:

62fadb313b74854a818de4b4c0dc2e2049282b28ec88091a9497321203fb016e

交易b中有一个输入交易引用了交易a的输出交易,它们的脚本是一对题与解。题:交易a的输出脚本,若干个脚本指令和转账接收方的公钥哈希。

    OP_DUPOP_HASH160be10f0a78f5ac63e8746f7f2e62a5663eed05788OP_EQUALVERI
FY OP_CHECKSIG

解:交易b的输入脚本是一串字符,这么一长串字符只包含两个元素:签名和公钥。

    3046022100ba1427639c9f67f2ca1088d0140318a98cb1e84f604dc90ae00ed
7a5f9c61cab02210094233d018f2f014a5864c9e0795f13735780cafd51b950f50353
4a6af246aca30103a63ab88e75116b313c6de384496328df2656156b8ac48c75505cd2
0a4890f5ab

下面来看这两段脚本是如何执行,来完成解题过程的。

首先执行的是输入脚本。因为脚本是从左向右执行的,那么先入栈的是签名,随后是公钥,接着执行的是输出脚本。从左向右执行,第一个指令是OP_DUP,复制栈顶元素,如图2-10所示。

图2-10 复制栈顶元素

OP_HASH160计算栈顶元素哈希并得到pubkeyhash,如图2-11所示。

图2-11 栈顶元素HASH160

如图2-12所示,将输出脚本中的公钥哈希入栈,为了和前面计算得到的哈希区别,称它为pubkeyhash'。

图2-12 公钥哈希入栈

如图2-13所示,OP_EQUALVERIFY检查栈顶前两元素是否相等,如果相等继续执行;否则,中断执行,返回失败。

图2-13 检查哈希值是否相等

如图2-14所示,OP_CHECKSIG使用栈顶前两元素执行签名校验操作,如果相等,返回成功;否则,返回失败。

图2-14 返回结果

这样一串指令执行下来,就可以验证这道数学题是否做对了,也就验明了想要花费钱包地址中比特币的人是否拥有对应的私钥。上面的执行过程可以在脚本模拟器中执行,能够看到每步执行的状态。 su/nIK0rS73E4KpPcDvCrsw8jqfqf+xoRnPrnagCD3QNDUV7n805OrCBCUY/H1yv

点击中间区域
呼出菜单
上一章
目录
下一章
×