$$\rightleftharpoonup{xx}$$
$$\longleftharp{xx}$$,
$$\longrightharp{xx}$$,
Dane wejściowe do badania
W tym badaniu użyto dwóch inteligentnych kontraktów Solidity jako danych wejściowych do weryfikacji. Pierwszy z nich był kontraktem w stylu Simple DAO, który służył jako studium przypadku re-entrancy. Drugim był zmniejszony kontrakt księgowy/przejściowy stanowy, zaprojektowany do testowania ograniczeń na poziomie kontraktu w porównaniu z podwójnym wydatkowaniem. Oryginalny kod źródłowy Solidity służył jako wejście do procesów abstrakcji i weryfikacji zdefiniowanych w tym protokole. Ogólny proces transformacji stosowany dla takich kontraktów jest zilustrowany na Rysunku 1, który pokazuje stopniową transformację kodu źródłowego Solidity w modele FSM, Event-B i SMV podczas weryfikacji.
Modelowanie granicy
Modelowanie formalne koncentruje się na logice przepływu sterowania inteligentnymi kontraktami, w tym na zachowaniu wejścia i wyjścia funkcji, wykonaniu wewnętrznego oraz przejściach stanów na poziomie kontraktów. Widoczność funkcji (publiczna, zewnętrzna, wewnętrzna i prywatna) była reprezentowana razem z odpowiadającym im zachowaniem stosu wywołań istotnym dla analizy re-entry. Typy abstrakcyjnych przejść (wywołanie, wysłanie, transfer) traktowano jako operacje przenoszenia eterów.
Zdefiniowano inwarianty na poziomie kontraktu, aby zapewnić unikalność transakcji i zapobiec podwójnemu wydawaniu w obrębie granicy abstrakcji. Aby odzwierciedlić wymagania dotyczące wyboru walidacji i integralności bloku, warstwa abstrakcji protokoł-logika została zdefiniowana na podstawie przejść stanów na poziomie protokołu zarówno Proof of Work, jak i Proof of Stake.
Granica abstrakcji nie obejmowała elementów warstwy sieciowej, w tym harmonogramowania dostarczenia wiadomości oraz opóźnień spowodowanych wieloma skokami, rozwiązywaniem forków, przeciwnikami sieciowymi stosującymi strategie bizantyjskie, semantyką Ethereum Virtual Machine oraz semantyką gazową kontrolowaną przez węzły sieciowe, propagacją wyjątków, asynchronicznymi wykonaniami, złożonym zachowaniem awaryjnym oraz ostatecznością na poziomie sieci. W związku z tym wyniki ustalania podwójnych wydatków dotyczą tylko inwariantów na poziomie kontraktu i nie stanowią umowy na poziomie sieci co do ostateczności.
Narzędzia i konfiguracja
Platforma Rodin była używana do modelowania, udoskonalania, generowania zobowiązań dowodowych oraz uruchamiania modelu Event-B (wersja 3.7.0), który umożliwiał dowodom PP, ML, SMT i Atelier-B automatyczne i interaktywne uruchamianie prób. nuXmv wersja 2.0.0 była uruchamiana na wygenerowanych modelach SMV w trybie pełnej eksploracji CTL w celu przeprowadzenia kontroli modeli CTL.
Wszystkie wykonania weryfikacji były wykonywane w kontrolowanym środowisku obliczeniowym, wykorzystując Ubuntu 22.04 LTS, OpenJDK 11 oraz Python 3.10.12. Web3 (7.6.0), NetworkX (3.4.2), Matplotlib (3.8.0), Graphviz (0.20.3), NumPy (1.26.4) oraz Pandas (2.2.2) zostały użyte do implementacji elementów symulacji i wizualizacji. Ten projekt gwarantował, że formalne wyniki weryfikacji i symulacji mogły być odtwarzane przy tych samych warunkach wykonywania.
Przepływ transformacji
Proces weryfikacji składał się z czterech faz. Kontrakty na wytrzymałość zostały po raz pierwszy przetłumaczone na reprezentację automatu stanów skończonych (FSM-SC). Model FSM-SC został następnie zakodowany w Event-B, z wyraźnie określonymi inwariantami i stopniami udoskonalenia. Abstrakcja FSM-SC została przetłumaczona na model SMV w nuXmv. Wyniki weryfikacji przedstawiono jako statystyki dotyczące umorzenia zobowiązań dowodowych i kontroli modeli CTL, a kontrprzykłady przedstawiono jako przypadki.
Budowa FSM
Każdy kontrakt był abstrahowany jako skończona maszyna stanów zdefiniowana jako:

