外部調(diào)用: sendTransaction/call
函數(shù)調(diào)用一般分外部調(diào)用和內(nèi)部調(diào)用兩種, 外部調(diào)用是通過JSON-RPC接口實現(xiàn)對合約函數(shù)的調(diào)用, 有3種調(diào)用方式:
testInstance.testFunc.sendTransaction();
testInstance.testFunc();
testInstance.testFunc.call();
區(qū)別如下:
-
testInstance.testFunc.sendTransaction()
; 會創(chuàng)建一個交易,調(diào)用之后會返回一個交易hash值酷含,它會廣播到網(wǎng)絡(luò)抓督,等待礦工打包, 它會消耗gas妒貌。 -
testInstance.testFunc.call()
; 它完全是一個本地調(diào)用恰矩,不會向區(qū)塊鏈網(wǎng)絡(luò)廣播任何東西,它的返回值完全取決于 testFunc 方法的代碼,不會消耗gas。 -
testInstance.testFunc()
; 它會比較特殊仿野,由于有constant標(biāo)識的方法不會修改狀態(tài)變量,所以它不會被編譯器執(zhí)行她君。所以脚作,如果testFunc() 有constant標(biāo)識,它并不會被編譯器執(zhí)行缔刹,web3.js會執(zhí)行call()的本地操作鳖枕。相反如果沒有constant標(biāo)識,會執(zhí)行sendTransaction()操作桨螺。
測試代碼如下:
pragma solidity ^0.4.12;
contract Test {
uint public testMem;
function testFunc1() returns (string resMes){
testMem++;
resMes = "try to modify testMem,but has no constant label";
}
function testFunc2() constant returns (string resMes){
testMem--;
resMes = "try to modify testMem and has constant label";
}
}
內(nèi)部調(diào)用: call,callcode,delegatecall
內(nèi)部調(diào)用是指合約內(nèi)調(diào)用其他合約的函數(shù). 除了直接調(diào)用函數(shù),還可以通過call,callcode,delegatecall的方式調(diào)用其他合約的函數(shù),區(qū)別如下:
- CALL:是在 被調(diào)用者 的上下文中執(zhí)行,只能修改被調(diào)用者的storage;
- CALLCODE和DELEGATECALL: 是在 調(diào)用者 的上下文中執(zhí)行, 可以修改調(diào)用者的storage;
- CALLCODE 阻止msg.sender和msg.value傳遞; 而DELEGATECALL不阻止;
- 在A的函數(shù)中,B.callcode(c的函數(shù)): c看到msg.sender是B;
- 在A的函數(shù)中,B.delegatecall(c的函數(shù)): c看到msg.sender是A;
合約示例如下:
contract D {
uint public n;
address public sender;
function callSetN(address _e, uint _n) {
// E的 storage被修改,D未修改
_e.call(bytes4(sha3("setN(uint256)")), _n);
}
function callcodeSetN(address _e, uint _n) {
// D的 storage被修改, E未修改
_e.callcode(bytes4(sha3("setN(uint256)")), _n);
}
function delegatecallSetN(address _e, uint _n) {
// D的storage被修改, E未修改
_e.delegatecall(bytes4(sha3("setN(uint256)")), _n);
}
}
contract E {
uint public n;
address public sender;
function setN(uint _n) {
n = _n;
sender = msg.sender;
// D通過callcodeSetN調(diào)用, msg.sender是D . E的storage不會更新
// 通過C.foo()調(diào)用,msg.sender是C . E的storage不會更新
}
}
contract C {
function foo(D _d, E _e, uint _n) {
_d.delegatecallSetN(_e, _n);
}
}