Me gustaría llamar a un contrato y tratar los valores devueltos manualmente usando el ensamblaje EVM en Solidity. Por ejemplo, esto debería simplemente sumar dos números.
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)
}
}
El problema es que esto devuelve un error de falta de gas, que se origina en la call...
línea.
¿Cómo puedo solucionar este problema?
El error se debió a un elemento no manejado en la pila que dejó el call
código de operación. El código relevante de trabajo y optimizado está aquí:
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
}
Gracias a @chriseth por señalar mi error.
EDITAR:
Como señaló @Ilan, la final mstore
no es estrictamente necesaria, ya que no nos importa mantener esa memoria asignada. Si los datos devueltos son un objeto de montón como una matriz, debe asegurarse de que la memoria permanezca asignada.
call
se mide en bytes, no en bits. SE no me deja editar para corregir porque no es "un cambio lo suficientemente grande" a pesar de que tiene un impacto masivo en la precisión de la respuesta. :/mstore(0x40,add(x,0x20));
success
, ¿qué sucede exactamente? ¿Se carga la siguiente salida para c
tomar el booleano de éxito como un argumento de entrada (junto con x
) y el error? Muchas gracias.swap n
operación donde n
se indica qué tan abajo en la pila está la variable. En sol ~3.5, cuando dejamos un elemento adicional en la pila, las direcciones se desvían en 1 y, por lo tanto, el argumento de la siguiente función es el 1 anterior x
en la pila, lo que da una dirección de memoria no válida.
Nicolás Massart
tjaden hess
Nicolás Massart
tjaden hess
Nicolás Massart