比特币智能合约入门(1)

当谈及智能合约时,大多数人都会下意识地把比特币排除在讨论范围之外,因为当下流行的观点是比特币不具有智能合约的能力。但是今天想跟大家介绍的内容则正好与此相反:即比特币从诞生之日即拥有支持智能合约的能力,下面让我们一起来探个究竟。

比特币 UTXO 模型

比特币底层采用了一个很特别的交易模型设计,即 UTXO(Unspent Transaction Outputs) 模型。这里我们简单介绍一下,首先来看最基本的交易是如何表示的。

每一个比特币交易主要包括两部分:输入记录和输出记录。

每条输出记录主要包括的信息:

此输出中包含的比特币数量; 一段脚本代码,通常称为锁定脚本;

每条输入记录主要包括的信息:

对前一个交易输出部分的引用; 一段脚本代码,通常称为解锁脚本;

假设有人已成功发给我一个比特币,那么在链上就存在一条历史交易记录 TX_1。它的输出记录中有一条(记为 TX_1_OUT_1)包含了数量为 1 的比特币以及一个只能由我的私钥才能“开启”的“锁”(锁定脚本)。也正是由于这把“锁”的存在,别人才无法使用这个比特币。

现在我想把这个比特币转给另外一个人,那么需要构建一个新的交易记录 TX_2。它的输入记录中包含了对之前交易输出 TX_1_OUT_1 的引用,以及一段由我的私钥签名过的“钥匙”(解锁脚本);输出记录(记为 TX_2_OUT_1)中则包含了比特币数量 1,以及一个只有接收人才能“开启”的“锁”(另一个锁定脚本)。

这个新的交易 TX_2 被我发送给矿工,且只有通过了矿工对解锁脚本有效性的验证后,这笔交易才会被记录到链上账本中。此时也意味着我完成了一个接收+花费比特币的典型场景。

此时,输出 TX_1_OUT_1 由于被 TX_2 中的输入成功使用,会被系统被标记为已花出。如果尝试利用它再次构建交易,会被矿工认定为“双花”从而拒绝。与之相对,输出 TX_2_OUT_1 由于尚未被任何输入使用,故被称为未花费输出(即 UTXO)。

UTXO模型

所以我们可以这样比喻比特币的流转:每一个输入指向了前一个交易输出,当且仅当它能提供合适的“钥匙”打开前一个输出上的“锁”时,才能移动其中包含的比特币到新的输出。谁拥有输出的这个“钥匙”,谁就拥有其中的比特币。

比特币脚本(Script)及虚拟机(BVM)

实际上,比特币底层使用一种名为 Script 的脚本语言来实现“锁”和“钥匙”,它是一种基于栈的脚本语言。简单来说,它是一串由操作码(opcode)组成的指令集合,下面是几个简单的操作码例子:(完整的操作码列表请参考这里)

操作码 描述
OP_2 将数值 2 推至栈顶
OP_ADD 将栈顶的两个元素数值相加
OP_EQUAL 如果栈顶上2个元素相等,则返回 true;否则返回 false

锁定脚本(位于前一个交易的输出中)和解锁脚本(位于当前交易的输入中)都是由其中一些操作码串联组成,每个操作码后面可以有零至两个操作子。

为了验证解锁脚本的有效性,比特币虚拟机(BVM)被引入比特币节点。BVM 将锁定脚本连接在解锁脚本的后面,从而形成完整的执行脚本。这个完整的脚本会被 BVM 执行。当执行完毕后,如果栈顶元素的布尔值为 true(即数值非零),则认为脚本执行成功,反之其他情况则认为脚本执行失败。

脚本执行实例

我们来看个例子。假设在一个交易中,一些比特币被使用以下脚本锁定在输出中:

OP_1 OP_2 OP_ADD OP_EQUAL

在另外一个试图花费这个输出的交易中,使用以下脚本:

OP_3

为了说明这里的解锁授权是如何进行的,我们来分步模拟一下脚本的执行过程。

首先,解锁脚本在前,锁定脚本在后,将两个脚本连接起来形成完整的脚本:

OP_3 OP_1 OP_2 OP_ADD OP_EQUAL

接着,BVM 开始从头执行完整的脚本,具体的步骤如图所示:

BVM执行过程

最终结束时留在栈顶上的唯一值为 true,这说明整个脚本执行成功。如果使用任何不是 3 的值作为解锁脚本,则整个脚本的执行结果必然为 false,意味着无法成功解锁。

比特币智能合约

正如前面这个脚本例子所展示的那样,对于一个特定的锁定脚本,只有特定的解锁脚本与之连接后,才能使得整体的执行结果为 true。从这个意义上讲,任何花费比特币的行为都可以看做是一个合约:其中一方提供一定数量的比特币并且约定好条款;另一方只能在提供满足这些条款的证据时,才能花费合约中被锁定的比特币。之所以称之为“智能”合约,也是因为它仅需要由比特币网络矿工来自动确认和执行。

正是借助于比特币脚本语言的通用性及灵活表达性,我们才能在比特币网络上执行任意复杂的合约,这也是在未来的文章里我想给大家深入展示的。

栏目
728_90 cn stocks