BTC脚本
比特币交易脚本语言,称为脚本Scripts,是一种类似Forth的逆波兰表示法的基于堆栈的执行语言。 如果听起来不知所云,可能是你没有学习过20世纪60年代的编程语言,但是没关系,我们将在本章中解释这一切。 放置在UTXO上的锁定脚本和解锁脚本都以此脚本语言编写。 当一笔比特币交易被验证时,每一笔输入中的解锁脚本与其相应的锁定脚本一起执行,以确定这笔交易是否满足支付条件。
脚本是一种非常简单的语言,设计范围有限,可在一些硬件上执行,与嵌入式设备一样简单。 它仅需要做最少的处理,许多现代编程语言可以做的事情它都不能做。 但将它用于验证可编程货币,这就是一个深思熟虑的安全特性。
图灵不完备性
比特币脚本语言包含许多操作码,但在一个重要方面受到故意的限制——除了条件流程控制之外,没有循环或复杂的流程控制功能。这样就确保了脚本语言不是图灵完备的Turing Complete,这意味着脚本的复杂性有限,执行时间也是可预见的。脚本并不是一种通用语言,这些限制确保该语言不被用于创造无限循环或其它类型的逻辑炸弹,这样的炸弹可以植入在一笔交易中,引起针对比特币网络的“拒绝服务”攻击。记住,每一笔交易都会被网络中的全节点验证,受限制的语言能防止交易验证机制被作为漏洞。
无状态验证
比特币交易脚本语言是无状态的的,在执行脚本之前没有状态,或者在执行脚本之后也没有保存状态。所以执行脚本所需的所有信息都已包含在脚本中。可以预见的是,一个脚本能在任何系统上以相同的方式执行。如果您的系统验证了一个脚本,可以确信的是比特币网络中的任何其他系统也将能验证这个脚本,这意味着一个有效的交易对每个人而言都是有效的,而且每一个人都知道这一点。这种结果的可预见性是比特币系统的一项至关重要的优势。
脚本构建(锁定与解锁)
比特币的交易验证引擎依赖于两类脚本来验证比特币交易:锁定脚本和解锁脚本。
锁定脚本是一个放置在输出上面的花费条件:它指定了今后花费这笔输出必须要满足的条件。 由于锁定脚本往往含有一个公钥或比特币地址(公钥哈希值),在历史上它曾被称为脚本公钥scriptPubKey。
解锁脚本是这样一个脚本,它“解决”或满足由锁定脚本放置在输出上的条件,并允许使用输出。解锁脚本是每一笔比特币交易输入的一部分,而且往往含有一个由用户的比特币钱包(通过用户的私钥)生成的数字签名。由于解锁脚本常常包含一个数字签名,因此它曾被称作脚本签名ScriptSig。在大多数比特币应用的源代码中,ScriptSig便是我们所说的解锁脚本
每一个比特币验证节点会通过同时执行锁定和解锁脚本来验证一笔交易。每个输入都包含一个解锁脚本,并引用了之前存在的UTXO。 验证软件将复制解锁脚本,检索输入所引用的UTXO,并从该UTXO复制锁定脚本
注意,UTXO被永久地记录在区块链中,因此是不变的,并且不受在新交易中引用失败的尝试的影响。只有正确满足输出条件的有效交易才会导致输出被视为“已花费”,然后从未花费交易输出集(UTXO set)中移除。
下图是最常见类型的比特币交易(P2PKH:对公钥哈希的付款)的解锁和锁定脚本的示例,显示在脚本验证之前解除锁定和锁定脚本的连接所产生的组合脚本:
脚本执行堆栈
比特币的脚本语言被称为基于堆栈的语言,因为它使用一种被称为堆栈stack的数据结构。堆栈是一个非常简单的数据结构,可以被视为一叠卡片。堆栈允许两个操作:推送push和弹出pop。 推送就是在堆栈顶部添加一个项目。 弹出从堆栈中删除最顶端的项。堆栈上的操作只作用于堆栈最顶端项目。堆栈数据结构也被称为“后进先出”( Last-In-First-Out)或 “LIFO” 队列。
脚本语言执行脚本时,从左到右处理每个项目。数字(数据常量)被推送入堆栈。操作码(Operators)从堆栈中推送或弹出一个或多个参数,对其进行操作,并可能将结果推送到堆栈上。例如,操作码 OP_ADD 将从堆栈中弹出两个项目,对它们求和,并将求和结果值推送到堆栈上。
条件操作码(Conditional operators)对一个条件进行评估,产生一个 TRUE 或 FALSE 的布尔结果(boolean result)。例如, OP_EQUAL 从堆栈中弹出两个项目,如果它们相等,则推送为 TRUE(由数字1表示),否则推送为 FALSE(由数字0表示)。比特币交易脚本通常包含条件操作码,以便它们可以产生用来表示有效交易的 TRUE 结果。
一个简单的脚本
如下图,脚本“ 2 3 OPADD 5 OP_EQUAL ”演示了算术加法操作码 OP_ADD ,该操作码将两个数字相加,然后把结果推送到堆栈, 后面的条件操作符 OP_EQUAL 是验算之前的两数之和是否等于 5 。为了简化起见,前缀OP在演示步骤过程中被省略了
尽管绝大多数解锁脚本都指向一个公钥哈希值(本质上就是比特币地址),因此需要使用资金的所有权证明,但脚本不必那么复杂。任何解锁和锁定脚本的任何组合如果结果为真(TRUE),则为有效。前面用于脚本语言示例的简单算术计算同样也是一个有效的锁定脚本,该脚本能用于锁定交易输出。
使用部分算术脚本作为锁定脚本的示例:
3 OP_ADD 5 OP_EQUAL
该脚本能被包含解锁脚本输入的一笔交易所满足:
2
验证软件将锁定和解锁脚本组合起来,结果脚本是:
2 3 OP_ADD 5 OP_EQUAL
正如在上图中所看到的一步步例子,当脚本被执行时,结果是OP_TRUE,交易有效。这不仅是一个有效的交易输出锁定脚本,同时产生的UTXO也能被任何具备算术技能知道数字2能够满足脚本的人所花费。
以下是一个稍微复杂一点的脚本,它用于计算 2+7-3+1 。注意,当脚本在同一行包含多个操作码时,堆栈允许一个操作码的结果由下一个操作码执行。
2 7 OP_ADD 3 OP_SUB 1 OP_ADD 7 OP_EQUAL
P2PKH(Pay-to-Public-Key-Hash)
比特币网络处理的大多数交易都是由“付款至公钥哈希”或P2PKH脚本锁定的输出,这些输出都含有一个锁定脚本,将输入锁定为一个公钥哈希值,即我们常说的比特币地址。由P2PKH脚本锁定的输出可以通过提供一个公钥和由相应私钥创建的数字签名来解锁(花费)。
该笔交易的输出内容为以下形式的锁定脚本:
P_DUP OP_HASH160 <Cafe Public Key Hash> OP_EQUALVERIFY OP_CHECKSIG
脚本中的 Cafe Public Key Hash 即为咖啡馆的比特币地址,但该地址不是基于Base58Check编码。事实上,大多数比特币地址的公钥哈希public key hash都显示为十六进制码,而不是大家所熟知的以1开头的基于Bsase58Check编码的比特币地址。
上述锁定脚本相应的解锁脚本是:
<Cafe Signature> <Cafe Public Key>
将两个脚本结合起来可以形成如下组合验证脚本:
<Cafe Signature> <Cafe Public Key> OP_DUP OP_HASH160
<Cafe Public Key Hash> OP_EQUALVERIFY OP_CHECKSIG
只有当解锁脚本与锁定脚本的设定条件相匹配时,执行组合验证脚本时才会显示结果为真(TRUE)