Tekst oryginalny: „Vanity Addresses” autorstwa foobar

Zestawienie: Owsianka na noc, sposób na DeFi

Zniknęło 160 milionów dolarów, a Wintermute stracił fundusze. Wintermute to jeden z najsprytniejszych funduszy animujących rynek w branży. Pewnego wrześniowego ranka, kiedy osoba odpowiedzialna za Wintermute obudziła się, stwierdził, że jeden z ich ważnych portfeli stracił 9 cyfr.fundusze. Co więc doprowadziło do kradzieży Wintermute'a? Jest to spowodowane słabą losowością generatora adresów próżnych. Hakerzy Black Hat brutalnie wymuszają od zera parę klucza prywatnego i adresu publicznego, a następnie duża ilość zasobów kryptograficznych zostaje przeniesiona.

Istnieje również historia o włamaniu do Indexed Finance, podczas którego w październiku 2021 r. skradziono 16 milionów dolarów, a następnie skradzione środki przeniesiono na adresy zaczynające się od 0xba5ed… . Nie wiedzieli jednak, że na adres prywatny miała również wpływ luka w zabezpieczeniach związana z złą losowością, która nękała Wintermute, w związku z czym we wrześniu 2022 r. wszystkie pieniądze zostały ponownie skradzione i trafiły na inny zhakowany adres portfela. Złodzieje są bezwzględni.

Jakie zatem problemy napotkali ci utalentowani programiści i czego możemy się od nich nauczyć?

Po lewej stronie znajduje się normalny adres używany w umowie WETH. Po prawej stronie znajduje się ładny adres z 14 zerami wiodącymi do optymalizacji robota MEV. Najpopularniejszym typem adresu próżnego jest adres z wieloma zerami wiodącymi.

Po pierwsze, czym jest adres próżny (znany również jako adres próżny)? Adres próżny odnosi się do adresu publicznego, który użytkownik celowo tworzy w celu powiązania ze swoim portfelem lub inteligentną umową. Może zaczyna się od 0x0000000, może zaczyna się od 0xdeadbeef, a może jest to jakiś inny zwykły adres. Istnieje kilka powodów ich popularności:

1. Optymalizacja gazu: Wintermute zaoszczędził 15 000 dolarów dzięki zastosowaniu adresu EOA z wieloma zerami wiodącymi. Brzmi głupio? Wiele osób się zgodzi, ale tak działa EVM. Jeśli w adresie jest dużo zer, opłata za gaz transakcyjny może spaść. Jeśli więc użyjesz inteligentnego adresu kontraktu z dużą liczbą zer wiodących, użytkownicy będą zadowoleni, gdy wejdą z nim w interakcję, ponieważ pozwala im to zaoszczędzić pieniądze.

W żółtej księdze Ethereum opisano, w jaki sposób wiodące adresy zerowe mogą umożliwić tańszy gaz

2. Marka umowy. Czy wiesz, że umowa na token 1inch zaczyna się od 0x111111111...?

Umowa na token 1-calowy

3. Powtarzalność wieloniciowa. Moim zdaniem jest to najwyższy priorytet i dlatego każdy protokół powinien używać adresu próżnego do swojego wdrożenia. Twoja aplikacja może istnieć w 15 różnych łańcuchach EVM i wszędzie mieć ten sam adres! Czy nie byłoby to łatwiejsze dla programistów i użytkowników?

Kiedy więc ładny adres jest bezpieczny?

Istnieją dwa rodzaje adresów Ethereum: konta będące własnością zewnętrzną (EOA) i konta inteligentnych kontraktów. Jeśli korzystałeś z portfela takiego jak MetaMask, każdy znajdujący się w nim adres to EOA, który służy do podpisywania wiadomości i przetwarzania transakcji. Porównaj to z kontem inteligentnej umowy, takim jak umowa Uniswap, z którym ludzie mogą wchodzić w interakcję, ale nie może podejmować własnych działań bez aktywacji. Podsumowując, sprawa jest bardzo prosta. Adresy typu Good Number nie są bezpieczne dla kont EOA, ale są bezpieczne dla kont smart kontraktowych.

