三個方法都是用來進行合約交互的方法。由于沒有進行更進一步的封裝,不是最好的選擇,一般不會直接使用到它們;另外一個顯著的問題由于可以使用任意參數類型,在語言層面不能保證類型安全,所以不推薦使用。
call() 方法
call()
是一個底層的接口,用來向一個合約發送消息[1],也就是說如果你想實現自己的消息傳遞,可以使用這個函數。函數支持傳入任意類型的任意參數,并將參數打包成32字節,相互拼接后向合約發送這段數據。
函數的傳輸的數據
由于向另一個合約發送數據時,找不到對應的方法簽名,會默認調用fallback()
函數[2],所以我們可以通過這個來看看call()
傳的具體數據。
pragma solidity ^0.4.0;
contract Person{
bytes fail;
function(){
fail = msg.data;
}
function getFail() returns (bytes){
return fail;
}
}
contract CallTest{
function callData(address addr) returns (bool){
return addr.call("abc", 256);
}
}
下圖實際操作演示。
可以看到,由于沒有找到對應的函數調用,最終調用的是fallback()
函數,通過fail
字段,我們看到了收到msg.data
:
0x61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100
前32字節為abc
對應的acii編碼值,后32位為256
的對應編碼值[3]。
call指定函數
如果第一個參數剛好是四個字節,會認為這四個字節指定的是函數簽名的序號值,生成方式參見ABI協議的函數選擇器[4]。由如果你只是想傳個參數值,而不是想指定一個函數序號,應避免第一個參數剛好是四個字節。
pragma solidity ^0.4.0;
contract Person{
uint age = 10;
function increaseAge(string name, uint num) returns (uint){
return ++age;
}
function getAge() returns (uint){
return age;
}
}
contract CallTest{
function callByFun(address addr)returns (bool){
bytes4 methodId = bytes4(keccak256("increaseAge(string,uint256)"));
return addr.call(methodId,"jack", 1);
}
}
通過下圖的gif可以看看操作演示:
函數的結果
call()
的返回結果是一個bool
,表示是否成功的調用,或者是失敗引起了EVM異常。該方法無法直接訪問函數返回結果(因為需要事前知道編碼和返回結果大小)。
call()
的返回結果即使成功,并不能說操作成功了,只是沒有出現異常,比如我們第一個例子中,實際是調用到了fallback()
函數。
delegatecall()
call
與delegatecall
的功能類似,區別僅在于后者僅使用給定地址的代碼,其它信息則使用當前合約(如存儲,余額等等)。
函數的設計目的是為了使用存儲在另一個合約的庫代碼。
所以開發者在提供這樣的庫時,就要如何安排存儲來達到這樣的目的。
參考資料
關于作者
專注基于以太坊的相關區塊鏈技術,了解以太坊,Solidity,Truffle。
博客:http://me.tryblockchain.org
-
關于這個的詳細說明,可以參考這里。http://ethereum.stackexchange.com/questions/8168/understanding-namereg-callregister-myname-style-call-between-contracts ?
-
類似構造函數的定義方式。 ?
-
參數編碼格式與ABI的編碼格式一致,直接參考ABI。 ?
-
詳細了解ABI格式,可以參考:【文檔翻譯系列】ABI詳解 ?