Ich möchte einen Vertrag aufrufen und die Rückgabewerte per EVM-Montage in Solidity manuell bearbeiten. Dies sollte beispielsweise einfach zwei Zahlen zusammenzählen.
contract Test1 {
function add(int a, int b) returns(int){ //Simply add the two arguments and return
return a+b;
}
function() returns (int){ //If the function signature doesn't check out, return -1
return -1;
}
}
contract Test2 {
Test1 test1;
function Test2(){ //Constructor function
test1 = new Test1(); //Create new "Test1" function
}
function test(int a, int b) constant returns (int c){
address addr = address(test1); //Place the test1 address on the stack
bytes4 sig = bytes4(sha3("add(int256,int256)")); //Function signature
assembly {
let x := mload(0x40) //Find empty storage location using "free memory pointer"
mstore(x,sig) //Place signature at begining of empty storage
mstore(add(x,0x04),a) //Place first argument directly next to signature
mstore(add(x,0x24),b) //Place second argument next to first, padded to 32 bytes
call(5000, addr, 0, //Issue call, providing 5k gas and 0 value to "addr"
x, 0x44, add(x,0x80), 0x20) //Inputs start at location "x" and are 68 bytes long, outputs start 128 bytes after x, and are 32 bytes long
c := mload(add(x,0x80)) //Assign output value to c
mstore(0x40,add(x,0x100)) // Set storage pointer to empty space
}
}
function test2(int a, int b) constant returns(int c){ //Make sure the Test1 function works properly
return test1.add(a,b); // (It does)
}
}
Das Problem ist, dass dies einen Out-of-Gas-Fehler zurückgibt, der von der call...
Leitung stammt.
Wie kann ich dieses Problem beheben?
Der Fehler war auf ein nicht behandeltes Element auf dem Stapel zurückzuführen, das vom call
Opcode hinterlassen wurde. Der funktionierende und optimierte relevante Code ist hier:
assembly {
let x := mload(0x40) //Find empty storage location using "free memory pointer"
mstore(x,sig) //Place signature at begining of empty storage
mstore(add(x,0x04),a) //Place first argument directly next to signature
mstore(add(x,0x24),b) //Place second argument next to first, padded to 32 bytes
let success := call( //This is the critical change (Pop the top stack value)
5000, //5k gas
addr, //To addr
0, //No value
x, /Inputs are stored at location x
0x44, //Inputs are 68 bytes long
x, //Store output over input (saves space)
0x20) //Outputs are 32 bytes long
c := mload(x) //Assign output value to c
mstore(0x40,add(x,0x44)) // Set storage pointer to empty space
}
Danke an @chriseth für den Hinweis auf meinen Fehler
BEARBEITEN:
Wie @Ilan betonte, ist das Finale mstore
nicht unbedingt erforderlich, da es uns egal ist, diesen Speicher zugewiesen zu halten. Wenn die zurückgegebenen Daten ein Heap-Objekt wie ein Array sind, müssen Sie sicherstellen, dass der Speicher zugewiesen bleibt.
call
wird in Bytes gemessen, nicht in Bits. SE lässt mich nicht zur Korrektur bearbeiten, da die Änderung nicht "groß genug" ist, obwohl sie massive Auswirkungen auf die Genauigkeit der Antwort hat. :/mstore(0x40,add(x,0x20));
success
Was genau passiert, wenn Sie den Anrufausgang nicht zuweisen? Wird die folgende Ausgabe geladen, c
um den booleschen Erfolg als Eingabeargument (zusammen mit x
) und den Fehler auszugeben? Danke vielmals.swap n
Operation, in der angegeben n
ist, wie weit unten im Stapel sich die Variable befindet. Wenn wir in sol ~3.5 ein zusätzliches Element auf dem Stack belassen, sind die Adressen um 1 versetzt, und daher ist das Argument für die nächste Funktion das, was 1 oben x
auf dem Stack war, was eine ungültige Speicheradresse ergibt.
Nicolas Massart
Tjaden Hess
Nicolas Massart
Tjaden Hess
Nicolas Massart