call vs delegatecall vs callcode
调用已部署合约的function 就靠它们了。合约之间的交互,其中包括正常使用,也包括恶意使用(攻击)。
使用它们调用已部署合约的function需要知道两件事:
已部署合约的 address。
该合约的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。