Ich habe mir die Dokumente angesehen und suche nach Erläuterungen zum Unterschied zwischen require
and assert
und throw
and revert
.
assert(boolesche Bedingung): Ausführung abbrechen und Zustandsänderungen rückgängig machen, wenn Bedingung falsch ist (für internen Fehler verwenden)
require(bool condition): Ausführung abbrechen und Zustandsänderungen rückgängig machen, wenn Bedingung falsch ist (für falsch formatierte Eingaben verwenden)
Wie ziehen Sie speziell in Bezug auf assert
und require
die Grenze zwischen fehlerhafter Eingabe und internem Fehler?
Bei der Wahl zwischen assert()
und sind zwei Aspekte zu beachtenrequire()
assert(false)
kompiliert zu 0xfe
, was ein ungültiger Opcode ist, verbraucht das gesamte verbleibende Gas und macht alle Änderungen rückgängig.
require(false)
kompiliert, zu 0xfd
dem der REVERT
Opcode gehört, was bedeutet, dass das verbleibende Gas zurückerstattet wird. Der Opcode kann auch einen Wert zurückgeben (nützlich zum Debuggen), aber ich glaube nicht, dass dies derzeit in Solidity unterstützt wird. (2017-11-21)
Aus den Dokumenten (Hervorhebung von mir)
Die require-Funktion sollte verwendet werden, um sicherzustellen, dass gültige Bedingungen wie Eingaben oder Vertragsstatusvariablen erfüllt sind, oder um Rückgabewerte von Aufrufen externer Verträge zu validieren. Bei richtiger Anwendung können Analysetools Ihren Vertrag auswerten, um die Bedingungen und Funktionsaufrufe zu identifizieren, die zu einer fehlgeschlagenen Bestätigung führen. Richtig funktionierender Code sollte niemals eine fehlgeschlagene Assertion-Anweisung erreichen; In diesem Fall liegt ein Fehler in Ihrem Vertrag vor, den Sie beheben sollten.
Der obige Auszug ist ein Verweis auf die noch (Stand 2017-11-21) experimentelle und undokumentierte SMTChecker
.
Ich verwende ein paar Heuristiken, um mir bei der Entscheidung zu helfen, welche ich verwenden soll.
require()
für:require(external.send(amount))
owned
Vertragssituationrequire
häufiger verwenden,assert()
für:assert
seltener verwendenIm Grunde assert
ist es nur da, um zu verhindern, dass etwas wirklich Schlimmes passiert, aber es sollte nicht möglich sein, dass die Bedingung als falsch ausgewertet wird.
Die Funktionen require()
und assert()
wurden Solidity vor der Byzantium-Fork hinzugefügt, in v0.4.10
. Vor Byzanz verhielten sie sich identisch, wurden aber bereits zu unterschiedlichen Opcodes kompiliert. Dies bedeutete, dass sich einige Verträge, die vor Byzanz eingesetzt wurden , nach dem Fork anders verhielten , wobei der Hauptunterschied darin bestand, dass mit der Rückerstattung von nicht verbrauchtem Gas begonnen wurde.
assert()
und require()
Rollback-Änderungen werden in die Blockchain geschrieben, sodass balance[_to] =balance[_from] +_value
sie zurückgesetzt werden, wenn eine assert()
Bedingung require()
ausgelöst wird, nicht wahr? (Ich spreche nach der Hardfork im letzten Jahr). Oder wird assert()
immer wieder etwas geändert?solc
: gist.github.com/maurelian/02904ae729fb11213cde20ba05a202e6 Es wird Sie warnen, dass es möglich ist, dass die zweite Assertion-Anweisung für bestimmte Werte wahr ist.Ich verwende require
für die Eingabevalidierung, da es etwas effizienter ist als if/throw.
function foo(uint amount) {
require(amount < totalAmount);
...
}
Wo as assert
eher zum Abfangen von Laufzeitfehlern verwendet werden sollte:
function foo(uint amount) {
...
__check = myAmount;
myAmount -= amount;
assert(myAmount < __check);
...
}
revert
wird Änderungen rückgängig machen und ungenutztes Gas in einer späteren Version von Ethereum zurückerstatten, aber ATM verhält sich genauso wie Throw.
assert
Argument zu haben. Dies könnte zu einem Debugging-Albtraum führen, bei dem die Operationen für den Zustand verkettet, aber für die Bestätigung vergessen wurden.assert()
und require()
Rollback-Änderungen werden in die Blockchain geschrieben, sodass balance[_to] =balance[_from] +_value
sie zurückgesetzt werden, wenn eine assert()
Bedingung require()
ausgelöst wird, nicht wahr? (Ich spreche nach der Hardfork im letzten Jahr). Oder wird assert()
immer wieder etwas geändert? Aber beide assert()
und require()
Rollback-Änderungen werden in die Blockchain geschrieben, sodass balance[_to] =balance[_from] +_value
sie zurückgesetzt werden, wenn eine assert()
Bedingung require()
ausgelöst wird, nicht wahr? (Ich spreche nach der Hardfork im letzten Jahr). Oder wird assert()
immer wieder etwas geändert?Ich denke, keine der Antworten ist richtig.
assert
ist für Bedingungen reserviert, mit denen erwartet wird, dass statische Codeanalysetools (möglicherweise Solidity-Compiler in zukünftigen Versionen) die Fehlerwarnung des Entwicklers zur Kompilierzeit erkennen können.
require
ist reserviert für Fehlerzustände falscher Eingabedaten für Funktionen (im Vergleich zu erwarteten/gültigen Eingabedaten), die erst zur Ausführungszeit erkannt werden können. Dies entspricht Funktionsvoraussetzungen in der Programmiersprache Argot. Der Compiler kann aufgrund der unendlichen Möglichkeiten der Eingabedaten nicht helfen.
throw
wird zugunsten des Zurücksetzens verworfen.
revert
ist für Fehlerbedingungen reserviert, die sich auf die Geschäftslogik auswirken. Zum Beispiel sendet jemand eine Stimme, wenn die Abstimmung bereits abgeschlossen ist.
require
und revert
sind in Bezug auf die interne EVM-Implementierung größtenteils ähnlich, aber Entwickler werden den Unterschied zu schätzen wissen.
Assert
eignet sich zur Überprüfung von Zuständen, die nicht eintreten sollen, aber eintreten.
Require
eignet sich zur Überprüfung auf unerwünschte Zustände, die auftreten können.
Solidity hat einen SMTChecker , der die Verwendung assert
sehr cool macht, weil er beweisen kann, dass Ihre Invarianten wahr sind:
Solidity implementiert einen formalen Verifikationsansatz, der auf SMT (Satisfiability Modulo Theories) und Hornlösung basiert . Das SMTChecker-Modul versucht automatisch zu beweisen, dass der Code die Spezifikation erfüllt, die durch die Anweisungen
require
und angegeben wird.assert
Das heißt, es betrachtetrequire
Aussagen als Annahmen und versucht zu beweisen, dass die Bedingungen innerhalbassert
von Aussagen immer wahr sind. Wenn ein Behauptungsfehler gefunden wird, kann dem Benutzer ein Gegenbeispiel gegeben werden, das zeigt, wie die Behauptung verletzt werden kann. Wenn der SMTChecker für eine Eigenschaft keine Warnung ausgibt, bedeutet dies, dass die Eigenschaft sicher ist.
assert
, um verletzte Invarianten zu finden.Sie assert
helfen Ihnen beim Fangen, wenn das Unmögliche passiert.
Das SMTChecker-Tutorial bietet ein gutes Beispiel assert
dafür, das zu lang ist, um es hier aufzunehmen: Es wird dringend empfohlen, es zu lesen.
Wenn der SMTChecker Sie dazu bringt, mit der Verwendung von zu beginnen, assert
ist das eine sehr gute Sache. Aber denken Sie daran, dass sie für Invarianten sind , wie die Solidity-Dokumentation erwähnt:
Assert sollte nur zum Testen auf interne Fehler und zum Überprüfen von Invarianten verwendet werden. Korrekt funktionierender Code sollte niemals eine Panik erzeugen, nicht einmal bei ungültiger externer Eingabe. Wenn dies passiert, dann gibt es einen Fehler in Ihrem Vertrag, den Sie beheben sollten. Sprachanalysetools können Ihren Vertrag auswerten, um die Bedingungen und Funktionsaufrufe zu identifizieren, die eine Panik auslösen.
Paul Razvan Berg