如何通過UTXO交易實(shí)現(xiàn)支持EVM規(guī)范的智能合約
Qtum賬戶抽象層(AAL)實(shí)現(xiàn)簡析
Qtum設(shè)計(jì)上以比特幣UTXO為基礎(chǔ)賬戶模型,并實(shí)現(xiàn)了支持EVM規(guī)范的智能合約,這是通過賬戶抽象層(Account Abstract Layer, AAL)來完成的。AAL對(duì)UTXO賬戶和EVM合約賬戶之間進(jìn)行了適配,這樣通過AAL可以使用UTXO交易輸出實(shí)現(xiàn)在鏈上創(chuàng)建智能合約,發(fā)送交易到合約賬戶用于觸發(fā)合約的執(zhí)行,完成執(zhí)行后AAL最終對(duì)執(zhí)行結(jié)果進(jìn)行處理并適配至UTXO。由于采用了AAL,合約開發(fā)者不需關(guān)心對(duì)合約操作相關(guān)的UTXO轉(zhuǎn)換細(xì)節(jié),即可使用EVM的特性進(jìn)行開發(fā)而且兼容現(xiàn)有以太坊的智能合約。本文通過對(duì)從UTXO交易到智能合約執(zhí)行的實(shí)現(xiàn)代碼進(jìn)行解讀,初略分析了AAL的工作過程。
1.UTXO交易新增的腳本操作碼
Qtum 針對(duì)UTXO交易腳本新增了三個(gè)操作碼OP_CREATE,OP_CALL和OP_SPEND,目的是用于為UTXO和EVM賬戶模型之間的轉(zhuǎn)換提供操作支持。這些操作碼定義在opcodetype枚舉類型中:
這個(gè)三個(gè)操作碼分別有以下作用:
OP_CREATE用于智能合約的創(chuàng)建;
OP_CALL用于合約的執(zhí)行;
OP_SPEND用于合約余額的花費(fèi)。
為了在區(qū)塊生成過程中,識(shí)別并處理由這幾個(gè)操作碼控制的交易,在用于UTXO模型交易的類CTransaction中增加了HasCreateOrCall()和HasOpSpend()函數(shù),用于新區(qū)塊中對(duì)mempool中的交易處理,并在腳本操作碼解析的EvalScript()函數(shù)中增加了相應(yīng)的處理。
2.UTXO交易到EVM模型交易的轉(zhuǎn)換
產(chǎn)生新的區(qū)塊時(shí),除了對(duì)UTXO交易進(jìn)行常規(guī)的參數(shù)合法性、共識(shí)規(guī)則、DDOS攻擊檢查等之外,還需要使用操作碼檢查函數(shù)HasCreateOrCall()判斷交易輸出是否包含OP_CREATE或OP_CALL,分別對(duì)應(yīng)著EVM需要執(zhí)行合約創(chuàng)建或合約調(diào)用。這部分有以下的處理過程:
2.1 進(jìn)行EVM模型的賬號(hào)參數(shù)提取
合約在EVM的執(zhí)行用到了data、gasPrice、gasLimit、VM version這幾個(gè)參數(shù),這些參數(shù)是通過RPC調(diào)用sendtocontract 發(fā)送的,sendtocontract會(huì)生成一個(gè)UTXO交易,并在交易輸出中使用了OP_CALL操作碼,之后交易會(huì)廣播到區(qū)塊鏈網(wǎng)絡(luò)上。AAL中從UTXO到EVM的適配是通過QtumTxConverter類實(shí)現(xiàn)的,在這一步中該類的成員函數(shù)extracTIonQtumTransacTIons()和parseEthTXParams()完成對(duì)所有此類UTXO交易輸出的參數(shù)提取。代碼片段如下:
以上代碼首先判斷如果opcode 為OP_CALL,則說明地址為vecAddr的合約已經(jīng)創(chuàng)建,因此直接轉(zhuǎn)換成EVM格式的地址receiveAddress,否則為OP_CREATE,對(duì)應(yīng)合約的創(chuàng)建,沒有該字段,所以不做提取。接下來依次完成了data、gasPrice、gasLimit、VM version的提取,這些都是EVM執(zhí)行bytecode時(shí)必不可少的參數(shù)。
2.2 進(jìn)行EVM賬戶模型的交易轉(zhuǎn)換
交易轉(zhuǎn)換是通過QtumTxConverter類的函數(shù) createEthTX()完成,使用前面一步提取的參數(shù)和UTXO的交易輸出vout創(chuàng)建了QtumTransacTIon類型的交易。由于QtumTransacTIon派生自EVM中的dev::eth::Transaction類,因此和EVM執(zhí)行相關(guān)的操作QtumTransaction類都支持。
首先代碼etp.receiveAddress == dev::Address()判斷該合約是EVM狀態(tài)中沒有而需要新創(chuàng)建的還是EVM狀態(tài)已經(jīng)包含的合約,差別只在于合約地址。然后,QtumTransaction()構(gòu)造函數(shù)完成了部分的交易參數(shù)構(gòu)造,接下來的語句提取交易的發(fā)送者(sender),之后設(shè)置交易HASH。一個(gè)UTXO交易支持多個(gè)輸入和輸出,Qtum的AAL設(shè)計(jì)考慮到了這種情況,因此AAL支持一個(gè)交易輸出包含UTXO賬號(hào)和合約賬號(hào),通過最后設(shè)置的nOut指示該交易的nOut輸出是發(fā)送到智能合約的,所以該輸出將觸發(fā)合約執(zhí)行。這樣就按照EVM的賬號(hào)模型完成了交易的轉(zhuǎn)換。
3.合約執(zhí)行及執(zhí)行結(jié)果的UTXO轉(zhuǎn)換
合約的執(zhí)行會(huì)改變狀態(tài)(由QtumState類的實(shí)例化對(duì)象globalState統(tǒng)一管理),對(duì)于合約的狀態(tài),Qtum沿用了EVM定義,所以能兼容所有的符合EVM規(guī)范的智能合約。但是賬戶金額的轉(zhuǎn)移(transfer),Qtum做了UTXO的轉(zhuǎn)換,這意味著智能合約和普通的UTXO模型賬號(hào)之間能完成交互,這是AAL實(shí)現(xiàn)UTXO支持智能合約的重要的一環(huán)。下面簡要介紹一下合約執(zhí)行和狀態(tài)結(jié)果的轉(zhuǎn)換過程。
3.1 合約執(zhí)行環(huán)境構(gòu)建及合約執(zhí)行
合約的執(zhí)行是對(duì)合約處理中很關(guān)鍵的一步,直接對(duì)合約的狀態(tài)產(chǎn)生影響。通過ByteCodeExec類實(shí)現(xiàn)了EVM對(duì)合約bytecode的執(zhí)行,主要函數(shù)是performByteCode()。這一步的主要流程是使用上面提取的交易參數(shù),來進(jìn)行虛擬機(jī)執(zhí)行環(huán)境的構(gòu)建,之后完成合約的執(zhí)行,其代碼如下:
首先是構(gòu)建合約執(zhí)行環(huán)境,由BuildEVMEnvironment()完成??梢钥吹竭@個(gè)執(zhí)行環(huán)境是針對(duì)每個(gè)獨(dú)立交易進(jìn)行的,這樣就最大限度的把不同交易的合約執(zhí)行過程隔離開,避免合約執(zhí)行過程中的交叉影響。然后構(gòu)建一個(gè)新的sealEngine類,該類是EVM執(zhí)行引擎,由createSealEngine()函數(shù)具體完成。中間對(duì)出現(xiàn)的可能狀態(tài)異常進(jìn)行檢查,之后globalState-》execute()完成合約的執(zhí)行,這里使用到了構(gòu)建的執(zhí)行環(huán)境envInfo和EVM執(zhí)行引擎se。
3.2 合約執(zhí)行結(jié)果的UTXO轉(zhuǎn)換
合約執(zhí)行完成后的結(jié)果保存在vector《ResultExecute》 result,vector向量理記錄了每個(gè)合約執(zhí)行產(chǎn)生的EVM賬戶間transfer關(guān)系,AAL通過把這些transfer轉(zhuǎn)換成UTXO交易,完成了從EVM賬戶模型到UTXO模型交易的轉(zhuǎn)換。這一處理是通過processingResults()函數(shù)實(shí)現(xiàn)的,以下是代碼片段。
首先定義了ByteCodeExecResult類型的resultBCE變量,用于保存轉(zhuǎn)換的結(jié)果。使用操作碼OP_SPEND,用于實(shí)現(xiàn)交易的花費(fèi),這是因?yàn)楸忍貛诺腢TXO通過私鑰簽名在交易輸入解鎖后來實(shí)現(xiàn)余額花費(fèi)的,而EVM執(zhí)行涉及不同賬戶之間的transfer,所以需要通過OP_SPEND實(shí)現(xiàn)這些transfer到UTXO模型交易的轉(zhuǎn)換。如果execRes.excepted不為None,即合約執(zhí)行異常,則將余額返還給合約調(diào)用者。否則,如果沒有異常,則將扣除消耗的gas之后的剩余gas返還給合約的調(diào)用者。對(duì)于合約執(zhí)行中出現(xiàn)的transfer其UTXO交易保存在result[i].tx中。因此,經(jīng)過這一步處理合約執(zhí)行產(chǎn)生的不同UTXO賬戶之間的交易就保存在valueTransfers向量中了,最終這些交易會(huì)包含進(jìn)新的區(qū)塊中。至此AAL模塊就完成了從EVM交易到UTXO的轉(zhuǎn)換。
4.總結(jié)
AAL通過新增的UTXO腳本操作碼,協(xié)助完成合約的創(chuàng)建、執(zhí)行和花費(fèi)。在合約創(chuàng)建和執(zhí)行前,需要進(jìn)行UTXO交易到EVM模型交易的轉(zhuǎn)換,之后使用構(gòu)建的EVM執(zhí)行環(huán)境和引擎,完成合約的執(zhí)行。AAL最終對(duì)合約的執(zhí)行結(jié)果進(jìn)行處理并從EVM適配至UTXO,這樣就實(shí)現(xiàn)了基于UTXO的智能合約。AAL使得Qtum兼容符合EVM規(guī)范的智能合約,為Dapp提供一個(gè)新的基礎(chǔ)平臺(tái),同時(shí)UTXO的優(yōu)點(diǎn)使得諸如并行處理、隱私性等優(yōu)點(diǎn)能得以保留。