区块链/等级 IV · 深入协议/4.1 Script/课程 1
脚本:一种栈式语言
让执行保持有界的栈,以及中本聪禁用的操作码
材料一张纸和一支笔可访问比特币区块浏览器比特币维基的“脚本”页面
好奇
- 为什么没有循环的语言能够防止每个节点陷入无限计算?
- 2010年的哪个具体漏洞,让任何人在其被修复之前都能花费他人的币?
- 一个输出如何在携带数据的同时,被标记为可证明不可花费?
探索
- 比特币脚本是一种小型的、基于栈的语言,风格类似 Forth。它没有循环和递归,因此被刻意设计为非图灵完备。这一设计确保每个脚本的执行成本都是有界且可预测的。任何脚本都无法无限运行或消耗无限资源,从而保护网络免受通用代码所固有的拒绝服务攻击。这种简洁性使任何节点都能以确定性的方式验证交易,而不必担心资源耗尽。
- 每个输出都带有一个锁定脚本(scriptPubKey),用于规定花费条件。花费方通过 scriptSig 提供解锁数据,自 SegWit 起也可通过见证数据提供。执行时会将数据压入栈中,并从左到右依次应用操作码。如果执行顺利结束且栈顶留下一个真值,则该花费有效。这种二元结果在从不暴露私钥的情况下保障比特币的安全,依靠的是密码学验证而非身份核验。
- 2010年,由于发现了若干漏洞(其中一个位于 OP_LSHIFT,可能导致节点崩溃),中本聪出于预防考虑禁用了包括 OP_CAT 和 OP_MUL 在内的多个操作码。更严重的缺陷在于 scriptSig 与 scriptPubKey 被拼接成同一个脚本来执行:以 OP_RETURN 结尾的 scriptSig 可以在锁定脚本运行之前就以“true”结束执行,使任何人都能花费任何人的币。最初的修复让 OP_RETURN 直接失败;后来的版本更进一步,将 scriptSig 和 scriptPubKey 作为两个独立的脚本分别执行。这一改动强化了安全模型,并移除了可能被利用或引发不可预测行为的危险操作。
- OP_RETURN 将输出标记为可证明不可花费。它用于在链上嵌入少量数据,而不创建有效的花费路径。由于没有任何签名能够满足 OP_RETURN,这些币会被永久锁定,但其关联数据仍是区块链历史的一部分。由于这类输出永远无法被花费,节点会将它们排除在可花费币的集合之外。
- 要理解其运作机制,可在纸上逐步执行“2 3 OP_ADD 5 OP_EQUAL”。从空栈开始。压入2,再压入3。执行 OP_ADD,弹出这两个数并压入它们的和(5)。再压入一个5。执行 OP_EQUAL 比较栈顶的两个元素;由于两者相等,它压入真值(1)。最终栈中只剩一个真值,满足有效性要求。这个简单的算术示例展示了操作码如何通过操作栈来判定条件。
- 打开区块浏览器,找到任意一笔标准交易。在输出部分找到 scriptPubKey:传统 P2PKH 输出以 OP_DUP 开头,原生 SegWit 输出以 OP_0 开头,Taproot 输出则以 OP_1 开头。然后打开一笔花费此类输出的交易,找到满足该脚本的签名和公钥(传统输出位于 scriptSig 中,SegWit 和 Taproot 输出位于见证数据中),它们会让所有验证节点的执行栈上留下一个真值。
关键事实
- 比特币脚本不具备图灵完备性,没有循环或递归结构。
- 2010年,在发现漏洞后,中本聪出于预防考虑禁用了 OP_CAT、OP_MUL 等操作码。
- 自2010年起,scriptSig与scriptPubKey是分别执行而非拼接在一起的。
- OP_RETURN 将输出标记为可证明不可花费,用于嵌入数据。
- 有效的花费会在执行栈顶留下一个真值。
- 由于不支持任意计算,每个脚本的执行成本都是有界的。
练习 九、六、三、一
9收集列出比特币脚本中的9个操作码。注明哪些是被禁用的,哪些会直接修改栈,以及哪些用于执行加密校验。
6归类将这9个操作码按功能归为6类:算术、比较、栈操作、密码学验证、数据嵌入,或已弃用/已移除。
3提炼将这6类归纳为脚本被刻意保持受限的3个原因。
1凝结写1句话,说明有界执行如何保护网络免受恶意脚本的侵害。
事实核查
日期、数字及数据均依据下方列出的已发表资料进行核实。