Testowanie inteligentnych kontraktów
Strona ostatnio zaktualizowana: 26 lutego 2026
Publiczne blockchainy takie jak Ethereum są niezmienne, co sprawia, że trudne jest wprowadzenie zmian do inteligentnego kontraktu po jego wdrożeniu. Wzorce aktualizacji kontraktów służące do przeprowadzania "wirtualnych aktualizacji" istnieją, ale są trudne do wdrożenia i wymagają społecznego konsensusu. Co więcej, aktualizacja może naprawić błąd dopiero po jego wykryciu — jeśli atakujący jako pierwszy odkryje podatność, Twój inteligentny kontrakt jest narażony na ryzyko exploita.
Z tych powodów testowanie inteligentnych kontraktów przed wdrożeniem w sieci Mainnet jest minimalnym wymogiem dla bezpieczeństwa. Istnieje wiele technik testowania kontraktów i oceny poprawności kodu; wybór zależy od potrzeb. Niemniej, zestaw testów złożony z różnych narzędzi i podejść jest idealny do wychwycenia mniejszych i większych błędów w zabezpieczeniach kodu kontraktu.
Wymagania wstępne
Ta strona wyjaśnia, w jaki sposób testować inteligentne kontrakty przed wdrożeniem ich do sieci Ethereum. Zakłada się, że znasz inteligentne kontrakty.
Czym jest testowanie inteligentnych kontraktów?
Testowanie inteligentnych kontraktów to proces weryfikacji poprawności pracy kodu inteligentnego kontraktu. Testowanie jest pomocne w celu sprawdzenia, czy dany inteligentny kontrakt spełnia wymagania niezawodności, używalności oraz bezpieczeństwa.
Pomimo tego, że istnieją różne podejścia, większość metod testowania wymaga uruchomienia inteligentnego kontraktu przy użyciu niewielkiej ilości próbnych danych, które powinien obsłużyć. Jeśli kontrakt da poprawny rezultat dla próbnych danych, zakłada się, że pracuje poprawnie. Większość narzędzi do testowania zapewnia zasoby do pisania i wykonywania przypadków testowych (opens in a new tab), aby sprawdzić, czy wykonanie kontraktu odpowiada oczekiwanym wynikom.
Dlaczego testowanie inteligentnych kontraktów jest ważne? Znaczenie testowania inteligentnych kontraktów
Ponieważ inteligentne kontrakty często zarządzają aktywami finansowymi o wysokiej wartości, drobne błędy programistyczne mogą prowadzić, i często prowadzą, do ogromnych strat dla użytkowników (opens in a new tab). Jednakże rygorystyczne testowanie może pomóc w znalezieniu defektów i problemów w kodzie inteligentnego kontraktu oraz naprawieniu ich przed wypuszczeniem na sieci głównej.
Chociaż możliwe jest zaktualizowanie kontraktu w przypadku odkrycia błędu, aktualizacje są skomplikowane i mogą skutkować błędami (opens in a new tab), jeśli są obsługiwane nieprawidłowo. Aktualizacja kontraktu podważa zasadę niezmienności i obarcza użytkowników dodatkową potrzebą zaufania. Z drugiej strony kompleksowy plan testowania kontraktu ogranicza ryzyka związane z bezpieczeństwem kontraktu i redukuje potrzebę wykonywania złożonych czynności niezbędnych do aktualizacji po wdrożeniu.
Metody testowania inteligentnych kontraktów
Metody testowania inteligentnych kontraktów Ethereum dzielą się na dwie szerokie kategorie: testowanie zautomatyzowane i testowanie ręczne. Zarówno testowanie zautomatyzowane, jak i testowanie manualne oferuje unikalne korzyści, ale też wady. Można jednak połączyć oba sposoby w celu stworzenia solidnego planu do analizy kontraktu.
Testowanie zautomatyzowane
Testowanie zautomatyzowane wykorzystuje narzędzia, które automatycznie sprawdzają kod inteligentnego kontraktu pod kątem błędów działania. Zaleta testowania zautomatyzowanego polega na wykorzystaniu skryptów (opens in a new tab) do kierowania oceną funkcjonalności kontraktu. Testy oparte na skryptach mogą być przeprowadzone z minimalnym ludzkim wkładem, co czyni testowanie zautomatyzowane bardziej efektywnym niż testowanie manualne.
Testowanie zautomatyzowane jest szczególnie użyteczne, kiedy testy są powtarzalne i wymagające czasowo; trudne do przeprowadzenia manualnego; podatne na błąd ludzki lub obejmują ocenę krytycznych funkcji kontraktu. Jednak narzędzia do testowania zautomatyzowanego mogą mieć wady — mogą pominąć niektóre błędy i generować wiele wyników fałszywie dodatnich (opens in a new tab). Stąd łączenie testowania automatycznego oraz testowania manualnego inteligentnych kontraktów jest rozwiązaniem doskonałym.
Testowanie ręczne
Testowanie manualne jest wspomagane przez człowieka i obejmuje uruchamianie każdego przypadku testowego z zestawu osobno w procesie analizowania poprawności inteligentnego kontraktu. To przeciwieństwo testowania zautomatyzowanego, które pozwala przeprowadzić wiele izolowanych testów na kontrakcie jednocześnie, otrzymując na koniec raport pokazujący wszystkie pozytywne i negatywne wyniki testu.
Testowanie manualne może być przeprowadzone przez jedną osobę realizującą pisemny plan testowy, który obejmuje różne scenariusze dotyczące testowania. Można również zaangażować do tego grupę osób, która będzie wchodziła w interakcję z inteligentnym kontraktem na przestrzeni konkretnego okresu w ramach testowania manualnego. Testerzy będą porównywali rzeczywiste zachowanie kontraktu z zachowaniem oczekiwanym, oznaczając każdą z różnic jako błąd.
Efektywne testowanie ręczne wymaga znacznych zasobów (umiejętności, czasu, pieniędzy i wysiłku), a z powodu błędów ludzkich możliwe jest przeoczenie pewnych błędów podczas wykonywania testów. Jednak ręczne testowanie może być również korzystne — na przykład tester (np. audytor) może kierować się intuicją, aby wykrywać skrajne przypadki, które nie zostałyby wykryte przez narzędzie do automatycznego testowania.
Zautomatyzowane testowanie inteligentnych kontraktów
Testowanie jednostkowe
Testowanie jednostkowe ocenia funkcje kontraktu osobno i sprawdza, czy każdy komponent działa poprawnie. Dobre testy jednostkowe powinny być proste, szybkie w wykonaniu i jasno wskazywać, co poszło nie tak, jeśli testy się nie powiodą.
Testy jednostkowe są przydatne do sprawdzania, czy funkcje zwracają oczekiwane wartości i czy przechowywanie kontraktów jest poprawnie aktualizowane po wykonaniu funkcji. Co więcej, uruchomienie testów jednostkowych po wprowadzeniu zmian w kodzie kontraktów gwarantuje, że dodanie nowej logiki nie poskutkuje błędami. Poniżej znaleźć można kilka wskazówek dotyczących wydajnego korzystania z testów jednostkowych:
Wskazówki dotyczące testowania jednostkowego inteligentnych kontraktów
1. Zrozum logikę biznesową swojego kontraktu oraz jego przepływ pracy
Przed napisaniem testów jednostkowych, zalecane jest poznanie funkcji oferowanych przez inteligentny kontrakt oraz sposobów, w jakie użytkownicy będą korzystali z tych funkcji. Jest to szczególnie przydatne do przeprowadzania testów „szczęśliwej ścieżki” (opens in a new tab), które określają, czy funkcje w kontrakcie zwracają prawidłowe dane wyjściowe dla prawidłowych danych wejściowych użytkownika. Wyjaśnimy tę koncepcję na tym (skróconym) przykładzie kontraktu aukcyjnego (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}Pokaż wszystkoTo jest prosty kontrakt aukcyjny zaprojektowanym, aby przyjmować oferty podczas okresu oferowania. Jeśli highestBid wzrośnie, poprzedni licytant z najwyższą ofertą otrzymuje zwrot swoich pieniędzy; po zakończeniu okresu licytacji beneficiary wywołuje kontrakt, aby otrzymać swoje pieniądze.
Testy jednostkowe dla takiego kontraktu dotyczyłyby różnych funkcji, których mógłby użyć użytkownicy Przykładem może być test jednostkowy, który sprawdza, czy użytkownik może złożyć ofertę, gdy aukcja jest w toku (tj. wywołania bid() kończą się powodzeniem) lub taki, który sprawdza, czy użytkownik może złożyć wyższą ofertę niż bieżąca highestBid.
Zrozumienie operacyjnego przepływu pracy kontraktu pomaga również w pisaniu testów, które sprawdzają, czy wykonanie spełnia wymagania. Na przykład kontrakt aukcyjny określa, że użytkownicy nie mogą składać ofert po zakończeniu aukcji (tzn. gdy auctionEndTime jest mniejsze niż block.timestamp). W związku z tym programista może uruchomić test jednostkowy, który sprawdza, czy wywołania funkcji bid() kończą się powodzeniem, czy niepowodzeniem, gdy aukcja jest zakończona (tj. gdy auctionEndTime > block.timestamp).
2. Oceń wszystkie założenia związanie z wykonaniem kontraktu
Ważnym jest dokumentowanie każdego założenia dotyczącego wykonania kontraktu i napisanie testu jednostkowego, który weryfikuje poprawność tego założenia. Poza zapewnieniem ochrony przez niespodziewanym wykonaniem, testowanie twierdzeń zmusza do przemyśleń na temat działań, które mogą doprowadzić do złamania zabezpieczeń inteligentnego kontraktu. Przydatną wskazówką jest wykroczenie poza "testy idealnego scenariusza" i napisanie testów negatywnych, które sprawdzają, czy funkcja kończy się niepowodzeniem, kiedy dane wejściowe są niewłaściwe.
Wiele środowisk testów jednostkowych pozwala na tworzenie twierdzeń-prostych zasad działania kontraktu, oraz uruchamianie testów sprawdzających te twierdzenia podczas wykonywania. Programista pracujący przy kontrakcie aukcyjnym opisanym powyżej, może sformułować następujące twierdzenia przed uruchomieniem testów negatywnych:
-
Użytkownicy nie mogą składać ofert, kiedy aukcja się skończyła lub jeszcze się nie rozpoczęła.
-
Kontrakt aukcyjny traci ważność, jeśli oferta jest niższa od minimalny progu.
-
Użytkownicy, których oferta nie wygrała otrzymują zwrot środków
Uwaga: Innym sposobem testowania założeń jest pisanie testów, które wyzwalają modyfikatory funkcji (opens in a new tab) w kontrakcie, zwłaszcza instrukcje require, assert i if…else.
3. Zmierz stopień pokrycia kodu
Pokrycie kodu (opens in a new tab) to metryka testowania, która śledzi liczbę gałęzi, linii i instrukcji w kodzie wykonanych podczas testów. Testy powinny mieć dobre pokrycie, aby zminimalizować ryzyko nieprzetestowanych słabych punktów. Bez wystarczającego pokrycia, można niewłaściwie ocenić kontrakt, jako bezpieczny z uwagi na to, że wszystkie testy zakończyły się poprawnie, podczas gdy słabe punkty wciąż istnieją w nieprzetestowanych ścieżkach kodu. Odnotowanie wysokiego pokrycia kodu daje jednakże pewność, że wszystkie polecania i funkcje w inteligentnym kontrakcie zostały wystarczająco przetestowane pod kątem poprawności.
4. Korzystaj z dobrze rozwiniętych struktur testowych
Jakość narzędzi używanych do testów jednostkowych twojego inteligentnego kontraktu jest kluczowa. Idealne środowisko testowe jest regularnie konserwowane; zawiera użyteczne funkcje (np. rejestrowanie logów czy raportowanie) oraz zostało szeroko wykorzystane przez innych doświadczonych programistów.
Środowiska testów jednostkowych dla inteligentnych kontraktów napisanych w Solidity, dostępne są w różnych językach programowania (głównie JavaScript, Python i Rust). Zapoznaj się z poniższymi przewodnikami w celu uzyskania informacji o przeprowadzaniu testów jednostkowych w różnych środowiskach:
- Uruchamianie testów jednostkowych za pomocą Brownie (opens in a new tab)
- Uruchamianie testów jednostkowych za pomocą Foundry (opens in a new tab)
- Uruchamianie testów jednostkowych za pomocą Waffle (opens in a new tab)
- Uruchamianie testów jednostkowych za pomocą Remix (opens in a new tab)
- Uruchamianie testów jednostkowych za pomocą Ape (opens in a new tab)
- Uruchamianie testów jednostkowych za pomocą Hardhat (opens in a new tab)
- Uruchamianie testów jednostkowych za pomocą Wake (opens in a new tab)
Testowanie integracyjne
Podczas gdy testy jednostkowe służą do debugowania funkcji inteligentnych kontraktów odrębnie, testy integracyjne oceniają komponenty inteligentnego kontraktu jako całość. Testy integracyjne mogą wykryć problemy wynikające z wywołań międzykontraktowych lub interakcji pomiędzy różnymi funkcjami tego samego inteligentnego kontraktu. Na przykład testy integracyjne mogą pomóc sprawdzić, czy takie elementy jak dziedziczenie (opens in a new tab) i wstrzykiwanie zależności działają prawidłowo.
Testowanie integracyjne jest przydatne, jeśli Twój kontrakt wykorzystuje architekturę modułową lub łączy się z innymi kontraktami on-chain w trakcie wykonywania. Jednym ze sposobów przeprowadzania testów integracyjnych jest na określonej wysokości (przy użyciu narzędzia takiego jak Forge (opens in a new tab) lub Hardhat (opens in a new tab)) i symulowanie interakcji między Twoim kontraktem a wdrożonymi kontraktami.
Rozdzielony blockchain będzie zachowywał się podobnie do sieci głównej i będzie zawierał konta z przypisanymi im stanami i saldami. Działa jednak tylko jako lokalne środowisko programistyczne w trybie piaskownicy, co oznacza, że nie będziesz potrzebować prawdziwego ETH do transakcji, a Twoje zmiany nie wpłyną na prawdziwy protokół Ethereum.
Testowanie oparte na właściwościach
Testowanie oparte na właściwościach to proces sprawdzania, czy inteligentny kontrakt spełnia określone właściwości. Właściwości określają fakty dotyczące zachowania kontraktu, które mają pozostać prawdziwe w różnych scenariuszach. Przykładem właściwości inteligentnego kontraktu może być: „Operacje arytmetyczne w kontrakcie nigdy nie przekraczają ani nie przekraczają dozwolonego zakresu”.
Analiza statyczna i analiza dynamiczna to dwie powszechne techniki przeprowadzania testów opartych na właściwościach, a obie mogą zweryfikować, czy kod programu (w tym przypadku inteligentnego kontraktu) spełnia pewną predefiniowaną właściwość. Niektóre narzędzia do testowania opartego na właściwościach zawierają predefiniowane reguły dotyczące oczekiwanych właściwości kontraktu i sprawdzają, czy kod jest zgodny z tymi regułami, podczas gdy inne umożliwiają tworzenie niestandardowych właściwości dla inteligentnego kontraktu.
Analiza statyczna
Analizator statyczny przyjmuje jako dane wejściowe kod źródłowy inteligentnego kontraktu i zwraca wyniki deklarujące, czy kontrakt spełnia daną właściwość, czy nie. W przeciwieństwie do analizy dynamicznej analiza statyczna nie polega na wykonywaniu kontraktu w celu sprawdzenia jego poprawności. Analiza statyczna z kolei rozważa wszystkie możliwe ścieżki, którymi inteligentny kontrakt może podążyć podczas wykonywania (tj. bada strukturę kodu źródłowego, aby określić, jakie to będzie miało znaczenie dla działania kontraktu w czasie wykonywania).
Linting (opens in a new tab) i testowanie statyczne (opens in a new tab) to popularne metody przeprowadzania analizy statycznej kontraktów. Obie metody wymagają analizy niskopoziomowych reprezentacji wykonania kontraktu, takich jak abstrakcyjne drzewa składni (opens in a new tab) i grafy przepływu sterowania (opens in a new tab) generowane przez kompilator.
W większości przypadków analiza statyczna przydaje się do wykrywania problemów bezpieczeństwa, takich jak stosowanie niebezpiecznych konstrukcji, błędy składniowe lub naruszenia standardów kodowania w kodzie kontraktu. Wiadomo jednak, że analizatory statyczne generalnie słabo sobie radzą z wykrywaniem głębszych luk w zabezpieczeniach i mogą generować zbyt wiele fałszywych wyników dodatnich.
Analiza dynamiczna
Analiza dynamiczna generuje symboliczne dane wejściowe (np. w wykonaniu symbolicznym (opens in a new tab)) lub konkretne dane wejściowe (np. w fuzzingu (opens in a new tab)) do funkcji inteligentnych kontraktów, aby sprawdzić, czy jakikolwiek ślad wykonania narusza określone właściwości. Ta forma testowania opartego na właściwościach różni się od testów jednostkowych tym, że przypadki testowe obejmują wiele scenariuszy, a generowaniem przypadków testowych zajmuje się program.
Fuzzing (opens in a new tab) jest przykładem dynamicznej techniki analitycznej służącej do weryfikacji dowolnych właściwości w inteligentnych kontraktach. Fuzzer wywołuje funkcje w kontrakcie docelowym z losowymi lub błędnymi wariantami zdefiniowanej wartości wejściowej. Jeśli inteligentny kontrakt wejdzie w stan błędu (np. taki, w którym potwierdzenie nie powiedzie się), problem zostanie oznaczony, a dane wejściowe kierujące wykonanie na podatną ścieżkę zostaną wygenerowane w raporcie.
Fuzzing jest przydatne do oceny mechanizmu walidacji danych wejściowych inteligentnych kontraktów, ponieważ niewłaściwa obsługa nieoczekiwanych danych wejściowych może skutkować niezamierzonym wykonaniem i wywołać niebezpieczne skutki. Ta forma testowania opartego na właściwościach może być idealna z wielu powodów:
-
Pisanie przypadków testowych obejmujących wiele scenariuszy jest trudne. Test właściwości wymaga jedynie zdefiniowania zachowania i zakresu danych, za pomocą których można przetestować to zachowanie — program automatycznie generuje przypadki testowe na podstawie zdefiniowanej właściwości.
-
Zestaw testów może nie obejmować w wystarczającym stopniu wszystkich możliwych ścieżek w programie. Nawet przy 100% pokryciu istnieje ryzyko pominięcia przypadków brzegowych.
-
Testy jednostkowe sprawdzają, czy kontrakt wykonuje się poprawnie dla danych z próbki, ale nie wiadomo, czy kontrakt wykonuje się poprawnie dla danych wejściowych spoza próbki. Testy właściwości wykonują docelowy kontrakt z wieloma wariantami danej wartości wejściowej w celu znalezienia śladów wykonania powodujących błędy asercji. Zatem test właściwości daje większą gwarancję, że kontrakt zostanie wykonany prawidłowo dla szerokiej klasy danych wejściowych.
Wskazówki dotyczące przeprowadzania testów opartych na właściwościach dla inteligentnych kontraktów
Przeprowadzanie testów opartych na właściwościach zazwyczaj rozpoczyna się od zdefiniowania właściwości (np. braku przepełnień liczb całkowitych (opens in a new tab)) lub zbioru właściwości, które chcesz zweryfikować w inteligentnym kontrakcie. Podczas pisania testów właściwości może być również konieczne zdefiniowanie zakresu wartości, w ramach których program może generować dane dla danych wejściowych transakcji.
Po poprawnym skonfigurowaniu narzędzie do testowania nieruchomości będzie wykonywać funkcje inteligentnych kontraktów przy użyciu losowo generowanych danych wejściowych. Jeśli wystąpią jakiekolwiek naruszenia asercji, powinieneś otrzymać raport z konkretnymi danymi wejściowymi, które naruszają ocenianą właściwość. Aby rozpocząć testowanie oparte na właściwościach za pomocą różnych narzędzi, zapoznaj się z poniższymi przewodnikami:
- Analiza statyczna inteligentnych kontraktów za pomocą Slither (opens in a new tab)
- Analiza statyczna inteligentnych kontraktów za pomocą Wake (opens in a new tab)
- Testowanie oparte na właściwościach za pomocą Brownie (opens in a new tab)
- Fuzzing kontraktów za pomocą Foundry (opens in a new tab)
- Fuzzing kontraktów za pomocą Echidna (opens in a new tab)
- Fuzzing kontraktów za pomocą Wake (opens in a new tab)
- Symboliczne wykonanie inteligentnych kontraktów za pomocą Manticore (opens in a new tab)
- Symboliczne wykonanie inteligentnych kontraktów za pomocą Mythril (opens in a new tab)
Ręczne testowanie inteligentnych kontraktów
Testowanie manualne inteligentnych kontraktów przeprowadzane jest często w późnym etapie cyklu rozwoju produktu, już po testach automatycznych. Ta forma testowania ocenia inteligentny kontrakt jako jeden w pełni zintegrowany produkt, aby sprawdzić, czy działa zgodnie ze specyfikacją techniczną.
Testowanie kontraktów na lokalnym blockchainie
Podczas gdy testowanie zautomatyzowane przeprowadzone w lokalny środowisku programistycznym może zapewnić przydatne informacje o debugowaniu, warto wiedzieć również, jak inteligentny kontrakt zachowuje się w środowisku produkcyjnym. Jednakże wdrożenie do głównej sieci Ethereum wiąże się z opłatami gazowymi — nie wspominając już o tym, że użytkownicy mogą stracić prawdziwe pieniądze, jeśli Twój inteligentny kontrakt wciąż zawiera błędy.
Testowanie kontraktu na lokalnym blockchainie (znanym również jako sieć deweloperska) jest zalecaną alternatywą dla testowania w sieci Mainnet. Blockchain lokalny jest kopią blockchainu Ethereum uruchomioną lokalnie na twoim komputerze, która symuluje zachowanie warstwy wykonawczej Ethereum. Dzięki temu możesz zaprogramować transakcje do interakcji z kontraktem bez konieczności ponoszenia znaczących kosztów.
Uruchamianie kontraktów na blockchainie lokalnym może być przydatną formą testowania manualnego. Inteligentne kontrakty są wysoce komponowalne, co pozwala na integrację z istniejącymi protokołami — ale nadal trzeba się upewnić, że tak złożone interakcje na łańcuchu dają prawidłowe wyniki.
Więcej o sieciach deweloperskich.
Testowanie kontraktów w sieciach testowych
Sieci testowe działają dokładnie jak sieć główna Ethereum z tą różnicą, że używają eteru (ETH), który nie ma rzeczywistej wartości. Wdrożenie kontraktu w sieci testowej oznacza, że każdy może wejść z nim w interakcję (np. za pośrednictwem frontendu dapki) bez narażania środków na ryzyko.
Ta forma testowania manualnego jest przydatna w szczegółowej ocenie przepływu aplikacji z perspektywy użytkownika. Tu beta testerzy mogą również przeprowadzać testy i raportować wszelkie problemy w logice biznesowej kontraktu oraz jego ogólnej funkcjonalności.
Wdrożenie na sieci testowej po testowaniu na lokalnym blockchainie jest idealne z uwagi na to, że w przypadku pierwszego zachowanie bliższe jest wirtualnej maszynie Ethereum. Jest zatem powszechne wśród wielu natywnych projektów Ethereum, aby wdrożyć dapkę na sieć testową w celu oceny inteligentnego kontraktu w warunkach odpowiadających tym ze świata rzeczywistego.
Więcej o sieciach testowych Ethereum.
Testowanie a weryfikacja formalna
Choć testowanie pomaga potwierdzić, że kontrakt daje zakładane rezultaty dla niektórych danych wejściowych, nie może ono jednoznacznie dowieźć tego samego dla danych wejściowych, które nie były użyte podczas testów. Testowanie inteligentnego kontraktu nie może zatem zagwarantować "poprawności funkcjonalnej" (tzn. nie może wykazać, że program zachowuje się zgodnie z wymaganiami dla wszystkich zestawów wartości wejściowych).
Formalna weryfikacja jest podejściem do oceny poprawności oprogramowania poprzez sprawdzenie, czy formalny model programu zgadza się z formalną specyfikacją. Model formalny jest abstrakcyjną matematyczną reprezentacją programy, podczas gdy specyfikacja formalna definiuje własności programu (np. logiczne założenia na temat działania programu).
Ponieważ własności te zapisane są w jeżyku matematycznym, możliwym staje się zweryfikowanie czy formalnym (matematyczny) model systemu odpowiada specyfikacji, korzystając z logicznych reguł wnioskowaniu. Narzędzia formalnej weryfikacji mają zatem za zadanie dostarczyć "matematyczny dowód" poprawności systemu.
W przeciwieństwie do testowania weryfikacja formalna może być użyta do sprawdzenia, czy wykonanie inteligentnego kontraktu spełnia formalną specyfikację dla wszystkich wykonań (tzn. nie ma błędów), bez potrzeby uruchamiania go na przykładowych danych. To nie tylko skraca czas poświęcony na przeprowadzenie wielu testów jednostkowych, ale również jest bardziej efektywne w wychwytywaniu ukrytych słabych punktów. Należy jednak pamiętać, że techniki formalnej weryfikacji prezentują całe spektrum trudności implementacji oraz użyteczności.
Więcej o formalnej weryfikacji inteligentnych kontraktów.
Testowanie a audyty i programy bug bounty
Jak wspomniano powyżej, rygorystyczne testowanie rzadko może zagwarantować kompletny brak błędów w kontrakcie; podejście weryfikacji formalnej może zapewnić silniejsze przekonanie o poprawności, ale obecnie jest trudne w użyciu i kosztowne.
Można jednak jeszcze bardziej zwiększyć szansę na wyłapanie słabego punktu w kontrakcie poprzez przegląd kodu przeprowadzony przez niezależny podmiot. Audyty inteligentnych kontraktów (opens in a new tab) i programy bug bounty (opens in a new tab) to dwa sposoby na zaangażowanie innych do analizy Twoich kontraktów.
Audyty przeprowadzane są przez audytorów doświadczonych w odnajdywaniu przypadków błędów zabezpieczeń i kiepskich rozwiązań programistycznych w inteligentnych kontraktach. Audyt zawiera zwykle testowanie (czasem również weryfikację formalną) jak również ręczny przegląd całej bazy kodu.
I na odwrót, program bug bounty zwykle polega na oferowaniu nagrody finansowej osobie (powszechnie określanej jako haker w białym kapeluszu (opens in a new tab)), która odkryje lukę w zabezpieczeniach inteligentnego kontraktu i ujawni ją deweloperom. Nagrody za znalezienie błędu są podobne do audytów, ponieważ obejmują proszenie innych o pomoc w znalezieniu defektów inteligentnego kontraktu.
Główną różnicą jest to, że programy nagród za znalezienie błędów są otwarte dla szerszej społeczności programistów i hakerów. Przyciągają również szeroką grupę hakerów etycznych i niezależnych specjalistów od bezpieczeństwa wyposażonych w unikalne umiejętności i doświadczenie. To może być ich przewaga nad audytami, które bazują głównie na zespołach, które mogą mieć ograniczoną lub wąską ekspertyzę.
Narzędzia i biblioteki do testowania
Narzędzia do testowania jednostkowego
-
solidity-coverage (opens in a new tab) – narzędzie do sprawdzania pokrycia kodu dla inteligentnych kontraktów napisanych w Solidity.
-
Waffle (opens in a new tab) – framework do zaawansowanego tworzenia i testowania inteligentnych kontraktów (oparty na ethers.js).
-
Remix Tests (opens in a new tab) – narzędzie do testowania inteligentnych kontraktów w Solidity. Działa pod wtyczką do Remix IDE pod nazwą "Solidity Unit Testing", która jest używana do pisania i przeprowadzania testów na kontrakcie._
-
OpenZeppelin Test Helpers (opens in a new tab) – biblioteka asercji do testowania inteligentnych kontraktów Ethereum. Upewnij się, że Twoje kontrakty zachowują się zgodnie z oczekiwaniami!_
-
Framework do testów jednostkowych Brownie (opens in a new tab) – Brownie wykorzystuje Pytest, bogaty w funkcje framework testowy, który pozwala pisać małe testy przy minimalnej ilości kodu, dobrze skaluje się w przypadku dużych projektów i jest wysoce rozszerzalny.
-
Testy Foundry (opens in a new tab) – Foundry oferuje Forge, szybki i elastyczny framework do testowania Ethereum, zdolny do wykonywania prostych testów jednostkowych, sprawdzania optymalizacji gazu i fuzzingu kontraktów.
-
Testy Hardhat (opens in a new tab) – framework do testowania inteligentnych kontraktów oparty na ethers.js, Mocha i Chai.
-
ApeWorx (opens in a new tab) – oparty na Pythonie framework do tworzenia i testowania inteligentnych kontraktów dla Wirtualnej Maszyny Ethereum.
-
Wake (opens in a new tab) – oparty na Pythonie framework do testowania jednostkowego i fuzzingu z zaawansowanymi możliwościami debugowania i obsługą testowania międzyłańcuchowego, wykorzystujący pytest i Anvil w celu uzyskania najlepszego doświadczenia użytkownika i wydajności.
Narzędzia do testowania opartego na właściwościach
Narzędzia do analizy statycznej
-
Slither (opens in a new tab) – oparty na Pythonie framework do statycznej analizy Solidity, służący do znajdowania luk w zabezpieczeniach, poprawy zrozumienia kodu i pisania niestandardowych analiz dla inteligentnych kontraktów.
-
Ethlint (opens in a new tab) – Linter do egzekwowania najlepszych praktyk w zakresie stylu i bezpieczeństwa dla języka programowania inteligentnych kontraktów Solidity.
-
Cyfrin Aderyn (opens in a new tab) – oparty na Rust analizator statyczny zaprojektowany specjalnie z myślą o bezpieczeństwie i rozwoju inteligentnych kontraktów Web3.
-
Wake (opens in a new tab) – oparty na Pythonie framework do analizy statycznej z detektorami luk i jakości kodu, drukarkami do wyodrębniania przydatnych informacji z kodu oraz wsparciem dla pisania niestandardowych podmodułów.
-
Slippy (opens in a new tab) – prosty i potężny linter dla Solidity.
Narzędzia do analizy dynamicznej
-
Echidna (opens in a new tab) – szybki fuzzer kontraktów do wykrywania luk w inteligentnych kontraktach poprzez testowanie oparte na właściwościach.
-
Diligence Fuzzing (opens in a new tab) – zautomatyzowane narzędzie do fuzzingu przydatne do wykrywania naruszeń właściwości w kodzie inteligentnych kontraktów.
-
Manticore (opens in a new tab) – dynamiczny framework do symbolicznego wykonywania analizy kodu bajtowego EVM.
-
Mythril (opens in a new tab) – narzędzie do oceny kodu bajtowego EVM do wykrywania luk w kontraktach przy użyciu analizy skażenia, analizy konkolikowej i sprawdzania przepływu sterowania.
-
Diligence Scribble (opens in a new tab) – Scribble to język specyfikacji i narzędzie do weryfikacji w czasie wykonywania, które pozwala na adnotowanie inteligentnych kontraktów właściwościami, które umożliwiają automatyczne testowanie kontraktów za pomocą narzędzi takich jak Diligence Fuzzing lub MythX.
Powiązane samouczki
- Przegląd i porównanie różnych produktów do testowania _
- Jak używać Echidna do testowania inteligentnych kontraktów
- Jak używać Manticore do znajdowania błędów w inteligentnych kontraktach
- Jak używać Slither do znajdowania błędów w inteligentnych kontraktach
- Jak mockować kontrakty Solidity na potrzeby testowania
- Jak uruchamiać testy jednostkowe w Solidity przy użyciu Foundry (opens in a new tab)
Dalsza lektura
- Szczegółowy przewodnik po testowaniu inteligentnych kontraktów Ethereum (opens in a new tab)
- Jak testować inteligentne kontrakty Ethereum (opens in a new tab)
- Przewodnik po testowaniu jednostkowym dla programistów od MolochDAO (opens in a new tab)
- Jak testować inteligentne kontrakty jak gwiazda rocka (opens in a new tab)