Rekursiver Aufruf endet mit „VM-Ausnahme: ungültiger Opcode“.

Ich laufe f:

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

was VM Exception: invalid opcodein Browser-Festigkeit erzeugt. Ist das ein Fehler oder erwartetes Verhalten?

Antworten (2)

Scheint ein Fehler in der ethereumjs-vm zu sein, die browser-solidity über universal-dapp.js verwendet .

runCode.js hat seltsamen Code ...

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

...weil sich die Stapellänge von der Tiefe unterscheidet, die gegen die 1024 (Wert von fee.stackLimit.v) in opFns.js geprüft wird:

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

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

Wenn die Stapellängenprüfung korrekt ist, könnte sie zumindest einen anderen zurückgeben, der eine Verwechslung des Tiefenbegrenzungsfehlers mit einem ungültigen Opcode vermeiden würde.


Hinweis: Der folgende Code ist ein interner Funktionsaufruf , der „in einfache Sprünge innerhalb der EVM übersetzt“ wird. Das bedeutet, dass die Stapeltiefe nicht zunimmt .

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

Um die Stapeltiefe zu erhöhen, ist ein externer Funktionsaufruf erforderlich , verwenden Sie this.f(). Auch die explizite Bezeichnung der Funktion als external(oder public) verbessert die Übersichtlichkeit.

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

Darüber hinaus hat die EIP150 -Hardfork das Durchbrechen der Stack-Tiefe praktisch unmöglich gemacht (da das gesamte Gas zuerst erschöpft wäre):

Ersetzen Sie die maximale Call-Stack-Tiefe des „harten Limits“ durch ein weicheres Limit, bei dem das Erstellen eines tiefen Call-Towers eine exponentiell wachsende Menge an Gas erfordern würde. Dadurch werden Call-Stack-Tiefenbegrenzungsangriffe als eine Kategorie von Problemen, um die sich Vertragsentwickler kümmern sollten, vollständig entfernt ...

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

es wird einen unendlichen rekursiven Aufruf provozieren. Es handelt sich also nicht um einen Solidity-Browser-Bug.

versuchen Sie etwas wie:

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

Diese Funktionsaufrufe werden innerhalb der EVM in einfache Sprünge übersetzt.

Bearbeiten : Der Fehler wird durch eine Stapellänge von 1024 generiert, sodass der Compiler nicht mehr zum Ziel springen konnte (jeder Aufruf von f () führt einen Jump to Jumpdest durch).

74 JUMPDEST
75 PUSH 50
77 PUSH 4a
79 JUMP

Bei jedem rekursiven Aufruf (..=>97=>74=>97...) wird 0X50 zum Stack hinzugefügt, bis wir die Stake-Limit-Länge erreichen, dann springt der Fehler heraus. Dieser Fehler wird ausgelöst, solange der Vertrag über genügend Restgas verfügt (also keine outOfGas-Ausnahme).

Ich weiß, dass dies ein unendlich rekursiver Aufruf ist, aber deshalb habe ich eine outOfGasoder - stackDepthAusnahme erwartet. Warum invalid opcode? Ein einfacher Sprung sollte kein ungültiger Opcode sein.
Bitte lesen Sie den Bearbeitungshinweis in meiner vorherigen Antwort