Bloodline's Blog Notes and thoughts from Bloodline

区块链学习笔记(三)——比特币机制的原理

Comments

本文是对 coursera 的 Bitcoin and Cryptocurrency Technologies课程笔记。

比特币共识机制保证了:

  • 只增账本。

  • 去中心化的协议。

  • 矿工来验证交易。

3.1 比特币交易

mechanics-01

上图中,每个 block 只有一笔交易。交易 1 为造币交易,没有输入,也不需要签名。交易 2 中,Alice 转给 Bob 17 个币,转给自己 8 个币,并且签名。

地址转换 (change address)

对于第 2 笔交易,25 个币完全被消耗,17 个给 Bob,多余的 8 个币转给自己的另外的地址,这个过程为地址转换。

有效性验证 (transaction valid)

对于第 4 笔交易,如何验证其合法性?需要验证其输入,来自于第 2 笔交易的第 2 个输出,8 个币转给 Alice(自己)。

验证新的交易是否有足够的币支付,只需要通过哈希指针向后查找。

合并资金 (merge value)

新建交易,交易中有两个输入(分别是之前交易中收到的 17 个币和 2 个币),一个输出(19 个币都指向自己的地址)这样就可以将其合并起来了。

共同支付 (join payment)

mechanics-02

上图中的交易 4 有两个输入(分别是之前交易中指向 Carol 的 6 个币和指向 Bob 的 2 个币),一个输出(指向 David),最后需要 Carol 和 Bob 两个签名。

比特币真实交易数据

mechanics-03

  • 元数据 (metadata)

存放内部信息。此交易哈希值(hash),交易的规模(size),输入(vin_sz)和输出数量(vout_sz),以及锁定时间(lock_time)。

  • 输入列表 (inputs)

列表中的每个输入包含:前一个交易的输出(prev_out),它包括两个属性,之前那笔交易的哈希值(hash),以及那笔交易中当前输出的索引(n);输入还包含一个签名(signature),用来证明支配此输出的签名。

  • 输出列表 (output)

列表中的每个输出包含:币值(value),scriptPubKey 中有一个的公钥和一个比特币脚本。

3.2 比特币脚本 (Bitcoin scripts)

堆栈式脚本语言,非图灵完备,无函数功能。

执行过程

mechanics-04

  • 前两条为数据指令(data instruction),直接会被至于栈顶

  • OP_DUP 指令,将最上层的 <pubKey> 复制,并置于栈顶

  • OP_HASH160指令对栈顶的指令计算哈希值 <pubKeyHash>,并将结果替换当前栈顶的 <pubKey>

  • 向栈顶推送数据:此交易发送方指定公钥的哈希值,用来生成签名来兑换比特币。

  • 栈顶两个数据,发送者指定的公钥哈希值,接收者获取比特币时声明的公钥的哈希值。执行 EQUALVERIFY 指令,来检查顶部两个数值是否相等,如果不相等则抛出错误并停止执行。假如值相等,说明接收者使用的公钥正确。指令会移除栈顶的两条数据。

  • 现在栈顶只剩 sigpubKey。接下来使用 OP_CHECKSIG 指令检查签名的有效性,同时移除这两条数据。签名实际上是对整个交易签名,所以指令实际上是验证整个交易是否有效。

比特币脚本有 256 个指令(instructions),15 个目前不可用,75 个被保留(将来可能会扩展)。执行成功则交易有效,执行出错则交易失败。

还有一个多重签名验证指令 OP_CHECKMULTISIG : 指令要求指定 n 个公钥和一个参数 t。指令执行成功的条件是,在 n 个公钥中至少有 t 个签名是有效的。

脚本的意思是,只有拥有此比特币的人,使用其私钥创建 <sig>,结合 <pubKey>,才能使得脚本运行通过,也就是才能使用这笔比特币。

一个真实的比特币交易细节:Bitcoin - Transaction Details

比特币重要 bug:OP_CHECKMULTISIG 指令会从对战中弹出一个额外的数值,需要在使用额外的虚拟变量存储,然后忽略掉。修复的成本很高,所以一直存在。

销毁证明 (proof of burn)

