本節介紹的內容將有助于為你日后解決智能合約的Bug做好充足準備。
注意:在系統中添加新組件時總是有風險的。設計不當的故障保護措施本身可能會成為一個漏洞,許多精心設計的故障保護措施之間的交互也會造成漏洞的存在。仔細研究智能合約中使用的每一項技術,并仔細研究它們如何協同工作以創建一個健壯的系統。
智能合約高效升級
智能合約在運行過程中發現Bug或者代碼需要改進,這會影響整個系統的健壯性,如果發現無法解決的Bug,那就可能會造成嚴重的經濟損失。
在本文中,我們無法涉及到任何復雜的問題。然而有兩種最常用的基本方法,最簡單的是擁有一個注冊表智能合約,包含該智能合約最新版本的地址。對于智能合約用戶來說,用戶可以無縫的將該智能合約調用和數據轉發到最新版本的智能合約中。
無論采用哪種技術,模塊化和組件之間的隔離都是非常重要,這樣代碼更改就不會破壞智能合約功能、孤立數據或需要大量的移植代碼成本。這樣代碼更改就不會破壞功能、孤立數據或需要大量的移植成本。
同樣重要的是要有一個安全的方式,讓社區決定升級智能合約代碼。根據您的智能合約,代碼更改可能需要由單個受信任方、一組成員或全部涉眾投票批準。如果這個過程可能需要一些時間,那么您需要考慮是否有其他方法可以在發生攻擊時更快地做出反應,例如緊急停止或斷路器。
示例1:使用注冊表智能合約來存儲智能合約的最新版本
在本例中,調用不會被轉發,因此用戶應該在每次與當前地址交互之前獲取該地址。
contract SomeRegister {
address[] previousBackends;
address owner;
function SomeRegister() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner)
_;
}
function changeBackend(address newBackend) public
onlyOwner()
returns (bool)
{
if(newBackend != backendContract) {
previousBackends.push(backendContract);
backendContract = newBackend;
return true;
}
return false;
}
}
這種方法有兩個主要缺點:
1. 用戶必須始終查找當前地址,否則任何人都會使用舊版本的智能合約來冒充
2. 在更換智能合約時,您需要仔細考慮如何處理智能合約數據
另一種方法是讓智能合約將調用和數據轉發到最新版本的智能合約:
示例2:使用DELEGATECALL轉發數據和調用
contract Relay {
address public currentVersion;
address public owner;
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
function Relay(address initAddr) {
currentVersion = initAddr;
owner = msg.sender; // this owner may be another contract with multisig, not a single contract owner
}
function changeContract(address newVersion) public
onlyOwner()
{
currentVersion = newVersion;
}
function() {
require(currentVersion.delegatecall(msg.data));
}
}
這種方法避免了先前的問題,但有其自身的問題。您在此智能合約中如何存儲數據時必須格外小心。如果新智能合約的存儲架構與第一個不同,則數據可能最終損壞。此外,該模式的簡單版本無法從函數返回值,而只能轉發它們,這限制了其適用性。(更復雜的實現嘗試通過內聯匯編代碼和返回大小注冊表來解決此問題。)
無論采用哪種方法,找到合適的智能合約升級的方法很重要,否則當在智能合約中發現不可避免的bug時,后果將變得不可預計。
斷路器Circuit Breakers
如果滿足某些條件,斷路器將停止智能合約的執行,并且在發現新bug時非常有用。例如如果發現一個bug,智能合約中的大多數操作可能會被掛起,而現在唯一有效的操作是撤回。您可以給某些受信任方觸發斷路器的能力,也可以使用編程規則在滿足某些條件時自動觸發特定斷路器。
示例:
bool private stopped = false;
address private owner;
modifier isAdmin() {
require(msg.sender == owner);
_;
}
function toggleContractActive() isAdmin public {
// You can add an additional modifier that restricts stopping a contract to be based on another action, such as a vote of users
stopped = !stopped;
}
modifier stopInEmergency { if (!stopped) _; }
modifier onlyInEmergency { if (stopped) _; }
function deposit() stopInEmergency public {
// some code
}
function withdraw() onlyInEmergency public {
// some code
}
減速帶Speed Bumps (減緩智能合約操作)
減速帶降低了操作的速度,因此,如果發生惡意操作,則有時間進行恢復。例如DAO在成功請求拆分DAO和這樣做的能力之間需要27天。這樣可以確保將資金保留在智能合約內,從而增加了收回的可能性。對于DAO,在減速帶給定的時間內如沒有采取任何有效的措施時,結合我們的其他技術,它們可以非常有效。
示例:
struct RequestedWithdrawal {
uint amount;
uint time;
}
mapping (address =》 uint) private balances;
mapping (address =》 RequestedWithdrawal) private requestedWithdrawals;
uint constant withdrawalWaitPeriod = 28 days; // 4 weeks
function requestWithdrawal() public {
if (balances[msg.sender] 》 0) {
uint amountToWithdraw = balances[msg.sender];
balances[msg.sender] = 0; // for simplicity, we withdraw everything;
// presumably, the deposit function prevents new deposits when withdrawals are in progress
requestedWithdrawals[msg.sender] = RequestedWithdrawal({
amount: amountToWithdraw,
time: now
});
}
}
function withdraw() public {
if(requestedWithdrawals[msg.sender].amount 》 0 && now 》 requestedWithdrawals[msg.sender].time + withdrawalWaitPeriod) {
uint amountToWithdraw = requestedWithdrawals[msg.sender].amount;
requestedWithdrawals[msg.sender].amount = 0;
require(msg.sender.send(amountToWithdraw));
}
}
速率限制Rate Limiting
速率限制暫停或需要批準才能進行重大更改。例如在某個時間段內(例如在1天之內最多只能提取100個以太幣),可能只允許存款人提取一定數量或一定比例的總存款-該時間段內的其他提取可能會失敗或需要某種特殊的批準 。或者速率限制可以處于智能合約級別,在一段時間內僅由智能合約發行一定數量的代幣。
智能合約版本推出
智能合約新版本在正式推出使用前,已經經過大量的安全測試和代碼測試。
至少您應該:
1. 擁有100%測試覆蓋率的完整測試軟件。
2. 在自己的testnet上部署。
3. 在公共測試網上進行大量測試和漏洞賞金。
4. 測試應允許各種參與者大量參與智能合約進行交互。
5. 在主網上部署beta版本,并限制風險數量。
自動棄用AUTOMATIC DEPRECATION
在測試過程中,您可以在一定的時間段后通過阻止任何操作來強制自動棄用。例如alpha版本智能合約可能會工作幾個星期后,然后自動關閉所有操作,但最終退出除外。
modifier isActive() {
require(block.number 《= SOME_BLOCK_NUMBER);
_;
}
function deposit() public isActive {
// some code
}
function withdraw() public {
// some code
}
在早期階段,您可以限制用戶(或整個智能合約)使用以太坊的數量,從而降低風險。
Bug賞金計劃
進行賞金計劃的一些技巧:
· 決定使用(BTC和/或ETH)哪種代幣進行賞金分配。
· 決定賞金的預算總金額。
· 根據預算,確定三層獎勵:
1. 最低獎勵
2.最高獎勵
3.如果發現非常嚴重的漏洞,將授予額外的獎勵
· 確定賞金管理員(3個可能是典型的理想人選)。
· 首席開發人員可能應該是賞金評委之一。
· 當收到一個bug報告時,開發負責人,在評審的建議下,應該評估bug的嚴重性。
· 這個階段的工作應該是在一個私有的存儲庫中進行的,問題應在Github上歸檔。
· 如果這是一個應該被修復的bug,那么在私有repo中,開發人員應該編寫一個測試用例,測試用例應該失敗,從而確認這個bug。
· 開發人員應實施此修復程序并確保測試可以通過;根據需要編寫其他測試,向賞金獵人顯示修復方法,將補丁程序合并回公共倉庫。
· 確定賞金獵人是否有其他關于修復的反饋。
· 賞金評委根據對漏洞的可能性和影響的評估來確定獎勵的大小。
· 在整個過程中讓賞金參與者了解情況,然后盡量避免延遲向他們發送獎勵。
關于三層獎勵的示例,請參見以太坊的賞金計劃:https://bounty.ethereum.org/
獎勵的價值將根據影響的嚴重程度而有所不同。對輕微“bug”的獎勵從0.05個BTC開始。重要的bug,例如導致共識問題的bug,將會得到最多5個BTC的獎勵。如果存在非常嚴重的漏洞,則可能獲得更高的回報(最多25個BTC)。
責任編輯:ct
評論
查看更多