前文泛谈了区块链和比特币的一些知识和概念,本章将从数据结构,脚本和语言上更加精准地描述比特币。
1 比特币的交易
比特币交易的过程其实就是在不停地创造区块,为了理解上的方便,我们先看一个简单模式的账簿,在这个账簿里,每一笔交易依次被添加到账簿里。
那我们如何使用这个账簿来创造一种货币呢?你可能会想到(也是许多人误认为的比特币使用的方式):建立一个以账户为核心的系统:
地址转换
如上图所示,Alice把币转给了自己,实际上比特币一个交易中输出的币,要么在另一个交易被完全消费掉,要么就一个都不被消费,不存在部分消费的情况。
有效验证
当一个新的交易被加入总账,只需要核查其引用的交易输出,在期间(一直到账本最新交易记录)有没有被其他的交易用作输入,就能判断其有效性。
资金合并
如果某人的资金源自多笔交易的输出,要想合并起来花掉,只需要发起一个交易,交易里有多个输入和一个输出,输出地址是他自己的地址即可。
共同支付
与资金和并唯一的不同在于多个输入引用的“上一笔交易”的输出地址不同,因此,需要多个签名。
交易语法
比特币交易在网络上传输的数据结构都是一串字符。可读性非常的好。
一个真实的比特币交易程序段
如上图所示,一个比特币分成三部分:元数据,一系列的输入和一系列的输出
- 元数据:存放输入输出的数量等信息,还有这笔交易的哈希值,也就是该交易的ID,还有一个“锁定时间”容后再谈
- 输入:包括之前交易的哈希值和输出索引以及签名(必须有签名证明我们有资格支配这笔比特币)
- 输出:所有输出之和必须小于等于输入之和,差额就是前文提到的交易费
最后还有一长串字符串类似接收地址,实际上,每个输出都要与一个特定的公钥(地址/身份)对应,该字符串还有一部分像指令的东西,它其实就是比特币的脚本。
2 比特币的脚本
一篇有助理解的比特币脚本概述
我们知道,比特币交易需要通过某人的签名来获取他在前一笔交易中获得的资金(实质上就是输出)。
那么,检验交易的合法性的过程是怎么完成的呢?
秘密在于,交易的输入和输出包括了脚本(而不是单纯的签名)。为了检验一个交易的合法性,我们把交易的输入脚本和上一笔交易的输出脚本串联起来,只有被成功的执行完毕之后才可以获取资金。
这两个脚本,一个是输出脚本(scriptPubKey),一个是输入脚本(scriptSig)。
比特币脚本语言是基于堆栈式的,每个指令只被执行一次。
最常见的输出脚本 输入脚本和输出脚本串联实例
常见比特币脚本指令
脚本很简单,总共有两类指令:数据指令和工作码指令,其中数据指令是把数据放到栈顶,而工作码指令则通常是用栈顶元素作为输入,产生一个输出。
这里的签名究竟是对什么的签名?实际上,比特币中只能对一个事情进行签名——就是整个交易。最后,完成了所有的指令,脚本的输出就是一个true,表示这个交易是正当合法的。
理论上来讲,我们可以通过脚本为比特币支付设定更多条件,但实际上绝大多数比特币使用的脚本都非常基础就如前文实例那样。当然,实际中也会使用一些其他指令,比如支付给脚本的哈希值(Pay-to-script-hash,简称P2SH)。
销毁证明
销毁证明(proof of burn)脚本,用于销毁比特币。
如果交易代码的运行结果是将比特币转到“销毁证明”脚本,那么这笔比特币将被销毁。
实际应用中主要是用来引导客户使用其他数字货币系统。
3 比特币脚本的应用
第三方支付交易
想要去Bob的网站购物。Alice想货到付款,Bob想款到付货,那么就要用托管交易来完成这个看似矛盾的过程,引入第三方进行托管。假设有第三方Judy,那么需要使用MULTISIG(多重签名)来完成这个交易。三个人中,只要有两个人对交易进行签名,那么交易就是有效的。假设ALice和Bob都是诚实的,那么Bob会给Alice发货,Alice收到Bob的货物后,会进行签收。假设出现了争端,比如Alice不满意货物,货物运输过程中丢失了,或者Bob不想让Alice退货等,那么Alice和Bob只会有一个人同意。这时候,需要让Judy来裁决到底把钱支付给Alice还是Bob。因此,托管方只有当Alice和Bob出现纠纷的时候才使用。
绿色地址
假设Alice向Bob支付的时候,Bob不在线,所以他无法通过查看区块链的更新来确认转账是否完成。一般来说,一个交易需要6次确认,才能确信它确实被加到区块链中了,但这需要一个小时。但是如果Alice仅仅只是在Bob店里买一包咪咪,这显然不可接受。在这里,我们需要引入“银行”的概念。这种类型的支付不通过区块链进行,而是Alice向“银行”支付一定数额的比特币,然后银行把相应的比特币给Bob,银行保证这里没有双重支付攻击。注意,在这里的银行系统的保证就不是比特币系统的保证了,而是现实世界中的一种信任保证。为了系统能正常运作,Bob也需要信任银行。引入这种机制是为了快速支付,但是同样存在风险。
高效小额支付
高效的小型支付。假设Alice使用了Bob的网络服务,而且是计时收费的,就像我们打电话需要移动公司提供信号一样。我们需要根据时间进行计费。计费方式存在一种问题:假设Alice每一分钟就生成一笔账单,而且账单一直累加;如果创造了太多笔交易,那么交易费会增多,这样对Alice不利。
如果我们能够把每一分钟的小账单组合成一个大账单,可以节约很大一笔交易费。我们建立一个MULTISIG交易,这笔账单需要Alice和Bob共同签名,以获得相应数目的比特币。这样,每一分钟,Alice就签署一笔交易,给Bob应有的支付,其余的发给自己。注意,这些交易仅仅与Alice签名,Bob未签名,它们不会被发送到区块链上。当Alice使用完Bob的服务后,向Bob发出停止提供服务的请求,并且Alice停止签署账单。之后,Bob停止服务,并且他要收取相应的费用,同时把剩余的比特币给Alice。但是,上面的那些交易不会被系统所接受,因为只有Alice自己进行了签名,而Bob没有签名。
但是这种方式有很多大量的潜在双重支付,它不像绿色地址那样对双重支付有很强的防范性。实际上,Bob可以仅签署最后一个交易,这样就不会有任何双重支付的潜在企图了。
但是,上面那种方式有一个缺陷,如果Bob永远不签署最后一个交易呢?那么这笔交易会被一直托管,而且Alice无法得到她应得的剩余的比特币。为了解决这个问题,引入Lock Time机制。
锁定时间
为了防止Bob不签署的行为,在小额支付协议开始之前,Alice和Bob先签订一个交易,约定向Alice退还所有的比特币,但是这个“退款”行为被上了锁,直到锁定时间到了为止。Alice发起MULTISIG交易把比特币转入第三方托管之后,在向网络宣布这笔交易之前,她会从Bob那里要求这个退款交易。这样如果过了t时间Bob还没有在最后一个交易上签名的话,她可以通过这笔交易收回所有的比特币。
智能合约
所谓智能合约(smart contracts),就是那些不同于需要通过法律或者仲裁机构来保护执行的普通合约,智能合约是比特币系统里可以用技术手段来强制执行的合约,上述的应用场景其实就是运用了不同智能合约的实例。
4 比特币的区块
实际的区块链不是以实际交易数目作为区块数量的,这样会大幅度降低系统接受交易的速率。区块链把大量的交易放到一个区块上,这样可以大幅提高速率。
区块链把两个基于哈希值的数据结构结合起来:第一个数据结构是区块的哈希链,每一个区块都有一个区块头部,里面有一个哈希指针指向上一个区块。第二个数据结构是梅克尔树,它把区块内的所有交易组织起来。
image
所有比特币区块的信息
此外,区块头部还包含挖矿谜题相关的信息,一个nonce,一个时间戳和一个点数(表示找到这个区块的难度)。区块头部是挖矿过程中唯一哈希值化的,所以要验证一个区块的链,只要检查区块头部即可。在区块头部唯一的交易数据是交易树的树根。
每个区块的梅克尔树上都有一个有意思的交易:“coinbase”交易。这是创造新比特币的一种交易,与一般交易信息有一下不同:
- 一般只有一个输入和输出
- 输入值是空的hash指针。
- 输出值比25BTC略多,输出值由2部分组成,一个是挖矿的奖励,另一个是交易费
- 还有一个“coinbase”参数,矿工可以在这里放置任何值
5 比特币网络
比特币网络是一个点对点的网络,沿用了很多已有的点对点网络的理念。由于随时有新的节点进入,旧的节点离开,比特币网络实际上一直在变化,并不存在一个确定的地理学意义上的拓扑结构。启动一个新节点时,会先向种子节点(通过多种方式查找)发送一个简单的消息,询问是否知道其他节点,并重复多次最终成为一个完全合格的节点。
加入网络自然是为了维护区块链,当我们发起一个交易时,我们想让整个网络都知道。这是通过一个“泛洪(flooding)”算法完成的。首先将交易告知邻近节点,这些节点会进行一系列检验,决定是否接受并传播这笔交易。如果检验通过,这些节点会把这笔交易信息继续传播给其邻近的节点。当节点接收到一个交易信息时,会把交易放入一个交易池(交易池中的交易还没被打包进区块链),如果交易已经存在,则不会把它传播出去,这样确保了泛洪会自动终结。由于每个交易都有独一无二的哈希值,所以节点可以方便地查询交易是否存在自己的交易池。
节点校验交易的步骤如下:
- 交易验证:利用脚本检验结果是否为真
- 检查是否有双重支付
- 检查交易是否已被接收
- 节点只会接收和传递白名单上的标准脚本
由于网络中存在延迟,因此结点的交易池会有一些差异,这对双重支付有较大的影响。假设Alice同时向Bob和Carlo支付了比特币,也就是双重支付。一些结点会先接收到支付给Bob的,另一些先接收到给Carlo的。结点会把先接收到的交易信息加入到交易池中(假设检验通过了),如果结点发现有双重支付的企图,那么结点会丢掉后来的交易信息。因此,结点会对把哪个交易信息加入区块链产生歧义,称之为“Race Condition”。
但这对比特币来说不是问题:打包下一个区块的矿工会打破这个僵局,如果其中一条交易进入区块,其他的节点则会把另一条交易剔除出交易池,因为这是一个双重支付。
至于区块的传播,与上述交易的传播类似,也受“Race Condition”的限制,如果两个有效的区块同时被挖到,只有其中一个区块可以进入长期共识链,具体哪个区块则取决于其他节点在哪个区块上扩展区块链,另一个区块则被丢弃。
校验一个区块更为复杂,除了确认头部,确定哈希值是可接受范围内,还必须确认区块里的每个交易。最后,一个节点往外传播的区块必须是最长的一条区块链上新加入的区块(“最长”取决于当前的认知)
区块传播时间完全有效节点:必须永久在线以接收所有的交易数据,维护所有未被消费的交易输出。
轻量节点(简单付款验证,SPV,Simple Payment Verification):只存储它们关心的,需要检验的部分交易。依赖全节点区验证其他所有交易。
硬分叉:由于修订协议导致所有节点根据运行协议版本的不同去扩展两条不同的区块链,且无法合并。
软分叉:给有效验证规则添加限制。这样,旧的规则会接受所有的原来有效区块,而新的规则会拒绝一部分。这样,可以避免hard fork中造成的区块链永远分裂。这样的风险在于,旧的结点会发现自己的一些区块被拒绝,即使它们不知道原因。这样,它们会逐步更新自己的软件。
网友评论