销毁证明用于证明比特币已经被消耗,不能再用于支付。使用 OP_RETURN 来实现,它会以抛出错误的形式结束脚本,所有指令都不会执行。同时会将 40 字节的数据嵌入到交易中。

有两个用处:

  • 销毁少量的币和一定的交易费(0.0001 BTC)写入某些信息或留言

  • 存在性证明 proof of existence,数字产权的验证 Monegraph,智能资产协议 Mastercoin 等。

脚本哈希值 (hash of the script)

为了简化多重支付地址 MULTISIG 的情况,比特币加入了脚本哈希值(P2SH)。交易的发送者不需要发送复杂脚本,只需要发送包含脚本哈希值的简单脚本。简单脚本会读取栈顶的值,验证是否与兑换脚本的哈希值相等。验证之后再将兑换脚本反序列化,并作为脚本执行,此时会进行签名验证。

解决两个问题:

  • 使得发送方支付更简单。收款方只需要通知发送方一个哈希地址即可。

  • 由于矿工必须查找没有被消费掉的输出脚本,所以 P2SH 通过将脚本简化为哈希值,提升了矿工的效率。

3.3 比特币脚本应用 (Applications of Bitcoin Scripts)

托管交易 (escrow transaction)

为避免在线交易过程中出现分歧,可以加入第三方当做裁判,由 MULTISIG 实现。比如三个人中,只需其中一个人和裁判签名,交易即为有效。

绿色地址 (green addresses)

接收方不在线,或者在线但没有时间,无法通过查看区块链的更新来确认交易。比特币使用第三方银行,银行从账户扣钱,然后从银行的绿色地址转账给接收者。接收者相信银行的话,则可以确信迟早会收到比特币。实际上,由于信任银行或交易所也有风险,这种交易并不靠谱。

高效小额支付 (efficient micro-payments)

例如,Bob 是 Alice 的手机通讯供应商,每分钟按照流量计费,但是每分钟支付一次不仅浪费算力,交易手续费也很高。于是引入合并支付,也就是通过连续的小额支付。在 Alice 开始打电话时,会发起一个可能花费的最大金额的 MULTISIG 交易。这个交易需要 Alice 和 Bob 双方的签名才能生效。随着 Alice 打电话的过程,每隔一分钟签名一次,挂机时,通知 Bob 切断服务,同时 Bob 也会进行签名并将交易发布到区块链上。

mechanics-05

在技术上,所有这些交易都是双重支付(double spends)。但实际中,Bob 只有收到最后一个 Alice 签名时,才会进行签名,所以网络中并不会真实存在双重支付。

锁定时间 (Lock Time)

上述情景问题,如果 Bob 一直不签字,则这些比特币将一直存在于第三方。锁定时间可以解决:在发起交易时,可以添加 lock_time 字段,在 t 时间后,Bob 还没有签字的话,将会发生退款。lock_time 实际告诉矿工,在 t 时间之后这笔交易才能生效,并发布出去。

智能合约 (Smart Contract)

智能合约可以利用技术手段进行交易处理和合约保存。具体涉及到区块链 2.0 技术等,可参考这里

步骤:

  1. 多方用户共同参与制定一份智能合约;

  2. 合约通过 P2P 网络扩散并存入区块链;

  3. 区块链构建的智能合约自动执行。

3.4 比特币区块 (Bitcoin blocks)

mechanics-06

比特币区块包含两种数据结构。上面的部分是基于哈希的区块链。每个区块的区块头部都包含一个哈希指针指向上一个区块。第二种数据结构是梅克尔树(Merkle tree),它将包含的交易用高效的方式(log(n))组织起来。

mechanics-07

区块头部除了以很多 0 开头的哈希值,还有时间戳、随机数以及一个标记挖掘难度的点数。区块头部是挖矿过程中唯一进行哈希计算的,因此要验证区块,只需要查看区块头部即可。区块头部唯一与交易有关的是梅克尔树的树根 mrkl_root

mechanics-08

特别的是,梅克尔树的交易中有一个特殊的交易,币基交易(Coinbase transaction),这个交易创造了比特币。

mechanics-09