Dlaczego więc tak jest? Wyjaśnimy to bardziej szczegółowo poniżej, ale zależy to od sposobu wygenerowania adresu próżnego. W przypadku kont EOA przeglądasz miliony kluczy prywatnych, aż znajdziesz taki, który odpowiada ładnie wyglądającemu adresowi publicznemu. Jednak klucz prywatny kontroluje środki na koncie EOA, więc jeśli losowość używana do przeglądania klucza prywatnego zostanie naruszona, całe konto zostanie zniszczone. Z drugiej strony tworzenie adresów próżnych inteligentnych kontraktów wymaga jedynie przechodzenia przez publiczne nasiona, które nie przyznają żadnych praw administracyjnych do inteligentnego kontraktu.

Oto dlaczego Wintermute zawodzi, a OpenSea odnosi sukces – generowanie kluczy prywatnych w niezabezpieczonej pamięci przy użyciu niezabezpieczonego oprogramowania jest złe. Ale generowanie publicznych nasion w ten sposób jest całkiem niezłe! Zatem dobry adres EOA to droga do bankructwa, natomiast dobry adres inteligentnego kontraktu to droga do sukcesu.

Dlaczego protokół wymaga ładnego adresu?

Prostsza dokumentacja! Możesz wskazać adres kontraktu we wszystkich sieciach;

Możliwość weryfikacji użytkownika! Ten sam adres kontraktu pojawi się tylko wtedy i tylko wtedy, gdy kod bajtowy będzie dopasowywany bajt po bajcie;

Programiści mogą zweryfikować! Ponieważ identyczne adresy kontraktów występują tylko w przypadku dokładnego dopasowania, możesz wychwycić trudne, małe modyfikacje w skrypcie wdrażania;

Łatwiejsza integracja! Inne protokoły mogą na stałe zakodować adres kontraktu w kodzie wielołańcuchowym bez konieczności używania instrukcji if opartych na identyfikatorze łańcucha.

UWAGA: Zaraz zagłębimy się w szczegółową instrukcję obsługi. Składając wszystkie elementy w całość, zagłębiamy się w przestrzeń techniczną i skupiamy się na deweloperach inteligentnych kontraktów, którzy mają doświadczenie we wdrażaniu inteligentnych kontraktów w łańcuchu dostaw. Jeśli jesteś zainteresowany, czytaj dalej, a jeśli nie jest to dla Ciebie, nie martw się o dotrzymanie kroku. Na koniec czeka Cię dodatkowe wyzwanie techniczne (z nagrodami).

Inteligentny kontrakt, piękny adres

Istnieje sposób na wygenerowanie adresów próżnych inteligentnych kontraktów, które są w 100% bezpieczne, bez względu na to, jakiego oprogramowania używasz, nie ma znaczenia, czy technologia iteracyjna wycieknie do opinii publicznej. Nazywa się to „metodą fabryczną CREATE2” i nie tylko zapewnia ładny adres, ale jest także niezawodnym sposobem zapewnienia tego samego adresu wdrożenia kontraktu w wielu łańcuchach. Umożliwia także innym bezproblemowe wdrażanie kodu w Twoim imieniu bez konieczności udostępniania klucza prywatnego lub założeń jednorazowych.

Najpierw krótki przegląd tego, jak wybrać inteligentny adres kontraktu. Istnieją dwie opcje wdrożenia: CREATE i CREATE2. W przypadku wdrażania inteligentnej umowy bezpośrednio z EOA domyślnym procesem jest CREATE. Adres jest ustalany poprzez zmieszanie adresu twórcy kontraktu z wartością jednorazową twórcy kontraktu. Ta wartość jednorazowa odnosi się do liczby transakcji wysłanych z danego adresu, więc nowy portfel zaczyna się od 0 i jest zwiększany o 1 za każdym razem, gdy wysyłana jest nowa transakcja. Oto magiczna formuła adresu inteligentnego kontraktu wdrożona przez CREATE:

nowy_adres = hash(nadawca, nonce)

Mniej powszechny, ale bardziej interesujący jest adres inteligentnego kontraktu wdrażany za pomocą CREATE2, a oto jego wzór:

nowy_adres = hash(0xFF, nadawca, sól, kod bajtowy)

To pierwsze wydaje się prostsze, prawda? Podajmy jednak przykład, gdzie ta prostota może być szkodliwa w porównaniu z bardziej niezawodnym procesem CREATE2.

Airy Alice: Wystąpił problem z obsługą wielu łańcuchów

