Я бегу f
:
contract Test {
function f() {
f();
}
}
который производит VM Exception: invalid opcode
в браузере-solidity. Это ошибка или ожидаемое поведение?
Похоже, это ошибка в 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
? Простой прыжок не должен быть недопустимым кодом операции.Бадр Беллай