比特币源码情景分析之script脚本验证(1)

Bitcoin script是一种简单的指令运行框架

1)脚本概述

脚本主要由两部分构成:脚本对象CScript和执行函数VerifyScript。
脚本对象分为两类:scriptSig和scriptPublicKey
scriptSig位于交易中的txin中,而scriptPubKey位于txout中,scriptSig解锁scriptPubKey,具体关系如下:

比特币源码情景分析之script脚本验证(1)

一个交易可能有多个txin和多个txout。一个txin引用一个未花掉的txout,比如上图的C.txin_1引用A.txout_1
那么C.txin_1.scriptSig解锁A.txout_1.scriptPubKey锁定脚本,当scriptSig解锁scriptPubkey时,只需先执行解锁脚本scriptSig,然后执行锁定scriptPub即可,验证通过证明C.txin_1可以合法使用A.txout_1生成新的交易

2)脚本构成

一个脚本由指令和数据构成,比如
<largedata> <sig> OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
上面<>中的是数据,OP_开头的是指令,所有的指令占一个字节(8位)
其实这个脚本真正存储结构如下
OP_PUSHDATA4 [F0][00][00][E0] <largedata> [0F] <sig> OP_DUP OP_HASH160 [14]<pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
上图橙色的部分是数据长度,也就说脚本中的所有数据都是以数据大小为先导。这个很好理解,一段非格式化的数据,顺序取值时必须知道即将要取得对象的是什么类型的,占用空间多大。

3)脚本执行

脚本执行就是解释脚本内容,涉及读取脚本中的数据到堆栈stack, 读取stack的内容,生成数据并写入stack
1)largedata数据获取指令 OP_PUSHDATA4 [F0][00][00][E0] :
    脚本执行函数读取第一个字节,发现是OP_PUSHDATA4指令,便知道接下来4个字节空间存储的是数据的大小信息,于是取接下来的4个字节,拼接为一个32位数字,然后就知道数据的长度N,紧接着取N个字节放置到stack,这N个字节就是数据的内容

2)sig数据获取指令 [0F] <sig>:
    上一个操作执行完成后,执行函数又读取一个字节,发现是0F, 不是任何合法指令便认为是数据的大小信息,于是读取接下来16个字节的内容作为数据并

3)复制数据指令OP_DUP:
    上一个操作执行完成后,执行函数又读取一个字节,发现是OP_DUP, 该指令的规则是读取stack栈顶的数据并复制一份压入栈顶
上面的执行过程对应的stack变化如下:
比特币源码情景分析之script脚本验证(1)

4)交易脚本模板

    节点在生成交易时可以编写任意的脚本,但是由于锁定脚本(scriptPubKey)解锁脚本(scriptSig)是由网络中不同的节点生成和执行的,所以锁定节点要想解锁交易必须得能理解对应的锁定脚本才能生成合适的解锁脚本,因此需要一些标准的脚本模板。这样通过比较锁定脚本和标准脚本,就能知道锁定脚本是什么脚本,需要产生什么解锁脚本。
    目前主流的交易脚本模板如下:
  1.P2PK(Pay-to-Public-Key)
    蓝色部分是解锁脚本,红色部分是锁定脚本, 该脚本目前不常用,因为锁定脚本中包含pubkey, 即包含用户的钱包地址信息,且由于锁定脚本是存储在区块链上的,任何人都能查询到,自然缺乏隐私保护
<sig> <pubKey>  OP_CHECKSIG

 2.P2PKH(Pay-to-Public-Key-Hash):
    P2PKH可以很好的解决P2PK的隐私问题,因为P2PKH的锁定脚本中只有pubKeyHash值,其他用户没法从pubKeyHash反推出pubKey进而获取钱包地址。
    目前,比特币网络大部分交易都是以P2PKH方式进行的,其解锁脚本与锁定脚本如下:
<sig> <pubKey> OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
按照我们前面的分析,该脚本在内存中的真正数据结构如下
[0x08] <sig> [0x06] <pubKey> OP_DUP OP_HASH160 [0x14] <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

解锁脚本执行完成后栈里已经有sig, pubkey两个数据,锁定脚本的执行流程如下:

堆栈 指令 操作
([sig], [pubkey]) OP_DUP 复制栈顶元素[pubkey]至堆栈S
([sig,[pubkey], [pubkey]) OP_HASH160 pop出一个元素,并进行hash160运算,将结果push回S
([sig,[pubkey], [hash160_pubkey])  0×14  push后面20字节的数据
 ([sig,[pubkey], [hash160_pubkey], [hash160])  OP_EQUALVERIFY  pop出2个元素,比较大小。若不相等,则标记交易为无效
  ([sig,[pubkey])  OP_CHECKSIG  pop出两个元素,验证签名,如成功,则压入TRUE;否则压入FALSE
 (TRUE)    

 3.MS(Multiple Signatures)多重签名
    N是存档公钥总数,M是要求**交易的最少公钥数。
OP_0 <Sig1> <Sig2>  M <pubKey 1> <pubKey2> ... <pubKeyN> N OP_CHECKMULTISIG 
 
4.P2SH(Pay-to-Script-Hash)
   MS和P2PK一样,将pubkey暴露在锁定脚本中,缺乏隐私,因而P2SH出现了,该脚本锁定脚本中只有hash,并将多签名锁定脚本交给解锁者生成
<Sig1> <Sig2> <2 PK1 PK2 PK3 PK4 PK5 5 OP_CHECKMULTISIG> OP_HASH160 8ac1d7a2fa204a16dc984fa81cfdf86a2a4e1731 OP_EQUAL
这个脚本模板的特点是会执行两次验证
1)第一层验证,红色部分是锁定脚本,蓝色部分是解锁脚本
2)第二层验证,蓝色部分是锁定脚本,紫色部分是解锁脚本

/********************************
* 本文来自****博主"爱踢门"
* 转载请标明出处:http://blog.****.net/itleaks
******************************************/
比特币源码情景分析之script脚本验证(1)