bitcoin 源碼解析 - 交易 Transaction(三) - Script
之前的章節已經比較粗略的解釋了在Transaction體系當中的整體運作原理。接下來的章節會對這個體系進行分解,比較詳細描述細節的構成。
本章將要詳細分析bitcoin交易中的交易腳本-script到底是什么東西。
回顧和概要
在前面的文章中提到,在bitcoin的體系中,一個交易是被發布到比特幣的整體系統中的,而能夠操控之前交易的的TxOut(被鎖住的coin),是需要能夠操控這個TxOut的人提供"鑰匙"來控制。就像前文描述的,coin在整個系統中是像流水一樣的在體系中進行流通,而coin在其中在分叉點的時候會有一個像 “鎖” 的東西把coin鎖在這個節點上。而根據這個鎖產生了一個新的交易,繼續流通被這個鎖所鎖住的coin,是需要提供一個"鑰匙"的。
所以這里的比喻:“鎖”和“鑰匙”就是比特幣交易中的交易腳本Script
其中
“鎖” 對應着 scriptPubKey
“鑰匙”對應着 scriptSig
但是單純的把Script理解為“鎖”和“鑰匙”實在是太淺薄了。只能完成這點事情的並不能體現Script 的強大,也無法對后人創立“智能合約”有所啟發。
所以在我看來,比特幣的Script實際上是:
scriptPubKey 是上一個交易(out)提出的一個 “問題”
而
scriptSig 是我想使用上一個交易中錢,那么我就對你提出的這個問題提供我的“答案”
因為公私鑰的關系,所以如果scriptPubKey 提出的問題是公鑰相關的問題,那么很明顯,只有持有私鑰的人才能回答這個問題,所以就簡化為剛才的所說的“鎖”和“鑰匙”的關系。
而另一方面,如何確認提供的“答案”就是能回答“問題”的呢?這就說明Script是需要被執行驗證的,而且這個驗證的過程只需要txin提供的scriptSig 和驗證者自己從自己的記錄中找到的txout的scriptPubKey ,而這個驗證者就是廣大的礦工們。
整個系統精妙的地方就在於,scriptPubKey是驗證者(礦工)各自獨立持有的東西,其安全性由自己所保證的,而想要完成交易的人只需要提供scriptSig給廣大驗證者就行,不需要一些多余的上下文(可以理解為上下文由驗證者自己持有,雖然大家都互不信任,但是對於最廣大的人來說,這個上下文都是相同的)。
另一個方面不太被大多數人所注意到的是:
實際上剛才的模型簡化為了“問題”和“答案”,但是這個“問題”可不是很容易提供的。
這個“問題”應該滿足2個方面的要求:
- 問題的答案必須是十分明確的,唯一的,不能是個模糊的要求(這點在代碼中就是“代碼就是法律”的體現吧(笑),或許這就是智能合約無法完成真正人們所向往的替代所有合同執行的原因,因為合同雖然簽訂了,但是其中的內容其實很多是有討價划價,鑽空子的空間的)
- 答案必須容易的被驗證而不需要其他上下文環境。(這點就是這個問題提出的困難的地方,也就是這個問題要么正向很難,逆向很容易,要么驗證需要提供其他的附加的上下文環境。)
而公私密鑰的模式其實是完美的符合了這2方面的要求的。
那么有沒有其他的問題呢?那是當然有的,比如我提出了一個數學問題,這個問題的解是唯一的並且可以很容易的驗證我的回答對不對
那么我就可以創建一筆交易,而這筆交易的txin就提供這個問題的答案,只要我的這個tx優先被礦工打包進入區塊中,並成為最長鏈,那么這個問題下的錢就歸我了。
這個場景就是符合正向很難,逆向容易的場景。
接下來就解釋 比特幣系統中的 CScript 到底是怎么運作的。
CScript
在比特幣源碼當中,對於CScript 單獨列出了 script.c/script.h 來實現這塊體系(對比把tx,block等所有實現全部放在main.c/.h來說),可見得中本聰在一開始設計這套體系的時候就把這塊的內容看的相當的重要。事實上這套體系也確實很復雜,但是也是得益於這套體系,才能取得現在的地位,如果沒有這個設計,比特幣的實用性會被大幅度減弱。
class CScript : public vector<unsigned char> { // 把各種類型的數據序列化到 vector 中 CScript& operator<<(char b) { return (push_int64(b)); } CScript& operator<<(short b) { return (push_int64(b)); } CScript& operator<<(int b) { return (push_int64(b)); } CScript& operator<<(long b) { return (push_int64(b)); } CScript& operator<<(int64 b) { return (push_int64(b)); } CScript& operator<<(unsigned char b) { return (push_uint64(b)); } CScript& operator<<(unsigned int b) { return (push_uint64(b)); } CScript& operator<<(unsigned short b) { return (push_uint64(b)); } CScript& operator<<(unsigned long b) { return (push_uint64(b)); } CScript& operator<<(uint64 b) { return (push_uint64(b)); } CScript& operator<<(opcodetype opcode) CScript& operator<<(const uint160& b) CScript& operator<<(const uint256& b) CScript& operator<<(const CBigNum& b) CScript& operator<<(const vector<unsigned char>& b) { // } bool GetOp(const_iterator& pc, opcodetype& opcodeRet, vector<unsigned char>& vchRet