Solidity安全贴士(四)

软件工程开发技巧

倡导“时刻准备失败”,预估错误。

1.升级有问题的合约

如果代码中发现了错误或者需要对某些部分做改进都需要更改代码。最简单的是专门设计一个注册合约,在注册合约中保存最新版合约的地址。

组件之间进行模块化和良好的分离。

根据合约,升级代码可能会需要通过单个或多个受信任方参与投票决定。如果这个过程会持续很长时间,必须要考虑是否要换成一种更加高效的方式以防止遭受到攻击,例如紧急停止或断路器。

例(1)使用注册合约存储合约的最新版本

下面例子中,调用没有被转发,因此用户必须每次在交互之前都先获取最新的合约地址。

contract SomeRegister {
    address backendContract;
    address[] previousBackends;
    address owner;

    function SomeRegister() {
        owner = msg.sender;
    }

    modifier onlyOwner() {
        if (msg.sender != owner) {
            throw;
        }
        _;
    }

    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() {
        if (msg.sender != owner) {
            throw;
        }
        _;
    }

    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() {
        if(!currentVersion.delegatecall(msg.data)) throw;
    }
}

这样做的问题是必须在合约里小心的存储数据。如果新的合约和先前的合约有不同的存储层,数据可能会被破坏。

2.断路器(暂停合约功能)

由于断路器在满足一定条件时将会停止执行,所以如果发现错误时可以使用断路器。例如,如果发现错误,大多数操作可能会在合约中被挂起,这时唯一的操作就是撤销。我们可以授权给任何你受信任的一方,提供给他们触发断路器的操作,或者设计一个在满足某些条件时自动触发某个断路器的程序规则。

bool private stopped = false;
address private owner;

modifier isAdmin() {
    if(msg.sender != owner) {
        throw;
    }
    _;
}

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
}

3.速度碰撞(延迟合约动作)

速度碰撞使动作变慢,所以如果发生了恶意操作就有时间去恢复。

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;

        if(!msg.sender.send(amountToWithdraw)) {
            throw;
        }
    }
}

4.速率限制

速率限制暂停或需要批准进行实质性更改。 例如,只允许存款人在一段时间内提取总存款的一定数量或百分比(例如,1天内最多100个ether) - 该时间段内的额外提款可能会失败或需要某种特别批准。 或者将速率限制做在合约级别,合约期限内只能发出发送一定数量的代币。

5.合约发布

在将大量资金放入合约之前,合约应当进行大量的长时间的测试。
至少应该:
- 拥有100%测试覆盖率的完整测试套件(或接近它)
- 在自己的testnet上部署
- 在公共测试网上部署大量测试和错误奖励
- 彻底的测试应该允许各种玩家与合约进行大规模互动
- 在主网上部署beta版以限制风险总额

6.自动弃用

在合约测试期间,可以在一段时间后强制执行自动弃方法以阻止其他操作继续进行。例如,alpha版本的合约工作几周后自动关闭除最终退出的所有操作。

modifier isActive() {
    if (block.number > SOME_BLOCK_NUMBER) {
        throw;
    }
    _;
}

function deposit() public
isActive() {
    // some code
}

function withdraw() public {
    // some code
}

7.限制每个用户/合约的Ether数量

在早期阶段,可以通过限制任何用户(或整个合约)的Ether数量来降低风险。

猜你喜欢

转载自blog.csdn.net/qq_26769677/article/details/81517581