call vs delegatecall vs callcode

调用已部署合约的function 就靠它们了。合约之间的交互,其中包括正常使用,也包括恶意使用(攻击)。

使用它们调用已部署合约的function需要知道两件事:

  1. 已部署合约的 address。

  2. 该合约的ABI。

可以概括为:调哪里(address)的合约去做什么(function)。

注意:DELEGATECALL 已经取代了 CALLCODE(在以太坊Homestead版本之后)

它们的不同:

contract D {
  uint public n;
  address public sender;

  function callSetN(address _e, uint _n) {
    _e.call(bytes4(sha3("setN(uint256)")), _n); // E的storage被修改, D 不变
  }

  function callcodeSetN(address _e, uint _n) {
    _e.callcode(bytes4(sha3("setN(uint256)")), _n); // D的storage被修改, E 不变

  }

  function delegatecallSetN(address _e, uint _n) {
    _e.delegatecall(bytes4(sha3("setN(uint256)")), _n); // D的storage被修改, E 不变

  }
}

contract E {
  uint public n;
  address public sender;

  function setN(uint _n) {
    n = _n;
    sender = msg.sender;
    // 如果 D 调用callcodeSetN , msg.sender 是 D . E的 storage 不变
    //  如果被 C.foo()调用, msg.sender 是 C. E的 storage 不变

    // 当被D的callcodeSetN 或 C.foo() 调用时,this 指代D
  }
}

contract C {
    function foo(D _d, E _e, uint _n) {
        _d.delegatecallSetN(_e, _n);
    }
}

根据代码,可以总结出两个不同:

storage 和 context的不同

  • 当D call E 时,context 是E的,操作的也是E的storage
  • 当D callcode E时,context 是D的,操作的也是D的storage,同理delegatecall

msg.sender的不同

  • 当C引用D,D delegatecall E ,E 的 msg.sender 是C,由此D E二者的,msg.send 和msg.value 都是一样的。

  • 当D callcode E 时,E的 msg.sender是D,同理delegatecall

总结:

call 理解为 调用一个实例的方法,delegatecall 是import 另一个合约然后调用方法。

错误示范:

contract NameReg {
    bytes32 public nn;
    bytes public calldata;

    function register(bytes32 name) {
      nn = name;    
    }

    function() {
        calldata = msg.data;
    }

    function doesNotCallRegister() {
        this.call("register", "MyName");
    }
 }

doesNotCallRegister()会失败,失败后触发一个匿名方法(fallback 方法)。因为solidity中查找方法名字不是简单的的字符串搜索,而是在EVM虚拟机层面,计算方法签名的hash——MethodID,以它的高位4字节为key进行查找。

正确方法:

watch_addr.call(bytes4(sha3("Bar(int256)")), 42);

调用了 watch_addr的Bar方法,并传值 42。

results matching ""

    No results matching ""