智能合约设计模式 - 紧急停止
本文为智能合约设计模式系列的一部分。
目的
在紧急情况时可以关闭合约关键功能
动机
即使是经过严格审核和测试的代码也可能包含bug或缺陷。智能合约也不例外。一般,这些漏洞只有在被攻击使才会发现,一旦发现bug,却很难被修复,因为不可变是区块链的核心原则之一。虽然有几个模式允许一定程度地升级代码(如代理模式),但这些解决方案通常需要大量的开发时间。在升级修复程序前,攻击者可能会盗走合约中的所有可用资金。
这种模式提供了阻止关键方法调用来暂停合约的可能性,从而防止攻击者持续攻击。当然,这个模式可以防止任何类型的bug攻击,不管是由被攻击发现还是主动发现,指导合约被修复或采取了其他措施。
适用性
在以下条件时使用紧急停止模式
- 希望暂停合约
- 希望保护关键功能被未暴露的bug攻击
- 希望应对潜在的失败
参与者和协作
此模式有三个主要参与者:中心组件是一个状态变量,用于指示合约是否停止。每个重要方法检查此变量,在合约停止时,禁止执行或允许执行。第三个参与者是有权发布合约停止的调用者,它可能是合约所有人,也可能是大多数用户。
实现
合约停止或为停止,保存在布尔类型状态变量中,合约初始时该值为false
。将其设为true
就可在紧急情况是停止合约。建议通过方法调用,利用访问限制模式确保只有授权用户才可以调用此方法。防止恶意调用同时又兼顾去中心化的一个选择是制定一个触发规则。根据具体情况,可以采用各种规则(例如,最近一个小时提取了合约10%的金额)
状态变量一旦被设置为true
,再次使用访问限制模式确保关键方法不能被调用,通过一个抛出异常的修饰符实现。在停止期间应该有可用方法,因为它们可以帮助解决这种情况,例如让用户提取存款,以相同方式保护。
另一个设计决策时,紧急停止是否可以恢复。如果合约应是可恢复的,例如采取了可升级预防措施,则通过将状态变量置回false
来恢复合约。此方法和启动紧急停止方法类似,也应防止未授权调用。
代码示例
这个模式的具体实现依赖于底层智能合约的逻辑。重要问题,如是否可以恢复,哪些功能不可用,哪些可用等,应在部署合约前评估和仔细测试。
下面的代码展示了紧急停止模式的基本框架,并提供了两个受影响的示例方法。简便起见,省略具体业务逻辑。
// This code has not been professionally audited, therefore I cannot make any promises about
// safety or correctness. Use at own risk.
contract EmergencyStop {
bool isStopped = false;
modifier stoppedInEmergency {
require(!isStopped);
_;
}
modifier onlyWhenStopped {
require(isStopped);
_;
}
modifier onlyAuthorized {
// Check for authorization of msg.sender here
_;
}
function stopContract() public onlyAuthorized {
isStopped = true;
}
function resumeContract() public onlyAuthorized {
isStopped = false;
}
function deposit() public payable stoppedInEmergency {
// Deposit logic happening here
}
function emergencyWithdraw() public onlyWhenStopped {
// Emergency withdraw happening here
}
}
第5行的布尔变量isStopped
是一个状态变量,记录合约是否停止。两个修饰符检查它,分别是合约未停止(第7行)和合约停止(第12行),以限制它们修饰的方法调用。第17行的修饰符检查方法的调用者是否有权,例如可以通过require(msg.sender == owner)
限定为合约所有者或增加投票机制。第22行和第26行中的两个方法使用它,通过将状态变量isStopped
设置为true
或false
,停止或恢复合约。
第30行的deposit
方法是一个关键方法,一旦合约停止,它就不可访问。此限制是通过stoppedInEmergency
修饰符实现。如果在停止期间调用此方法,则会引发异常。第34行的emergencywithdraw
正好相反,只有在紧急停止时,它才可以访问。应该提供这样的紧急方法,使合约用户在停止期间能够访问他们的资金。否则,用户必须相信合约所有人不会擅自冻结资金。
结果
紧急停止模式给合约增加一个快速可靠的方法,当发现bug或安全问题时立即停止任何敏感方法。这将留下足够的时间权衡所有选项,并可能升级合约以修复安全漏洞。
此模式的负面结果是增加了合约的不可预测性。除非执行一个令人信服的规则集,否则授权者始终有可能滥用停止。因此,该模式实现的紧急停止只应作为最后手段,而不应被用作可预测事件的暂停机制,这种情况下,应采用具有定时转换的状态机模式,以便用户能够预测合约行为,并最小化系统的信任依赖。
已知应用
紧急停止模式最普遍的应用是OpenZeppelin库的可暂停合约。其它合约通过继承它使用此功能。好几个应用采用这个技术,一个例子是OmiseGO,一种旨在实现去中心化网络中金融包容和交互的代币。合约从第274行开始使用旧版本的可暂停合约。
另一个不太常见的可能是自己实现。这种方式的一个例子是百万以太网主页合约,它的主合约实现了紧急暂停模式,并使合同所有人能够在任意时间停止多个方法。
推而广之
如果你的智能合约或去中心化应用,需管理重要的数据,例如数据代表现实世界中的某种有价资产。不管它采用哪种链,公链还是联盟链,除非对所有节点有绝对的控制权,否则都建议使用本模式。至少它可以作为最后的措施避免更大的损失。本模式的一个关键点是如何确保它不被滥用,这使得它在联盟链中更容易被接受,身份机制和链下的信任联系保证了对其调用的监督以及滥用追查惩罚。
完整内容请查看智能合约设计模式系列