為什么會提出EVM租賃機制?
區(qū)塊鏈基礎設施技術背后的邏輯都面臨著一個問題:“區(qū)塊鏈虛擬機數(shù)據(jù)越來越多怎么辦?”,因此存儲空間的消耗長期以來一直是區(qū)塊鏈領域內(nèi)一個受人關注的話題。任何區(qū)塊鏈,尤其是具有智能合約功能和附加狀態(tài)的區(qū)塊鏈,據(jù)估計存儲空間的需求會在短短10年內(nèi)超出大多數(shù)計算機和服務器的存儲能力。
這種磁盤空間需求的膨脹將導致全節(jié)點會集中到那些能負擔得起價格高昂的具有非常大存儲的價格的人手中。簡而言之,未來就會出現(xiàn)由于存儲設備的門檻導致資源的集中,也就與去中心化背道而馳。
舉例而言,數(shù)年來隨著區(qū)塊鏈市場的蓬勃發(fā)展,以太坊交易數(shù)量越來越多,單個區(qū)塊體積的最大值限制使得區(qū)塊空余空間顯得越來越小。如圖,相比比特幣而言以太坊的區(qū)塊大小更加呈現(xiàn)增量上升的模式,甚至在2017年后以太坊的區(qū)塊大小由不足0.1TB上升至接近0.4TB。
因此,越來越多的開發(fā)者和相關技術都在進行底層基礎設施的探索,想要商業(yè)應用于區(qū)塊鏈技術真正實現(xiàn)技術的融合,就需要不斷去提出新的思想和新的技術探索,因此本文提出共享存儲的設想,幫助區(qū)塊鏈的基礎設施早一步更好的搭建商業(yè)設施的橋梁。
QIP-17:Qtum-x86內(nèi)的存儲區(qū)租賃
對此在Qtum x86設計上所做的變更可以劃分為以下3個部分:
DeltaDB 重新傳播和租賃行為
“休眠”狀態(tài)的智能合約行為
“喚醒”休眠狀態(tài)的方法
首先,用于不同狀態(tài)的術語解釋
· 活躍:該狀態(tài)要求支付一定的租金,從而在區(qū)塊鏈上保持其活躍性并易于訪問
· 休眠:未能在適當?shù)臅r間內(nèi)支付租金時所處的狀態(tài),并且在沒有通過交易進行重新傳播的情況下不能通過智能合約直接訪問
· 喚醒:這是將休眠狀態(tài)恢復到活躍狀態(tài)的動作,以便可以再次通過智能合約直接訪問它
DeltaDB 重新傳播和租賃行為
合約的每一個狀態(tài),包括它自己的字節(jié)碼,都有一個通過區(qū)塊高度表示的租金計時器。一旦該計時器值為0,就會從活躍狀態(tài)切換到休眠狀態(tài),并且節(jié)點可以安全地從其內(nèi)部數(shù)據(jù)庫中刪除與該狀態(tài)相關的大部分數(shù)據(jù)。當訪問或修改狀態(tài)時,會隱性地進行租金支付,這會被計入至該操作的gas開銷中。當通過訪問數(shù)據(jù)進行租金支付時,該狀態(tài)會將其計時器重置為RENT_TERM。無法通過預付款的方式將一個狀態(tài)的計時器設置為大于RENT_TERM的值。
使用DeltaDB當前的共識模型時,讀取一個狀態(tài)(通常)不會向DeltaDB證明樹添加新的delta(狀態(tài)更改/通知)。使用本文提出的存儲區(qū)租金提案,每個狀態(tài)訪問都會通過向DeltaDB證明樹提交一個delta從而引起狀態(tài)的“重新傳播”。雖然這對合約甚至大多數(shù)區(qū)塊鏈開發(fā)人員而言都沒有影響,但還是會帶來許多副作用:
· SPV(輕錢包)節(jié)點可以證明狀態(tài)最近一次租金支付的時間
· 相反,它允許可以從SPV或全節(jié)點的內(nèi)部數(shù)據(jù)庫中刪除狀態(tài)和大多數(shù)證明開銷的證明
· SPV節(jié)點可以更快地獲得狀態(tài)數(shù)據(jù)的抗審查證明,通過更頻繁地傳播合約中最常用的數(shù)據(jù),需要掃描的區(qū)塊也更少
· 除了喚醒狀態(tài)所需的開銷之外,這不會消耗額外的區(qū)塊空間,因為DeltaDB證明樹會以單個32字節(jié)長的哈希值的形式保存在區(qū)塊頭中,而不會帶來其他的開銷
· 由于能夠證明不再需要比RENT_TERM更舊的數(shù)據(jù), 這可以大大降低Qtum-x86區(qū)塊鏈理論上的最大磁盤空間消耗,尤其是在進行修剪操作時。通過修剪,經(jīng)過500個區(qū)塊后,大多數(shù)喚醒狀態(tài)下的交易就不再需要存儲了
當然,這將限制節(jié)點存儲的數(shù)據(jù)僅限于持續(xù)共識所需的數(shù)據(jù)。區(qū)塊鏈上的證明總是可用的,例如出版證明等用例。然而,這些證明通常只會被添加一次,并且之后也是偶爾才會被訪問,因此對于共識而言是非必要的。
對于每個休眠狀態(tài),節(jié)點需要記錄以下數(shù)據(jù):
· 狀態(tài)最后一次傳播所處的區(qū)塊高度(即最后一次支付租金的時間)
· 索引數(shù)據(jù)的密鑰哈希
智能合約
大多數(shù)關于存儲租賃設計的提案都要求智能合約具有明確且易錯的租金管理和意識。在這種設計中,一切都是隱性的,在特定合約設計之外不需要進行租金檢測操作。通過這個提案,一定程度上會對不可避免的異常行為產(chǎn)生影響,包括合約試圖訪問休眠狀態(tài)時拋出的異常。
· 與以太坊的異常模型會消耗所有的gas不同,該機制只會消耗合約產(chǎn)生異常時的那部分gas,以及一定的“異常稅”
· 所有修改后的狀態(tài)都會被恢復,這點與以太坊的異常模型類似,并且這些被恢復的狀態(tài)不會在DeltaDB中傳播
· 在發(fā)生異常之前訪問的所有活躍狀態(tài)都會有租金支付,因此這些狀態(tài)會在DeltaDB中傳播
· 如果活躍狀態(tài)被修改了并且實際執(zhí)行過程中從未讀取過該狀態(tài),則狀態(tài)不會有租金支付,因此也不會在DeltaDB中傳播。如果執(zhí)行沒有以異常結束,則將傳播修改后的新狀態(tài)
· 如果執(zhí)行附加了喚醒狀態(tài),則此狀態(tài)會被標記為“已訪問”,因此即使在執(zhí)行中出現(xiàn)異常,該狀態(tài)仍會在DeltaDB中傳播并恢復。請注意,恢復狀態(tài)下存在“喚醒稅”,必須在合約執(zhí)行開始前支付。如果發(fā)送到賬戶的用于支付喚醒稅的gas數(shù)太少,則將不會進行任何恢復操作,除了返回表明執(zhí)行失敗的收據(jù)之外,不會執(zhí)行其他的操作并且所有gas都會被消耗掉
· 如果執(zhí)行附加了喚醒狀態(tài),但該狀態(tài)已經(jīng)處于被喚醒的狀態(tài),那么這個已經(jīng)處于喚醒狀態(tài)的狀態(tài)將被忽略,并且也不會消耗任何gas。這使得那些為確保合約成功執(zhí)行而謹慎地加入即將到期的喚醒狀態(tài)的人不必支付成本。附加的休眠狀態(tài)將被喚醒并需支付喚醒稅
· 在上述這些被附加的狀態(tài)已經(jīng)處于喚醒狀態(tài)的情況下,該狀態(tài)會被認為是已訪問的,因此會在DeltaDB中傳播并且INDEX_TAX + PROP_TAX將按狀態(tài)鍵收費
這種隱性租金支付和異常設計方案意味著大多數(shù)合約完全不需要擔心租賃機制的正常運作。但是,對于那些需要對租賃機制有一些自我意識的合約,則需要添加一些額外的系統(tǒng)接口:
uint32_t remainingRent(uint8_t * key,size_t keylen); -- 針對區(qū)塊而言,將返回特定狀態(tài)鍵所需支付的剩余租金。如果狀態(tài)處于休眠狀態(tài)或尚未寫入,則返回0
uint32_t remainingExternalRent(UniversalAddressABI * target,uint8_t * key,size_t keylen); -- 與remainingRent方法的行為相同,但作用于外部合約
uint32_t remainingExternalBytecodeRent(UniversalAddressABI * target); -- 與remainingExternalRent方法的行為相同,但該方法會檢查外部合約的字節(jié)碼而不是狀態(tài)鍵。請注意,不需要內(nèi)部版本,因為通過執(zhí)行合約的行為,剩余的租金將始終是RENT_TERM
uint32_t BlockData-》 RENT_TERM -- 這是一個區(qū)塊常量(可能之后會由DGP修改),其最大租期為
GAS模型
當前Qtum-x86虛擬機中用于存儲的gas模型設計還沒有完全實現(xiàn),不然要是實現(xiàn)了的話,本提議將完全地改變它。所以,現(xiàn)在最好是暫時放下手中的設計工作。
定義:
PROP_TAX -- 對任何添加到DeltaDB樹的傳播收取的稅費
READ_TAX(size) -- 從節(jié)點數(shù)據(jù)庫讀取狀態(tài)所收取的稅費。這個開銷不是固定不變的,可能是由最小成本加上一定長度后的每字節(jié)成本構成。這會對存儲開銷產(chǎn)生影響,例如將數(shù)據(jù)復制到VM內(nèi)存中
EXEC_TAX -- 為了執(zhí)行合約而初始化一個新的VM實例所收取的稅費
SHORT_READ_TAX(size) -- 當前執(zhí)行中讀取先前從數(shù)據(jù)庫讀取的狀態(tài)所收取的稅費。其他方面與READ_TAX類似
INDEX_TAX(key_size) -- 對數(shù)據(jù)庫中數(shù)據(jù)建立索引收取的稅費。這設計的相對便宜,并且包括了在需要時對密鑰進行哈希的成本
WRITE_TAX(size) -- 將狀態(tài)寫入數(shù)據(jù)庫而收取的稅費
EXTERNAL_TAX -- 訪問外部賬戶狀態(tài)的一小筆額外稅費
LIBEXEC_COST -- 執(zhí)行任何可信庫合約的固定成本。請注意,為防止濫用,可信庫合約的大小存在嚴格限制
STORE_REFUND(size) -- 假設狀態(tài)減少為0,則因修改狀態(tài)而給予的退款
DIRTY_STORE_REFUND(old_size) -- 與STORE_REFUND類似,但如果狀態(tài)減少為0并且它是在當前執(zhí)行中創(chuàng)建的(即,它從未寫入數(shù)據(jù)庫),則返回一筆更大金額的退款
PROP_REFUND -- 如果狀態(tài)在執(zhí)行開始時未建立,而在執(zhí)行期間建立,且在執(zhí)行完成之前減少到0,則給予退款,這意味著不需要進行傳播。這只適用于以null狀態(tài)作為開始狀態(tài)和結束狀態(tài)的修改。即,如果狀態(tài)切換過程為“abc” - 》 0 - 》“abc”,則仍將收取PROP_TAX的費用,但如果狀態(tài)切換過程為0 - 》“abc” - 》“xyz” - 》 0,則將會退款
CLEANING_REFUND(size) -- 這是額外的退款,用來激勵對存儲進行清理。僅在狀態(tài)重置為0時有效,而在狀態(tài)調(diào)整時不會給予退款
WAKE_TAX(size) -- 將狀態(tài)恢復為“活躍”狀態(tài)的額外開銷
SLEEPING_REFUND -- 打破休眠狀態(tài)的固定退款
實際操作
在閱讀時請注意:
初始化合約執(zhí)行:PROP_TAX + READ_TAX(size)+ EXEC_TAX
首次執(zhí)行外部合約:PROP_TAX + READ_TAX(size)+ EXEC_TAX + EXTERNAL_TAX
第二次執(zhí)行外部合約:SHORT_READ_TAX(size)+ EXEC_TAX + EXTERNAL_TAX
遞歸地執(zhí)行合約:SHORT_READ_TAX(size)+ EXEC_TAX
合約自我銷毀:PROP_TAX + STORE_REFUND(size)+ CLEANING_REFUND(size)
內(nèi)部首次大小檢查:PROP_TAX + INDEX_TAX(key_size) - 這可用于強制支付(便宜的)租金而不用將狀態(tài)實際讀入內(nèi)存;當前這只是讀取一個0字節(jié)長的狀態(tài)。(狀態(tài)讀取通常返回數(shù)據(jù)的實際大小)
外部首次大小檢查:PROP_TAX + INDEX_TAX(key_size)+ EXTERNAL_TAX
內(nèi)部第二次大小檢查:INDEX_TAX(key_size) -- 這里的第二次表示發(fā)生在先前的大小檢查或狀態(tài)讀取之后
外部第二次大小檢查:INDEX_TAX(key_size)+ EXTERNAL_TAX
內(nèi)部首次讀?。篜ROP_TAX + READ_TAX(size)+ INDEX_TAX(key_size)
內(nèi)部第二次讀取:SHORT_READ_TAX(size)+ INDEX_TAX(key_size) -- 讀取在同一執(zhí)行過程中寫入的狀態(tài)
首次寫入新狀態(tài):PROP_TAX + WRITE_TAX(size)+ INDEX_TAX(key_size)
首次寫入新狀態(tài),設置為0:INDEX_TAX(key_size) - 這是一個空操作,所以正常情況下不應該執(zhí)行
第二次寫入新狀態(tài):WRITE_TAX(size)+ STORE_REFUND(size)+ INDEX_TAX(key_size)
第二次寫入新狀態(tài),設置為0:DIRTY_STORE_REFUND(old_size)+ INDEX_TAX(key_size)+ PROP_REFUND
首次寫入現(xiàn)有狀態(tài):PROP_TAX + WRITE_TAX(size)+ STORE_REFUND(old_size)+ INDEX_TAX(key_size)
首次寫入現(xiàn)有狀態(tài),設置為0:PROP_TAX + STORE_REFUND(old_size)+ INDEX_TAX(key_size)+ CLEANING_REFUND(size)
第二次寫入現(xiàn)有狀態(tài):WRITE_TAX(size)+ STORE_REFUND(old_size)+ INDEX_TAX(key_size)
第二次寫入現(xiàn)有狀態(tài),設置為0:PROP_TAX + STORE_REFUND(old_size)+ INDEX_TAX(key_size)+ CLEANING_REFUND(size) - 與首次寫入相同
第二次寫入先前在第一次寫入時設置為0的現(xiàn)有狀態(tài):WRITE_TAX(大?。? INDEX_TAX(key_size) - 基本上與正常的第二次寫入相同
首次寫入休眠狀態(tài):PROP_TAX + WRITE_TAX(size)+ INDEX_TAX(key_size) - 請注意,在這種情況下,減少數(shù)據(jù)無法給與退款,但WAKE_TAX預期會高于退款金額。另請注意,第二次寫入與寫入正常的現(xiàn)有(臟)狀態(tài)相同
首次寫入休眠狀態(tài),設置為0:PROP_TAX + INDEX_TAX(key_size)+ SLEEPING_REFUND - 理論上這應該與平均鍵大?。ㄐ∮?2字節(jié))四舍五入后的值相抵消
注意:在第一次寫入之后,休眠狀態(tài)在設置為0、調(diào)整大小等方面會被視為與其他狀態(tài)的行為相同
外部首次讀?。篜ROP_TAX + READ_TAX(size)+ INDEX_TAX(key_size)+ EXTERNAL_TAX
外部第二次讀取:SHORT_READ_TAX(size)+ INDEX_TAX(key_size)+ EXTERNAL_TAX
獨立代碼執(zhí)行:EXEC_TAX - “獨立”執(zhí)行只是一段UTXO中的代碼,執(zhí)行一次而不對狀態(tài)進行存儲,因此除了執(zhí)行之外沒有任何其他成本
休眠狀態(tài)的預執(zhí)行恢復:WAKE_TAX(size)+ INDEX_TAX(size)+ PROP_TAX - 請注意,這是在執(zhí)行原始合約之前發(fā)生的
注意:恢復休眠狀態(tài)后,所有gas成本與正常的活躍狀態(tài)的存儲相同
可信庫執(zhí)行:PROP_TAX + LIBEXEC_COST - 這顯然不會帶來每字節(jié)長度的開銷。除了實際執(zhí)行代碼所需的gas成本之外,該執(zhí)行的成本是固定的。這是為了使可信庫執(zhí)行更具可預測性
注意:代表合約的所有受信任庫讀/寫與正常的合約執(zhí)行相同,不會帶來EXTERNAL_TAX
雖然這個操作列表看起來非常大,但實際上它是非常公式化的,并且在代碼的實現(xiàn)過程中不會太難。它是非常有規(guī)則的,應該只需要處理很少的邊界情況。上面定義的每個常量或方法應該是不言自明的,并且應該考慮到節(jié)點和更大網(wǎng)絡所需的所有成本。
這種方法的一些風險在于退款必須是保守的,以避免出現(xiàn)下面這種投機取巧的情況:例如,先將數(shù)據(jù)寫入狀態(tài),然后將狀態(tài)修改為較小的大小,而不是在開始簡單地就寫入較小的狀態(tài)。退款行為與以太坊不同,執(zhí)行操作后的任何剩余的gas都會被發(fā)送回收款人,其中數(shù)量不超過發(fā)送給合約的總gas數(shù)。如果允許發(fā)送回多于合約中發(fā)送的gas數(shù),那么可以人為地利用高的gas價格輸出Qtum,從而以比初始支付時更高的gas價格進行退款。
AAL賬戶抽象層修改
為了適當?shù)匦藜艉霞s交易中的無關數(shù)據(jù),所有的合約執(zhí)行和交易創(chuàng)建都將經(jīng)由AAL支出并進行壓縮。這也會極大地簡化將來其他的QIPs,例如基于UTXO模型的“一次性擁有”狀態(tài)的提案。目前,合約執(zhí)行僅在執(zhí)行中的資金實際用于智能合約時才由AAL支出。此外,合約創(chuàng)建交易僅在合約自毀時花費。這允許SPV節(jié)點利用一些額外的功能來跟蹤合約行為,但這會以在 UTXO集中保留重復且不太相關的數(shù)據(jù)為代價。DeltaDB中的SPV目標訪問和跟蹤方法將有效地取代此功能。
新的節(jié)點分類
目前,Qtum生態(tài)系統(tǒng)中有三種主要類型的節(jié)點:
1. 存檔節(jié)點 :該類節(jié)點包含整個區(qū)塊鏈的數(shù)據(jù)。UTXO集被修剪至不包含重復數(shù)據(jù),但所有已花費的交易數(shù)據(jù)會保存在磁盤上,數(shù)據(jù)存取較慢。該類節(jié)點可用于任何用例,包括委托,常規(guī)錢包,歷史數(shù)據(jù)分析,開發(fā)等。
2. 修剪的全節(jié)點: 該類節(jié)點類似于一個全節(jié)點,會下載并驗證整個區(qū)塊鏈,但會刪除那些可證明不被使用的數(shù)據(jù)。特別地,這包括已花費的交易數(shù)據(jù)以及舊的區(qū)塊數(shù)據(jù)。除歷史數(shù)據(jù)分析外,該節(jié)點能夠處理全節(jié)點的所有用例。
3. SPV節(jié)點 :這種類型的節(jié)點通過按需下載與當前錢包“相關”的數(shù)據(jù)以及整個區(qū)塊鏈的區(qū)塊頭來進行驗證和證明。該類節(jié)點是非常輕量級的,通常用于移動設備和“快速同步”的錢包。節(jié)點是去中心化的,但會受審查的影響,因為無法證明它所連接的全節(jié)點是否具有應該存在的數(shù)據(jù)。通常認為這種最終的安全性是穩(wěn)定的,但容易受到女巫攻擊。這種類型的節(jié)點通常僅可用于錢包和一些有限類型的智能合約的開發(fā)。值得注意的是,它不能用于委托。
基于本提案提出的新功能和可證明的行為,提出了一種新的節(jié)點分類方案:快速開發(fā)節(jié)點。該類節(jié)點使用了SPV節(jié)點的通用安全范例,并且初始時需要下載以下數(shù)據(jù):
· 使用最佳區(qū)塊狀態(tài)樹根節(jié)點的完整EVM數(shù)據(jù)(這是無法避免的)
· 區(qū)塊鏈的所有區(qū)塊頭(與SPV相同)
· DeltaDB證明以及那些最近RENT_PERIOD區(qū)塊的數(shù)據(jù)(根據(jù)區(qū)塊頭的DeltaDBRoot進行驗證)。修改后的數(shù)據(jù)可以在處理時進行修剪
· 相關的UTXOs和當前受控錢包的證明(與SPV相同)
按需下載的數(shù)據(jù)包括:
1. 為新區(qū)塊委托UTXO
2. 用于證明UTXO存在性的UTXO證明(即區(qū)塊哈希和merkle路徑),然后接受區(qū)塊將其作為委托花費
3. 需要已花費的用于委托UTXO的UTXOs的證明(與SPV節(jié)點相同)
4. 需要已花費且為相關地址創(chuàng)建的UTXOs的證明(與SPV節(jié)點相同)
5. 需要與合約交互并跟蹤RENT_PERIOD內(nèi)的DeltaDB狀態(tài)變化的交易(不僅僅是UTXO)。交易數(shù)據(jù)在執(zhí)行后被修剪,只留下DeltaDB跟蹤數(shù)據(jù)
6. 正在進行的區(qū)塊頭下載
對于委托和安全性這類關鍵目的而言,這種方案并不是安全的,因為它的核心安全性仍然是由SPV保證的。然而,對于智能合約開發(fā)而言這已經(jīng)完全足夠了,同時也可作為SPV節(jié)點的一個功能更強大的版本。歷史合約執(zhí)行可以忽略,但進行中的新合約執(zhí)行可以完全地被執(zhí)行和跟蹤。最初的同步過程只會比SPV慢一點,主要是因為需要下載完整的EVM和修剪的DeltaDB數(shù)據(jù)。帶寬成本也只比SPV節(jié)點略高,主要用于完整地下載智能合約執(zhí)行所涉及到的所有交易。
原理
這種特定的租賃實現(xiàn)方式不同于現(xiàn)有的已提出的大多數(shù)方案。這其中一個最大的擔憂是,智能合約本身已經(jīng)相當復雜了,租賃方案所帶來的額外的復雜性會大大增加智能合約代碼中的問題和漏洞利用的可能性。該提案以一種不同的方式利用DeltaDB的特性從而使租賃系統(tǒng)對生態(tài)系統(tǒng)有益,而在大多數(shù)情況下不需要任何額外的智能合約邏輯。
此外,該提案的一個重點是將節(jié)點達成共識的所需和其他內(nèi)容分離開來。將那些很少訪問且永遠不會更新的數(shù)據(jù)上鏈是完全可以接受的,但這些數(shù)據(jù)對節(jié)點而言應該是無關的。當然,仍然可以證明數(shù)據(jù)在某個區(qū)塊高度時在區(qū)塊鏈上的存在性,但是,預計它不會被網(wǎng)絡上的大多數(shù)節(jié)點直接存儲和訪問。這種證明可以在不消耗任何會帶來gas開銷的區(qū)塊鏈資源的情況下完成。此類歷史數(shù)據(jù)可以轉移到歸檔節(jié)點上。對于僅需要訪問某個智能合約的休眠數(shù)據(jù)的應用程序,可以使用部分歸檔節(jié)點。這基本上是一個標準的修剪節(jié)點,但它會存儲相關智能合約的完整歷史數(shù)據(jù)。
策略
這將在Qtum-x86的初始版本中實現(xiàn)。在發(fā)布后更改該存儲模型是非常困難的,因此,在已經(jīng)實現(xiàn)的情況下推出Qtum-x86是有很大好處的。
待實現(xiàn)
· 需要計算不同的RENT_TERM值的理論上的數(shù)據(jù)上限
· 需要計算對于一個合約執(zhí)行的完整區(qū)塊而言,DeltaDB merkle樹的大小,以及一個典型的區(qū)塊
· 這并不能完全消除對存儲所有數(shù)據(jù)的“歸檔節(jié)點”的需求。為了無信任地同步一個全節(jié)點,仍然必須且/或需要從區(qū)塊數(shù)據(jù)重建所有的“休眠”數(shù)據(jù),以便證明區(qū)塊鏈的當前狀態(tài)是有效的
評論
查看更多