Call,CallCode,DelegateCall,StaticCall,你分得清嗎?
孔乙己懂“回”字的四種寫法,你會智慧合約的四種呼叫方式嗎?
在中大型的專案中,我們不可能在一個智慧合約中實現所有的功能,而且這樣也不利於分工合作。一般情況下,我們會把程式碼按功能劃分到不同的庫或者合約中,然後提供介面互相呼叫。
在Solidity中,如果只是為了程式碼複用,我們會把公共程式碼抽出來,部署到一個library中,後面就可以像呼叫C庫、Java庫一樣使用了。但是library中不允許定義任何storage型別的變數,這就意味著library不能修改合約的狀態。如果需要修改合約狀態,我們需要部署一個新的合約,這就涉及到合約呼叫合約的情況。
合約呼叫合約有下面4種方式:
- CALL
- CALLCODE
- DELEGATECALL
- STATICCALL
1.CALL vs. CALLCODE
CALL和CALLCODE的區別在於:程式碼執行的上下文環境不同。
具體來說,CALL修改的是被呼叫者的storage,而CALLCODE修改的是呼叫者的storage。
我們寫個合約驗證一下我們的理解:
pragma solidity ^0.4.25; contract A { int public x; function inc_call(address _contractAddress) public { _contractAddress.call(bytes4(keccak256("inc()"))); } function inc_callcode(address _contractAddress) public { _contractAddress.callcode(bytes4(keccak256("inc()"))); } } contract B { int public x; function inc() public { x++; } }
我們先呼叫一下inc_call(),然後查詢合約A和B中x的值有什麼變化:
可以發現,合約B中的x被修改了,而合約A中的x還等於0。
我們再呼叫一下inc_callcode()試試:
可以發現,這次修改的是合約A中x,合約B中的x保持不變。
2.CALLCODE vs. DELEGATECALL
實際上,可以認為DELEGATECALL是CALLCODE的一個bugfix版本,官方已經不建議使用CALLCODE了。
CALLCODE和DELEGATECALL的區別在於:msg.sender不同。
具體來說,DELEGATECALL會一直使用原始呼叫者的地址,而CALLCODE不會。
我們還是寫一段程式碼來驗證我們的理解:
pragma solidity ^0.4.25;
contract A {
int public x;
function inc_callcode(address _contractAddress) public {
_contractAddress.callcode(bytes4(keccak256("inc()")));
}
function inc_delegatecall(address _contractAddress) public {
_contractAddress.delegatecall(bytes4(keccak256("inc()")));
}
}
contract B {
int public x;
event senderAddr(address);
function inc() public {
x++;
emit senderAddr(msg.sender);
}
}
我們首先呼叫一下inc_callcode(),觀察一下log輸出:
可以發現,msg.sender指向合約A的地址,而非交易發起者的地址。
我們再呼叫一下inc_delegatecall(),觀察一下log輸出:
可以發現,msg.sender指向的是交易的發起者。
3.STATICCALL
STATICCALL放在這裡似乎有濫竽充數之嫌,因為目前Solidity中並沒有一個low level API可以直接呼叫它,僅僅是計劃將來在編譯器層面把呼叫view和pure型別的函式編譯成STATICCALL指令。
view型別的函式表明其不能修改狀態變數,而pure型別的函式則更加嚴格,連讀取狀態變數都不允許。
目前是在編譯階段來檢查這一點的,如果不符合規定則會出現編譯錯誤。如果將來換成STATICCALL指令,就可以完全在執行時階段來保證這一點了,你可能會看到一個執行失敗的交易。
話不多說,我們就先看看STATICCALL的實現程式碼吧:
可以看到,直譯器增加了一個readOnly屬性,STATICCALL會把該屬性置為true,如果出現狀態變數的寫操作,則會返回一個errWriteProtection錯誤。
就聊到這裡,相信大家已經掌握了合約的四種呼叫方式了吧~
更多文章歡迎關注“鑫鑫點燈”專欄:https://blog.csdn.net/turkeycock
或關注飛久微信公眾號: