Исключение в контракте отменяет весь стек, использует весь газ
Я добавил чек, чтобы убедиться, что когда я отправляю эфир, если он не работает, я пытаюсь прервать транзакцию с помощью броска. Например: Исключения на целостность
Когда вы совершаете вызов функции, которая выполняет этот бросок из другого контракта, а бросок не выполняется, весь газ используется в газовом бюджете этого источника. (gasUsed == gasSent в квитанции о транзакции). Это происходит независимо от того, сколько газа я отправляю.
Бросок находится внутри сложной петли (бизнес-логика), но это похоже на приведенный выше пример:
// inside complicated business logic I wish to roll back if this fails
if (a.send(remittance) == false) {
throw;
}
Код, выполняющий вызов из другого контракта, выглядит следующим образом. Отмечу, что в настоящий момент установлен только один обратный вызов:
for(uint i = 0;i < callbacks.length; i++) {
callbacks[i].oracleCallback(_value, _time, _info);
}
Я использую довольно недавнюю сборку ведущей ветви geth и довольно недавней сборки (в начале января) прочности.
Вот квитанция о транзакции (частный тестовый сервер):
{"transactionHash":"0x61474d8ecd82d5c95d7c9c548f5a4cd5213b16775809580189860bd69773c07a","transactionIndex":0,"blockNumber":440377,"blockHash":"0xb88779e44dabd14fcc55f7a0cdd31aaeab6417b990ed626748d8726463173bad","cumulativeGasUsed":800000,"gasUsed":800000,"contractAddress":null,"logs":[]}
1 ответ
Да, все исключения EVM приводят к откату всех изменений состояния, и весь газ лишается шахтера. Таким образом, хотя ошибки бросания удобны, иногда имеет смысл просто вернуть некоторое значение ошибки. Вы также можете использовать события, чтобы сигнализировать о возникновении исключения.
При вызове других контрактов все исключения затухают, за исключением случаев, когда вы используете send
или call
. Это низкоуровневые функции, отправляющие Ether или вызов метода другого контракта, но они возвращают только true
или false
, если возникло исключение.
Например, если вы хотите позвонить другому контракту, но защитите себя от исключений и ограничите использование газа, используйте это:
uint gasPerCall = msg.gas / callbacks.length - 200000;
bytes4 abi = bytes4(sha3("oracleCallback(int256,uint256,bytes32)"));
for(uint i = 0; i < callbacks.length; i++) {
if (callbacks[i].call.gas(gasPerCall)(abi, _value, _time, _info) == false) {
FailedCallbackEvent(abi, gasPerCall, msg.gas);
}
}
Замените типы в ABI на правильные типы. Если количество отправленного газа не имеет значения, вы можете просто использовать callbacks[i].call(abi,_value,_time,_info);
Если вы хотите отправить Ether вместе с вызовом, вы также можете использовать callbacks[i].call.value(1 ether)(abi,_value,_time,_info);
Это вернет true, если вызов преуспел, а false в противном случае.
См. документы