1. 程式人生 > >比特幣交易指令碼詳解

比特幣交易指令碼詳解

其實我們可以這樣看待比特幣的交易:『交易的發起者懸賞若干比特幣,在網路上貼出了一到數學題,誰解出了這道數學題,懸賞就歸誰了』。 順著這個思路,Alice對Bob的轉賬可以理解為『Alice把一道只有Bob才能解開的數學題發到網路上,Bob解出題並拿走了懸賞』。那麼,每個交易資料中都會出現的『指令碼』就是題和解,『指令碼語言』就是用來描述題和解的工具。

pic01

『輸入指令碼』和『輸出指令碼』

Hash:

9c50cee8d50e273100987bb12ec46208cb04a1d5b68c9bea84fd4a04854b5eb1

輸入交易:

前導輸入的Hash:
437b95ae15f87c7a8ab4f51db5d3c877b972ef92f26fbc6d3c4663d1bc750149

輸入指令碼 scriptSig:
3045022100efe12e2584bbd346bccfe67fd50a54191e4f45f945e3853658284358d9c062ad02200121e00b6297c0874650d00b786971f5b4601e32b3f81afa9f9f8108e93c752201
038b29d4fbbd12619d45c84c83cb4330337ab1b1a3737250f29cec679d7551148a

輸出交易:

轉賬值:
0.05010000 btc

輸出指令碼 scriptPubKey:
OP_DUP OP_HASH160 be10f0a78f5ac63e8746f7f2e62a5663eed05788 OP_EQUALVERIFY OP_CHECKSIG

假設Alice是轉賬傳送者,Bob是接受者。那麼『輸入交易』表明了Alice要動用的比特幣的來源,『輸出交易』表明了Alice要轉賬的數額和轉賬物件——Bob。那麼,你可能要問,資料中的『輸入指令碼』和『輸出指令碼』是不是就是題和解?對了一半!

原先發送幣的一方,控制指令碼執行,以便比特幣在下一個交易中使用。想花掉幣的另一方必須把以前記錄的執行為真的指令碼,放到輸入區。

換句話說,在一個交易中,『輸出指令碼』是數學題,『輸入指令碼』是題解,但不是這道數學題的題解。我開始看Wiki的時候,在這裡遇到了一些障礙,沒法理解『輸入指令碼』和『輸出指令碼』的聯絡。但是在考慮交易間的關係後,就明白了。

假設有這麼一系列交易:
pic02
1. 上圖的三個交易都是單輸入單輸出交易
2. 每個『輸入交易』『輸出交易』中,都包含對應的『指令碼』
3. 交易a,Alice轉賬給Bob;交易b,Bob轉賬給Carol;交易c,Carol轉賬給Dave
4. 當前交易的『輸入』都引用前一個交易的『輸出』,如交易b的『輸入』引用交易a的『輸出』

按照之前的說法,交易a中的『輸出指令碼』就是Alice為Bob出的數學題。那麼,Bob想要引用交易a『輸出交易』的比特幣,就要解開這道數學題。題解是在交易b的『輸入指令碼』裡給出的!Bob解開了這道題,獲得了獎金,然後在交易b中為Carol出一道數學題,等待Carol來解…

所以說,下圖中相同顏色的『輸出』和『輸入』才是一對題和解:
pic03

比特幣在交易中使用腳本系統,與FORTH(一種編譯語言)一樣,指令碼是簡單的、基於堆疊的、並且從左向右處理,它特意設計成非圖靈完整,沒有LOOP語句。

要理解比特幣指令碼,先要了解『堆疊』,這是一個後進先出(Last In First Out )的容器,腳本系統對資料的操作都是通過它完成的。比特幣腳本系統中有兩個堆疊:主堆疊和副堆疊,一般來說主要使用主堆疊。舉幾個簡單的例子,看下指令是如何對堆疊操作的(完整的指令集在Wiki裡可以找到):

  • 常數入棧:把一段常數壓入到堆疊中,這個常數成為了棧頂元素

pic04

  • OP_DUP:複製棧頂元素

pic05

  • OP_EQUALVERIFY:檢查棧頂兩個元素是否相等

pic06

交易b中有一個『輸入交易』引用了交易a的『輸出交易』,它們的指令碼是一對題與解:
題:交易a的『輸出指令碼』,若干個指令碼指令和轉賬接收方的『公鑰雜湊』

OP_DUP OP_HASH160 be10f0a78f5ac63e8746f7f2e62a5663eed05788 OP_EQUALVERIFY OP_CHECKSIG

解:交易b的『輸入指令碼』,這麼一長串只是兩個元素,『簽名』和『公鑰』(sig & pubkey)

3046022100ba1427639c9f67f2ca1088d0140318a98cb1e84f604dc90ae00ed7a5f9c61cab02210094233d018f2f014a5864c9e0795f13735780cafd51b950f503534a6af246aca301
03a63ab88e75116b313c6de384496328df2656156b8ac48c75505cd20a4890f5ab

下面來看下這兩段指令碼是如何執行,來完成『解題』過程的。
1. 首先執行的是『輸入指令碼』。因為指令碼是從左向右執行的,那麼先入棧的是『簽名』,隨後是『公鑰』
pic07

2. 接著,執行的是『輸出指令碼』。從左向右執行,第一個指令是OP_DUP——複製棧頂元素
pic08

3. OP_HASH160——計算棧頂元素Hash,得到pubkeyhash
pic09

4. 將『輸出指令碼』中的『公鑰雜湊』入棧,為了和前面計算得到的雜湊區別,稱它為pubkeyhash’
pic10

5. OP_EQUALVERIFY——檢查棧頂前兩元素是否相等,如果相等繼續執行,否則中斷執行,返回失敗
pic11

6. OP_CHECKSIG——使用棧頂前兩元素執行簽名校驗操作,如果相等,返回成功,否則返回失敗
pic12

這樣一串指令執行下來,就可以驗證這道數學題是否做對了,也就是說驗明瞭想要花費『錢包地址』中比特幣的人是否擁有對應的『私鑰』。上面的執行過程是可以在指令碼模擬器中執行的,能夠看到每一步執行的狀態,感興趣的童鞋可以嘗試一下。

其實除了標準的P2PKH交易指令碼,還有P2SH的Multi-Sig指令碼以及真正的『解謎交易』指令碼,我們可以在今後接著討論。

參考
[1] 申屠青春(我看比特幣), 【比特幣指令碼】
[2] Bitcoin Wiki, 【Script】