首页 百科知识 交易脚本和脚本语言

交易脚本和脚本语言

时间:2022-04-04 百科知识 版权反馈
【摘要】:比特币客户端通过执行脚本来验证交易。比特币脚本是一个类似Forth的脚本语言。当交易被验证时,每个输入上的解锁脚本都将与相应的锁定脚本一起执行,以查看是否符合花费条件。这种脚本语言允许表达几乎接近无限的条件。过去,解锁脚本被称为脚本签名,因为它通常包含数字签名。比特币交易脚本通常都会包含条件操作符,因此可以产生TRUE的结果来表示一个有效交易。

比特币客户端通过执行脚本来验证交易。比特币脚本是一个类似Forth的脚本语言。不管是UTXO上的锁定脚本(受限),还是包含签名的解锁脚本,都是用这种脚本语言写的。当交易被验证时,每个输入上的解锁脚本都将与相应的锁定脚本一起执行,以查看是否符合花费条件。

如今,比特币网络处理的大部分交易都是类似“爱丽丝付给鲍勃”这种形式的,它们都基于叫作“支付给公钥哈希”的相同脚本。但是,使用脚本锁定输出、解锁输入,意味着通过使用编程语言,交易可以包含无限多的条件。比特币交易不仅限于“爱丽丝付给鲍勃”这种形式和模式。

这还只是这种脚本语言表达能力的“冰山一角”。在本节中,我们将演示比特币交易脚本语言的组成元素,展示它如何用于表达复杂的支付条件,以及这些条件是如何在解锁脚本中被满足的。

比特币的交易验证引擎依赖两种类型的脚本:一个是锁定脚本,一个是解锁脚本。

锁定脚本是放置在输出上的一个受限,它设定条件,只有满足这些条件,输出才能在未来被花费掉。由于锁定脚本通常包含公钥或者比特币地址,所以过去它曾被称为脚本公钥(scriptPubKey)。在本书中我们称其为“锁定脚本”,更能体现这个脚本技术在更多领域应用的可能性。在大多数比特币应用中,所谓的锁定脚本在程序源码中一般体现为“scriptPubKey”。

解锁脚本是“解决”或者满足一个输出上的锁定脚本设置的条件,从而允许输出被重新使用。解锁脚本是每个交易输入的一部分。大多数情况下,它们包含一个数字签名,这个签名由用户的钱包应用根据私钥创建。过去,解锁脚本被称为脚本签名(scriptSig),因为它通常包含数字签名。在大多数比特币应用中,源码一般把解锁脚本写成scriptSig。在本书中,我们称其为“解锁脚本”,以体现更广泛的锁定脚本要求,因为不是所有锁定脚本都包含签名。

每个比特币客户端都通过同时执行锁定和解锁脚本来验证交易。对于交易中的每个输入,验证软件首先检索被引用的UTXO。这个UTXO包含锁定脚本,它定义了花费输出所需要满足的条件。验证软件随后读取输入中包含的用于尝试花费UTXO的解锁脚本,接下来验证软件将同时执行这两个脚本。

在早期的比特币客户端中,解锁和锁定脚本是被连接起来并顺序执行的。出于安全考虑,这种情况在2010年被改变了,因为当时发现存在漏洞,一个允许非法的解锁脚本推送数据入栈,并污染锁定脚本。在现在的实现中,脚本是分开执行的,利用堆栈在两个脚本间传递数据,接下来我们将进行具体说明。

首先,利用堆栈执行引擎运行解锁脚本。解锁脚本成功运行后[比如,没有“悬空”(dangling)操作符],主栈(不是替代栈)将被复制,接着锁定脚本被执行。如果利用从解锁脚本堆栈上复制来的数据,执行锁定脚本的结果为“真”(TRUE),那么解锁脚本就成功满足了锁定脚本设定的条件,从而输入拥有花费这笔UTXO的有效授权。如果组合脚本执行完后存在任何非真的结果,则输入是无效的,因为它无法满足设置在UTXO上的花费条件。请注意UTXO是永久记录在区块链上的,因此它不会因这笔交易失败而变化或受到影响。只有一笔有效的、能正确满足UTXO使用条件的交易,才会导致这笔UTXO被标注为“已花费”,并从可用(未花费)UTXO集合中被移除。

图5.1是一个最普通的比特币交易(支付到公钥哈希)的解锁和锁定脚本的例子,显示了在脚本验证前将解锁和锁定脚本连接形成组合脚本的结果。

