Рекурсивный вызов заканчивается «Исключением виртуальной машины: недопустимый код операции».

Я бегу f:

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

который производит VM Exception: invalid opcodeв браузере-solidity. Это ошибка или ожидаемое поведение?

Ответы (2)

Похоже, это ошибка в ethereumjs-vm, которую браузер-солидность использует через universal-dapp.js .

runCode.js имеет странный код...

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

...потому что длина стека отличается от глубины, которая сверяется с 1024 (значение fee.stackLimit.v) в opFns.js:

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

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

Если проверка длины стека верна, она может, по крайней мере, вернуть другое, что позволит избежать путаницы ошибки ограничения глубины с недопустимым кодом операции.


Примечание: следующий код представляет собой внутренний вызов функции , который «превращается в простые переходы внутри EVM». Это означает, что глубина стека не увеличится .

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

Для увеличения глубины стека необходим вызов внешней функции , используйте this.f(). Явное обозначение функции как external(или public) также улучшает ясность.

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

Кроме того, хардфорк EIP150 сделал практически невозможным проникновение в глубину стека (поскольку сначала будет исчерпан весь газ):

Замените «жесткое ограничение» максимальной глубины стека вызовов на более мягкое ограничение, при котором создание глубокой вышки вызовов потребует экспоненциально растущего количества газа. Это полностью устраняет атаки с ограничением глубины стека вызовов как категорию проблем, о которых должны беспокоиться разработчики контрактов...

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

это спровоцирует бесконечный рекурсивный вызов. Так что это не ошибка браузера Solidity.

попробуйте что-то вроде:

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

Эти вызовы функций транслируются в простые переходы внутри EVM.

Изменить : ошибка генерируется длиной стека 1024, поэтому компилятор больше не может перейти к месту назначения (каждый вызов f() выполняет переход к Jumpdest).

74 JUMPDEST
75 PUSH 50
77 PUSH 4a
79 JUMP

каждый рекурсивный вызов (..=>97=>74=>97...) 0X50 добавляется в стек до тех пор, пока мы не достигнем предельной длины ставки, после чего выскакивает ошибка. Эта ошибка будет срабатывать, пока у контракта достаточно остатка газа (поэтому нет исключения outOfGas).

Я знаю, что это бесконечный рекурсивный вызов, но поэтому я ожидал outOfGasисключения stackDepth. Почему invalid opcode? Простой прыжок не должен быть недопустимым кодом операции.
пожалуйста, прочитайте примечание к редактированию в моем предыдущем ответе