比特币交易脚本语言(以下简称脚本),是一种类似Forth的逆波兰表达式的基于堆栈的执行语言。放置在UTXO上的锁定脚本和解锁脚本都以此脚本语言编写。当一笔比特币交易被验证时,每一个输入值中的解锁脚本与其对应的锁定脚本同时 (互不干扰地)执行,以确定这笔交易是否满足支付条件。
脚本是一种非常简单的语言,被设计为在执行范围上有限制,可在一些硬件上执行,可能与嵌入式装置一样简单。 它仅需要做最少的处理,许多现代编程语言可以做的花哨的事情它都不能做。 但用于验证可编程货币,这是一个经深思熟虑的安全特性。
比特币脚本语言包含许多操作码,但都故意限定为一种重要的模式——除了有条件的流控制以外,没有循环或复杂流控制能力。这样就保证了脚本语言的图灵非完备性,这意味着脚本有限的复杂性和可预见的执行次数。脚本并不是一种通用语言,这些限制确保该语言不被用于创造无限循环或其它类型的逻辑炸弹,这样的炸弹可以植入在一笔交易中,引起针对比特币网络的“拒绝服务”攻击。记住,每一笔交易都会被网络中的全节点验证,受限制的语言能防止交易验证机制被作为一个漏洞而加以利用。
比特币交易脚本语言是没有中心化主体的,没有任何中心主体能凌驾于脚本之上,也没有中心主体会在脚本被执行后对其进行保存。所以执行脚本所需信息都已包含在脚本中。可以预见的是,一个脚本能在任何系统上以相同的方式执行。如果您的系统验证了一个脚本,可以确信的是每一个比特币网络中的其他系统也将验证这个脚本,这意味着一个有效的交易对每个人而言都是有效的,而且每一个人都知道这一点。这种结果可预见性是比特币系统的一项至关重要的良性特征。
比特币的交易验证引擎依赖于两类脚本来验证比特币交易:锁定脚本和解锁脚本。
锁定脚本是一个放置在输出上面的花费条件:它指定了今后花费这笔输出必须要满足的条件。
由于锁定脚本往往含有一个公钥或比特币地址(公钥哈希值),历史上它曾被称为脚本公钥(scriptPubKey)。在大多数比特币应用程序中,我们所称的“锁定脚本”将以scriptPubKey的形式出现在源代码中。
解锁脚本是一个“解决”或满足被锁定脚本在一个输出上设定的花费条件的脚本,它将允许输出被消费。
解锁脚本是每一笔比特币交易输入的一部分,而且往往含有一个由用户的比特币钱包(通过用户的私钥)生成的数字签名。由于解锁脚本常常包含一个数字签名,因此它曾被称作ScriptSig。在大多数比特币应用的源代码中,ScriptSig便是我们所说的解锁脚本。但并非所有解锁脚本都一定会包含签名。
UTXO被永久地记录在区块链中,因此是不变的,并且不受在新交易中引用失败的尝试的影响。 只有正确满足输出条件的有效交易才能将输出视为“开销来源”,继而该输出将被从未花费的交易输出集(UTXO set)中删除。
比特币的脚本语言被称为基于堆栈的语言,因为它使用一种被称为堆栈的数据结构。堆栈是一个非常简单的数据结构,可以被视为一叠卡片。栈允许两个操作:push和pop(推送和弹出)。 Push(推送)在堆栈顶部添加一个项目。 Pop(弹出)从堆栈中删除最顶端的项。栈上的操作只能作用于栈最顶端项目。堆栈数据结构也被称为“后进先出”( Last-In-First-Out)或 “LIFO” 队列。
脚本语言通过从左到右处理每个项目来执行脚本。数字(数据常量)被推到堆栈上。操作码(Operators)从堆栈中推送或弹出一个或多个参数,对其进行操作,并可能将结果推送到堆栈上。例如,操作码 OP_ADD 将从堆栈中弹出两个项目,添加它们,并将结果的总和推送到堆栈上。
条件操作码(Conditional operators)对一个条件进行评估,产生一个 TRUE 或 FALSE 的布尔结果(boolean result)。例如, OP_EQUAL 从堆栈中弹出两个项目,如果它们相等,则推送为 TRUE(由数字1表示),否则推送为 FALSE(由数字0表示)。比特币交易脚本通常包含条件操作码,以便它们可以产生用来表示有效交易的 TRUE 结果。
绝大多数解锁脚本都指向一个公钥哈希值(本质上就是比特币地址),因此如果想要使用资金则需验证所有权,但脚本本身并不需要如此复杂。任何解锁和锁定脚本的组合如果结果为真(TRUE),则为有效。
使用部分算术操作码脚本作为锁定脚本的示例:3 OP_ADD 5 OP_EQUAL
该锁定脚本能被此解锁脚本为输入的一笔交易所满足:2
验证软件将锁定和解锁脚本组合起来,结果脚本是:
2 3 OP_ADD 5 OP_EQUAL
当脚本被执行时,结果是OP_TRUE,交易有效。不仅该笔交易的输出锁定脚本有效,同时UTXO也能被任何知晓这个运算技巧(知道是数字2)的人所使用。
在最初版本的比特币客户端中,解锁和锁定脚本是以连锁的形式存在,并被依次执行的。出于安全因素考虑,在2010 年比特币开发者们修改了这个特性——因为存在“允许异常解锁脚本推送数据入栈并且污染锁定脚本”的漏洞。
首先,使用堆栈执行引擎执行解锁脚本。如果解锁脚本在执行过程中未报错(例如:没有“悬挂”操作码),则复制主堆栈(而不是备用堆栈),并执行锁定脚本。如果从解锁脚本中复制而来的堆栈数据执行锁定脚本的结果为“TRUE",那么解锁脚本就成功地满足了锁定脚本所设置的条件,因此,该输入是一个能使用该UTXO的有效授权。如果在合并脚本后的结果不是”TRUE“以外的任何结果,输入都是无效的,因为它不能满足UTXO中所设置的使用该笔资金的条件。
比特币网络处理的大多数交易花费的都是由“付款至公钥哈希”(或P2PKH)脚本锁定的输出,这些输出都含有一个锁定脚本,将输入锁定为一个公钥哈希值,即我们常说的比特币地址。由P2PKH脚本锁定的输出可以通过提供一个公钥和由相应私钥创建的数字签名来解锁(使用)。
例如,Alice下达了向Bob咖啡馆的比特币地址支付0.015比特币的支付指令,该笔交易的输出内容为以下形式的锁定脚本:
OP_DUP OP_HASH160 <Cafe Public Key Hash> OP_EQUALVERIFY OP_CHECKSIG
脚本中的 Cafe Public Key Hash 即为咖啡馆的比特币地址,但该地址不是基于Base58Check编码。事实上,大多数比特币地址的公钥哈希值都显示为十六进制码,而不是大家所熟知的以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)。换句话说,只有当解锁脚本得到了收款方的有效签名,交易执行结果才会被通过(结果为真),该有效签名是从与公钥哈希值相匹配的收款方的私钥中所获取的。
参考文章:https://wizardforcel.gitbooks.io/masterbitcoin2cn/content/ch06.html