图5.1 组合scriptSig和scriptPubKey来评估一个交易脚本

比特币交易脚本语言,叫作脚本,是一个与Forth类似的逆波兰式表示的基于堆栈的执行语言。如果听起来感觉乱七八糟,可能是因为你没有学习过20世纪60年代的编程语言。脚本是一种非常简单的语言,只能执行限定的功能并且只能在一些特定的硬件上执行,它就像嵌入式设备,比如手持计算器,一样简单。它只要求最低的处理能力,也不能像其他现代编程语言一样可以做很多有意思的事情。在可编程货币的情境下,它其实是一种特别设计的安全特性。

比特币的脚本语言被称作基于堆栈的语言,因为它使用了一种叫作堆栈数据结构。堆栈是一种非常简单的数据结构,你可以将它看作一堆卡片。堆栈只运行两种操作,入栈(push)和出栈(pop)。入栈是将一个项目添加到栈的顶部。出栈则从栈顶部移除一个项目。

脚本语言通过从左到右处理每个项目来执行脚本。数字(数据常量)被压入栈中。操作符将一个或多个参数压入栈中,或者从栈中移除,操作它们,并有可能将结果压入栈中。例如,OP_ADD从栈中移出两个项目,把它们相加,然后将求和结果压入栈中。

条件操作符评估一个条件,产生一个真(TRUE)或假(FALSE)的布尔结果。比如,OP_EQUAL从堆栈中移出两个项目,如果两个项目相等,则把TRUE(TRUE以数字1代表)压入栈中,如果不相等,则压入FALSE(用数字0表示)。比特币交易脚本通常都会包含条件操作符,因此可以产生TRUE的结果来表示一个有效交易。

在图5.2中,脚本23 OP_ADD 5 OP_EQUAL演示了算术加法操作符OP_ADD,将两个数相加,并把结果压入栈中;接着,条件操作符OP_EQUAL检查求和结果是否等于5。为了更加简洁,“OP_”前缀在后面的操作示例中将被省略。

以下是一个稍微复杂的脚本,用于计算2+7-3+1。注意,当脚本在一行中包含多个操作符时,堆栈允许一个操作符的结果被下一个操作符使用。

请使用铅笔和纸张验证一下前面的脚本。当脚本执行完毕,你会发现堆栈中只剩一个TRUE值。

虽然大多数锁定脚本都指向比特币地址或者公钥,从而要求证明所有者花费这笔资金的权限,但实际上,脚本并不要求一定要那么复杂。任何锁定和解锁脚本的组合,只要能够得到一个为“真”的结果,就是有效的。前面用于说明脚本语言的例子中,简单的算术运算也是一个有效的锁定脚本,可用于锁定一笔交易输出。

使用算术运算例子的一部分作为锁定脚本。

只要交易包含这样一个解锁脚本,以上条件就能得到满足。

验证软件将上述锁定和解锁脚本进行组合,形成如下脚本。

我们可以在图5.2的操作范例中看到,当这个脚本被执行后,结果是OP_TRUE,使得交易有效。不仅这个交易输出锁定脚本有效,只要有一点算术技巧,知道数字2能满足算术运算结果,任何人都能够花费这笔UTXO。

图5.2 比特币脚本简单数学运算的验证过程

比特币交易脚本语言包含很多操作符,但是特意在一方面进行了限制——没有循环,也没有条件控制以外的复杂流程控制能力。这使得这种语言不是图灵完备的,意味着脚本的复杂性有限,执行时间也可以预测。脚本不是通用语言。这些限制条件确保该语言不能用于创建无限循环或者其他形式的“逻辑炸弹”,从而避免这些伎俩以某种方式嵌入到交易中,并导致对比特币网络产生拒绝服务攻击。记住,任何交易均会被网络上的所有完全节点验证。一个限定功能的语言能够防止交易验证机制被当作弱点利用。

比特币交易脚本语言是无状态的,在脚本执行前没有状态,执行后也不会保存状态。如此,所有需要被脚本执行的信息必须包含在脚本当中。可以预见的是,脚本在所有系统中都会以同样的方式运行。如果你的系统验证了脚本,可以确定其他比特币网络中的系统也一样能够验证这个脚本。也就是说,一个有效的交易对任何人都是同样有效的,而且任何人都知道这点。这种可预测结果的特性是比特币系统的一个重要特点。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