特点:

  1. 只有一个单一的输入和单一的输出。

  2. 输出的值为 25 个币多一点,25 个币是矿工的收入,另一小部分是交易手续费。

  3. 此交易不消耗之前的比特币,因此,prev_out 参数为空指针。

  4. Coinbase的参数由矿工任意指定。

创世区块被创造的时候,该参数引用了伦敦《泰晤士时报》的一则报道:2009 年 1 月 3 日,财政大臣拯救银行。

可以在 blockchain.info 查看真实的区块数据结构,类似于下图。

mechanics-10

3.5 比特币网络 (Bitcoin network)

比特币的 P2P 网络:

  • 特定的协议(基于 TCP 端口 8333)

  • 以随意的拓扑结构连成网络

  • 所有的节点平等

  • 新的节点随时可加入

  • 无响应的节点 3 小时后会被遗忘

整个网络共同维护区块链。使用“泛洪”算法(flooding algorithm)来实现。

假设 Alice 支付比特币给 Bob,某个节点接收到了这个交易,于是,它会将这个交易广播给所有与之连接的节点。有点像八卦,所以也叫八卦(gossip)协议。其他节点经过验证后,也会将交易广播给所有与其连接的节点。当节点接收到一个交易后,会存放到一个交易池中,里面的交易还没有被打包放入区块链。如果收到的交易已存在于池中,便不会广播出去。

校验标准:

  • 验证交易在当前区块链是有效的

  • 只会接收和广播在白名单中的标准脚本

  • 是不是已经在交易池中(避免死循环)

  • 是否为双重支付

同一个币支付给不同的接收者,产生的两笔交易,节点会忽略后接收到的交易。但是如果网络中不同的节点接收到了不同的交易。如下图

mechanics-11

这样节点就被分成两派,这称为“竞态条件”(race condition)。

这取决于挖掘出下一个区块的矿工,接收了是哪个交易。打包出的区块包含其受到的第一条交易,广播出去,其他区块会把另一条交易当做双重支付放弃掉。

泛洪算法延迟情况,下图表示被网络中不同数量的节点接收所花费的时间。三条线分别代表被 25%、50%、75% 的节点接收所需要的时间。

mechanics-12

完全有效节点(fully validating node)会把完整的共识区块链保存下来。同时未消费的比特币的完整列表(UTXO)需要保存在内存中,保证能够快速验证有效性。

轻量节点(lightweight node),也叫简单付款节点(simple payment verification clients,SPV)。只存储需要进行校验的部分交易。没有所有的交易历史记录,也没有未被消费的比特币列表。可能只需要几十兆的数据。

3.6 限制和优化 (Limitations & improvements)

比特币中的限制

  • 平均 10 分钟产生一个区块

  • 区块大小限定为 1 MB

  • 每个区块最多 20000 个签名操作

  • 每个比特币为 1 亿聪(satoshis)

  • 2100 万总量

  • 50、25、12.5 的挖矿回报

每个区块 1MB,每个交易大概 250 字节,所以每块最多为 4000 个交易,十分钟一个交易,相当于每秒只能 7 个交易。另外,签名算法(ESDSA)也可能在将来被破解。

优化

硬分叉 (hard-forking)

强行引入新的特性,使得之前版本的协议失效。现实中不可能所有的节点都及时升级,那老节点和新节点会分别维护并扩展其区块链。

软分叉 (soft forks)

引入新的特性,使得验证规则更严格。加入大部分的节点都运行了新协议,并使用更严格的新规则,使得老节点会挖到一些无效的区块,因为这些区块会包含一些在新规则下无法被验证有效的交易。而新节点产生的区块,老节点都会验证通过。这样老节点便不得不去更新协议。不会产生硬分叉,但会临时产生一些小型分叉。

P2SH 便是软分叉的经典例子。对于老节点,一个有效的 P2SH 会被验证通过,它只会验证这个哈希值跟前一笔交易输出的哈希值是否一样,但不会去进一步验证脚本是否合法,而新节点会去做这个校验。

软分叉可能性:

  • 新的签名格式

  • 额外的元数据(指定币基参数格式、区块中包含 UTXO 的梅克尔树等)

其他的修改可能需要硬分叉,特别是区块大小的问题。