solidity的call跟delegatecall

call
原型
.call(...) returns (bool)
简介
调用后内置变量 msg .sender的值会修改为调用者合约地址,涉及存储修改,修改的是被调用者合约的内存(
的)。默认情况下将所有可用的gas传输过去,gas传输量可调。执行失败时返回false。
如果调用的函数不存在,则调用fallback函数;

实例
//call的函数调用
nameReg.call("register", "MyName");
nameReg.call(bytes4(keccak256("fun(uint256)")), a);
//设置调用时的gas和传输的钱
nameReg.call.gas(1000000).value(1 ether)("register", "MyName");

delegatecall
原型
.delegatecall(...) returns (bool)

简介
调用后内置变量 msg .sender的值不会修改为调用者合约地址,
涉及存储修改,修改的是调用者合约的内存(当前合约的),默认情况下将所有可用的gas传输过去,gas传输量可调。执行失败时返回false。
本函数目的在于让合约能够在不传输自身状态(如balance、storage)的情况下使用其他合约的代码。

实例
nameReg.delagatecall.gas(1000000)("register", "MyName");
//delegatecall不支持.value


call与delegatecall对比简析
相同之处
(1)调用时会将本合约所有可用的gas传输过去
(2)执行失败均返回false

不同之处
(1)call可以使用.value传ETH给被调用合约
(2)假设在contract_test合约中分别有nameReg.call("somefunction")以及nameReg.delegatecall("somefunction")
    nameReg.call以nameReg合约的身份在nameReg中执行somefunction
    nameReg.delegatecall以contract_test合约的身份在nameReg中执行somefunction
(3)delegatecall的目的就是让合约在不用传输自身状态(如balance、storage)的情况下可以使用其他合约的代码

测试如下:

pragma solidity ^0.4.0;

contract Proxy {
  /**
  * @dev Tells the address of the implementation where every call will be delegated.
  * @return address of the implementation to which it will be delegated
  */
  function implementation() public view returns (address);

  /**
  * @dev Fallback function allowing to perform a delegatecall to the given implementation.
  * This function will return whatever the implementation call returns
  */
  function () payable public {
    address _impl = implementation();
    require(_impl != address(0));

    assembly {
      let ptr := mload(0x40)
      calldatacopy(ptr, 0, calldatasize)
      let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0)
      let size := returndatasize
      returndatacopy(ptr, 0, size)

      switch result
      case 0 { revert(ptr, size) }
      default { return(ptr, size) }
    }
  }
}

library SomeLib {
    event CalledSomeLib(address _from);
    function calledSomeLibFun() public {
        emit CalledSomeLib(this);
    }
}

contract TestContract {
    uint num = 10;
    event callMeMaybeEvent(address sender, address _from);
    function callMeMaybe() payable public {
        num = num + 1;
        emit callMeMaybeEvent(msg.sender,this);
    }
    function getNum() public view returns(uint256){
        return num;
    }
}

contract CallsTestContract is Proxy {
    uint num;
    address smart;
    
    function set(address _addr) public {
        smart = _addr;
    }
    
    function implementation() public view returns (address){
        return smart;
    }
    
    function callTheOtherContract(address _contractAddress) public {
        require(_contractAddress.call(bytes4(keccak256("callMeMaybe()"))));
        require(_contractAddress.delegatecall(bytes4(keccak256("callMeMaybe()"))));
        require(address(this).call(bytes4(keccak256("callMeMaybe()"))));
        require(address(this).delegatecall(bytes4(keccak256("callMeMaybe()"))));
        SomeLib.calledSomeLibFun();
    }
    
    function getNum() public view returns(uint256){
        return num;
    }
}

部署合约 (账户地址:0xca35b7d915458ef540ade6068dfe2f44e8fa733c)
1、先部署TestContract; (合约地址:0xcbbe6ec46746218a5bed5b336ab86a0a22804d39)
2、再部署CallsTestContract;    (合约地址:0x100eee74459cb95583212869f9c0304e7ce11eaa)
3、以TestContract合约地址为参数,调用CallsTestContract的set方法;
4、以TestContract合约地址为参数,调用CallsTestContract的callTheOtherContract方法;

生成logs如下:


