CALL und CALLCODE nehmen die gleiche Anzahl von Operanden (im Ausführungsstapel). Für das Ausnahme-Flag, das oben auf den Stapel geschoben wird: 0 bedeutet Ausnahme, 1 bedeutet erfolgreiche Ausführung. CALL ist einfach zu verstehen, aber ich konnte den feinen Unterschied zwischen CALL & CALLCODE nicht verdauen. Im gelben Papier steht, dass z
CALLCODE: Das bedeutet, dass der Empfänger tatsächlich derselbe Account ist wie bisher, nur dass der Code überschrieben wird.
Was bedeutet es, dass der Code überschrieben wird ? Bedeutet das, dass ich den Vertrag bitten kann, einen externen Code auszuführen? Es wäre hilfreich, wenn mir jemand ein Beispiel geben könnte, um zwischen den beiden zu unterscheiden.
EDIT: DELEGATECALL wurde in Homestead hinzugefügt, was ist der Unterschied?
DELEGATECALL
sagt im Grunde, dass ich ein Vertrag bin und Ihnen erlaube (delegiere), mit meinem Speicher zu tun, was immer Sie wollen . DELEGATECALL
ist ein Sicherheitsrisiko für den sendenden Vertrag, der darauf vertrauen muss, dass der empfangende Vertrag die Speicherung gut behandelt.
DELEGATECALL
war ein neuer Opcode, der eine Fehlerbehebung war, für CALLCODE
die nicht erhalten wurde msg.sender
und msg.value
. Wenn Alice Bob aufruft, der es DELEGATECALL
mit Charlie tut, ist das msg.sender
in DELEGATECALL
Alice (während wenn CALLCODE
verwendet msg.sender
würde, wäre das Bob).
Wenn D CALL auf E ausführt, läuft der Code im Kontext von E: die Speicherung von E wird verwendet.
Wenn D CALLCODE auf E ausführt, wird der Code im Kontext von D ausgeführt. Stellen Sie sich also vor, dass sich der Code von E in D befindet. Immer wenn der Code in den Speicher schreibt, schreibt er in den Speicher von Konto D und nicht in 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);
}
}
Wenn D CALLCODE auf E ausführt, msg.sender
ist in E D , wie im obigen Code kommentiert.
Wenn ein Konto C D aufruft und D DELEGATECALL auf E ausführt, msg.sender
befindet sich in E C . Das heißt, E hat das gleiche msg.sender
und msg.value
wie D.
Sie können oben im Solidity Browser schnell testen .
Um den Unterschied zwischen dem Anruf, dem Anrufcode und dem Delegiertenanruf zu zeigen, können wir das Beispiel des folgenden Codes betrachten:
Verträge können auf drei Arten interagieren
Anruf: Durch direkten Aufruf von einem Vertrag über eine Funktion, die nicht den Wert des Anrufers, sondern den Wert des Angerufenen festlegt. Und der Absender in diesem Fall ist nur der Anrufer
CallCode : Beim Aufruf über CallCode ruft der Aufrufer die Funktion des Aufgerufenen auf und sendet seinen eigenen Wert (oder ändert seinen eigenen Wert mit den aufgerufenen Parametern), aber es werden keine Änderungen im Speicher des Aufgerufenen widergespiegelt. Auch hier ist Absender der Anrufer selbst.
DelegateCall : Wenn ein dritter Vertrag einen Delegiertenaufruf an eine Funktion in callee im Namen des Aufrufers aufruft und Speicheränderungen am Wert des Aufrufers vorgenommen werden und nichts im Speicher des aufgerufenen widergespiegelt wird.
Hier ist der Absender nicht mehr der Anrufer, sondern der dritte Vertragsanrufhelfer
Pragma Solidität ^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);
}
}
Eine Aktualisierung des Beispiels von @eth für solidity v6:
Funktionsdefinitionen müssen seinpublic
keccak256
anstelle vonsha3
Anrufargumente verwendenabi.encode()
address()
Vertragsadresse zu bekommen
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
wurde zugunsten von delegatecall
Version 0.8.4 verworfen
aktualisiere die Antwort von @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
anstelle von zu korrigieren encodePacked
). Prost!encodeWithSignature
.
Loi.Luu
(send money out of the contract to some different address)
den Callcode auszuführen?eth
Pawel Bylica
eth
Travis Jacobs
this
das gleiche in den beiden Kontexten, ähnlich wiemsg.sender
undmsg.value
?eth
Benutzer2284570
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.
Und in diesem Fall, welches Ether-Gleichgewicht wird verwendet, das Gleichgewicht von E oder das Gleichgewicht von D?eth
this.balance
wäre das Guthaben von D. Siehe Codekommentar, der Wert von "this" ist D, wenn es entweder von callcodeSetN oder C.foo() von D aufgerufen wird .Benutzer2284570