Wyobraź sobie, że twórca kryptowalut o imieniu Alice tworzy dwa inteligentne kontrakty: fork Uniswap o nazwie GriddleSwap i projekt NFT o nazwie ph00ts. Wszystkie są niezmiennymi, niezależnymi prymitywami, co oznacza, że ​​nie ma żadnych zewnętrznych zależności ani ryzyka mostu międzyłańcuchowego. Alice wdraża GriddleSwap w Ethereum przy użyciu wartości nonce 0, a następnie wdraża ph00ts w Ethereum przy użyciu wartości nonce 1. Niestety Alice ma krótki czas skupienia i przez kilka minut była rozproszona na kryptograficznym Twitterze, zanim wdrożyła swoją pracę na Binance Smart Chain (BSC), drugiej co do wielkości platformie inteligentnych kontraktów.

Ups, pomyliłem kolejność wdrażania!

Ale czekaj! Pomieszała kolejność wdrażania i wdrożyła ph00ts przed GriddleSwap. Ponieważ adres inteligentnego kontraktu opiera się tylko na adresie twórcy, a we wdrożonym łańcuchu bloków Ethereum gridleswap ma dokładnie ten sam adres co BSC ph00ts! Co gorsza, adres Ethereum ph00ts jest taki sam jak adres BSC GriddleSwap. Myślenie, że użytkownicy końcowi będą zdezorientowani, to niedopowiedzenie. W rzeczywistości złośliwi wdrażający mogą go wykorzystać, aby oszukać ludzi, aby pomyśleli, że zachowanie kontraktu w łańcuchu jest takie samo — co jest uczciwym założeniem, biorąc pod uwagę ten sam adres!

Ostrożna Alicja: nadal będą problemy

Nawet jeśli Alicja jest ostrożna podczas wdrażania i nigdy nie myli kolejności swoich wartości jednorazowych, istnieją inne problemy. Jeśli Alicja poprawnie wdroży się w Ethereum i BSC, ale następnie dokona niepowiązanej transakcji w Polygon, wartość nonce 0 została wykorzystana. Nigdy nie będzie mogła tam wdrożyć GriddleSwap, ponieważ jej wartość jednorazowa została zwiększona. Dlatego klucze prywatne wdrażającego muszą być chronione za wszelką cenę. Jeśli Alice to ujawni, złośliwy sabotażysta może dokonać niepowiązanych transakcji. Jeśli Alicja je utraci, utraci także możliwość ponownego wdrożenia pod ten adres w nowym łańcuchu. Jest to trwała luka, której klucz prywatny musi chronić uczciwa osoba. Jeśli nawet programiści rdzenia Bitcoin nie mogą tego zrobić, jak reszta z nas może to zrobić?

Rozwiązanie: UTWÓRZ2

Na szczęście istnieje lepszy sposób na uzyskanie spójnych adresów w łańcuchach — taki, który nie opiera się na tajnych kluczach prywatnych, nie polega na jednym wdrażającym i jest odporny na błędy wdrażającego. Zapamiętaj wzór na znalezienie adresu inteligentnego kontraktu wdrożonego za pomocą CREATE2:

nowy_adres = hash(0xFF, nadawca, sól, kod bajtowy)

Pierwszy parametr 0xFF jest wartością stałą, którą można zignorować. Drugi parametr (adres nadawcy) można zapewnić spójność, wybierając wdrożenie CREATE2Factory z0age 0x0000000000FFe8B47B3e2130213B802212439497 w większości łańcuchów EVM. Trzecim parametrem jest wybrana przez użytkownika sól, za pomocą której możemy znaleźć dobry adres, a następnie zachować go w niezmienionym stanie w łańcuchu. Czwarty to kod bajtowy kontraktu, który służy jako przydatna kontrola poprawności, aby upewnić się, że wdrażamy dokładnie tę samą funkcjonalność w łańcuchu. Wszystkie cztery parametry mogą pozostać takie same, niezależnie od tego, co zrobi pojedynczy wdrażający.

Dlaczego jest to lepsze? W przeciwieństwie do klucza prywatnego, sól wybrana przez wdrażającego może zostać upubliczniona! Znajomość soli pozwala na wdrożenie kontraktu, ale nie masz żadnej kontroli nad zasobami i funkcjonalnością kontraktu. Ponieważ nie wiąże się to z żadnymi tajnymi informacjami, każdy może wdrożyć kontrakt w nowym łańcuchu bez ujawniania lub udostępniania klucza prywatnego. Parametr bytecode zapewnia również, że te nowe wdrożenia bez uprawnień będą miały ten sam adres wtedy i tylko wtedy, gdy kod bajtowy jest taki sam. Dlatego użytkownicy końcowi uzyskują silniejsze gwarancje bez konieczności wprowadzania szczegółowych różnic w kodzie.