[
    {
        "from": "0xcbbe6ec46746218a5bed5b336ab86a0a22804d39",
        "topic": "0x6f047d87ead77bed598fcd7848a13aaf5af905a842213507a0ebc158d568351b",
        "event": "callMeMaybeEvent",
        "args": {
            "0": "0x100eeE74459CB95583212869f9c0304e7cE11EAA",
            "1": "0xCBbe6ec46746218A5beD5b336AB86a0A22804d39",
            "sender": "0x100eeE74459CB95583212869f9c0304e7cE11EAA",
            "_from": "0xCBbe6ec46746218A5beD5b336AB86a0A22804d39",
            "length": 2
        }
    },
    {
        "from": "0x100eee74459cb95583212869f9c0304e7ce11eaa",
        "topic": "0x6f047d87ead77bed598fcd7848a13aaf5af905a842213507a0ebc158d568351b",
        "event": "callMeMaybeEvent",
        "args": {
            "0": "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c",
            "1": "0x100eeE74459CB95583212869f9c0304e7cE11EAA",
            "sender": "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c",
            "_from": "0x100eeE74459CB95583212869f9c0304e7cE11EAA",
            "length": 2
        }
    },
    {
        "from": "0x100eee74459cb95583212869f9c0304e7ce11eaa",
        "topic": "0x6f047d87ead77bed598fcd7848a13aaf5af905a842213507a0ebc158d568351b",
        "event": "callMeMaybeEvent",
        "args": {
            "0": "0x100eeE74459CB95583212869f9c0304e7cE11EAA",
            "1": "0x100eeE74459CB95583212869f9c0304e7cE11EAA",
            "sender": "0x100eeE74459CB95583212869f9c0304e7cE11EAA",
            "_from": "0x100eeE74459CB95583212869f9c0304e7cE11EAA",
            "length": 2
        }
    },
    {
        "from": "0x100eee74459cb95583212869f9c0304e7ce11eaa",
        "topic": "0x6f047d87ead77bed598fcd7848a13aaf5af905a842213507a0ebc158d568351b",
        "event": "callMeMaybeEvent",
        "args": {
            "0": "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c",
            "1": "0x100eeE74459CB95583212869f9c0304e7cE11EAA",
            "sender": "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c",
            "_from": "0x100eeE74459CB95583212869f9c0304e7cE11EAA",
            "length": 2
        }
    },
    {
        "from": "0x100eee74459cb95583212869f9c0304e7ce11eaa",
        "topic": "0x5e7091f10fdcc639a86214a4a72e1b3d6e058d14be100fbe52d22ca354ef18ec",
        "event": "CalledSomeLib",
        "args": {
            "0": "0x100eeE74459CB95583212869f9c0304e7cE11EAA",
            "_from": "0x100eeE74459CB95583212869f9c0304e7cE11EAA",
            "length": 1
        }
    }
]

分析logs得到的msg.sender顺序为 :
    0x100eee74459cb95583212869f9c0304e7ce11eaa ---> CallsTestContract(当前合约地址)
    0xca35b7d915458ef540ade6068dfe2f44e8fa733c ---> 执行函数的账户地址
    0x100eee74459cb95583212869f9c0304e7ce11eaa ---> CallsTestContract(当前合约地址)
    0xca35b7d915458ef540ade6068dfe2f44e8fa733c ---> 执行函数的账户地址
    
CallsTestContract的callTheOtherContract方法里有四种调用:(logs的_from字段)
    第一种是以TestContract身份去调用TestContract的代码,如果涉及修改内存,修改的是TestContract的内存;
    第三种是以CallsTestContract身份去调用TestContract的代码;如果涉及修改内存,修改的是CallsTestContract的内存;
    第二、四个也是以CallsTestContract身份去调用TestContract的代码,如果涉及修改内存,修改的是CallsTestContract的内存;
    
所以,运行结束后,TestContract的num值为11(仅修改一次), CallsTestContract的值为13(修改三次);

callcode: 调用后内置变量 msg.sender 的值会修改为调用者合约地址,涉及存储修改,修改的是调用者合约的内存(当前合约的)。

参考:
 https://www.jianshu.com/p/fd5075ff0ab9
 https://zhuanlan.zhihu.com/p/35292014

https://www.anquanke.com/post/id/145458
https://paper.seebug.org/633/    
        

你可能感兴趣的:(solidity的call跟delegatecall)