比特币系统上可以运行简单的程序,编写程序的语言叫做比特币脚本。理解比特币脚本是理解比特币交易如何进行上锁和解锁的基础。这一节就来介绍一下比特币脚本的基本原理,以及最为常用的上锁和解锁脚本的逻辑。

基本原理

比特币脚本是一个功能比较少的编程语言,满足比特币系统的正常运行需求,同时最大化保证了安全性。

比特币脚本是图令不完备的。所谓图灵不完备意思就是缺少当代语言所必须的一些功能,例如循环。但是,功能的有限也最大化的减小了系统的受攻击面积,例如,可以防止有人利用无线循环来给系统安一个炸弹。

比特币脚本是一种基于栈的语言。可以把栈想象成堆叠到一起的几本书,push 操作相当于添加一本书,pop 操作相当于抽出一本书。每次执行的都是最顶部的操作,每个操作都对应一个操作码,简称 opcode 。

总之,比特币脚本是一个图灵不完备的基于栈的语言。

Hello World

下面来运行一个最简单的比特币脚本,方便我们真正理解基于栈的执行过程。

比如,我们要判断一下 2+3 是不是等于6,写成比特币脚本就是,

2 3 OP_ADD 6 OP_EQUAL

这里 OP_ADD 就是一个操作码,它可以把栈上的两项内容都 pop 出来,然后把二者之和 push 到栈上。OP_EQUAL 也会把栈上两项内容 pop 出来,如果两者相等,结果就是 true ,否则,就是 false ,结果也会 push 到栈内。程序会从左到右顺序执行,例如,上面的代码执行的时候,2和3会先后被 push 进栈,3处在栈的最顶端。接下来,OP_ADD 会把2和3从栈里面 pop 出来,然后把5 push 进栈,现在栈里面只有一个5了。代码继续执行,6会被 push 进栈,这样栈里面就有两项内容了,于是 OP_EQUAL 就可以把6和5都 pop 出来,然后把比较结果 false push 进栈。

这样,代码执行完毕,栈中只剩下一项内容 false 。

上锁和解锁脚本

比特币交易也是通过执行比特币脚本来完成的,交易都会对应两个脚本,一个上锁脚本 scriptPubKey 和一个解锁脚本 scriptSig 。

scriptPubKey 是用来上锁的脚本 。查看一条具体的交易数据,

{
  "version": 1,
  "locktime": 0,
  "vin": [
    {
      "txid": "7957a35fe64f80d234d76d83a2a8f1a0d8149a41d81de548f0a65a8a999f6f18",
      "vout": 0,
      "scriptSig" : "3045022100884d142d86652a3f47ba4746ec719bbfbd040a570b1deccbb6498c75c4ae24cb02204b9f039ff08df09cbe9f6addac960298cad530a863ea8f53982c09db8f6e3813[ALL] 0484ecc0d46f1918b30928fa0e4ed99f16a0fb4fde0735e7ade8416ab9fe423cc5412336376789d172787ec3457eee41c04f4938de5cc17b4a10fa336a8d752adf",
      "sequence": 4294967295
    }
  ],
  "vout": [
    {
      "value": 0.01500000,
      "scriptPubKey": "OP_DUP OP_HASH160 ab68025513c3dbd2f7b92a94e0581f5d50f654e7 OP_EQUALVERIFY OP_CHECKSIG"
    },
    {
      "value": 0.08450000,
      "scriptPubKey": "OP_DUP OP_HASH160 7f9b1a7fb68d60c536c2fd8aeaa53a8f3cc025a8 OP_EQUALVERIFY OP_CHECKSIG",
    }
  ]
}

可以看到 scriptPubKey 是交易输出 vout 的一项,这个脚本的作用就是把比特币锁定。接收放必须满足上锁脚本中的条件,才能花费这个输出,也就是花费这次转给他的比特币。对应的 scriptSig 就是解锁脚本,它的作用就是去满足 scriptPubKey 中提出的条件,以便把输出花掉。

scriptSig 包含两项内容:一项是数字签名,另一项是公钥。只有这两项提供的都正确才能让上锁脚本中的条件被满足。上锁脚本 scriptPubKey 中包含的长长的随机数是公钥的哈希,也就是通常所说的比特币地址。OP_DUP 是一个操作码用来把栈最顶端的一项复制一下,然后把结果 push 回栈。OP_HASH160 是另外一个操作码,它会把栈最顶端的一项取出并对其进行如下的哈希运算:

A = RIPEMD160(SHA-256(X))

A 是哈希运算的结果,X 是栈的最顶部的那一项。如果 X 是公钥,那么 A 就是比特币地址。

OP_EQUALVERIFY 会比较上锁脚本中的公钥哈希值和解锁脚本中的公钥经过上面的双重哈希运算得到的结果,如果二者相同,那么代码就会继续往下执行,也就是执行到 OP_CHECKSIG 这一步。这一步的作用是检查解锁脚本中的数字签名和公钥是否匹配,把比较结果 true,push 到栈中。数字签名是公钥对应的私钥生成的,所以可以体现对资金的使用权。

总结

总之,比特币脚本是一个基于栈的简单脚本语言,主要用来服务比特币交易的过程。其中,涉及到的两个最重要的脚本是上锁脚本 scriptPubKey 和解锁脚本 scriptSig 。

参考: