比特币的运行机制
1. 比特币的交易
每个交易中都会有一个输入值和输出值。输入值可以看成是将被消费掉的币(这些币是前一个交易创造出来的),把输出看成是在本次交易中创造出来的币。铸造新币时,只会创造新币,而不会消费旧币(就像财奴币里的造币,只有输出,没有输入)。每笔交易都有一个独一无二的ID。每笔交易中可能有多个输出,输出的索引从0开始。
如下图,交易1是造币,没有输出,也没有签名;第二笔交易的输入是第一笔交易的第0个输出,输出17个币给鲍勃,剩下8个币留给自己,并签名;多输入多输出也类似。
地址转换: 由于一个交易中输出的币,要么在另一个交易中被完全消费掉,要么一个都不被消费,不存在只消费部分的情况。如交易1中爱丽丝获得了25个币,交易二中她必须把这25个币全部消费掉,所以她要把剩下的8个币转给自己,这8个币可以转到另外一个地址(不同于交易1中获得25个币的地址),前提是这个地址为爱丽丝所有,这就是地址转换。
有效验证: 当新的交易被加入总帐,我们要验证这25个币之前有没有被花费掉。我们只需要从爱丽丝所引用的交易开始,一直核查到账本上最新记录的交易为止即可,不需要从账本建立之初的交易开始核查。
资金合并: 只需要发起一个交易,交易里有两个输入和一个输出,输出的地址是他自己的地址。
** 共同支付:** 两个人共同发起一个交易,交易里有两个输入和一个输出,这两个输入所引用的“上一笔交易”的输出地址不能相同,这笔交易需要这两个人的签名。
交易语法:
- 元数据:包含这笔交易的规模,输入的数量,输出的数量,还有这笔交易的哈希值和锁定时间(用于未来支付)。
- 输入:包含之前那笔交易的哈希值,使其成为指向那个特定交易的哈希指针。另外还包含了之前交易输出的索引和签名。
- 输出:包括输出的金额和公钥脚本。
2.比特币的脚本
最常见的比特币交易,就是通过某人的签名去取得他前一笔交易中获得的资金。这种情况下,我们希望输出包含这样的信息:凭借地址X的所有者的签名,才可以获得这笔资金。我们知道地址其实就是一个公钥的哈希值,所以仅仅说地址X并没有告诉我们公钥在哪里,也没有一个给我们一个检查签名的方法。所以交易输出必须这样描述:凭借哈希值为X的公钥,以及这个公钥所有者的签名,才可以获得这笔资金。如图为最常见的比特币脚本。
同时,交易的输入也包含了脚本。为了确认一笔交易正确地获取了上一笔交易所输出的资金,我们必须把交易的输入脚本和上一笔交易的输出脚本串联起来,这个串联脚本必须被成功地执行后才可以获取资金。输出脚本指定了一个公钥(或是公钥哈希值的地址),输入脚本指定了一个对应公钥的签名。
指令的分类:
- 数据指令:把数据推到堆栈的最上面
- 工作指令:用堆栈顶部的数据作为输入值,用来计算一个函数
执行过程:前两条指令是数据指令,分别是输入脚本中的签名和用来验证签名的公钥,直接把他们堆到堆栈最上面。接着执行复制指令OP_DUP,将堆栈最上层的公钥复制,并置于堆栈最上层;下一个指令是OP_HASH160,该指令取得最上层的数据,并计算其哈希值,然后将结果再堆到堆栈最上层。接下来,将发送者指定的公钥的哈希值堆到堆栈最上面。这时候,执行EQUALVERIFY命令,检查堆栈顶部的两个数值是否相等。假设其相等,也就代表接受者使用的是正确的公钥。这条指令会移除堆栈顶部的两条数据。到这里我们已经证实接收者使用的公钥确实就是交易里指定的公钥,但我们必须证实这个签名是真的。这时,使用OP_CHECKSIG来检查签名。假如没有碰到任何差错的话,这个脚本的输出就是TRUE。
补充:一些比特币脚本
销毁证明:
销毁证明(proof of burn)脚本,用于销毁比特币。
如果交易代码的运行结果是将比特币转到“销毁证明”脚本,那么这笔比特币将被销毁。
实际应用中主要是用来引导客户使用其他数字货币系统。
3. 比特币脚本的应用
第三方支付交易
如果爱丽丝用比特币向鲍勃买东西,爱丽丝想货到付款,鲍勃想见款发货,一个好的办法就是使用第三方支付交易。第三方支付交易可以用“MULTISIG”(多重签名)来轻易实现。引入一个第三方仲裁员,爱丽丝发起一个多重签名的交易,并规定:三个人中有两个人签名之后,资金才能被取走。这个交易纳入区块链后,资金被第三方监管。如果鲍勃并未发货或其他原因,爱丽丝会和第三方仲裁员一起签名,将比特币退还给爱丽丝;如果爱丽丝收货后并不签名确认,第三方仲裁员可以和鲍勃以七千名,完成资金的实际支付。
绿色地址
假如爱丽丝要转账给鲍勃,而鲍勃不在线(或者鲍勃在线但没有时间),所以他无法通过查看区块链的更新来确认转账是否完成。一般来说,一个交易需要获得6次确认,我们才能确信它已经确实被加到区块链中。如果爱丽丝只是在鲍勃的店里买一个热狗,这么长时间才确认显然不可接受。所以,比特币采用了第三方银行的做法。爱丽丝将比特币转给银行,银行再从绿色地址转给鲍勃,并且银行保证不会双重支付,而鲍勃也相信这一点。所以,当鲍勃看到银行的签名时就可以确认自己迟早会收到这些比特币了。现实中,这存在很大的风险,很多提供绿色地址的机构都因为失信问题倒闭了。
高效小额支付
假设鲍勃是爱丽丝的手机流量提供商,根据爱丽丝每分钟使用的流量计费。但每分钟支付一次是不现实的。对此,我们可以把每分钟的费用累积起来,最后一次性支付。爱丽丝先发起一个MULTISIG交易,把可能花费的最大金额转到MULTISIG地址,这个交易需要爱丽丝和鲍勃两个人的交易才能生效。之后每隔一分钟,爱丽丝就签名一次,向鲍勃支付这分钟所产生的费用,剩下的钱转给自己。注意,这里没有鲍勃的签名,所以交易并不会被纳入区块链。最后,爱丽丝用完服务会告诉鲍勃“我用好了,你可以切断我的服务了”,并在最后一个交易签名。鲍勃收到后切断服务,也在最后一个交易上签名,这个交易将会被纳入区块链。事实上,所有的这些交易都是双重支付,但只要鲍勃只在最后一个交易上签名,双重支付就不会发生。但还存在一个问题,如果鲍勃不在最后一个交易上签名,那些比特币就会一直存在第三方托管地址,爱丽丝无法拿回剩下的比特币。为此,我们引入锁定时间(lock_time)。
锁定时间
为了防止Bob不签署的行为,在小额支付协议开始之前,Alice和Bob先签订一个交易,约定向Alice退还所有的比特币,但是这个“退款”行为被上了锁,直到锁定时间到了为止。Alice发起MULTISIG交易把比特币转入第三方托管之后,在向网络宣布这笔交易之前,她会从Bob那里要求这个退款交易。这样如果过了t时间Bob还没有在最后一个交易上签名的话,她可以通过这笔交易收回所有的比特币
智能合约
所谓智能合约(smart contracts),就是那些不同于需要通过法律或者仲裁机构来保护执行的普通合约,智能合约是比特币系统里可以用技术手段来强制执行的合约,上述的应用场景其实就是运用了不同智能合约的实例。
4. 比特币的区块
区块链非常聪明的将两个基于哈希值的数据结构结合起来:第一个数据结构是区块的哈希链,每一个区块都有一个区块头部,里面有一个哈希指针指向上一个区块。第二个是梅克尔树结构,以树状结构把区块类所有交易的哈希值进行排列存储。
区块头部还包含了挖矿谜题的相关信息,区块头部的哈希函数必须以一大堆零开头才有效。此外,区块头部还要包含一个矿工可以修改的“临时随机数”、一个时间戳和一个点数(用来表示找到这个区块的难度)。
每个区块的梅克尔树都一个特殊的交易,叫做币基交易:
- 它永远只有一个单一的输入和单一的输出
- 这个交易并不消费之前交易输出的比特币
- 这个输出由两部分组成:一个是区块奖励,一个是交易费
- 有一个币基参数,矿工可以放任何值进去(在解不出哈希谜题的时候可以修改可以尝试不同的参数)
5. 比特币网络
对于节点的下线,只要有一个节点有3个小时没有音讯,就会慢慢地被其他节点忘记。
当你启动一个节点的时候先向一个你知道的节点发送一个简单消息。这个节点就是你的种子节点,然后你就会问你的种子节点是不是还知道其他什么节点?重复这个过程,最后你可以选择和哪些节点相连。
节点收到一个新交易信息时:
- 首先,验证交易在当前的区块链是有效的,节点会针对每个前序交易的输出运行核验脚本,确保脚本的返回值都为真。
- 第二,检查是否有双重支付
- 第三,节点会检查这笔交易信息是不是已经被本节点接收过
- 第四,节点只会接收和传递在白名单上的标准脚本
竞态条件: 由于网络传递有延迟,如果发生双重支付,每个节点只会接收两个交易中最先到达的一个,结果就是众多的节点会对“哪一个交易应该被纳入区块链”发生分歧。这个取决于打包下一个区块的矿工,他会决定哪个交易最终被打包进这个区块。
零验证交易和费用替代策略:
零验证交易不是用来防止重复支付的,但由于矿工的缺省行为是把先接收到的交易放入交易池,这样,在零验证交易里就很难实现重复支付。
自从2013年,矿工的缺省行为变成了“费用替代策略”,即节点在遇到有冲突的交易时,会把交易手续费更高的交易放进自己的交易池。但在是否可以真正阻止重复支付的问题上收到了很大的争议。
最近,比特币采用了“有选择权”的费用替代策略的做法,也就是交易可以标记自己是否适合使用费用替代策略。
6.比特币的限制与优化
比特币的限制在于:
- 每个区块大小限定在1MB,每个交易大约是250字节,所以每块最多容纳4000个交易,平均10分钟产生一个区块,所以每分钟只能处理7个交易。(做一下比较:Visa平均每秒处理2000笔交易,峰值每秒处理10000笔交易;PayPal处理能力较弱,但峰值每秒也能处理100笔交易)
- 另一个限制是比特币所使用的密码算法。
硬分叉: 通过修订协议引入新的特性,可能会使前一版本的协议失效。即运行新版本协议的节点认定为有效的区块,会被运行旧版本协议的节点认定为无效。所以网络上的所有节点会根据其运行的协议版本去扩展两条不同的链。
软分叉: 加入新的特性,让现有的校验规则更加严格。那样老的节点依然会接收所有的区块,而新的节点会拒绝一些。