交易腳本和腳本語言
比特幣客戶端通過執(zhí)行腳本來驗(yàn)證交易。比特幣腳本是一個(gè)類似Forth的腳本語言。不管是UTXO上的鎖定腳本(受限),還是包含簽名的解鎖腳本,都是用這種腳本語言寫的。當(dāng)交易被驗(yàn)證時(shí),每個(gè)輸入上的解鎖腳本都將與相應(yīng)的鎖定腳本一起執(zhí)行,以查看是否符合花費(fèi)條件。
如今,比特幣網(wǎng)絡(luò)處理的大部分交易都是類似“愛麗絲付給鮑勃”這種形式的,它們都基于叫作“支付給公鑰哈?!钡南嗤_本。但是,使用腳本鎖定輸出、解鎖輸入,意味著通過使用編程語言,交易可以包含無限多的條件。比特幣交易不僅限于“愛麗絲付給鮑勃”這種形式和模式。
這還只是這種腳本語言表達(dá)能力的“冰山一角”。在本節(jié)中,我們將演示比特幣交易腳本語言的組成元素,展示它如何用于表達(dá)復(fù)雜的支付條件,以及這些條件是如何在解鎖腳本中被滿足的。
比特幣的交易驗(yàn)證引擎依賴兩種類型的腳本:一個(gè)是鎖定腳本,一個(gè)是解鎖腳本。
鎖定腳本是放置在輸出上的一個(gè)受限,它設(shè)定條件,只有滿足這些條件,輸出才能在未來被花費(fèi)掉。由于鎖定腳本通常包含公鑰或者比特幣地址,所以過去它曾被稱為腳本公鑰(scriptPubKey)。在本書中我們稱其為“鎖定腳本”,更能體現(xiàn)這個(gè)腳本技術(shù)在更多領(lǐng)域應(yīng)用的可能性。在大多數(shù)比特幣應(yīng)用中,所謂的鎖定腳本在程序源碼中一般體現(xiàn)為“scriptPubKey”。
解鎖腳本是“解決”或者滿足一個(gè)輸出上的鎖定腳本設(shè)置的條件,從而允許輸出被重新使用。解鎖腳本是每個(gè)交易輸入的一部分。大多數(shù)情況下,它們包含一個(gè)數(shù)字簽名,這個(gè)簽名由用戶的錢包應(yīng)用根據(jù)私鑰創(chuàng)建。過去,解鎖腳本被稱為腳本簽名(scriptSig),因?yàn)樗ǔ0瑪?shù)字簽名。在大多數(shù)比特幣應(yīng)用中,源碼一般把解鎖腳本寫成scriptSig。在本書中,我們稱其為“解鎖腳本”,以體現(xiàn)更廣泛的鎖定腳本要求,因?yàn)椴皇撬墟i定腳本都包含簽名。
每個(gè)比特幣客戶端都通過同時(shí)執(zhí)行鎖定和解鎖腳本來驗(yàn)證交易。對(duì)于交易中的每個(gè)輸入,驗(yàn)證軟件首先檢索被引用的UTXO。這個(gè)UTXO包含鎖定腳本,它定義了花費(fèi)輸出所需要滿足的條件。驗(yàn)證軟件隨后讀取輸入中包含的用于嘗試花費(fèi)UTXO的解鎖腳本,接下來驗(yàn)證軟件將同時(shí)執(zhí)行這兩個(gè)腳本。
在早期的比特幣客戶端中,解鎖和鎖定腳本是被連接起來并順序執(zhí)行的。出于安全考慮,這種情況在2010年被改變了,因?yàn)楫?dāng)時(shí)發(fā)現(xiàn)存在漏洞,一個(gè)允許非法的解鎖腳本推送數(shù)據(jù)入棧,并污染鎖定腳本。在現(xiàn)在的實(shí)現(xiàn)中,腳本是分開執(zhí)行的,利用堆棧在兩個(gè)腳本間傳遞數(shù)據(jù),接下來我們將進(jìn)行具體說明。
首先,利用堆棧執(zhí)行引擎運(yùn)行解鎖腳本。解鎖腳本成功運(yùn)行后[比如,沒有“懸空”(dangling)操作符],主棧(不是替代棧)將被復(fù)制,接著鎖定腳本被執(zhí)行。如果利用從解鎖腳本堆棧上復(fù)制來的數(shù)據(jù),執(zhí)行鎖定腳本的結(jié)果為“真”(TRUE),那么解鎖腳本就成功滿足了鎖定腳本設(shè)定的條件,從而輸入擁有花費(fèi)這筆UTXO的有效授權(quán)。如果組合腳本執(zhí)行完后存在任何非真的結(jié)果,則輸入是無效的,因?yàn)樗鼰o法滿足設(shè)置在UTXO上的花費(fèi)條件。請(qǐng)注意UTXO是永久記錄在區(qū)塊鏈上的,因此它不會(huì)因這筆交易失敗而變化或受到影響。只有一筆有效的、能正確滿足UTXO使用條件的交易,才會(huì)導(dǎo)致這筆UTXO被標(biāo)注為“已花費(fèi)”,并從可用(未花費(fèi))UTXO集合中被移除。
圖5.1是一個(gè)最普通的比特幣交易(支付到公鑰哈希)的解鎖和鎖定腳本的例子,顯示了在腳本驗(yàn)證前將解鎖和鎖定腳本連接形成組合腳本的結(jié)果。
圖5.1 組合scriptSig和scriptPubKey來評(píng)估一個(gè)交易腳本
比特幣交易腳本語言,叫作腳本,是一個(gè)與Forth類似的逆波蘭式表示的基于堆棧的執(zhí)行語言。如果聽起來感覺亂七八糟,可能是因?yàn)槟銢]有學(xué)習(xí)過20世紀(jì)60年代的編程語言。腳本是一種非常簡單的語言,只能執(zhí)行限定的功能并且只能在一些特定的硬件上執(zhí)行,它就像嵌入式設(shè)備,比如手持計(jì)算器,一樣簡單。它只要求最低的處理能力,也不能像其他現(xiàn)代編程語言一樣可以做很多有意思的事情。在可編程貨幣的情境下,它其實(shí)是一種特別設(shè)計(jì)的安全特性。
比特幣的腳本語言被稱作基于堆棧的語言,因?yàn)樗褂昧艘环N叫作堆棧的數(shù)據(jù)結(jié)構(gòu)。堆棧是一種非常簡單的數(shù)據(jù)結(jié)構(gòu),你可以將它看作一堆卡片。堆棧只運(yùn)行兩種操作,入棧(push)和出棧(pop)。入棧是將一個(gè)項(xiàng)目添加到棧的頂部。出棧則從棧頂部移除一個(gè)項(xiàng)目。
腳本語言通過從左到右處理每個(gè)項(xiàng)目來執(zhí)行腳本。數(shù)字(數(shù)據(jù)常量)被壓入棧中。操作符將一個(gè)或多個(gè)參數(shù)壓入棧中,或者從棧中移除,操作它們,并有可能將結(jié)果壓入棧中。例如,OP_ADD從棧中移出兩個(gè)項(xiàng)目,把它們相加,然后將求和結(jié)果壓入棧中。
條件操作符評(píng)估一個(gè)條件,產(chǎn)生一個(gè)真(TRUE)或假(FALSE)的布爾結(jié)果。比如,OP_EQUAL從堆棧中移出兩個(gè)項(xiàng)目,如果兩個(gè)項(xiàng)目相等,則把TRUE(TRUE以數(shù)字1代表)壓入棧中,如果不相等,則壓入FALSE(用數(shù)字0表示)。比特幣交易腳本通常都會(huì)包含條件操作符,因此可以產(chǎn)生TRUE的結(jié)果來表示一個(gè)有效交易。
在圖5.2中,腳本23 OP_ADD 5 OP_EQUAL演示了算術(shù)加法操作符OP_ADD,將兩個(gè)數(shù)相加,并把結(jié)果壓入棧中;接著,條件操作符OP_EQUAL檢查求和結(jié)果是否等于5。為了更加簡潔,“OP_”前綴在后面的操作示例中將被省略。
以下是一個(gè)稍微復(fù)雜的腳本,用于計(jì)算2+7-3+1。注意,當(dāng)腳本在一行中包含多個(gè)操作符時(shí),堆棧允許一個(gè)操作符的結(jié)果被下一個(gè)操作符使用。
請(qǐng)使用鉛筆和紙張驗(yàn)證一下前面的腳本。當(dāng)腳本執(zhí)行完畢,你會(huì)發(fā)現(xiàn)堆棧中只剩一個(gè)TRUE值。
雖然大多數(shù)鎖定腳本都指向比特幣地址或者公鑰,從而要求證明所有者花費(fèi)這筆資金的權(quán)限,但實(shí)際上,腳本并不要求一定要那么復(fù)雜。任何鎖定和解鎖腳本的組合,只要能夠得到一個(gè)為“真”的結(jié)果,就是有效的。前面用于說明腳本語言的例子中,簡單的算術(shù)運(yùn)算也是一個(gè)有效的鎖定腳本,可用于鎖定一筆交易輸出。
使用算術(shù)運(yùn)算例子的一部分作為鎖定腳本。
只要交易包含這樣一個(gè)解鎖腳本,以上條件就能得到滿足。
驗(yàn)證軟件將上述鎖定和解鎖腳本進(jìn)行組合,形成如下腳本。
我們可以在圖5.2的操作范例中看到,當(dāng)這個(gè)腳本被執(zhí)行后,結(jié)果是OP_TRUE,使得交易有效。不僅這個(gè)交易輸出鎖定腳本有效,只要有一點(diǎn)算術(shù)技巧,知道數(shù)字2能滿足算術(shù)運(yùn)算結(jié)果,任何人都能夠花費(fèi)這筆UTXO。
圖5.2 比特幣腳本簡單數(shù)學(xué)運(yùn)算的驗(yàn)證過程
比特幣交易腳本語言包含很多操作符,但是特意在一方面進(jìn)行了限制——沒有循環(huán),也沒有條件控制以外的復(fù)雜流程控制能力。這使得這種語言不是圖靈完備的,意味著腳本的復(fù)雜性有限,執(zhí)行時(shí)間也可以預(yù)測。腳本不是通用語言。這些限制條件確保該語言不能用于創(chuàng)建無限循環(huán)或者其他形式的“邏輯炸彈”,從而避免這些伎倆以某種方式嵌入到交易中,并導(dǎo)致對(duì)比特幣網(wǎng)絡(luò)產(chǎn)生拒絕服務(wù)攻擊。記住,任何交易均會(huì)被網(wǎng)絡(luò)上的所有完全節(jié)點(diǎn)驗(yàn)證。一個(gè)限定功能的語言能夠防止交易驗(yàn)證機(jī)制被當(dāng)作弱點(diǎn)利用。
比特幣交易腳本語言是無狀態(tài)的,在腳本執(zhí)行前沒有狀態(tài),執(zhí)行后也不會(huì)保存狀態(tài)。如此,所有需要被腳本執(zhí)行的信息必須包含在腳本當(dāng)中??梢灶A(yù)見的是,腳本在所有系統(tǒng)中都會(huì)以同樣的方式運(yùn)行。如果你的系統(tǒng)驗(yàn)證了腳本,可以確定其他比特幣網(wǎng)絡(luò)中的系統(tǒng)也一樣能夠驗(yàn)證這個(gè)腳本。也就是說,一個(gè)有效的交易對(duì)任何人都是同樣有效的,而且任何人都知道這點(diǎn)。這種可預(yù)測結(jié)果的特性是比特幣系統(tǒng)的一個(gè)重要特點(diǎn)。
免責(zé)聲明:以上內(nèi)容源自網(wǎng)絡(luò),版權(quán)歸原作者所有,如有侵犯您的原創(chuàng)版權(quán)請(qǐng)告知,我們將盡快刪除相關(guān)內(nèi)容。