- 这章剖析比特币运行机制。
- 前两章泛泛讨论比特币
- 这章近距离了解比特币用的数据结构、实际脚本以及语言,
- 这种较为精准的介绍会为本书后面的章节建立场景。
- 本章帮助我们真正懂得比特币实质
- 如2章
- 比特币的共识机制设定了一个只允许往里写入的账簿
- 一旦数据被写入,它将永远储存在那
- 去中心化(或分布式)协议确保账簿中存储数据的共识
- 矿工会执行协议并确认交易,
- 这些机制确保每一笔交易都真实发生,且账簿中的每个比特币都没有被使用过。
- 账簿形成一种货币系统。
- 同时假设,可用货币奖励矿工,使矿工积极完成记账操作
- 本章:
- 如何建立货币系统、如何奖励矿工,从而保证整个流程有序运行。
3.1比特币的交易
- 比特币交易的过程其实就是不停地创造区块,
- 先看一个简单模式的账簿,
- 这个账簿里,
- 每一笔交易依次被添加到账簿里。
- 如何用这个账簿来创造一种货币?
- (许多人误认作比特币使用的方式):
- 建立一个以账户为核心的系统,
- 可以创新币且放入某人账号,然后就可以转给其他人。
- 一笔交易这样:“把爱丽丝账户17转给鲍勃”,
- 然后由爱丽丝签名。
- 图3.1,爱丽丝在第一笔交易里收25
- 转17给鲍勃,她账户还有8
- 不便之处,
- 如果想确认一笔交易是否真实就必须跟踪每一个账户的余额
- 当爱想要转给戴15
- 她是否真的有15?
- 为搞清楚,须回过头去看和爱丽丝有关的所有交易,
- 加总来确认当时的余额
- 更有效的办法,如另外增加一个数据字段,
- 更新每次交易后的账户余额,
- 但这增加了记账的工作量。
- 比特币没有用这种记账方式,
- 用了第1章1.5节里提到的“财奴币”相类似的方法来记录交易。
- 这种方式就像财奴币里的付币,
- 每个交易中都有一个输入值和输出值。
- 输入值是将被消费掉的币(这些币是前一个交易创造出来的),
- 输出是在本次交易中创造出来的币。
- 铸造新币时,只创造新币,不消旧币(就像财奴币里的造币,只有输出,没有输入)。
- 每笔交易都有一个独一无二ID。
- 每笔交易可多输出,
- 输出索引从0,
- 称第一个输出为“输出0”
- 图3.2。
- 1是铸造新币的交易,因此没输入,也没签名;
- 输出是向爱丽丝转移25个币。
- 爱丽丝想把些币转给鲍勃,她就创造了一条新交易,
- 图3.2中的交易2。
- 她必须明确指出币的来源(引用之前某笔交易)
- 爱丽丝指出本次交易的币来自交易1中的输出0(交易1唯一输出)
- 即向爱丽丝转25个币
- 爱丽丝还要明确收款人也就是输出的地址,
- 两输出,
- 转17给鲍勃,
- 转8给爱丽丝自己。
- 两输出,
- 整个交易由爱丽丝签名这样,
- 大家就知道这笔交易爱丽丝确实授权了
- 地址转换。
- 为什么爱丽丝要把币转给自己?
- 比特币就像财奴币中描述的币一样,交易中输出的币,要么在另
个交易中被完全消费,要么就一个都不消费,不存在只消费部分 - 爱丽丝只需付给鲍勃17,但爱丽丝在上一交易中实际获得25,
- 为把这些币全部消费掉,她必须再转给自己8
- 8可转到另一地址(不同于交易1中获得25个币的地址)
- 但前提是该地址为爱丽丝所有,这就叫地址转换
- 有效验证。
- 当一个新的交易被加入总账,它的有效性是否易验证?
- 核査一下爱丽丝引用的交易输出,确认她确实有25没花
- 因为用了哈希指针,所以核查很快。
- 为确认这25没被花,只需从爱丽丝所引用的交易开始,
- 一直核査到账本上最新记录的交易为止
- 不需从账本建立之初的交易开始核査
- 这种方法也不需增加额外数据结构(将看到,加入新数据结构将进一步提速)
- 资金合并。
- 和财奴币一样,比特币交易可能有许多输入与输出,资金分隔与合并也很容易。
- 假如鲍勃在两笔不同的交易中分别收到17和2,
- 现在他想把这两笔钱合并起来花掉,这很容易,他只需发起一个交易,交易里有两个输入和一个输出,输出的地址是他自己的地
址,这样,鲍勃就把两个交易合二为一了
- 现在他想把这两笔钱合并起来花掉,这很容易,他只需发起一个交易,交易里有两个输入和一个输出,输出的地址是他自己的地
- 共同支付。
- 如果卡罗尔和鲍勃想共同支付戴维,
- 可发起一个交易,
- 交易里两输入和一输出
- 两输入引用的“上一笔交易”的输出地址不同,
- 因此,这笔交易需两个签名:
- 卡罗尔的和鲍勃的。
- 因此,这笔交易需两个签名:
- 交易语法。
- 比特币交易涉及的概念就是上面这些。
- 再看看比特币交易在底层是如何实现
- 实际上,比特币在网络上传输的数据结构都是一串字符,图3.3显示了一个真实的程序,经过编译就变成供机器执行的二进制代码
- 图3.3,
- 一个比特币交易分三
- 元数据、
- 一系列的输入
- 和一系列的输出
元数据
- 存放一些内部处理的信息:
- 这笔交易的规模、
- 输入数量
- 输出数量
- 此笔交易的哈希值,这个交易独一无二的ID。
- 可用哈希指针指向这个ID。
- “锁定时间”
- 输入
- 所有输入排成一个序列,输入格式都一样
- 需明确说明之前一笔交易的某个输出,
- 它包括之前那笔交易的哈希值,使其成为指向那个特定交易的哈希指针。
- 同时包括之前交易输出的索引和一个签名:
- 须有签名来证明我们有资格去支配这笔比特币
- 输出
- 所有输出排成一个序列。
- 每个输出的内容分成两部分。
- 所有输出的金额之和必须<=输入的金额之和。
- 当输出的总金额小于输入总金额时,输出的总金额与输入的总金额的差额部分,就作为交易费支付给为这笔交易记账的矿工
- 一长串字符看上去像接收地址
- 每个输出都要和一个特定的公钥(地址)对应,
- 所以这一长串字符里面确实有一部分看上去是公钥的哈希值,
- 但里面还有一部分看上去像指令集合的东西,
- 是一个比特币的脚本
3.2比特币的脚本
- 每个交易输出确定公钥,同时指定一脚本
- 这节学习比特币的工作控制语言,也叫脚本。
- 懂得为什么要用一个脚本而不是简单分配一个公钥
- 最常见的比特币交易,是通过某人的签名去取得他在前一笔交易
中获得的资金。- 希望交易的输出包含这样的信息:
- “凭借地址X的所有者的签名,才可获得这笔资金。”
- 地址就是一个公钥的哈希值,所以仅仅说地址Ⅹ并没告诉我们公钥在哪,也没有给我们一个检査签名的方法。
- 所以,交易输出须这样描述:“凭借哈希值为X的公钥,以及这个公钥所有者的签名,才可以获得这笔资金。”
- 这实际上就是最常见的比特币脚本,如图3.4
- 希望交易的输出包含这样的信息:
- 谁执行这脚本?
- 这一系列指令如何完成?
- 交易输入包括脚本(而不是签名)。
- 为确认一笔交易正确地获取了上一笔交易所输出的,
- 把交易的输入脚本和上一笔交易的输出脚本串联,
- 这个串联脚本必须被成功地执行后才可以获取资金。
- 输出脚本( scriptpubkey),
- 输入脚本( scripts)。
- 输出脚本只指定了一个公钥(或公钥哈希值的地址),
- 输入脚本指定一个对应公钥的签名。
- 图3.5就是两个脚本结合。
比特币脚本语言
- 这个脚本语言为比特币开发。
- 和Forh语言有相似,Forth是一种简单的堆栈式编程语言
- 比特币的脚本语言设计原则就是简明扼要,并内生地支持加密操作。
- 脚本里面有指令计算哈希值和检验签名
- 这种语言是堆栈式的,每个指令只被执行一次,是线性的,无法循环
- 指令数目给了一个执行时间与内存使用的上限
- 语言不是图灵完备的,不能随意运行强大函数功能
- 有意设计的,
- 矿工需执行这些网络上任意交易提交者所递交的脚本,
- 设计者不希望让他们提交可能无限循环的脚本。
- 执行比特币脚本只能产生两结果:
- 成功执行,交易有效;
- 脚本执行出现错误,整个交易无效拒绝记入区块链。
- 这个脚本语言十分简单。
- 256个指令,每个一字节。
- 256个指令中,15个目前不可用,75个被保留还没有具体定义(以后或
许可以被用来扩展),剩下的才是可用的
- 基本的算数、逻辑语句(如If-then)、抛出错误、过早返回
- 密码指令,如哈希函数语句、签名验证语句,
- 重要特殊指令是“ CHECKMIUILTTISIG”
- 査证多个签名。
- 重要特殊指令是“ CHECKMIUILTTISIG”
- 表3.1一些比特币工作控制语言里的常用语句
- CKMIUILTISIC要求指定n个公钥和一个参数t(作为一个临界值)。
- 正确执行的条件是:
- 在n个公钥中,至少可以选出t现时有效的签名。
- 3.3节会示范这个指令的用法,现在需认识到这个原生指令是非常强大的,它以一种极其精练的方式协助我们查验交易中的多方签名
- 不过,目前比特币多方签名功能实现过程中有个缺陷
- CHECKMULTISIG执行时会返回一个没用值
- 且系统还必须要安排一个堆機中的变量去储存它,然后再忽略掉
- 修复这个缺陷成本高,一直没被修复,第3章3.6节会再讨论。
- 但目前,这个程序缺陷也算是比特币的一个特性
执行一个脚本
- 堆栈语言里执行一个脚本,我们只需一个堆栈来垒积数据,不需分配任何内存与变量
- 两类
- 数据指令和工作码指令。
- 数据指令是把数据推到堆栈的最上面
- 工作码指令
- 用堆栈顶部的数据作为输入,计算一个函数
- 图3.5怎么执行。
- 图3.6展示每一条指令执行后的堆栈状态
- 前两条指令属数据指令,
- 脚本(包含在交易的输入项)中的签名
- 用来验证签名的公钥
- 后面几个指令是输出脚本(包含在上一交易的输出项中)里的指令