Bardziej szczegółowy przegląd można znaleźć w popularnonaukowym artykule OpenZeppelin.

Stwórz swój własny piękny adres

Myślisz, że Proof of Work (PoW) będzie bezużyteczny po fuzji Ethereum? Pomyśl jeszcze raz! Te same możliwości procesora graficznego, które pomagają znaleźć wstępne obrazy skrótu z dużą liczbą zer wiodących dla bloków Bitcoin, są również doskonałe w znajdowaniu wstępnych obrazów skrótu z dużą liczbą zer wiodących dla inteligentnych kontraktów EVM. z0age z OpenSea (dzięki wyjaśnieniom do tego posta) znalazł prostą konfigurację do utworzenia własnego adresu próżności.

1. Użyj rozległego.ai‌, aby uruchomić przykładową instancję GPU, która próbuje około 2 miliardów razy na sekundę i kosztuje około 25 centów/godzinę:

Obraz: nvidia / opencl

Karta graficzna: 1x RTX 3090

Wymagane miejsce na dysku do przydzielenia: 1,83 GB

2. SSH i zainstaluj rust + create2crunch

sudo apt install build-essential -y; curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y; źródło "$HOME/.cargo/env"; git clone https://github.com/0age/create2crunch && cd create2crunch; sed -i 's/0x4/0x40/g' src/lib.rs

3. Uruchom wyszukiwanie nasion. W przypadku zmiennych środowiskowych INIT_CODE_HASH jest keccak256 kodu tworzenia kontraktu. Wydruk przykładowego testu odlewniczego znajdziesz tutaj – pamiętaj o jego weryfikacji, zanim zaczniesz zużywać duże ilości zasobów obliczeniowych! LEADING powinna być liczbą wiodących bajtów zerowych, a TOTAL powinna być całkowitą liczbą bajtów zerowych, jaką chcesz w adresie kontraktu.

eksport FABRYKA="0x0000000000ffe8b47b3e2130213b802212439497"; eksport CALLER="0x0000000000000000000000000000000000000000"; eksportuj INIT_CODE_HASH="0xabc...def"; eksportuj WIODĄCY=5; eksport RAZEM=7; przebieg ładunku --zwolnienie $FACTORY $CALLER $INIT_CODE_HASH 0 $LEADING $TOTAL

Kiedy z0age po raz pierwszy wypuściło swoje repozytorium, było ono w stanie wykonać 1,9 miliarda prób na sekundę na wspomnianym sprzęcie rozległej AI. Od tego czasu wektoryzacja oszalała na niektórych rdzeniach OpenGL i zaobserwowałem 2,15 miliarda prób na sekundę. Oznacza to, że znalezienie adresu z 5 wiodącymi bajtami zerowymi zajęłoby 256^5/(2150000000 * 60) ~= 8 minut, a znalezienie adresu z 6 wiodącymi bajtami zerowymi zajęłoby 256^6/(2150000000 * 3600) ~ = 36 godzin. Adres z 7 wiodącymi bajtami zerowymi zajmuje 256^7/(2150000000 * 86400) ~= 387 dni. Należy pamiętać, że jeden bajt jest równy dwóm znakom szesnastkowym, więc adres składający się z 5 bajtów wiodących będzie miał 10 zer. Oczywiście poszukiwania te można w pełni zrównoleglić, a rzeczywiste prawdopodobieństwo powodzenia w czasie będzie zgodne z rozkładem Poissona.

Wdróż fabrykę CREATE2

Wnikliwi czytelnicy mogli zauważyć, że fabryka CREATE2 istnieje już pod adresem 0x0000000000FFe8B47B3e2130213B802212439497 we wszystkich łańcuchach. To trochę problem z kurą i jajkiem. W jaki sposób spójne rozmieszczenie adresów zależy od spójnego rozmieszczenia adresów?

Kiedy po raz pierwszy dowiedziałem się o tym podejściu, pomyślałem, że to po prostu klucz prywatny trzymany przez kogoś mądrzejszego ode mnie (scenariusz „spostrzegawczej Alicji” powyżej). Ale w rzeczywistości jest to o wiele solidniejsze! Podejście założyciela ENS, Nicka Johnsona, oparte na transakcjach bez klucza, wykorzystuje fakt, że można odzyskać adres publiczny z dowolnego podpisu transakcji bez znajomości odpowiedniego klucza prywatnego, który go podpisał. Dlatego możliwe jest utworzenie transakcji („wdrożenie fabryki create2”), a następnie wymyślenie dla niej sfałszowanego podpisu, na przykład składającego się tylko z dwójek. Klucz prywatny do tego sfałszowanego podpisu istnieje, ale nikt nie wie, co to jest. Możemy jednak odzyskać adres publiczny odpowiadający „podpisowi bez klucza”, wysłać mu trochę ETH, a następnie przesłać podpisaną transakcję do pamięci. Pomimo niejasności tej metody, jest to transakcja ważna i właściwie jedyna ważna transakcja, jaką można wysłać z tego adresu publicznego.

