Smart Contracts testen
Letzte Änderung: @Astronaut828(opens in a new tab), 15. Juni 2024
Öffentliche Blockchains wie Ethereum sind unveränderlich, was es schwierig macht, den Code von Smart Contracts nach der Veröffentlichung zu verändern. Upgrade-Muster für Verträge zur Durchführung von „virtuellen Upgrades“ existieren, aber deren Implementierung ist schwierig und erfordert sozialen Konsens. Zudem kann ein Upgrade einen Fehler nur beheben, nachdem er entdeckt wurde – wenn ein Angreifer die Schwachstelle zuerst entdeckt, besteht die Gefahr, dass Ihr Smart Contract ausgenutzt wird.
Aus diesen Gründen ist das Testen von Smart Contracts vor ihrer Veröffentlichung auf dem Mainnet eine Sicherheits-Mindestanforderung. Es gibt viele Techniken zum Testen von Verträgen und zur Bewertung der Korrektheit des Codes; für welche davon Sie sich entscheiden, hängt von Ihren Anforderungen ab. Nichtsdestotrotz ist eine Test-Suite, die sich aus verschiedenen Werkzeugen und Ansätzen zusammensetzt, ideal für das Aufspüren sowohl kleinerer als auch größerer Sicherheitslücken im Vertragscode.
Voraussetzungen
Auf dieser Seite wird erklärt, wie Smart Contracts vor ihrer Veröffentlichung im Ethereum-Netzwerk getestet werden können. Sie setzt voraus, dass Sie mit Smart Contracts vertraut sind.
Was sind Smart-Contract-Tests?
Beim Testen von Smart Contracts wird überprüft, ob der Code eines Smart Contracts wie erwartet funktioniert. Die Tests sind nützlich, um zu prüfen, ob ein bestimmter Smart Contract die Anforderungen an Zuverlässigkeit, Benutzerfreundlichkeit und Sicherheit erfüllt.
Es gibt verschiedene Vorgehensweisen. Für die meisten Testmethoden ist es jedoch erforderlich, einen Smart Contract mit einer kleinen Stichprobe der Daten, die er voraussichtlich verarbeiten soll, auszuführen. Wenn der Vertrag korrekte Ergebnisse für die Beispieldaten liefert, wird davon ausgegangen, dass er ordnungsgemäß funktioniert. Die meisten Testwerkzeuge bieten Ressourcen zum Schreiben und Ausführen von Testfällen(opens in a new tab). Mit ihnen lässt sich prüfen, ob die Ausführung eines Vertrags die erwarteten Ergebnisse hervorbringt.
Warum ist es wichtig, Smart Contracts zu testen?
Da mithilfe von Smart Contracts häufig hochwertige finanzielle Vermögenswerte verwaltet werden, können kleine Programmierfehler zu massiven Verlusten für die Benutzer(opens in a new tab) führen, wozu es häufig auch kommt. Gründliches Testen kann jedoch dazu beitragen, Fehler und Probleme im Code eines Smart Contracts frühzeitig zu entdecken und zu beheben, bevor dieser im Mainnet veröffentlicht wird.
Es ist zwar möglich, einen Vertrag zu aktualisieren, wenn ein Fehler entdeckt wird. Upgrades sind allerdings komplex und können bei unsachgemäßer Handhabung u Fehlern führen(opens in a new tab). Durch die Aktualisierung eines Vertrags wird der Grundsatz der Unveränderlichkeit weiter ausgehebelt. Außerdem ist sie für die Benutzer mit zusätzlichen Vertrauensvorbehalten verbunden. Umgekehrt mindert ein umfassender Plan zum Testen Ihres Vertrags die Sicherheitsrisiken von Smart Contracts und reduziert die Notwendigkeit, nach der Veröffentlichung komplexe Logik-Upgrades durchzuführen.
Methoden zum Testen von Smart Contracts
Die Methoden zum Testen von Smart Contracts auf Ethereum lassen sich in zwei große Kategorien einteilen: automatisiertes Testen und manuelles Testen. Automatisierte Tests und manuelle Tests bieten einzigartige Vorteile, es müssen für sie aber auch Kompromisse eingegangen werden. Sie können beide Methoden kombinieren, um einen robusten Plan zur Analyse Ihrer Verträge zu entwerfen.
Automatisierte Tests
Beim automatisierten Testen werden Werkzeuge eingesetzt, die den Code eines Smart Contracts automatisch auf Fehler bei seiner Ausführung überprüfen. Der Vorteil automatisierter Tests ergibt sich aus der Verwendung von Skripten(opens in a new tab) zur Bewertung der Vertragsfunktionalitäten. Skriptgesteuerte Tests können so geplant werden, dass sie mit minimalen menschlichen Eingriffen wiederholt ausgeführt werden. Dies bedeutet, dass automatisierte Tests effizienter sind als manuelle Testverfahren.
Automatisierte Tests sind vor allem in den folgenden Fällen sinnvoll: bei sich wiederholenden und zeitaufwändigen Tests; wenn sie manuell nur schwer durchführbar sind; bei Anfälligkeit für menschliche Fehler; bei der Bewertung kritischer Vertragsfunktionen. Automatisierte Testwerkzeuge können jedoch auch Nachteile haben – bestimmte Bugs können übersehen werden und zu vielen falsch-positiven Ergebnissen(opens in a new tab) führen. Daher ist es ideal, automatisierte Tests mit manuellen Tests für Smart Contracts zu kombinieren.
Manuelle Tests
Manuelles Tests werden von Menschen durchgeführt, wobei jeder Testfall in Ihrer Test-Suite nacheinander ausgeführt wird, um die Korrektheit eines Smart Contracts zu analysieren. Dies steht im Gegensatz zu automatisierten Tests, bei denen Sie gleichzeitig mehrere isolierte Tests für einen Smart Contract durchführen können und einen Bericht mit allen fehlgeschlagenen und bestandenen Tests erhalten.
Manuelle Tests können von einer einzelnen Person gemäß eines schriftlichen Testplans durchgeführt werden, der verschiedene Testszenarien abdeckt. Im Rahmen manueller Tests können Sie auch mehrere Personen oder Gruppen über einen bestimmten Zeitraum mit einem Smart Contract interagieren lassen. Der Prüfer vergleicht das tatsächliche Verhalten des Smart Contracts mit dem erwarteten Verhalten und kennzeichnet jede Abweichung als Bug.
Effektive manuelle Tests erfordern erhebliche Ressourcen („Fähigkeiten“, „Zeit“, „Geld“ und „Aufwand“) und es kann – aufgrund menschlichen Irrtums – passieren, dass bestimmte Fehler bei der Ausführung von Tests übersehen werden. Aber auch manuelle Tests können von Vorteil sein – so kann ein menschlicher Tester (z. B. ein Auditor) mithilfe seiner Intuition Grenzfälle aufdecken, die ein automatisiertes Testwerkzeug übersehen würde.
Automatisierte Tests für Smart Contracts
Unit-Tests
Beim Unit-Testing werden die Vertragsfunktionen separat bewertet und die korrekte Funktionsweise jeder Komponente überprüft. Gute Unit-Tests sollten einfach und schnell durchführbar sein und eine klare Vorstellung davon vermitteln, was falsch gelaufen ist, wenn Tests fehlschlagen.
Unit-Tests sind nützlich, um zu prüfen, ob Funktionen die erwarteten Werte zurückgeben und ob der Datenspeicher des Vertrags nach Ausführung der Funktion ordnungsgemäß aktualisiert wird. Darüber hinaus wird durch die Durchführung von Unit-Tests nach Änderungen an der Codebasis eines Vertrags sichergestellt, dass durch das Hinzufügen neuer Logik keine Fehler entstehen. Im Folgenden finden Sie einige Richtlinien für die Durchführung effektiver Unit-Tests:
Richtlinien für Unit-Tests von Smart Contracts
1. Die Geschäftslogik und den Arbeitsablauf Ihrer Smart Contracts verstehen
Bevor Sie Unit-Tests schreiben, ist es hilfreich zu wissen, welche Funktionalitäten ein Smart Contract bietet und wie die Benutzer auf diese Funktionen zugreifen und sie nutzen. Dies ist besonders nützlich für die Durchführung von Happy-Path-Tests(opens in a new tab), mit denen festgestellt wird, ob Funktionen in einem Vertrag die richtige Ausgabe für gültige Benutzereingaben liefern. Wir erklären dieses Konzept anhand dieses (verkürzten) Beispiels eines Auktionsvertrags(opens in a new tab)
1constructor(2 uint biddingTime,3 address payable beneficiaryAddress4 ) {5 beneficiary = beneficiaryAddress;6 auctionEndTime = block.timestamp + biddingTime;7 }89function bid() external payable {1011 if (block.timestamp > auctionEndTime)12 revert AuctionAlreadyEnded();1314 if (msg.value <= highestBid)15 revert BidNotHighEnough(highestBid);1617 if (highestBid != 0) {18 pendingReturns[highestBidder] += highestBid;19 }20 highestBidder = msg.sender;21 highestBid = msg.value;22 emit HighestBidIncreased(msg.sender, msg.value);23 }2425 function withdraw() external returns (bool) {26 uint amount = pendingReturns[msg.sender];27 if (amount > 0) {28 pendingReturns[msg.sender] = 0;2930 if (!payable(msg.sender).send(amount)) {31 pendingReturns[msg.sender] = amount;32 return false;33 }34 }35 return true;36 }3738function auctionEnd() external {39 if (block.timestamp < auctionEndTime)40 revert AuctionNotYetEnded();41 if (ended)42 revert AuctionEndAlreadyCalled();4344 ended = true;45 emit AuctionEnded(highestBidder, highestBid);4647 beneficiary.transfer(highestBid);48 }49}Alles anzeigen
Hierbei handelt es sich um einen einfachen Auktionsvertrag, der für die Entgegennahme von Geboten während der Gebotsfrist entworfen wurde. Wenn das highestBid
(Höchstgebot) steigt, erhält der vorherige Höchstbietende sein Geld zurück; sobald die Gebotsfrist vorbei ist, ruft der Begünstigte
den Vertrag auf, um sein Geld zu erhalten.
Unit-Tests für einen Vertrag wie diesen würden verschiedene Funktionen abdecken, die ein Benutzer bei der Interaktion mit dem Vertrag aufrufen könnte. Here’s a possible translation into German: Ein Beispiel wäre ein Unit-Test, der überprüft, ob ein Benutzer ein Gebot abgeben kann, während die Auktion noch läuft (z. B. ob Aufrufe an bid()
erfolgreich sind), oder einer, der überprüft, ob ein Benutzer ein höheres Gebot als das aktuelle highestBid
(Höchstgebot) abgeben kann.
Ein Verständnis des operativen Arbeitsablaufs von Verträgen hilft auch beim Schreiben von Unit-Tests, die prüfen, ob die Ausführung den Anforderungen entspricht. Der Auktionsvertrag legt zum Beispiel fest, dass Benutzer keine Gebote abgeben können, sobald die Auktion beendet ist (d. h., wenn auctionEndTime
kleiner als block.timestamp
ist). Ein Entwickler könnte also einen Unit-Test durchführen, der überprüft, ob Aufrufe der Funktion bid()
erfolgreich sind oder fehlschlagen, wenn die Auktion vorbei ist (d. h., wenn auctionEndTime
> block.timestamp
).
2. Alle Annahmen im Zusammenhang mit der Vertragsausführung bewerten
Es ist wichtig, alle Annahmen über die Ausführung eines Vertrags zu dokumentieren und Unit-Tests zur Überprüfung der Korrektheit dieser Annahmen zu schreiben. Das Testen von Behauptungen bietet nicht nur Schutz vor unerwarteter Ausführung, sondern zwingt Sie auch dazu, über Vorgänge nachzudenken, die das Sicherheitsmodell eines Smart Contracts gefährden könnten. Ein nützlicher Tipp lautet, über „Glückliche-Benutzer-Tests“ hinauszugehen und negative Tests zu schreiben, die prüfen, ob eine Funktion bei falschen Eingaben fehlschlägt.
Viele Unit-Test-Frameworks ermöglichen das Aufstellen von Behauptungen – einfache Aussagen, die angeben, zu was ein Vertrag fähig ist und zu was nicht – und das Ausführen von Tests, um zu sehen, ob diese Behauptungen bei der Ausführung zutreffen. Ein Entwickler, der an dem oben beschriebenen Auktionsvertrag arbeitet, könnte vor der Ausführung negativer Tests die folgenden Behauptungen über sein Verhalten aufstellen:
Benutzer können keine Gebote abgeben, wenn die Auktion beendet ist oder noch nicht begonnen hat.
Der Auktionsvertrag bricht ab, wenn ein Gebot unter dem akzeptablen Schwellenwert liegt.
Benutzer, die den Zuschlag nicht erhalten, bekommen ihr Geld wieder gutgeschrieben.
Hinweis: Eine weitere Möglichkeit, Annahmen zu testen, besteht im Schreiben von Tests, die Funktionsmodifikatoren(opens in a new tab) in einem Vertrag auslösen, insbesondere die Anweisungen require
, assert
und if...else
.
3. Codeabdeckung messen
Die Codeabdeckung(opens in a new tab) ist eine Testmetrik, die die Anzahl der Verzweigungen, Zeilen und Aussagen in Ihrem Code verfolgt, die während der Tests ausgeführt werden. Die Tests sollten über eine gute Codeabdeckung verfügen, da es sonst zu „falsch-negativen“ Ergebnissen kommen kann, d. h. ein Vertrag besteht zwar alle Tests, aber es liegen noch Schwachstellen im Code vor. Der Nachweis einer hohen Codeabdeckung gibt jedoch Gewissheit, dass alle Aussagen/Funktionen in einem Smart Contract ausreichend auf ihre Korrektheit getestet wurden.
4. Gut entwickelte Test-Frameworks verwenden
Die Qualität der Werkzeuge, die für die Durchführung von Unit-Tests für Ihre Smart Contracts verwendet werden, ist entscheidend. Ein ideales Test-Framework ist eines, das regelmäßig gewartet wird, nützliche Funktionen bietet (z. B. Protokollierungs- und Berichtsfunktionen) und von anderen Entwicklern ausgiebig genutzt und geprüft wurde.
Unit-Test-Frameworks für Solidity Smart Contracts liegen in verschiedenen Sprachen vor (hauptsächlich in JavaScript, Python und Rust). In den folgenden Anleitungen finden Sie Informationen darüber, wie Sie Unit-Tests mit verschiedenen Test-Frameworks durchführen können:
- Durchführung von Unit-Tests mit Brownie(opens in a new tab)
- Durchführung von Unit-Tests mit Foundry(opens in a new tab)
- Durchführung von Unit-Tests mit Waffle(opens in a new tab)
- Durchführung von Unit-Tests mit Remix(opens in a new tab)
- Durchführung von Unit-Tests mit Ape(opens in a new tab)
- Durchführung von Unit-Tests mit Hardhat(opens in a new tab)
- Durchführung von Unit-Tests mit Wake(opens in a new tab)
Integrationstests
Bei Unit-Tests wird das Debugging für Vertragsfunktionen isoliert durchgeführt. Mithilfe von Integrationstests hingegen lassen sich die Komponenten eines Smart Contracts als Ganzes bewerten. Integrationstests können Probleme aufdecken, die sich aus vertragsübergreifenden Aufrufen oder Interaktionen zwischen verschiedenen Funktionen im selben Smart Contract ergeben. Integrationstests können beispielsweise dabei helfen, zu prüfen, ob Dinge wie Inheritance(opens in a new tab) und Dependency Injection ordnungsgemäß funktionieren.
Integrationstests sind sinnvoll, wenn Ihr Vertrag eine modulare Architektur oder während der Ausführung Schnittstellen mit anderen On-Chain-Verträgen aufweist. Eine Methode, Integrationstests durchzuführen, besteht darin, die Blockchain bei einer bestimmten Höhe zu (mithilfe eines Werkzeugs wie Forge(opens in a new tab) oder Hardhat(opens in a new tab)) und Interaktionen zwischen Ihrem Vertrag und bereits veröffentlichten Verträgen zu simulieren.
Die abgespaltete Blockchain verhält sich ähnlich wie das Mainnet und verfügt über Konten mit zugehörigen Zuständen und Guthaben. Sie fungiert jedoch nur als lokale Entwicklungsumgebung in einer Sandbox, d. h. Sie benötigen weder echte ETH für Transaktionen noch werden sich Ihre Änderungen auf das tatsächliche Ethereum-Protokoll auswirken.
Eigenschaftsbasierte Tests
Beim eigenschaftsbasierten Testen wird geprüft, ob ein Smart Contract eine bestimmte Eigenschaft erfüllt. Eigenschaften geben Fakten über das Verhalten eines Vertrags an, von denen erwartet wird, dass sie in verschiedenen Szenarien wahr bleiben – ein Beispiel für eine Smart-Contract-Eigenschaft könnte sein: „Bei arithmetischen Operationen im Vertrag darf es niemals zu einem Über- oder Unterlauf kommen.“
Statische Analysen und dynamische Analysen sind zwei gängige Techniken zur Durchführung eigenschaftsbasierter Tests. Mit beiden lässt sich verifizieren, dass der Code eines Programms (in diesem Fall eines Smart Contracts) eine vordefinierte Eigenschaft erfüllt. Für einige eigenschaftsbasierte Testwerkzeuge sind vordefinierte Regeln für erwartete Vertragseigenschaften festgelegt. Der Code wird dann anhand dieser Regeln geprüft. Andere Werkzeuge ermöglichen es Ihnen, benutzerdefinierte Eigenschaften für einen Smart Contract zu bestimmen.
Statische Analyse
Beim statischen Analyseprozess wird der Quellcode eines Smart Contracts als Eingabe verwendet. Die ausgegebenen Ergebnisse erklären, ob ein Vertrag eine Eigenschaft erfüllt oder nicht. Im Gegensatz zur dynamischen Analyse wird bei der statischen Analyse ein Vertrag nicht ausgeführt, um ihn auf seine Korrektheit zu prüfen. Bei der statischen Analyse werden stattdessen alle möglichen Pfade ermittelt, die ein Smart Contract während der Ausführung einschlagen könnte (d. h. sie untersucht die Struktur des Quellcodes, um festzustellen, was diese für den Betrieb des Vertrags während der Laufzeit bedeuten könnte).
Linting(opens in a new tab) und statische Tests(opens in a new tab) sind gängige Methoden für die Durchführung statischer Analysen von Verträgen. Beide erfordern die Analyse von Low-Level-Repräsentationen der Vertragsausführung, wie zum Beispiel von abstrakten Syntaxbäumen(opens in a new tab) und Kontrollflussdiagrammen(opens in a new tab), die vom Compiler ausgegeben werden.
In den meisten Fällen ist die statische Analyse nützlich, um Sicherheitsprobleme wie die Verwendung unsicherer Konstrukte, Syntaxfehler oder Verstöße gegen Codierungsstandards in einem Vertragscode zu erkennen. Es ist jedoch bekannt, dass statische Analysen im Allgemeinen nicht dazu geeignet sind, tiefer liegende Schwachstellen zu erkennen, und dass sie zu viele falsch-positive Ergebnisse liefern können.
Dynamische Analyse
Dynamische Analysen generieren symbolische Eingaben (z. B. bei der symbolischen Ausführung(opens in a new tab)) oder konkrete Eingaben (z. B. beim Fuzzing(opens in a new tab)) für die Funktionen eines Smart Contracts, um zu sehen, ob eine Ausführungsspur oder mehrere Ausführungsspuren bestimmte Eigenschaften verletzen. Diese Form des eigenschaftsbasierten Testens unterscheidet sich von Unit-Tests dadurch, dass die Testfälle mehrere Szenarien abdecken und ein Programm die Erstellung der Testfälle übernimmt.
Fuzzing(opens in a new tab) ist ein Beispiel für eine dynamische Analysetechnik zur Verifizierung beliebiger Eigenschaften in Smart Contracts. Ein Fuzzer ruft Funktionen in einem Zielvertrag mit zufälligen oder missgebildeten Variationen eines definierten Eingabewerts auf. Wenn der Smart Contract in einen Fehlerzustand eintritt (z. B. wenn eine Behauptung fehlschlägt), wird das Problem gekennzeichnet, und die Eingaben, die die Ausführung in Richtung des anfälligen Pfads steuern, werden in einem Bericht aufgeführt.
Fuzzing ist nützlich, um den Mechanismus zur Eingabevalidierung von Smart Contracts zu bewerten, da eine unsachgemäße Handhabung unerwarteter Eingaben eine unbeabsichtigte Ausführung zur Folge und gefährliche Auswirkungen haben kann. Diese Form eigentumsbasierter Tests kann aus vielen Gründen ideal sein:
Das Schreiben von Testfällen, die viele Szenarien abdecken, ist schwierig. Für einen Eigenschaftstest müssen Sie lediglich ein Verhalten und einen Datenbereich festlegen, mit dem das Verhalten getestet werden soll. Das Programm generiert automatisch Testfälle auf der Grundlage der festgelegten Eigenschaft.
Ihre Test-Suite deckt möglicherweise nicht alle möglichen Pfade innerhalb des Programms ausreichend ab. Selbst bei einer 100-prozentigen Abdeckung ist es möglich, Grenzfälle zu übersehen.
Unit-Tests beweisen, dass ein Vertrag für Beispieldaten korrekt ausgeführt wird. Ob der Vertrag für Eingaben außerhalb des Beispielbereichs allerdings korrekt ausgeführt wird, bleibt unbekannt. Eigenschaftstests führen einen Zielvertrag mit mehreren Variationen eines bestimmten Eingabewerts aus, um Ausführungsspuren zu finden, die Behauptungsfehler verursachen. Somit bietet ein Eigenschaftstest mehr Garantien, dass ein Vertrag für eine breite Klasse von Eingabedaten korrekt ausgeführt wird.
Richtlinien für die Durchführung von eigenschaftsbasierten Tests für Smart Contracts
Das Durchführen von eigenschaftsbasierten Tests beginnt in der Regel mit der Festlegung einer Eigenschaft (z. B. Abwesenheit von Ganzzahlüberläufen(opens in a new tab)) oder einer Sammlung von Eigenschaften, die Sie in einem Smart Contract verifizieren möchten. Möglicherweise müssen Sie beim Schreiben von Eigenschaftstests auch einen Wertebereich festlegen, innerhalb dessen das Programm Daten für Transaktionseingaben generieren kann.
Sobald das Eigenschafts-Testwerkzeug richtig konfiguriert ist, führt es Ihre Smart-Contract-Funktionen mit zufällig generierten Eingaben aus. Wenn irgendwelche Verstöße gegen Behauptungen vorliegen, sollten Sie einen Bericht mit konkreten Eingabedaten erhalten, die gegen die zu bewertende Eigenschaft verstoßen. In den folgenden Anleitungen finden Sie Informationen für den Einstieg in die Durchführung von eigenschaftsbasierten Tests mit verschiedenen Werkzeugen:
- Statische Analyse von Smart Contracts mit Slither(opens in a new tab)
- Statische Analyse von Smart Contracts mit Wake(opens in a new tab)
- Eigenschaftsbasierte Tests mit Brownie(opens in a new tab)
- Fuzzing von Verträgen mit Foundry(opens in a new tab)
- Fuzzing von Verträgen mit Echidna(opens in a new tab)
- Fuzzing von Verträgen mit Wake(opens in a new tab)
- Symbolische Ausführung von Smart Contracts mit Manticore(opens in a new tab)
- Symbolische Ausführung von Smart Contracts mit Mythril(opens in a new tab)
Manuelle Tests für Smart Contracts
Das manuelle Testen von Smart Contracts erfolgt oft erst später im Entwicklungszyklus, nachdem automatisierte Tests durchgeführt wurden. Bei dieser Form des Testens wird der Smart Contract als vollständig integriertes Produkt bewertet, um festzustellen, ob er die in den technischen Anforderungen festgelegten Leistungen erbringt.
Testen von Verträgen auf einer lokalen Blockchain
Automatisierte Tests, die in einer lokalen Entwicklungsumgebung durchgeführt werden, können zwar nützliche Debugging-Informationen liefern. Für Sie ist es allerdings wichtig, zu wissen, wie sich Ihr Smart Contract in einer Produktionsumgebung verhält. Bei einer Veröffentlichung auf dem Ethereum-Mainnet fallen jedoch Gas-Gebühren an – ganz zu schweigen davon, dass Sie oder Ihre Benutzer echtes Geld verlieren können, wenn Ihr Smart Contract noch Fehler aufweist.
Das Testen Ihres Vertrags auf einer lokalen Blockchain (auch bekannt als ein Entwicklungsnetzwerk) ist eine empfohlene Alternative zum Testen auf dem Mainnet. Eine lokale Blockchain ist eine Kopie der Ethereum-Blockchain, die lokal auf Ihrem Computer läuft und das Verhalten der Ausführungsebene von Ethereum simuliert. Dementsprechend können Sie Transaktionen so programmieren, dass sie mit einem Vertrag interagieren, ohne signifikante zusätzliche Kosten zu verursachen.
Die Ausführung von Verträgen auf einer lokalen Blockchain könnte als eine Form manueller Integrationstests nützlich sein. Smart Contracts sind in hohem Maße zusammensetzbar, sodass sie in bestehende Protokolle integriert werden können. Dennoch müssen Sie sicherstellen, dass solche komplexen On-Chain-Interaktionen die richtigen Ergebnisse liefern.
Mehr zu Entwicklungsnetzwerken.
Testen von Verträgen auf Testnetzen
Ein Testnetzwerk oder Testnet funktioniert genau wie das Ethereum Mainnet, mit dem Unterschied, dass es Ether (ETH) verwendet, das keinen realen Wert hat. Das Veröffentlichen Ihres Vertrags auf einem Testnetz bedeutet, dass jeder damit interagieren kann (z. B. über das Frontend der DApps), ohne Geldmittel zu riskieren.
Diese Form manueller Tests ist nützlich, um den End-to-End-Flow Ihrer Anwendung aus der Sicht des Benutzers zu bewerten. Hier können Beta-Tester auch Testläufe durchführen und etwaige Probleme mit der Geschäftslogik und der Gesamtfunktionalität des Vertrags melden.
Die Veröffentlichung in einem Testnetz nach dem Testen auf einer lokalen Blockchain ist ideal, da Ersteres eher dem Verhalten der Ethereum Virtual Machine entspricht. Daher ist es bei vielen Ethereum-nativen Projekten üblich, DApps in Testnetzen zu veröffentlichen, um so den Betrieb von Smart Contracts unter realen Bedingungen zu simulieren.
Testen vs. formale Verifizierung
Zwar helfen Tests bei der Bestätigung, dass ein Vertrag für einige Dateneingaben die erwarteten Ergebnisse liefert. Sie können jedoch nicht schlüssig beweisen, dass dies auch für Eingaben gilt, die während der Tests nicht verwendet wurden. Das Testen eines Smart Contracts kann daher keine „funktionale Korrektheit“ garantieren (d. h. es kann nicht zeigen, dass sich eine Anwendung für alle Arten von Eingabewerten wie gewünscht verhält).
Die formale Verifizierung ist ein Ansatz zur Bewertung der Korrektheit von Software, indem geprüft wird, ob ein formales Modell des Programms mit der formalen Spezifikation übereinstimmt. Ein formales Modell ist eine abstrakte mathematische Darstellung eines Programms, wohingegen eine formale Spezifikation die Eigenschaften eines Programms definiert (d. h. logische Behauptungen über die Ausführung des Programms).
Da die Eigenschaften in mathematischen Begriffen geschrieben sind, ist es möglich, zu verifizieren, ob ein formales (mathematisches) Modell des Systems eine Spezifikation mithilfe logischer Inferenzregeln erfüllt. Daher liefern formale Verifizierungswerkzeuge angeblich einen „mathematischen Beweis“ für die Korrektheit eines Systems.
Im Gegensatz zu Tests kann mit der formalen Verifizierung überprüft werden, ob die Ausführung eines Smart Contracts eine formale Spezifikation für alle Ausführungen erfüllt (d. h. keine Bugs aufweist), ohne dass sie mit Beispieldaten ausgeführt werden muss. Dies reduziert nicht nur den Zeitaufwand für die Durchführung von Dutzenden von Unit-Tests, sondern ist auch effektiver beim Aufspüren versteckter Schwachstellen. Abgesehen davon liegen die formalen Verifizierungstechniken auf einem Spektrum, das sich nach der Schwierigkeit der Implementierung und ihrer Nützlichkeit richtet.
Mehr zur formalen Verifizierung von Smart Contracts.
Testen vs. Audits und Bug Bounties
Wie bereits erwähnt, können strenge Tests nur selten die Abwesenheit von Bugs in einem Vertrag garantieren; formale Verifizierungsansätze können eine stärkere Garantie für die Korrektheit bieten, sind aber derzeit schwierig anzuwenden und mit erheblichen Kosten verbunden.
Dennoch können Sie die Wahrscheinlichkeit weiter erhöhen, dass Schwachstellen im Vertrag entdeckt werden, indem Sie eine unabhängige Code-Prüfung durchführen lassen. Smart-Contract-Audits(opens in a new tab) und Bug Bounties(opens in a new tab) sind zwei Möglichkeiten, um zu veranlassen, dass andere Ihre Verträge analysieren.
Die Audits werden von Auditoren durchgeführt, die erfahren darin sind, Sicherheitslücken und schlechte Entwicklungspraktiken in Smart Contracts aufzudecken. Ein Audit umfasst in der Regel Tests (und möglicherweise eine formale Verifizierung) sowie eine manuelle Überprüfung der gesamten Codebasis.
Im Gegensatz dazu wird bei einem Bug-Bounty-Programm üblicherweise eine finanzielle Belohnung an eine Person (allgemein als Whitehat-Hacker(opens in a new tab) bezeichnet) gezahlt, die eine Schwachstelle in einem Smart Contract entdeckt und sie den Entwicklern meldet. Bug Bounties ähneln insofern Audits, da ihre Funktionsweise mit einschließt, dass andere gebeten werden, bei der Suche nach Fehlern in Smart Contracts zu helfen.
Der Hauptunterschied besteht darin, dass Bug-Bounty-Programme der breiteren Entwickler-/Hacker-Community offenstehen und eine breite Klasse von ethischen Hackern und unabhängigen Sicherheitsexperten mit einzigartigen Fähigkeiten und einzigartiger Erfahrung ansprechen. Dies kann ein Vorteil gegenüber Audits von Smart Contracts sein, die sich hauptsächlich auf Teams stützen, die möglicherweise nur über begrenzte oder eingeschränkte Fachkenntnisse verfügen.
Testwerkzeuge und Bibliotheken
Unit-Testing-Werkzeuge
Solidity-Abdeckung(opens in a new tab) – Code-Abdeckungswerkzeug für in Solidity geschriebene Smart Contracts.
Waffle(opens in a new tab) – Framework für die Entwicklung und das Testen fortgeschrittener Smart Contracts (basierend auf ethers.js).
Remix Tests(opens in a new tab) – Werkzeug zum Testen von Solidity-Smart-Contracts. Arbeitet unter dem Remix IDE „Solidity Unit Testing“-Plug-in, das zum Schreiben und Ausführen von Testfällen für einen Vertrag verwendet wird.
OpenZeppelin Test Helpers(opens in a new tab) – Behauptungsbibliothek für das Testen von Ethereum-Smart-Contracts. Stellen Sie sicher, dass sich Ihre Verträge wie erwartet verhalten!
Brownie-Unit-Testing-Framework(opens in a new tab) – Brownie nutzt Pytest, ein funktionsreiches Test-Framework, mit dem Sie kleine Tests mit minimalem Code schreiben können. Außerdem lässt es sich gut für große Projekte skalieren und ist in hohem Maße erweiterbar.
Foundry Tests(opens in a new tab) – Foundry bietet mit Forge ein schnelles und flexibles Ethereum-Testing-Framework an, das einfache Unit-Tests, Gas-Optimierungsprüfungen und Vertrags-Fuzzing durchführen kann.
Hardhat Tests(opens in a new tab) – Framework zum Testen von Smart Contracts basierend auf ethers.js, Mocha und Chai.
ApeWorx(opens in a new tab) – Python-basiertes Entwicklungs- und Test-Framework für Smart Contracts für die Ethereum Virtual Machine.
Wake(opens in a new tab) – Python-basiertes Framework für Unit-Testing und Fuzzing mit starken Debugging-Funktionen und Unterstützung für Cross-Chain-Tests, das pytest und Anvil nutzt, um die beste Benutzererfahrung und Leistung zu bieten.
Eigenschaftsbasierte Testwerkzeuge
Werkzeuge zur statischen Analyse
Slither(opens in a new tab) – Python-basiertes Solidity-Framework zur statischen Analyse, um Schwachstellen aufzudecken, das Codeverständnis zu verbessern und benutzerdefinierte Analysen für Smart Contracts zu schreiben.
Ethlint(opens in a new tab) – Linter für die Durchsetzung von Best-Practices bezüglich Stil und Sicherheit für die Solidity-Programmiersprache für Smart Contracts.
Cyfrin Aderyn(opens in a new tab) – Rust-basiertes statisches Analysetool, das speziell für die Sicherheit und Entwicklung von Web3-Smart-Contracts konzipiert wurde.
Wake(opens in a new tab) – Python-basiertes Framework für die statische Analyse mit Detektoren für Schwachstellen und Codequalität, Druckern zum Extrahieren nützlicher Informationen aus dem Code und Unterstützung für das Schreiben benutzerdefinierter Submodule.
Werkzeuge zur dynamischen Analyse
Echidna(opens in a new tab) – Schneller Vertrags-Fuzzer zum Aufspüren von Schwachstellen in Smart Contracts mithilfe von eigenschaftsbasierten Tests.
Diligence Fuzzing(opens in a new tab) – Automatisiertes Fuzzing-Werkzeug zum Aufspüren von Eigenschaftsverstößen im Smart-Contract-Code.
Manticore(opens in a new tab) – Dynamisches symbolisches Ausführungs-Framework für die Analyse von EVM-Bytecode.
Mythril(opens in a new tab) – EVM-Bytecode-Bewertungswerkzeug zum Aufspüren von Vertragsschwachstellen mithilfe von Taint-Analysen, Concolic-Analysen und Kontrollflussprüfungen.
Diligence Scribble(opens in a new tab) – Scribble ist eine Spezifizierungssprache und ein Laufzeit-Verifizierungswerkzeug, mithilfe dessen Sie Smart Contracts mit Eigenschaften kennzeichnen können, sodass sich Verträge automatisch mit Werkzeugen wie Diligence Fuzzing oder MythX testen lassen.
Verwandte Tutorials
- Ein Überblick und Vergleich verschiedener Testprodukte _
- So verwenden Sie Echidna zum Testen von Smart Contracts
- So verwenden Sie Manticore zum Aufspüren von Bugs in Smart Contracts
- So verwenden Sie Slither, um Bugs in Smart Contracts zu finden
- So simulieren Sie Solidity-Verträge zu Testzwecken
- So führen Sie mit Foundry Unit-Tests in Solidity durch(opens in a new tab)