智能合约编写注意事项

原文连接

Overflow 与 Underflow

Solidity 可以处理 256 位数字, 最高为 2**256 - 1, 所以对 (2 ** 256 - 1) 加 1 会导致归 0.

同理, 对 unsigned 类型 0 做减 1 运算会得到 (2**256 - 1)

测试代码如下

pragma solidity 0.4.18;

contract OverflowUnderflow {
    uint public zero = 0;
    uint public max = 2**256 - 1;

    // zero will end up at  2 ** 256 - 1
    function underflow() public {
        zero -= 1;
    }

    function overflow() public {
        max += 1;
    }
}
复制代码

尽管他们同样危险, 但是在智能合约中, underflow 造成的影响更大.

比如, 账号 A 持有 X tokens, 如果他发起一笔 X + 1 tokens 的交易, 如果代码不进行校验, 则账号 A 的余额可能发生 underflow 导致余额变多.

可以引入 SafeMath Library 解决

pragma solidity 0.4.18;

library SafeMath {
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        if (a==0) {
            return 0;
        }
        uint c = a * b;
        assert(c / a == b);
        return c;
    }

    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a / b;
        return c;
    }

    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        assert(b <= a);
        return a - b;
    }

    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        assert(c >= a);
        return c;
    }
}

contract OverflowUnderflow {
    using SafeMath for uint;
    uint public zero = 0;
    uint public max = 2 ** 256 - 1;

    function underflow() public {
        zero = zero.sub(1);
    }

    function overflow() public {
        max = max.add(1);
    }
}
复制代码

Visibility 与 Delegatecall

  • Public functions 可以被任意地址调用

  • External functions 只能从合约外部调用

  • Private functions 只能从合约内部调用

  • Internal functions 允许从合约及其子合约调用

External functions 消耗的 gas 比 public 少, 因为其使用 calldataPublic 需要复制所有参数到 memory.

Delegatecall

引自 Solidity Docs

Delegatecall is identical to a message call apart from the fact that the code at the target address is executed in the context of the calling contract and msg.sender and msg.value do not change their values.

This means that a contract can dynamically load code from a different address at runtime. Storage, current address and balance still refer to the calling contract, only the code is taken from the called address.

这个特性可以用于构建 Library 和模块化代码. 但是与此同时, 这也有可能造成别人对你的代码进行操作.

下例中, 攻击者调用 pwn 方法获得了合约的拥有权.

pragma solidity 0.4.18;

contract Delegate {
    address public owner;

    function Delegate(address _owner) public {
        owner = _owner;
    }

    function pwn() public {
        owner = msg.sender;
    }
}

contract Deletagion {
    address public owner;
    Delegate delegate;

    function Delegation(address _delegateAddreses) public {
        delegate = Delegate(_delegateAddreses);
        owner = msg.sender;
    }

    // an attacker can call Delegate.pwn() in the context of Delegation, this means that pwn() will modify the state of **Delegation** and not Delegate, the result is that the attacker takes unauthorized ownership of the contract.
    function () public {
        if(delegate.delegatecall(msg.data)) {
            this;
        }
    }
}
复制代码

Reentrancy(TheDAO hack)

Solidity 中 call 函数被调用时, 如果带有 value 参数, 则会转发所有他所收到的 gas.

在一下代码片段中, call 函数在 sender 的余额实际减少前被调用. 这里有一个漏洞曾导致 TheDAO 攻击.

function withdraw(uint _amount) public {
  if(balances[msg.sender] >= _amount) {
    if(msg.sender.call.value(_amount)()) {
      _amount;
    }
    balances[msg.sender] -= amount;
  }
}
复制代码

引自 Reddit 的解释

In simple words, it's like the bank teller doesn't change your balance until she has given you all the money you requested. "Can I withdraw $500? Wait, before that , can I withdraw $500?"

And so on. The smart contracts as designed only check you have $500 at beginning once, and allow themselves to be interrupted.

转载于:https://juejin.im/post/5a6ec1c7f265da3e4c080c98

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值