W rezultacie każdy może wdrożyć fabrykę w nowym łańcuchu bez żadnych zastrzeżonych informacji, jednocześnie zapobiegając powodowaniu szkód przez złośliwe podmioty. Utworzenie EOA o jednym celu, który może wdrożyć tylko jedną transakcję, jest bardzo sprytną techniką.

Konkretne adresy i umowy utworzone podczas transakcji bezkluczykowych

Można to osiągnąć za pomocą trzech prostych poleceń „rzucania kuźni”. Kod bajtowy jest za długi, aby go tutaj skopiować, ale możesz postępować zgodnie z instrukcjami na stronie https://github.com/ProjectOpenSea/seaport/blob/main/docs/Deployment.md‌ bez pozwolenia w dowolnym wybranym łańcuchu. Z łatwością wdróż CREATE2 Factory!

Oczywiście, jeśli został już wdrożony, nie ma potrzeby jego ponownego wdrażania.

Uwaga dodatkowa: wymagania EIP-155 są okropne

Krótka próba wsparcia moich przygód związanych z zarządzaniem L1, możesz pominąć dalej. EIP-155‌ to propozycja wysunięta przez Vitalika w 2016 roku, która wprowadza koncepcję „identyfikatora łańcucha” w celu zapobiegania atakom poprzez powtarzanie. Każdy łańcuch ma swój własny unikalny identyfikator – 1 dla Ethereum, 56 dla BSC i 137 dla Polygon – który będzie uwzględniony w podpisanych transakcjach, aby zapobiec atakom polegającym na powtarzaniu, co zostało szybko przyjęte przez Ethereum, a wszystkie inne sieci EVM poszły za tą propozycją. To świetnie, ale problem pojawia się, gdy wybranych jest kilka sieci, takich jak Evmos, który niedawno zdecydował się wyraźnie zakazać transakcji przed EIP-155‌, co z dziwnych powodów zapobiega błędowi operacyjnemu, w którym Optimism wysłał 20 milionów tokenów OP do multisig (tak, znowu oni) adres, którego Wintermute nie istnieje i który twierdzi, że jest jego właścicielem, ale nigdy nie został zainicjowany. Jednak wyłączenie transakcji sprzed 155 znacznie przerwałoby cały zestaw wdrożeń międzyłańcuchowych, takich jak fabryka CREATE2 i wiodące projekty, takie jak Seaport. Te propozycje dotyczące zarządzania należy natychmiast wycofać, a takie szczegóły ochrony powinny pochodzić z portfela, a nie z warstwy konsensusu. Jeśli wielołańcuchowość jest przyszłością, te niepotrzebne ograniczenia będą ogromną przeszkodą dla najlepszych projektów we wdrażaniu na Twoim blockchainie.

Zabawne rzeczy: rozmieszczanie nagród

Obecnie https://delegate.cash jest wdrożony w 7 różnych sieciach EVM (Ethereum, Polygon, Optimism, Celo, Avalanche, Fantom i Arbitrum) oraz 7 sieciach testowych odpowiadających tym łańcuchom. Adres umowy dla wszystkich jest taki sam: 0x00000000000076A84feF008CDAbe6409d2FE638B.

Czy to wystarczy? Nie, potrzebujemy więcej łańcuchów. Ponieważ delegata jest niezależnym elementem pierwotnym z zerowymi zależnościami, oznacza to, że ryzyko wielu łańcuchów faktycznie wynosi zero. To czysta korzyść! Dlatego też dla pierwszych 5 osób, które wdrożą i zweryfikują inteligentną umowę delegatacash w nowej sieci i odpowiedniej sieci testowej, przyznam premię w wysokości 100 USDC!

Musisz tutaj użyć skryptu wdrażania z repozytorium open source. Może to wymagać wdrożenia fabryki CREATE2, jeśli nie istnieje, i nie zapomnij o weryfikacji Etherscan! Udanego wdrożenia i ciesz się nauką przez doświadczenie!