CALL и CALLCODE принимают одинаковое количество операндов (в стеке выполнения). Для флага исключения, помещаемого на вершину стека: 0 означает исключение, 1 означает успешное выполнение. CALL легко понять, но я не мог понять тонкую разницу между CALL и CALLCODE. В желтой бумаге указано, что для
CALLCODE: Это означает, что получатель фактически является той же учетной записью, что и в настоящее время, просто код перезаписывается.
Что означает перезаписанный код ? Означает ли это, что я могу попросить контракт выполнить какой-то внешний код? Было бы полезно, если бы кто-нибудь мог привести мне пример, чтобы различать их.
РЕДАКТИРОВАТЬ: DELEGATECALL был добавлен в Homestead, в чем разница?
DELEGATECALL
в основном говорит, что у меня контракт, и я разрешаю (делегирую) вам делать все, что вы хотите, с моим хранилищем . DELEGATECALL
представляет собой угрозу безопасности для контракта-отправителя, который должен быть уверен, что контракт-получатель будет хорошо обращаться с хранилищем.
DELEGATECALL
был новый код операции, который был исправлением ошибки, для CALLCODE
которого не сохранялись msg.sender
и файлы msg.value
. Если Алиса вызывает Боба, который делает это DELEGATECALL
с Чарли, то msg.sender
это DELEGATECALL
Алиса (тогда как, если бы CALLCODE
использовалось, это msg.sender
был бы Боб).
Когда D выполняет CALL для E, код выполняется в контексте E: используется память E.
Когда D выполняет CALLCODE для E, код выполняется в контексте D. Итак, представьте, что код E находится в D. Всякий раз, когда код записывает в хранилище, он записывает в хранилище учетной записи D, а не E.
contract D {
uint public n;
address public sender;
function callSetN(address _e, uint _n) {
_e.call(bytes4(sha3("setN(uint256)")), _n); // E's storage is set, D is not modified
}
function callcodeSetN(address _e, uint _n) {
_e.callcode(bytes4(sha3("setN(uint256)")), _n); // D's storage is set, E is not modified
}
function delegatecallSetN(address _e, uint _n) {
_e.delegatecall(bytes4(sha3("setN(uint256)")), _n); // D's storage is set, E is not modified
}
}
contract E {
uint public n;
address public sender;
function setN(uint _n) {
n = _n;
sender = msg.sender;
// msg.sender is D if invoked by D's callcodeSetN. None of E's storage is updated
// msg.sender is C if invoked by C.foo(). None of E's storage is updated
// the value of "this" is D, when invoked by either D's callcodeSetN or C.foo()
}
}
contract C {
function foo(D _d, E _e, uint _n) {
_d.delegatecallSetN(_e, _n);
}
}
Когда D выполняет CALLCODE для E, msg.sender
внутри E находится D , как указано в приведенном выше коде.
Когда учетная запись C вызывает D, а D выполняет DELEGATECALL для E, msg.sender
внутри E находится C. То есть E имеет то же самое, что msg.sender
и msg.value
D.
Вы можете быстро протестировать выше в Solidity Browser .
Показывая разницу между вызовом, кодом вызова и вызовом делегата, мы можем рассмотреть пример следующего кода:
Контракты могут взаимодействовать тремя способами
Вызов: путем прямого вызова из контракта через функцию, которая не устанавливает значение вызывающего объекта, а устанавливает значение вызываемого объекта. И отправителем в этом будет только вызывающий абонент
CallCode : при вызове через CallCode вызывающий вызывает функцию вызываемого объекта и отправляет свое собственное значение (или изменяет свое значение с помощью вызываемых параметров), но никакие изменения не отражаются в хранилище вызываемого объекта. Здесь также отправителем является сам вызывающий абонент.
DelegateCall : когда третий контракт вызывает вызов делегата к некоторой функции в вызываемом объекте от имени вызывающего объекта, и изменения в хранилище вносятся в значение вызывающего объекта, и ничего не отражается в хранилище вызываемого объекта.
Здесь отправитель больше не вызывающий абонент, а третий контракт Call Helper.
прочность прагмы ^0,4,0;
contract Caller {
uint public value;
address public sender;
function callSetValue(address _callee, uint _value) {
_callee.call(bytes4(sha3("setValue(uint256)")), _value); // Callee's storage is set as given , Caller's is not modified
}
function callcodeSetValue(address _callee, uint _value) {
_callee.callcode(bytes4(sha3("setValue(uint256)")), _value); // Caller's storage is set, Calee is not modified
}
function delegatecallSetValue(address _callee, uint _value) {
_callee.delegatecall(bytes4(sha3("setValue(uint256)")), _value); // Caller's storage is set, Callee is not modified
}
}
contract Callee {
uint public value;
address public sender;
function setValue(uint _value) {
value = _value;
sender = msg.sender;
// msg.sender is Caller if invoked by Caller's callcodeSetValue. None of Callee's storage is updated
// msg.sender is OnlyCaller if invoked by onlyCaller.justCall(). None of Callee's storage is updated
// the value of "this" is Caller, when invoked by either Caller's callcodeSetValue or CallHelper.justCall()
}
}
contract CallHelper {
function justCall(Caller _caller, Callee _callee, uint _value) {
_caller.delegatecallSetValue(_callee, _value);
}
}
Обновление примера @eth для Solidity v6:
определения функций должны бытьpublic
keccak256
на местеsha3
использование аргументов вызоваabi.encode()
address()
получить адрес контракта
pragma solidity ^0.6.0;
contract D {
uint public n;
address public sender;
function callSetN(address _e, uint _n) public {
_e.call(abi.encode(bytes4(keccak256("setN(uint256)")), _n)); // E's storage is set, D is not modified
}
/*
callcode is depreciated
function callcodeSetN(address _e, uint _n) public {
_e.callcode(abi.encode(bytes4(keccak256("setN(uint256)")), _n)); // D's storage is set, E is not modified
}
*/
function delegatecallSetN(address _e, uint _n) public {
_e.delegatecall(abi.encode(bytes4(keccak256("setN(uint256)")), _n)); // D's storage is set, E is not modified
}
}
contract E {
uint public n;
address public sender;
function setN(uint _n) public {
n = _n;
sender = msg.sender;
// msg.sender is D if invoked by D's callcodeSetN. None of E's storage is updated
// msg.sender is C if invoked by C.foo(). None of E's storage is updated
// the value of "this" is D, when invoked by either D's callcodeSetN or C.foo()
}
}
contract C {
function foo(D _d, E _e, uint _n) public {
_d.delegatecallSetN(address(_e), _n);
}
}
callcode
устарел в пользу delegatecall
версии 0.8.4
обновить ответ @atomh33ls
pragma solidity ^0.6.0;
contract E {
uint256 public n;
address public sender;
function setN(uint256 _n) public {
n = _n;
sender = msg.sender;
}
}
contract D{
uint256 public n;
address public sender;
function callSetN(address _e,uint256 _n) public {
_e.call(abi.encodePacked(bytes4(keccak256("setN(uint256)")),_n));
}
}
encode
вместо encodePacked
). Ваше здоровье!encodeWithSignature
.
Лои.Луу
(send money out of the contract to some different address)
callcode?эт
Павел Былица
эт
Трэвис Джейкобс
this
одинаковым в двух контекстах, подобноmsg.sender
иmsg.value
?эт
пользователь 2284570
When D does CALLCODE on E, the code runs in the context of D. So imagine that the code of E is in D. Whenever the code writes to storage, it writes to the storage of account D, instead of E.
И в таком случае, какой баланс эфира используется, баланс E или баланс D?эт
this.balance
будет балансом D. См. комментарий к коду, значение "this" равно D при вызове с помощью callcodeSetN D или C.foo() .пользователь 2284570