EVM作为一个沙盒环境严格限制了数据之间的交换,不可避免的使合约成为了一座座数据孤岛,限制了智能合约应用场景扩展时的想象空间,以太坊为了避免了合约成为数据孤岛,增加了跨合约调用的支持,简言之就是一个合约可以调用另一个合约,跨合约调用大大增强了智能合约的互操作性,简化了开发成本。
为了避免跨合约调用引入的一些不确定性,EVM也对跨合约调用采取了一些安全性方面的约束,比如跨合约调用必须是确定性的静态调用,在运行前即知晓被调用合约的地址,且调用结果是确定性的。
Solidity 中用call
,callcode
,delegatecall
这三个函数来进行跨合约的调用。三者又有所区别。
call 和 callcode 的区别在于:代码执行的上下文环境不同。具体来说,call 修改的是被调用者的storage,而 callcode 修改的是调用者的storage。换用另外一种说法是,callcode 通过调用其他的智能合约的函数,来修改自己的智能合约的变量。而 call 通过调用其他的智能合约的函数,来修改被调用智能合约的状态。如下图所示:
callcode 已经被官方推荐使用 delegatecall 替代了。如果使用,编译器会提示Warning: "callcode" has been deprecated in favour of "delegatecall"
。
callcode 与 delegatecall 都是通过调用其他的智能合约的函数,来修改自己的智能合约的变量。区别就是msg.sender不同。具体来说,delegatecall 会一直使用原始调用者的地址,而 callcode 不会。如下图所示:
上面的 callcode 跟 delegatecall 都是通过调用其他的智能合约的函数,来修改自己的智能合约的变量。如果自己合约中不存在这个变量,Solidity是怎么处理的呢?Solidity 会智能地新建一个变量。但实际是在一个未定义的位置存储了这个新建的变量的值,智能合约根本没法访问到。