智能合约的4种调用方式:call、callcode、delegatecall、staticcall

资料来源:

以太坊 - 深入浅出虚拟机  

以太坊 - 深入浅出虚拟机 | 登链社区 | 区块链技术社区虚拟机用来执行以太坊上的交易,更改以太坊状态。交易分两种:普通交易和智能合约交易。在执行交易时需要支付油费。智能合约之间的调用有四种方式https://learnblockchain.cn/2019/04/09/easy-evm/

1 四种合约调用方式

合约调用合约有下面4种方式:

CALL

CALLCODE             

官方已经废弃CALLCODE了

DELEGATECALL

STATICCALL

2 CALL vs. CALLCODE

CALL和CALLCODE的区别在于:代码执行的上下文环境不同。

具体来说,CALL修改的是被调用者的storage,而CALLCODE修改的是调用者的storage。

pragma solidity ^0.4.25;

contract A {

  int public x;

  function inc_call(address _contractAddress) public {

      _contractAddress.call(bytes4(keccak256("inc()")));

  }

  function inc_callcode(address _contractAddress) public {

      _contractAddress.callcode(bytes4(keccak256("inc()")));

  }

}

contract B {

  int public x;

  function inc() public {

      x++;

  }

}

3 CALLCODE vs. DELEGATECALL

实际上,可以认为DELEGATECALL是CALLCODE的一个bugfix版本,官方已经不建议使用CALLCODE了。

CALLCODE和DELEGATECALL的区别在于:msg.sender不同。

具体来说,DELEGATECALL会一直使用原始调用者的地址,而CALLCODE不会。

我们还是写一段代码来验证我们的理解:

pragma solidity ^0.4.25;

contract A {

  int public x;

  function inc_callcode(address _contractAddress) public {

      _contractAddress.callcode(bytes4(keccak256("inc()")));

  }

  function inc_delegatecall(address _contractAddress) public {

      _contractAddress.delegatecall(bytes4(keccak256("inc()")));

  }

}

contract B {

  int public x;

  event senderAddr(address);

  function inc() public {

      x++;

      emit senderAddr(msg.sender);

  }

}

STATICCALL

STATICCALL放在这里似乎有滥竽充数之嫌,因为目前Solidity中并没有一个low level API可以直接调用它,仅仅是计划将来在编译器层面把调用view和pure类型的函数编译成STATICCALL指令。

view类型的函数表明其不能修改状态变量,而pure类型的函数则更加严格,连读取状态变量都不允许。

目前是在编译阶段来检查这一点的,如果不符合规定则会出现编译错误。如果将来换成STATICCALL指令,就可以完全在运行时阶段来保证这一点了,你可能会看到一个执行失败的交易。

话不多说,我们就先看看STATICCALL的实现代码吧:

 

可以看到,解释器增加了一个readOnly属性,STATICCALL会把该属性置为true,如果出现状态变量的写操作,则会返回一个errWriteProtection错误。

查看原文