
Wprowadzenie
Inteligentne kontrakty są zaprogramowane tak, aby były trwałe i niezmienne po wdrożeniu w sieci blockchain. Ta niezmienność jest źródłem bezpieczeństwa i zaufania, ale stanowi również wyzwanie, gdy programiści muszą naprawiać błędy lub dodawać nowe funkcje.
Kiedy konieczne są modyfikacje, programiści muszą wydać zupełnie nową umowę z innym adresem. Chociaż ta stałość jest świetna dla bezpieczeństwa, może być ograniczająca w sytuacjach, gdy coś idzie nie tak lub wymaga poprawy.
Historia pokazuje, że luki w zabezpieczeniach inteligentnych kontraktów mogą prowadzić do katastrofalnych strat finansowych. Jednym z konkretnych przykładów jest poważny incydent, w wyniku którego utracono miliony dolarów z powodu luki, którą można było wykorzystać.
Incydenty te wskazują na znaczenie możliwości rozwiązywania problemów związanych z bezpieczeństwem i błędami po wdrożeniu. Możliwość aktualizacji kontraktów pozwala na wprowadzenie mechanizmu korygowania problemów bez utraty istniejącego stanu i doświadczenia użytkownika, co jest kluczowym aspektem aktualizowalności inteligentnych kontraktów.
Ta umiejętność może być ważna, aby zapobiec znacznym stratom i zachować integralność systemu.
Istnieje wiele sposobów wdrażania aktualizowalnych inteligentnych kontraktów, takich jak wzorce proxy inteligentnych kontraktów i techniki separacji danych. Ten artykuł dotyczy konkretnie sposobu wdrażania aktualizowalności przy użyciu wzorców proxy, które są popularnym sposobem rozwiązywania tego problemu w obszarze rozwoju łańcucha bloków.
Zrozumienie wzorca proxy
Wzorzec proxy to strukturalny wzorzec projektowy, w którym jedna umowa implementuje interfejs dla innej umowy. Architektura ta składa się z dwóch głównych części: umowy proxy i umowy implementacyjnej.
Zamiast komunikować się bezpośrednio z umową wdrożeniową, użytkownik będzie komunikować się z umową proxy, która następnie przekaże żądania dalej.
W tej konfiguracji zarówno umowa proxy, jak i umowa wdrożeniowa nie są zmieniane po wdrożeniu. Jednak możliwość aktualizacji jest osiągana poprzez umożliwienie proxy odwoływania się do różnych umów wdrożeniowych w miarę upływu czasu.
Oznacza to, że możecie nadal korzystać z tego samego adresu i interfejsu, podczas gdy funkcjonalność podstawy jest aktualizowana. Z punktu widzenia użytkownika aplikacja działa bez zarzutu, mimo że zmienia się logika zaplecza.
Umowa proxy odpowiada za interakcje z użytkownikami i przechowywanie wszystkich danych. Przechowuje adres umowy wdrożeniowej w specjalnym miejscu.
Kiedy wywołujecie funkcje w umowie proxy, wywoływana jest funkcja rezerwowa. Poniższa funkcja wykorzystuje mechanizm delegatecall ethereum do wywołania kodu z umowy implementacyjnej, ale wszelkie zmiany stanu są zapisywane w pamięci umowy proxy.
Główne implementacje wzorców proxy
Przezroczysty wzorzec proxy
Wzorzec Transparent Proxy umieszcza funkcjonalność aktualizacji w samej umowie proxy. Proxy posiada metodę upgradeTo, która służy do aktualizacji adresu wskazującego na umowę implementacyjną.
Stwarza to potencjalny problem: jeśli zarówno kontrakt proxy, jak i kontrakt implementacji zawierają metodę upgradeTo, nie jest jasne, która z nich powinna zostać wywołana, gdy użytkownik uruchamia tę funkcję.
Aby rozwiązać tę niejasność, opracowano rozwiązanie, w którym decyzja o tym, komu zlecić zadanie, opiera się na tym, kto wzywa umowę.
Jeśli wywołującym jest administrator proxy, wywołania są obsługiwane przez sam kontrakt proxy. W przeciwnym razie wywołania są delegowane do kontraktu implementacyjnego. Takie podejście zapewnia jasne wykonywanie funkcji i brak niejasności między operacjami administracyjnymi a zwykłymi operacjami użytkownika.
Uniwersalny standard proxy z możliwością aktualizacji
Uniwersalny standard proxy z możliwością aktualizacji ma inne podejście, umieszczając funkcję aktualizacji w umowie implementacyjnej, a nie w proxy. Proxy przekazuje wszystkie wywołania do umowy implementacyjnej, która posiada metodę upgradeTo wskazującą na nowsze wersje.
Ten wzorzec daje programistom większą kontrolę nad ścieżką aktualizacji. Jeśli programista zdecyduje się nie uwzględniać już metody upgradeTo w przyszłej wersji, umowa staje się trwale niezmienna i nie można jej dalej aktualizować.
Może to być pomocne, gdy kontrakt został dobrze przetestowany, a zespół programistów chce go zamrozić w ostatecznej wersji.
Ponieważ logika aktualizacji znajduje się w umowie wdrożeniowej, nie ma potrzeby, aby funkcja rezerwowa proxy sprawdzała, czy wywołujący jest administratorem przed przekazaniem wywołania.
Dzięki temu wzorzec ten jest bardziej wydajny niż wzorzec przezroczystego proxy. Wyeliminowano również potencjalny konflikt nazw, ponieważ metoda upgradeTo istnieje tylko w umowie implementacyjnej.
Wzorzec proxy sygnału nawigacyjnego
Wzorzec Beacon Proxy wprowadza architekturę składającą się z trzech elementów: umowy proxy, umowy beacon i umowy wdrożeniowej. Zamiast zapisywać adres wdrożenia, proxy zapisuje adres umowy beacon.
Umowa beacon zawiera adres umowy wdrożeniowej.
Kiedy użytkownik wywołuje proxy, najpierw pobiera adres kontraktu sygnału nawigacyjnego, a następnie wywołuje sygnał nawigacyjny, aby uzyskać adres kontraktu implementacyjnego. Na koniec pozostawia wywołanie kontraktu implementacyjnego.
Ta dodatkowa warstwa pośrednictwa ma bardzo konkretny cel.
Ten wzorzec jest szczególnie przydatny, gdy kilka umów proxy musi korzystać z tej samej implementacji. W prostszych wzorcach, aby zaktualizować implementację, trzeba by zaktualizować adres w każdej umowie proxy z osobna. Dzięki wzorcowi beacon wystarczy zaktualizować umowę beacon, a wszystkie powiązane z nią proxy automatycznie wskażą nową implementację.
Główne implementacje wzorców proxy
Wzorzec Diamond Proxy
Wzorzec Diamond Proxy rozwiązuje problem ograniczeń rozmiaru inteligentnych kontraktów, które zazwyczaj są ograniczone do około 24 kilobajtów. Wzorzec ten dzieli funkcjonalność na kilka mniejszych kontraktów, zwanych aspektami.
Umowa proxy zachowuje powiązania między selektorami funkcji a adresami aspektów zawierających te funkcje.
Gdy funkcja jest wywoływana na serwerze proxy, używa ona selektora funkcji, aby wyszukać aspekt zawierający tę funkcję i przekazuje wywołanie funkcji do odpowiedniego aspektu.
Aktualizacje są wykonywane poprzez modyfikację adresów aspektów w serwerze proxy. Ten wzorzec pozwala na znacznie większą funkcjonalność poprzez rozdzielenie funkcji między wiele umów, przy czym każdy aspekt pozostaje poniżej limitu rozmiaru.
Porównanie podejść opartych na wzorcach proxy
Każdy wzorzec proxy ma inne cechy, które sprawiają, że nadaje się do różnych zastosowań. Wszystkie cztery wzorce wymagają umowy proxy i delegowania użycia w celu przekazywania wywołań do umów implementacyjnych.
Wzorzec Transparent Proxy umieszcza funkcje aktualizacji w umowie proxy, a UUPS umieszcza funkcje aktualizacji w umowie wdrożeniowej. Wzorzec Beacon umieszcza funkcję aktualizacji we własnej umowie beacon, a wzorzec Diamond zazwyczaj umieszcza funkcję aktualizacji w umowach wdrożeniowych, ale nie jest to ściśle określone.
Jeśli chodzi o niezmienność, wzorce UUPS i Diamond mogą sprawić, że umowy będą trwale niezmienne poprzez usunięcie funkcji aktualizacji z przyszłych wersji. Wzorce Transparent i Beacon nie zapewniają takiej elastyczności.
W przypadku wielu serwerów proxy wzorzec Beacon ma zdecydowaną przewagę. W przypadku wzorców Transparent, UUPS i Diamond każdy serwer proxy musi być zmieniany indywidualnie podczas wdrażania nowej implementacji.
W przypadku wzorca Beacon należy zaktualizować tylko umowę beacon, a wszystkie serwery proxy automatycznie zaczną korzystać z nowej implementacji.
Wydajność paliwowa jest różna dla każdego wzorca. Wzorzec Transparent wymaga sprawdzenia przed każdym przekazaniem uprawnień, czy osoba wywołująca jest administratorem, więc jest bardziej kosztowny.
UUPS jest bardziej wydajny, ponieważ nie wymaga tej kontroli. Wszystkie wzorce z wyjątkiem Diamond mają dodatkowe koszty gazu związane z dodatkowymi operacjami wyszukiwania.
Wszystkie wzorce z wyjątkiem Diamond są ograniczone do 24 kilobajtów na kontrakt. Wzór Diamond pozwala, aby każdy aspekt miał rozmiar do 24 kilobajtów, co umożliwia uzyskanie znacznie większej łącznej funkcjonalności w wielu aspektach.
Opanuj bezpieczeństwo inteligentnych kontraktów
Poznaj zaawansowane techniki tworzenia bezpiecznych, aktualizowalnych umów dzięki naszym kursom prowadzonym przez ekspertów.
Ważne kwestie i ryzyka
Problemy związane z kolizjami pamięci masowej
Kolizje pamięci masowej stanowią poważne ryzyko podczas wdrażania wzorców proxy. Zmienne kontraktu są przechowywane w określonych slotach pamięci masowej, a jeśli zmienne kontraktu proxy i zmienne kontraktu implementacyjnego są przechowywane w tych samych slotach pamięci masowej, będą one wzajemnie się zakłócać.
Ponadto, jeśli kolejność zmiennych w umowie implementacyjnej ulegnie zmianie między wersjami, miejsca przechowywania mogą zostać ponownie przypisane, co spowoduje uszkodzenie danych.
Niezinicjalizowane umowy wdrożeniowe
Umowy implementacyjne muszą być inicjowane dokładnie raz za pomocą funkcji inicjalizacyjnej, która jest podobna do konstruktora w tradycyjnych umowach.
Programiści zapominają również o zainicjowaniu implementacji lub nie uwzględniają zabezpieczeń zapobiegających wielokrotnemu wywołaniu funkcji inicjalizacyjnej.
W takim przypadku atakujący mogą samodzielnie wywołać funkcję inicjalizacyjną i potencjalnie przejąć kontrolę nad kontraktem lub manipulować jego stanem.
Rzeczywiste incydenty związane z bezpieczeństwem
W ramach programu bug bounty wykryto krytyczną lukę w umowach proxy vault, w których umowy wdrożeniowe nie zostały poprawnie zainicjowane. Luka ta mogła zostać wykorzystana przez atakującego do zniszczenia umowy wdrożeniowej, co spowodowałoby unieważnienie powiązanych umów proxy. W innym incydencie, który miał miejsce w lipcu, smart kontrakty zostały zhakowane z powodu luki w kodzie inicjalizacyjnym, w którym funkcja inicjalizacyjna mogła być wywoływana wielokrotnie.
Alternatywne podejścia do możliwości aktualizacji
Wzorzec separacji danych
Alternatywą dla wzorców proxy jest podejście oparte na separacji danych. Podejście to opiera się na wykorzystaniu oddzielnych kontraktów dla pamięci masowej i logiki. Kontrakt logiczny komunikuje się z kontraktem pamięci masowej w celu odczytania lub aktualizacji danych.
Chociaż możliwe jest zastąpienie umowy logicznej nowymi wersjami, umowa dotycząca przechowywania danych jest stała i niezmienna. Oferuje to inny model aktualizacji, który może być odpowiedni w niektórych okolicznościach.
Warstwy weryfikacyjne
Żaden z powszechnie stosowanych wzorców proxy nie posiada mechanizmów weryfikacji poprawności nowej umowy wdrożeniowej przed dokonaniem aktualizacji.
Nowe wersje muszą zawierać całą niezbędną logikę biznesową, funkcje awaryjne i inne istotne elementy.
Można to osiągnąć poprzez wprowadzenie warstwy weryfikacyjnej, która będzie funkcjonować równolegle z warstwą proxy, warstwą logiki biznesowej i warstwą pamięci masowej. Ta dodatkowa warstwa gwarantuje, że aktualizacje będą spełniać określone kryteria, zanim zostaną dopuszczone do wykonania.
Najlepsze praktyki dotyczące umów z możliwością aktualizacji
Programiści projektujący umowy z możliwością aktualizacji powinni przestrzegać kilku kluczowych wytycznych, aby zapewnić bezpieczeństwo i niezawodność:
- •Zamiast tego polegaj na sprawdzonych implementacjach z dobrze przetestowanych bibliotek, które zostały dokładnie sprawdzone i zweryfikowane.
- •Zawsze upewnij się, że umowy implementacyjne są prawidłowo zainicjowane i że funkcje inicjalizacyjne mogą być wywołane tylko raz.
- •Nigdy nie inicjalizuj zmiennych stanu podczas ich deklaracji lub w konstruktorze, używaj funkcji inicjalizacyjnej do wszystkich ustawień stanu
- •Nie zmieniaj kolejności ani typów zmiennych stanu podczas tworzenia nowych wersji umów wdrożeniowych.
- •Jeśli potrzebne są nowe zmienne, należy je dodać po wszystkich istniejących zmiennych
- •W przypadku implementacji wzorca Diamond używaj specjalnych metod przechowywania, które są zoptymalizowane pod kątem tego wzorca.
- •Metoda UUPS jest preferowana w stosunku do wzorca Transparent Proxy Pattern, gdy jest to możliwe, ponieważ wymaga mniej gazu do rutynowych operacji
- •Upewnij się, że konto administratora serwera proxy jest wysoce bezpieczne, ponieważ konto to kontroluje proces aktualizacji i stanowi krytyczny punkt bezpieczeństwa
- •Na koniec, przed wdrożeniem wszystkie umowy powinny zostać poddane profesjonalnej kontroli przez doświadczonych specjalistów ds. bezpieczeństwa inteligentnych umów (smart contract security).
Końcowe przemyślenia
Wzorce proxy zapewniają potężny mechanizm aktualizacji inteligentnych kontraktów przy zachowaniu tego samego adresu i stanu. Kontrakt proxy wykorzystuje delegateCall do przekazania wykonania do kontraktów implementacyjnych, dzięki czemu można zmienić logikę bazową bez modyfikowania interfejsu użytkownika.
Jeśli jednak umowy z możliwością aktualizacji nie są prawidłowo wdrożone, mogą stanowić poważne zagrożenie dla bezpieczeństwa.
Programiści muszą dokładnie rozważyć kompromisy między różnymi typami wzorców proxy, przestrzegać ustalonych najlepszych praktyk i zapewnić przeprowadzenie odpowiednich audytów bezpieczeństwa w celu stworzenia niezawodnych i bezpiecznych inteligentnych kontraktów z możliwością aktualizacji.
Porównanie wzorców proxy
Porównując wzorce proxy, które można zastosować, należy wziąć pod uwagę kilka czynników:
Porównanie wzorców proxy
| Funkcja | Przejrzystość | UUPS | Beacon | Diament |
|---|---|---|---|---|
| Lokalizacja aktualizacji | Umowa proxy | Umowa wdrożeniowa | Umowa Beacon | Umowy wdrożeniowe |
| Trwała niezmienność | Nie | Tak | Nie | Tak |
| Wiele serwerów proxy | Indywidualne aktualizacje | Indywidualne aktualizacje | Aktualizacja pojedynczego sygnału nawigacyjnego | Indywidualne aktualizacje |
| Efektywność paliwowa | Wyższy koszt (kontrola administracyjna) | Niższe koszty | Średni (dodatkowe wyszukiwanie) | Średni (wyszukiwanie według cech) |
| Limit rozmiaru umowy | 24 KB | 24 KB | 24 KB | 24 KB na każdy aspekt |
| Złożoność wdrożenia | Średni | Średni | Średni | Średni |
Kwestie związane z wyborem wzorca
Kluczowe czynniki, które należy wziąć pod uwagę przy wyborze wzorca:
- •Wszystkie wzorce wymagają umowy proxy i wykorzystują delegację do przekazywania wywołań.
- •Wzorzec przezroczystego proxy służy do przechowywania funkcji aktualizacji w samej umowie proxy.
- •UUPS umieszcza funkcje aktualizacji w umowie wdrożeniowej
- •Wzorzec Beacon wykorzystuje oddzielną umowę beaconową do aktualizacji
- •Wzorzec Diamond zazwyczaj umieszcza funkcje aktualizacji w kontraktach implementacyjnych, ale specyfikacja nie wymaga tego.
- •Tylko wzorce UUPS i Diamond mają opcję trwałego uniemożliwienia zmiany umów poprzez usunięcie funkcji aktualizacji z przyszłych wersji
- •Wzór Beacon jest idealny, jeśli masz wiele serwerów proxy do aktualizacji w tym samym czasie, ponieważ aktualizacji wymaga tylko sygnał nawigacyjny, a nie każdy serwer proxy.
- •Maksymalny rozmiar kontraktu wynosi 24 kilobajty dla wzorców Transparent, UUPS i Beacon
- •Wzór Diamond obsługuje każdy aspekt o wielkości do 24 kilobajtów, dzięki czemu możesz obsługiwać znacznie większą funkcjonalność
- •Wszystkie wzorce mają średnią złożoność implementacji i istnieją dobrze ugruntowane biblioteki dla wzorców Transparent, UUPS i Beacon.


