Я просматривал документы и ищу разъяснения о разнице между require
и assert
и throw
и revert
.
assert(bool condition): прервать выполнение и вернуть изменения состояния, если условие ложно (используется для внутренней ошибки)
require(bool condition): прервать выполнение и вернуть изменения состояния, если условие ложно (используется для искаженного ввода)
В частности, в отношении assert
и require
, как вы проводите линию между искаженным вводом и внутренней ошибкой?
Есть два аспекта, которые следует учитывать при выборе между assert()
иrequire()
assert(false)
компилируется в 0xfe
, что является недопустимым кодом операции, израсходовав весь оставшийся газ и отменив все изменения.
require(false)
компилируется в 0xfd
который является REVERT
кодом операции, что означает, что он возместит оставшийся газ. Код операции также может возвращать значение (полезно для отладки), но я не думаю, что на данный момент это поддерживается в Solidity. (2017-11-21)
Из документов (выделено мной)
Функцию require следует использовать для проверки допустимых условий, таких как входные данные или переменные состояния контракта, или для проверки возвращаемых значений от вызовов внешних контрактов. При правильном использовании инструменты анализа могут оценить ваш контракт, чтобы определить условия и вызовы функций, которые приведут к ошибочному утверждению. Правильно функционирующий код никогда не должен доходить до ошибочного оператора assert; если это произойдет, в вашем контракте есть ошибка, которую вы должны исправить.
Приведенный выше отрывок является ссылкой на все еще (по состоянию на 21 ноября 2017 г.) экспериментальный и недокументированный файл SMTChecker
.
Я использую несколько эвристик, чтобы решить, что использовать.
require()
для:require(external.send(amount))
owned
ситуации с контрактом .require
чаще,assert()
для:assert
режеПо сути, assert
это просто для предотвращения чего-то действительно плохого, но условие не должно быть оценено как ложное.
Функции require()
и assert()
были добавлены в Solidity до форка Byzantium, в v0.4.10
. До Византии они вели себя одинаково, но уже компилировались под разные опкоды. Это означало, что некоторые контракты, развернутые до Byzantium , после форка вели себя по-другому , главное отличие заключалось в том, что началось возмещение неиспользованного газа.
assert()
и require()
откат записываются в блокчейн, чтобы balance[_to] =balance[_from] +_value
их можно было отменить, если сработает условие, не так ли assert()
? require()
(я говорю после прошлогоднего хардфорка). Или assert()
продолжают вноситься изменения?solc
: gist.github.com/maurelian/02904ae729fb11213cde20ba05a202e6 . Он предупредит вас, что второе утверждение утверждения может быть истинным для определенных значений.Я использую require
для проверки ввода, так как это немного эффективнее, чем if/throw.
function foo(uint amount) {
require(amount < totalAmount);
...
}
Где as assert
следует использовать больше для обнаружения ошибок во время выполнения:
function foo(uint amount) {
...
__check = myAmount;
myAmount -= amount;
assert(myAmount < __check);
...
}
revert
отменит изменения и возместит неиспользованный газ в более поздней версии Ethereum, но ATM действует так же, как и throw.
assert
аргументе. Это может привести к кошмару отладки, когда операции будут связаны для состояния, но забыты для утверждения.assert()
и require()
откат записываются в блокчейн, чтобы balance[_to] =balance[_from] +_value
их можно было отменить, если сработает условие, не так ли assert()
? require()
(я говорю после прошлогоднего хардфорка). Или assert()
продолжают вноситься изменения? Но оба изменения assert()
и require()
откат записываются в блокчейн, чтобы balance[_to] =balance[_from] +_value
их можно было отменить, если сработает условие, не так ли assert()
? require()
(я говорю после прошлогоднего хардфорка). Или assert()
продолжают вноситься изменения?Я думаю, что ни один из ответов не является правильным.
assert
зарезервирован для условий, поскольку ожидается, что инструменты статического анализа кода (возможно, компилятор Solidity в будущих версиях) смогут обнаружить ошибку, предупреждающую разработчика во время компиляции.
require
зарезервирован для ошибочных условий неправильных входных данных для функций (по сравнению с ожидаемыми/действительными входными данными), которые не могут быть обнаружены до времени выполнения. Это соответствует предварительным условиям функции на жаргоне языка программирования. Компилятор не может помочь из-за бесконечных возможностей входных данных.
throw
устарел в пользу возврата.
revert
зарезервирован для условий ошибок, влияющих на бизнес-логику. Например, кто-то отправляет голос, когда голосование уже близко.
require
и revert
в основном схожи с внутренней реализацией EVM, но разработчики оценят различие.
Assert
подходит для проверки условий, которые не должны произойти, но происходят.
Require
подходит для проверки нежелательных условий, которые могут возникнуть.
В Solidity есть SMTChecker , который делает использование assert
очень крутым, потому что он может доказать , что ваши инварианты верны:
Solidity реализует формальный подход к проверке, основанный на SMT (теориях выполнимости по модулю) и решении Хорна . Модуль SMTChecker автоматически пытается доказать, что код удовлетворяет спецификации, заданной операторами
require
иassert
. То есть он рассматриваетrequire
утверждения как предположения и пытается доказать, что условия внутриassert
утверждений всегда верны. Если обнаружена ошибка утверждения, пользователю может быть предоставлен контрпример, показывающий, как утверждение может быть нарушено. Если SMTChecker не выдает предупреждения для свойства, это означает, что свойство безопасно.
assert
, чтобы помочь вам найти инварианты, которые были нарушены.Вы используете assert
, чтобы помочь вам поймать, когда происходит невозможное.
Учебник SMTChecker представляет собой хороший пример assert
, который слишком длинный, чтобы включать его здесь: настоятельно рекомендуем прочитать его.
Если SMTChecker вдохновит вас начать использовать assert
, это очень хорошо. Но помните, что они предназначены для инвариантов , как упоминается в документах Solidity :
Assert следует использовать только для проверки внутренних ошибок и проверки инвариантов. Правильно функционирующий код никогда не должен вызывать панику, даже при недопустимом внешнем вводе. Если это произойдет, то в вашем контракте есть ошибка, которую вы должны исправить. Инструменты языкового анализа могут оценить ваш контракт, чтобы определить условия и вызовы функций, которые вызовут панику.
Пол Разван Берг