雖然相比其他互聯(lián)網(wǎng)技術(shù)袭厂,智能合約等區(qū)塊鏈技術(shù)相對安全,但是并非絕對安全球匕,即使是零漏洞的合約也有可能被竊取的私鑰劫持纹磺。先前的Bancor 和KICKICO黑客事件表明:攻擊者可以損害智能合約錢包。在這些攻擊中亮曹,即使合約具備可升級性機(jī)制橄杨,也可能無法修復(fù)已部署的智能合約。唯一的解決辦法是重新部署并正確初始化新的合約實(shí)例乾忱,以便為用戶恢復(fù)功能讥珍。
因此历极,所有智能合約開發(fā)者必須在合約設(shè)計(jì)階段整合一個(gè)遷移程序窄瘟。此外,企業(yè)必須做好在合約損害事件發(fā)生時(shí)實(shí)施遷移的準(zhǔn)備趟卸。
遷移過程有兩個(gè)步驟:
1蹄葱、恢復(fù)要遷移的數(shù)據(jù)
2、將數(shù)據(jù)寫入新合約
具體操作如下:
第一步:數(shù)據(jù)恢復(fù)
你需要從區(qū)塊鏈的某個(gè)特定區(qū)塊中讀取數(shù)據(jù)锄列。要想從損害事件(黑客攻擊或故障)中恢復(fù)數(shù)據(jù)图云,你需要在事件發(fā)生之前使用這個(gè)區(qū)塊,或者過濾攻擊者的操作邻邮。
如果可以的話竣况,請暫停合約。這對于用戶來說更加透明公平筒严,并能阻止攻擊者盯上那些對遷移不知情的用戶丹泉。
數(shù)據(jù)恢復(fù)的具體操作取決于你的數(shù)據(jù)結(jié)構(gòu)。
對于簡單類型的公共變量(public variables鸭蛙,例如 uint 或 address)來說摹恨,通過它們的getter來檢索特定值就可以了。而對于私有變量(private variables)娶视,你可以依賴事件晒哄,也可以計(jì)算變量的內(nèi)存偏移量,然后使用 getStorageAt[4]函數(shù)檢索它的值肪获。
由于元素的數(shù)量是已知的寝凌,因此數(shù)組也很容易恢復(fù)。
至于映射(mappings)的話孝赫,情況有點(diǎn)復(fù)雜较木。由于鍵(Keys)在映射過程中不會被存儲,所以你需要將它們進(jìn)行恢復(fù)才能訪問對應(yīng)的值(Values)寒锚。為了簡化鏈下追蹤的過程劫映,我們建議在值被存儲在映射中時(shí)觸發(fā)事件(emit
events)违孝。
在ERC20代幣合約中,你可以通過追蹤代幣的Transfer事件的地址來獲取代幣持有者列表泳赋。這個(gè)過程很難雌桑。
對此,我們準(zhǔn)備了兩個(gè)幫助方案:第一祖今,你可以掃描區(qū)塊鏈并自行檢索持有者校坑;第二,你可以依靠以太坊區(qū)塊鏈的公開Goog
BigTable存檔千诬。
如果你不熟悉web3 API耍目,無法從區(qū)塊鏈中提取信息。那么你可以使用ethereum-etl 徐绑,其提供了一系列腳本來簡化數(shù)據(jù)提取的過程邪驮。
如果你沒有已經(jīng)完成同步的區(qū)塊鏈,那么你可以使用Google BigQuery
API傲茄。圖1展示了如何通過BigQuery來收集某個(gè)特定代幣的所有地址:
圖1:利用Google BigQuery來恢復(fù)那些與在0x41424344這個(gè)地址中的代幣相關(guān)聯(lián)的Transfer事件的所有地址
BigQuery提供對區(qū)塊號的訪問毅访,因此你可以將查詢結(jié)果調(diào)整為返回特定區(qū)塊的交易。
一旦你恢復(fù)了所有代幣持有者的地址盘榨,你就可以離線查詢balanceOf函數(shù) 以恢復(fù)與每個(gè)持有者相關(guān)的余額喻粹,同時(shí)過濾余額為零的帳戶。
現(xiàn)在我們知道如何檢索將要遷移的數(shù)據(jù)草巡,接下來我們要將數(shù)據(jù)寫入新合約守呜。
第二步:數(shù)據(jù)寫入
完成數(shù)據(jù)收集后,你需要開啟新合約山憨。
對于簡單變量查乒,你可以通過合約的構(gòu)造函數(shù)來設(shè)置相應(yīng)的值。
如果你的數(shù)據(jù)無法保存在單筆交易中萍歉,那么情況會有點(diǎn)復(fù)雜侣颂,成本也會略高。每筆交易都包含在某個(gè)區(qū)塊中枪孩,該區(qū)塊限制了其交易可以使用的gas總量(即所謂的
GasLimit)憔晒。如果某筆交易的 gas
成本接近或超過此限制,那么礦工將不會將其打包進(jìn)該區(qū)塊內(nèi)蔑舞。因此拒担,如果想要遷移大量數(shù)據(jù),那么你必須將數(shù)據(jù)遷移拆分成多筆交易攻询。
這類情況的解決方案是:在合約中添加初始化狀態(tài)从撼,只有合約擁有者才能更改狀態(tài)變量,并且用戶無法執(zhí)行任何操作钧栖。
對于ERC20代幣低零,上述過程將需要以下步驟:
1婆翔、在初始化狀態(tài)下部署合約,
2掏婶、遷移余額啃奴,
3、將合約的狀態(tài)移至生產(chǎn)狀態(tài)雄妥。
初始化狀態(tài)可以通過使用OpenZeppelin提供的 Pausable 功能和指示初始化狀態(tài)的布爾值(boolean)來實(shí)現(xiàn)最蕾。
為了降低成本,我們可以使用batchTransfer(批量傳輸)函數(shù)(該函數(shù)允許你在單筆交易中設(shè)置多個(gè)帳戶)來實(shí)現(xiàn)余額的遷移:
圖2:batchTransfer 函數(shù)示例
建議
在合約部署之前做好遷移程序的功課老厌。
使用事件(events)來提高數(shù)據(jù)追蹤的效率瘟则。
如果你想要部署可升級合約,那么你必須準(zhǔn)備好遷移程序枝秤,因?yàn)槟愕拿荑€可能會受到損害醋拧,或者你的合約可能會受到錯(cuò)誤且不可逆轉(zhuǎn)的操縱。
智能合約帶來了新的開發(fā)范式——其不可變性要求用戶重新思考搭建應(yīng)用的方式宿百,并且需要更透徹全面的設(shè)計(jì)和開發(fā)過程趁仙。
作者:Trail of Bits Blog
翻譯:喏唄爾
來源:Unitimes