La llamada recursiva termina con `Excepción de VM: código de operación no válido`

estoy corriendo f:

contract Test {
    function f() {
        f();
    }
}

que produce VM Exception: invalid opcodeen la solidez del navegador. ¿Es eso un error o un comportamiento esperado?

Respuestas (2)

Parece ser un error en ethereumjs-vm que usa browser-solidity a través de universal-dapp.js .

runCode.js tiene un código extraño...

  function iterateVm (done) {
    if (runState.stack.length > 1024) {
      return done(ERROR.INVALID_OPCODE)
    }

... porque la longitud de la pila es diferente de la profundidad, que se compara con el 1024 (valor de fees.stackLimit.v) en opFns.js:

  // increment the runState.depth
  callOptions.depth = runState.depth + 1

  if (runState.depth >= fees.stackLimit.v ...

Si la verificación de la longitud de la pila es correcta, al menos podría devolver una diferente que evitaría confundir el error de límite de profundidad con un código de operación no válido.


Nota: el siguiente código es una llamada de función interna que se "traduce en simples saltos dentro de EVM". Esto significa que la profundidad de la pila no aumentará .

contract Test {
    function f() {
        f();
    }
}

Se necesita una llamada de función externa para aumentar la profundidad de la pila , use this.f(). Etiquetar explícitamente la función como external(o public) también mejora la claridad.

contract Test {
    function f() external {
        this.f();
    }
}

Además, la bifurcación dura EIP150 ha hecho que romper la profundidad de la pila sea prácticamente imposible (ya que todo el gas se agotaría primero):

Reemplace la profundidad máxima de la pila de llamadas de "límite estricto" con un límite más suave, donde la creación de una torre de llamadas profunda requeriría una cantidad de gas que crecería exponencialmente. Esto elimina por completo los ataques de límite de profundidad de la pila de llamadas como una categoría de problema por la que los desarrolladores de contratos deberían preocuparse...

contract Test {
    function f() {
        f();
    }
}

provocará una llamada recursiva infinita. Por lo tanto, no es un error del navegador de solidez.

intente algo como:

contract C {
function g(uint a) returns (uint ret) { return f(); }
function f() returns (uint ret) { return g(7) + f(); }
}

Estas llamadas a funciones se traducen en simples saltos dentro del EVM.

Editar : el error se genera por una longitud de pila de 1024, por lo que el compilador ya no puede saltar al destino (cada llamada f () realiza un salto a Jumpdest).

74 JUMPDEST
75 PUSH 50
77 PUSH 4a
79 JUMP

cada llamada recursiva (..=>97=>74=>97...) 0X50 se agrega a la pila hasta que alcanzamos la longitud del límite de participación y luego salta el error. Este error se disparará mientras el contrato tenga suficiente gas restante (por lo que no hay excepción de outOfGas).

Sé que esta es una llamada recursiva infinita pero, por lo tanto, esperaba una outOfGasexcepción stackDepth. ¿Por qué invalid opcode? Un simple salto no debería ser un código de operación no válido.
por favor lea la nota de edición en mi respuesta anterior