Qtum-x86智能合約創(chuàng)建過程
Qtum-x86虛擬機與以太坊EVM最大的區(qū)別之一就是智能合約實現(xiàn)過程。一般來說,智能合約開發(fā)人員會使用Remix,甚至用solc來進行開發(fā)工作,以便將合約編譯成字節(jié)碼。在EVM合約中,發(fā)送到區(qū)塊鏈的字節(jié)碼就是從“0”處開始執(zhí)行。當?shù)V工(或質押人)構建區(qū)塊并將合約交易完成上鏈時,字節(jié)開始執(zhí)行。底層的執(zhí)行過程基本概括如下:
1. 交易字節(jié)碼被發(fā)送到區(qū)塊鏈節(jié)點
2. 礦工/質押人開始構建一個新區(qū)塊,并開始完成包含交易的字節(jié)碼
3. 字節(jié)碼從“0”處開始執(zhí)行
4. 執(zhí)行為智能合約構造函數(shù)生成的solidity字節(jié)碼
5.“持久的(persistent)”字節(jié)碼被復制到內存中(即所需的全部數(shù)據(jù)和字節(jié)碼,通常不包括構造函數(shù))
6.構造函數(shù)修改后的“常量”在內存中更改,以匹配要部署的最終版本
7.合約執(zhí)行結束,同時指定了應該持久化到區(qū)塊鏈的內存范圍
8. 區(qū)塊鏈將數(shù)據(jù)保存到以太坊的Global State Trie,通過“code”字段將要持久化的字節(jié)碼與地址關聯(lián)起來
再次調用合約時,會執(zhí)行以下過程:
1. 當調用合約時,執(zhí)行從已持久化的合約字節(jié)碼的“0”處開始(不包括構造函數(shù))
2. Solidity自動生成的合約代碼解析ABI數(shù)據(jù),指定應對哪些函數(shù)和參數(shù)值執(zhí)行操作
3. 執(zhí)行函數(shù)
4. 函數(shù)返回的數(shù)據(jù)被復制到內存中,并在合約退出執(zhí)行時指定地址(開始地址和結束地址)
正是因為Solidity編寫可能存在相關漏洞,Qtum-x86就要求大多數(shù)智能合約開發(fā)人員需要更加細心。近期,許多智能合約編寫者都知道智能合約返回的數(shù)據(jù)大小是有固定限制的,EVM可通過一些專門的操作碼允許返回長度可變的數(shù)據(jù),當然Solidity會在需要時使用這些操作碼。
x86合約的處理過程有著明顯的不同,在許多方面更為復雜,但同時也更加靈活。其中部分原因在于它繼承了所使用的現(xiàn)有編程語言(例如,C或Rust)的差異,而沒有使用抽象了所有細節(jié)的專門構建的語言。首先,編寫合約的過程盡管更透明,但也更復雜:
1. 創(chuàng)建一個“SimpleABI”文件,該文件指定了可以從外部調用合約的接口
2. SimpleABI程序與生成“分派”代碼的ABI文件,以及其他必需的內容一起運行,這樣智能合約開發(fā)人員就可以不用關心解析和創(chuàng)建ABI數(shù)據(jù)的底層細節(jié)
3.然后,開發(fā)人員為指定的所有接口函數(shù)(當然還有其他所需的非接口函數(shù))編寫代碼-;如果沒有實現(xiàn)接口函數(shù),則會導致鏈接器錯誤
4.然后代碼被編譯成單獨的對象文件,這些對象文件會鏈成一個內聚的ELF文件(Linux和其他一些unix操作系統(tǒng)的標準二進制格式)
5. 然后,ELF文件被載入一個自定義的Qtum程序,該程序將提取出感興趣的數(shù)據(jù)并將其編譯成一個Qtum-x86字節(jié)碼格式的文件
在大多數(shù)情況下,使用Makefiles之類的工具進行設置之后,這種過程可以自動執(zhí)行,Qtum當然也會提供可以更改和修改的模板項目,以避免復雜的設置過程。
Qtum-x86字節(jié)碼格式不像Solidity那樣“扁平”。它有一個頭部區(qū)域,指定有關字節(jié)碼的一些信息,以及3個獨立的部分。頭部包含的數(shù)據(jù)有:
· 選項部分大小
· 代碼段大小
· 數(shù)據(jù)段大小
·“保留”(即尚未使用)
包含的三個部分是:
1. 選項 – 諸如選擇禁用或選擇啟用某些Qtum-x86功能的標志(例如禁用字節(jié)碼升級)或更豐富的數(shù)據(jù)(如依賴關系圖和元數(shù)據(jù))之類的東西
2. 代碼 – 合約的實際可執(zhí)行數(shù)據(jù)。該部分數(shù)據(jù)存儲為只讀和可執(zhí)行文件
3. 數(shù)據(jù) – 在合約執(zhí)行期間使用和/或修改的明文數(shù)據(jù)。該部分數(shù)據(jù)以讀寫方式存儲,且不可執(zhí)行
與EVM不同,x86所有內存都被認為既是數(shù)據(jù),也是代碼。存在一些保護機制,但它要求代碼和數(shù)據(jù)能清晰地區(qū)分開來,并存儲在單獨的內存區(qū)域中。這種保護機制是為了防止一些潛在的安全問題。盡管本身不會有安全問題,但如果代碼設計不當,就可能因為覆蓋了保護區(qū)代碼或執(zhí)行了調用方可控制的數(shù)據(jù),將小錯誤“放大”為更大的錯誤。因此,這就需要對Qtum-x86的字節(jié)碼格式進行結構化處理。
當然,ELF能夠指定所有這些內容,此外還能提供其他更多的功能,那么為什么不使用ELF作為字節(jié)碼格式呢?最大的原因是為了簡化共識模型。無論使用什么樣的字節(jié)碼格式,每個節(jié)點都必須完全一致地解析和理解這些字節(jié)碼是“共識關鍵”所在。ELF明顯更為復雜,而過去那些簡單的解析器又有幾個安全漏洞。因此,一種只保留我們所需內容的大大精簡過的格式才是最佳的選擇。這也允許編譯器的其他可能的中間輸出格式,例如PE格式(用于Windows.exe文件的格式)。
既然知道了最終的字節(jié)碼,就可以將其廣播到區(qū)塊鏈上。Qtum-x86中執(zhí)行合約和持久化合約的方法是類似的,但二者也有足夠的差異,由此可能會影響智能合約的開發(fā)人員:
1. 包含交易的字節(jié)碼被發(fā)送到區(qū)塊鏈節(jié)點
2. 礦工/質押人開始構建一個新的區(qū)塊,并開始完成包含交易的字節(jié)碼
3. 解析字節(jié)碼格式,并將每個部分被映射到VM的內存中
4. 字節(jié)碼從“代碼”內存的第0個位置開始執(zhí)行
5. 執(zhí)行由編譯器/鏈接器插入的“crt0”代碼,以初始化堆棧并準備執(zhí)行
6. 系統(tǒng)調用目的是使合約意識到正在被構建(因此不應期望出現(xiàn)合約調用)
7. 執(zhí)行智能合約構造函數(shù)代碼
8. 代碼退出并結束執(zhí)行
9. 區(qū)塊鏈持久化存儲了整個合約字節(jié)碼格式文件。字節(jié)碼作為地址的“bytecode”數(shù)據(jù)字段保存到DeltaDB中。一個表示構造、執(zhí)行和字節(jié)碼的“delta”會被插入到區(qū)塊頭中的Merkle哈希樹中
再次調用合約時,將執(zhí)行以下過程:
1. 調用合約時,執(zhí)行從“代碼”部分的“0”開始,與創(chuàng)建合約時的執(zhí)行方式相同。
2. 執(zhí)行由編譯器/鏈接器插入的“crt0”代碼,以初始化堆棧并準備執(zhí)行
3. 系統(tǒng)調用目的是使合約意識到正在被構建(因此不應期望出現(xiàn)合約調用)
4. 執(zhí)行SimpleABI生成的代碼并解析發(fā)送給合約的ABI數(shù)據(jù)。
5. SimpleABI代碼從“共享的合約通信堆?!敝蝎@取ABI數(shù)據(jù),并將其復制到參數(shù)堆棧中,并執(zhí)行用戶實現(xiàn)的接口函數(shù)。
6. 執(zhí)行用戶實現(xiàn)的功能和邏輯
7. 將邏輯的返回數(shù)據(jù)放入?yún)?shù)堆棧中并退出函數(shù)
8. SimpleABI生成的代碼獲取返回數(shù)據(jù)并將其推送到合約通信堆棧上
9. 退出合約
評論