Gdzie:
S = zbiór stanów
S₀ = stan początkowy
T = relacja przejściowa
V = mapowanie widoczności
G = predykaty strażnicze
A = akcje/aktualizacje stanu.
Algorytm 1: Konstrukcja FSM na podstawie Solidity
Wejście wejściowe: Kod źródłowy Solidity
Wyjście: FSM-SC
1) Analizować drzewo składni abstrakcyjnych kontraktów Solidity.
2) Utworzenie stanu początkowego S₀ na podstawie definicji konstruktora.
3) Dla każdej funkcji solidności f utworzyć odrębny stan sterowania S_f i zapisać widoczność V(f) ∈ {publiczne, zewnętrzne, wewnętrzne, prywatne}.
4) Dla każdego zdania w funkcji f wyprowadzamy przejście t poprzez wyodrębnianie predykatów strażniczych z warunków żądania/asercji oraz działań z aktualizacji zmiennych stanu.
5) Dodaj przejście t do T.
6) Tworzenie jawnych typów przejść dla operacji transferu Ether (wywołanie, wysłanie, transfer), wywołań wewnętrznych i zewnętrznych, wywołań delegatecall, samozniszczenie, użycie tx.origin, gałęzie warunkowe oraz konstrukcje pętli.
7) Zwróć FSM-SC = (S, S₀, T, V, G, A).
Każda funkcja Solidity jest powiązana z innym stanem sterowania FSM. Dla zrozumienia podatności, przepływy wykonawcze istotne dla podatności, np. takie przepływy z zewnętrznymi wywołaniami i aktualizacjami balansu, były wyraźnie abstrahowane do uporządkowanych przejść w uporządkowaną. Rysunek 2 opisuje przykładowy diagram FSM-SC dla kontraktu w stylu SimpleDAO, pokazujący, jak stany wejścia, przejścia wywołań zewnętrznych oraz sekwencje aktualizacji stanu były abstrahowane podczas tego etapu konstrukcji.
Kodowanie FSM do Zdarzenia-B
Przejście na FSM było przedstawione jako konstrukty Event-B. Wszystkie przejścia odpowiadają zdarzeniom Event-B, w tym konkretnym strażnikom i akcjom.
Algorytm 2: kodowanie FSM do Event-B
Wejście wejściowe: FSM-SC
Wyjście: Maszyna Event-B i kontekst
1) Zdefiniuj STATE_SET i FUNCTION w kontekście kontraktowym.
2) Deklaruj zmienne reprezentujące stan sterowania FSM i stan na poziomie kontraktu.
3) Reprezentować każdy stan sterowania FSM s ∈ S za pomocą current_state ∈ STATE_SET.
4) Dla każdego przejścia (s → s′, g, a) stwórz zdarzenie Zdarzenie-B E_t z:
5) GDZIE current_state = s ∧ g
6) WTEDY current_state := s′ ∥ zastosować(a)
7) Kodowanie ograniczeń widoczności za pomocą zabezpieczeń pochodzących z V(f).
8) Zdefiniuj inwarianty inv1–inv9, aby uchwycić właściwości bezpieczeństwa i spójności.
9) Definiuj INICJALIZACJĘ poprzez przypisanie wartości S₀ i domyślnych.
Zestaw stanów, aktualny stan, widoczność funkcji, stos wywołań, znacznik czasu transakcji, status transferu Ether, flaga wywołania delegata, flaga samozniszczenia oraz warunek weryfikacji to niektóre ze zmiennych uwzględnionych w modelu Event-B. Inwarianty (inv1-inv9) oraz działania inicjalizacji (act1-act6) odpowiadają tym w formalnej specyfikacji. Rysunek 3 przedstawia również graficzną prezentację, jak kluczowe zdarzenia istotne dla podatności, zwłaszcza przejścia związane z ponownym wejściem, są utrzymywane w kodowaniu Event-B. Ten rysunek wyjaśnia, jak wzorce strukturalne modelu FSM-SC pokazane na Rysunku 2 są odwzorowane na weryfikowalne zdarzenia Event-B.
Strategia udoskonalania
Wprowadzono dwa poziomy udoskonalenia. Na poziomie abstrakcyjnym reprezentowano przepływ kontroli kontraktów na wysokim poziomie oraz inwarianty rdzeniowe. Udoskonalony poziom dodał ograniczenia specyficzne dla kontraktów, w tym ograniczenia stosu wywołań, ograniczenia widoczności oraz warunki zapobiegania ponownemu wejściu.
Algorytm 3: Udoskonalenie i zwolnienie z obowiązkiem dowodu
Input: Maszyna abstrakcyjna i maszyna udoskonalona
Wyjście: Umorzone zobowiązania dowodowe i statystyki
1) Generowanie obowiązków dowodowych dla maszyny abstrakcyjnej w Rodinie.
2) Wykonanie włączonych automatycznych dowodów i zapisywanie wyników wypisu.
3) Generowanie zobowiązań do dostosowania dla maszyny rafinowanej.
4) Stosowanie automatycznych dowodów do obowiązków związanych z udoskonalaniem.
5) Wypełnianie pozostałych zobowiązań interaktywnie, gdy jest to konieczne.
6) Statystyki dowodów eksportu i raporty statusowe.
Raportowanie dowodów obejmowało liczbę inwariantów, poziomy udoskonalenia, generowane zobowiązania dowodowe, automatyczną szybkość wyładowania, interaktywną szybkość wyładowania oraz końcową szybkość wyładowania.
Specyfikacja właściwości CTL i sprawdzanie modeli
Abstrakcja FSM-SC została przetłumaczona na model SMV do weryfikacji czasowej w czasie rozgałęzień w nuXmv.
Algorytm 4: Sprawdzanie FSM do SMV i CTL
Wyniki: Wynik weryfikacji PASS/FAIL oraz ślady kontrprzykładów (jeśli występują)
1. Zapisz stany sterujące FSM jako wyliczony stan zmiennej SMV.
2. Przekształcić przejścia FSM na chronione przydziały następnego stanu (następnego stanu).
3. Utrzymywanie flag dla warunków związanych z podatnościami, takich jak połączenia zewnętrzne i aktualizacje salda.
4. Zakoduj właściwości CTL w nuXmv i wykonaj model checking.
5. Jeśli jakaś własność nie działa, wygeneruj kontrprzykładowe ślady reprezentujące sekwencje przejść FSM.
Sprawdzanie CTL obejmowało wymagania kolejności ponownego wejścia, finalizację aktualizacji stanu po operacjach transferowych, ograniczanie nieograniczonego rekurencyjnego wprowadzania do krytycznych sekcji oraz unikanie martwego punktu.
Weryfikacja właściwości bezpieczeństwa
Prosty kontrakt w stylu DAO, który zapobiega ponownemu wejściu, został potwierdzony poprzez zapewnienie bezpiecznego porządkowania między zewnętrznymi wywołaniami a aktualizacjami stanu za pomocą inwariantów i ograniczeń CTL. Tam, gdzie było to dozwolone, strażnicy ci sprawdzali ograniczenia dostępu i kontroli dostępu, zapewniając, że nieautoryzowane przejścia są ograniczone przez inwarianty. Model zredukowanego rejestru weryfikował niezmienniki unikalności transakcji i spójności księgi na poziomie zapobiegania w kontrakcie.
Raportowane wyniki
Sekcja Wyniki zawiera raporty dotyczące metryk strukturalnych FSM, metryk modelu Event-B oraz statystyk do dowodu zobowiązania i weryfikacji CTL. Wyniki dowodu formalnego oraz testów modelowych CTL są podawane osobno, aby rozróżnić dowody gwarancji poprawności za pomocą inwariantów oraz dowody weryfikacji czasowej za pomocą weryfikacji czasowej.