call(0.5.3版本)
https://solidity.readthedocs.io/en/develop/types.html#address
合約之間的調(diào)用有2種方式: 底層的call方式和 new 合約的方式
call:通過合約ContractAddres.call(編碼后的方法名和參數(shù))亿卤,返回調(diào)用是否成功扛点,以及返回值data
delegatecall :設(shè)計(jì)是為了調(diào)用其它合約的API用的,類似于 Copy了API合約的API函數(shù)到本地合約執(zhí)行益缠,會修改調(diào)用者合約的狀態(tài)變量越平。
staticcall: Since byzantium staticcall can be used as well. This is basically the same as call, but will revert if the called function modifies the state in any way
All three functions call, delegatecall and staticcall are very low-level functions and should only be used as a last resort as they break the type-safety of Solidity.
The .gas() option is available on all three methods, while the .value() option is not supported for delegatecall.
可以設(shè)置這次合約調(diào)用的 gas最大值、轉(zhuǎn)賬的Ether值
address(nameReg).call.gas(1000000)(abi.encodeWithSignature("register(string)", "MyName"));
address(nameReg).call.value(1 ether)(abi.encodeWithSignature("register(string)", "MyName"));
address(nameReg).call.gas(1000000).value(1 ether)(abi.encodeWithSignature("register(string)", "MyName"));
合約之間的調(diào)用建議的方式是:通過new 合約亩歹,通過合約的方式去調(diào)用书幕,而不是通過call的方式去調(diào)用新荤,因?yàn)檫@樣會失去控制權(quán)。(重入風(fēng)險)
使用方式:
- 通過abi的函數(shù)進(jìn)行編碼按咒,然后通過合約地址進(jìn)行調(diào)用
- 返回是否調(diào)用成功迟隅,以及返回?cái)?shù)據(jù)但骨。
- 返回?cái)?shù)據(jù)解碼之后,可以獲取調(diào)用返回值智袭。
bytes memory payload = abi.encodeWithSignature("register(string)", "MyName");
(bool success, bytes memory returnData) = address(nameReg).call(payload);
require(success);
http://me.tryblockchain.org/Solidity-call-callcode-delegatecall.html
- 三個方法都是用來進(jìn)行合約交互的方法奔缠。
- 由于沒有進(jìn)行更進(jìn)一步的封裝,不是最好的選擇吼野,一般不會直接使用到它們校哎;另外一個顯著的問題由于可以使用任意參數(shù)類型,在語言層面不能保證類型安全瞳步,所以不推薦使用闷哆。
<address>.call(...) returns (bool)
<address>.delegatecall(...) returns (bool)
代碼層面上面的區(qū)別
- call: 調(diào)用后內(nèi)置變量 msg 的值會修改為調(diào)用者,執(zhí)行環(huán)境為被調(diào)用者的運(yùn)行環(huán)境(合約的 storage)单起。
- delegatecall: 調(diào)用后內(nèi)置變量 msg 的值不會修改為調(diào)用者抱怔,但執(zhí)行環(huán)境為調(diào)用者的運(yùn)行環(huán)境(修改的成員變量的值體現(xiàn)在 調(diào)用者)。
第一種情況call調(diào)用嘀倒,B.temp1的值 Account(A).address屈留,成了A的合約地址了,因?yàn)槭茿合約調(diào)用了B合約,而不是 A.msg.sender
但是A合約調(diào)用自己的test()方法测蘑, A合約的 temp1 =A.msg.sender灌危,而B合約的temp1= Account(A).address
第二種情況delegatecall:假借B的手,調(diào)用了B合約的方法碳胳,但是修改了的卻是A合約的變量勇蝙,這個有點(diǎn)厲害。
pragma solidity >=0.4.22 <0.6.0;
contract A {
address public temp1;
uint256 public temp2;
function three_call(address addr) public {
bytes memory payload = abi.encodeWithSignature("test()");
(bool success, bytes memory returnData) = address(addr).call(payload);
//(bool success, bytes memory returnData) = address(addr).delegatecall(payload);
}
function test() public {
temp1 = msg.sender;
temp2 = 200;
}
}
contract B {
address public temp1;
uint256 public temp2;
function test() public {
temp1 = msg.sender;
temp2 = 100;
}
}
delegatecall的安全漏洞
因?yàn)閐elegatecall的執(zhí)行環(huán)境是調(diào)用者的環(huán)境挨约,假如A合約 里面提供了delegatecall 函數(shù)調(diào)用了B合約里面的方法味混,那么用戶張三調(diào)用A合約的該方法,如果B合約里面修改了某個環(huán)境變量的值烫罩,且該值A(chǔ)合約中也有惜傲,那么A合約中的成員變量
例如A合約 開放了callFunc方法,該方法可以讓用戶調(diào)用 某個合約地址的某個方法(因?yàn)槎紱]寫死)
contract A {
address owner;
function callFunc(address addr, bytes data) public {
addr.delegatecall(data);
//address(Attack).delegatecall(bytes4(keccak256("foo()"))); //利用代碼示意
}
}
現(xiàn)在有個黑客發(fā)現(xiàn)了這個Bug贝攒,于是黑客寫了一個Attack合約,用 Attack合約的地址时甚,和 foo函數(shù)去調(diào)用 A合約中的callFunc方法隘弊,通過在Attack合約中修改owner的地址,因?yàn)閳?zhí)行環(huán)境為調(diào)用者的環(huán)境荒适,所以直接修改了A合約的owner梨熙,并且可以發(fā)起轉(zhuǎn)賬,因?yàn)閳?zhí)行環(huán)境是調(diào)用者
contract Attack {
address owner;
address benifit;
function foo() public {
// any codes
benifit.send(100);
owner="黑客地址";
}
}