Jak w ogóle działa Ethereum? (Polish ver.)

READ STORY
Special thanks to Dawid Kurdziel for translating this post into Polish.

Wprowadzenie

Prawdopodobnie słyszałeś o blockchainie Ethereum, niezależnie od tego, czy wiesz co to jest, czy nie. Było to ostatnio w wiadomościach, w tym na okładkach niektórych poważnych czasopism, ale te artykuły mogą przypominać bełkot, jeśli nie ma się podstawowej wiedzy o Ethereum. A więc co to jest? Zasadniczo jest to publiczna baza danych, która prowadzi stały rejestr transakcji cyfrowych. Co ważne, ta baza danych nie wymaga żadnych centralnych władz do jej utrzymania i zabezpieczenia. Zamiast tego działa on jako system transakcyjny „bez zaufania” - ramy, w których osoby fizyczne mogą dokonywać transakcji typu „peer-to-peer” bez konieczności ufania stronie trzeciej LUB sobie nawzajem.

Nadal zdezorientowany? Tutaj przychodzi z pomocą ten tekst. Moim celem jest wyjaśnienie, jak Ethereum funkcjonuje na poziomie technicznym, bez skomplikowanych matematycznych i strasznie wyglądających formuł. Nawet jeśli nie jesteś programistą, mam nadzieję, że odejdziesz z przynajmniej lepszym zrozumieniem technologii. Jeśli niektóre części są zbyt techniczne i trudne do zgryzienia, to jest w porządku! Naprawdę nie ma potrzeby rozumienia każdego najdrobniejszego szczegółu. Polecam tylko skupić się na zrozumieniu rzeczy na ogólnym poziomie.

Wiele z tematów poruszanych w tym wpisie to rozbicie koncepcji omawianych w „Ethereum Yellow Paper”. Dodałem własne objaśnienia i diagramy, aby ułatwić zrozumienie Ethereum. Ci, którzy są na tyle odważni, by podjąć techniczne wyzwanie, mogą również przeczytać „Ethereum Yellow Paper”.

Zaczynajmy!

Definicja blockchaina

Blockchain to „kryptograficznie bezpieczna singletonowa maszyna transakcyjna ze stanem współdzielonym”. [1] ****To trochę dużo, prawda?

Rozbijmy to.

  • „Kryptograficznie bezpieczna” oznacza, że tworzenie waluty cyfrowej jest zabezpieczone przez złożone algorytmy matematyczne, które są niezwykle trudne do złamania. Pomyśl o czymś w rodzaju firewalla. Sprawiają one, że oszukiwanie systemu jest niemalże niemożliwe (np. tworzenie fałszywych transakcji, usuwanie transakcji itp.).
  • „Singletonowa maszyna transakcyjna” oznacza, że istnieje jedna kanoniczna instancja maszyny odpowiedzialnej za wszystkie transakcje tworzone w systemie. Innymi słowy, istnieje jedna globalna prawda, w którą wszyscy wierzą.
  • „Ze stanem współdzielonym” oznacza, że stan przechowywany na tej maszynie jest współdzielony i otwarty dla wszystkich.

Ethereum realizuje ten paradygmat blockchaina.

Objaśnienie paradygatu blockchaina Ethereum

Blockchain Ethereum jest zasadniczo opartą na transakcjach maszyną stanową. W informatyce maszyna stanowa odnosi się do czegoś, co odczyta serię danych wejściowych (ang. input) i na ich podstawie przejdzie do nowego stanu.

W maszynie stanu Ethereum zaczynamy od „stanu genezy”. Jest to równoważne z pustym kontem przed dokonaniem jakichkolwiek transakcji w sieci. Kiedy transakcje są wykonywane, stan ten zmienia się w stan końcowy. W każdym momencie ten stan końcowy reprezentuje obecny stan Ethereum.

Stan Ethereum ma miliony transakcji. Te transakcje są pogrupowane w „bloki”. Blok zawiera szereg transakcji, a każdy blok jest połączony z poprzednim blokiem.

Aby spowodować przejście z jednego stanu do drugiego, transakcja musi być ważna. Aby transakcja została uznana za ważną, musi przejść przez proces walidacji znany jako górnictwo (ang. mining). Górnictwo polega na tym, że grupa węzłów (ang. node) (tj. komputery) wykorzystuje swoje zasoby obliczeniowe do utworzenia bloku ważnych transakcji.

Każdy węzeł w sieci, który deklaruje się jako górnik, może podjąć próbę utworzenia i walidacji bloku. Wiele górników z całego świata próbuje tworzyć i weryfikować bloki w tym samym czasie. Każdy górnik dostarcza „dowód” matematyczny podczas zgłaszania bloku do łańcucha bloków, a dowód ten działa jako gwarancja: jeśli dowód istnieje, blok musi być ważny.

Aby blok mógł być dodany do głównego łańcucha bloków, górnik musi udowodnić to szybciej niż jakikolwiek inny konkurencyjny górnik. Proces walidacji każdego bloku przez górnika dostarczającego dowód matematyczny jest znany jako „proof of work (dowód pracy)”.

Za wykonanie tej pracy górnik, który zatwierdzi nowy blok, jest nagradzany pewną wartością. Co to za wartość? Blockchain Ethereum używa własnego, cyfrowego tokena zwanego „Ether”. Za każdym razem, gdy górnik potwierdza blok, nowe tokeny Ether są generowane i przyznawane.

Możesz się zastanawiać: co gwarantuje, że wszyscy trzymają się jednego łańcucha bloków? Skąd możemy mieć pewność, że nie ma podgrupy górników, którzy zdecydują się na stworzenie własnego łańcucha bloków?

Wcześniej zdefiniowaliśmy blockchain jako singletonową maszynę transakcyjną ze stanem współdzielonym. Używając tej definicji, możemy zrozumieć, że prawidłowy stan obecny jest jedną globalną prawdą, którą każdy musi zaakceptować. Posiadanie wielu stanów (lub łańcuchów) zrujnowałoby cały system, ponieważ niemożliwe byłoby uzgodnienie, który stan jest właściwy. Gdyby łańcuchy się rozeszły, mógłbyś mieć 10 monet na jednym łańcuchu, 20 na innym i 40 na innym.

W tym scenariuszu nie byłoby możliwości ustalenia, który łańcuch jest najbardziej „poprawny”. Za każdym razem, gdy generowane są różne ścieżki, pojawia się „rozwidlenie (ang. fork)”. Zazwyczaj chcemy unikać forków, ponieważ zakłócają one system i zmuszają ludzi do wybrania łańcucha, w który „wierzą”.

Aby określić, która ścieżka jest najbardziej poprawna i zapobiec wielu łańcuchom, Ethereum używa mechanizmu zwanego „Protokół GHOST”.

„GHOST” = „Greedy Heaviest Observed Subtree”

W prostych słowach, protokół GHOST mówi, że musimy wybrać ścieżkę, która miała najwięcej obliczeń wykonanych na niej. Jednym ze sposobów określenia tej ścieżki jest użycie numeru bloku z najnowszego bloku („leaf block” (blok liściowy)), który reprezentuje całkowitą liczbę bloków w bieżącej ścieżce (nie licząc bloku genezy). Im wyższy numer bloku, tym dłuższa droga i większy nakład pracy wydobywczej, którą należało włożyć w dotarcie do liścia. Użycie tego rozumowania pozwala nam na uzgodnienie kanonicznej wersji obecnego stanu.

Teraz, kiedy wiesz już ogólnie, czym jest łańcuch bloków, zajmijmy się głównymi elementami, z których składa się system Ethereum:

  • konta
  • stan
  • gaz i opłaty
  • transakcje
  • bloki
  • wykonanie transakcji
  • górnictwo (ang. mining)
  • dowód pracy (ang. proof of work)

Jedna uwaga przed rozpoczęciem: kiedy mówię „hasz” X, mam na myśli hasz KECCAK-256, którego używa Ethereum.

Konta

Globalny „stan współdzielony” Ethereum składa się z wielu małych obiektów („kont”), które są w stanie oddziaływać na siebie nawzajem za pomocą frameworka przekazującego informacje. Każde konto ma przypisany stan oraz 20-bajtowy adres. Adres w Ethereum to 160-bitowy identyfikator, który jest używany do identyfikacji każdego konta.

Istnieją dwa rodzaje kont:

  • Zewnętrzne konta, które są kontrolowane przez klucze prywatne i nie mają żadnego powiązanego z nimi kodu.
  • Konta kontraktów, które są kontrolowane przez swój kod kontraktu i mają powiązany z nimi kod.

Konta zewnętrzne vs. konta kontraktów

Ważne jest, aby zrozumieć zasadniczą różnicę między kontami zewnętrznymi a kontami kontraktów. Zewnętrzne konto może wysyłać wiadomości do innych kont zewnętrznych LUB do innych kont kontraktów poprzez utworzenie i podpisanie transakcji za pomocą swojego klucza prywatnego. Wiadomość pomiędzy dwoma kontami zewnętrznymi jest po prostu transferem wartości. Ale wiadomość z konta zewnętrznego do konta kontraktu aktywuje kod konta kontraktu, pozwalając mu na wykonanie różnych czynności (np. przeniesienie tokenów, zapis do wewnętrznej pamięci masowej, wygenerowanie nowych tokenów, wykonanie pewnych obliczeń, utworzenie nowych kontraktów itp.).

W przeciwieństwie do kont zewnętrznych, konta kontraktów nie mogą same inicjować nowych transakcji. Zamiast tego konta kontraktów mogą jedynie uruchamiać transakcje w odpowiedzi na inne transakcje, które otrzymały (z konta zewnętrznego lub z innego konta kontraktu). Więcej informacji na temat połączeń pomiędzy kontraktami poznamy w rozdziale „Transakcje i wiadomości”.

Dlatego każda akcja, która ma miejsce w blockchainie Ethereum, jest zawsze wprawiana w ruch przez transakcje uruchamiane z zewnętrznie kontrolowanych kont.

Stan konta

Stan konta składa się z czterech składników, które są obecne niezależnie od typu konta:

  • nonce: Jeśli konto jest kontem zewnętrznym, numer ten reprezentuje liczbę transakcji wysłanych z adresu konta.
  • Jeśli konto jest kontem kontraktu, to nonce jest liczbą kontraktów utworzonych przez konto.
  • balance: Ilość Wei posiadanych przez ten adres.
  • Na jeden Ether przypada 1e+18 Wei.
  • storageRoot: Hasz węzła korzeniowego (ang. root node) drzewa Merkle Patricia (objaśnimy drzewa Merkle'a później).
  • To drzewo koduje hasz zawartości pamięci masowej tego konta i jest domyślnie puste.
  • codeHash: Hasz EVM (Ethereum Virtual Machine - więcej o tym później) kod tego konta.
  • W przypadku kont kontraktów jest to kod, który zostaje zhaszowany i zapisany jako codeHash.
  • Dla kont zewnętrznych pole codeHash jest haszem pustego stringa.

Stan światowy

Dobrze, wiemy więc, że globalny stan Ethereum składa się z mapowania pomiędzy adresami kont i stanami kont. To mapowanie jest przechowywane w strukturze danych znanej jako drzewo Merkle Patricia.

Drzewo Merkle'a (ang. Merkle tree) (zwane również „Merkle trie”) jest rodzajem drzewa binarnego składającego się z zestawu węzłów z:

  • duża liczba węzłów liściowych (ang. leaf nodes) na dnie drzewa, które zawierają podstawowe dane
  • zestawem węzłów pośrednich, gdzie każdy węzeł jest haszem swoich dwóch węzłów potomnych
  • pojedynczy węzeł korzeniowy, również utworzony z haszu jego dwóch węzłów potomnych, reprezentujący szczyt drzewa

Dane na dole drzewa generowane są przez podzielenie na kawałki danych, które chcemy przechowywać. Następnie dzielimy kawałki na wiadra, a następnie bierzemy hasz z każdego wiadra i powtarzamy ten sam proces, do momentu aż łączna liczba pozostałych haszy wyniesie tylko jeden: root hash.

Drzewo to musi posiadać klucz dla każdej zapisanej w nim wartości. Zaczynając od węzła korzeniowego drzewa, klucz powinien wskazywać węzeł potomny, który należy śledzić, aby dotrzeć do odpowiedniej wartości, która jest przechowywana w węzłach liściowych. W przypadku Ethereum mapowanie klucza/wartości dla drzewa stanu odbywa się pomiędzy adresami i powiązanymi z nimi kontami, łącznie z saldem, nonce, codeHash i storageRoot dla każdego konta (gdzie storageRoot sam jest drzewem).

Source: Ethereum Whitepaper

Źródło: Dokumentacja Ethereum Ta sama struktura trie jest używana również do przechowywania transakcji i potwierdzeń. Dokładniej, każdy blok ma „nagłówek”, który przechowuje hasz węzła korzeniowego trzech różnych struktur Merkle trie, w tym:

  1. Trie stanu (ang. State trie)
  2. Trie transakcji (ang. Transactions trie)
  3. Trie potwierdzeń (ang. Receipts trie)

Zdolność do efektywnego przechowywania wszystkich tych informacji w drzewach Merkle'a jest niezwykle przydatna w Ethereum dla tego, co nazywamy „lekkimi klientami” lub „lekkimi węzłami”. Pamiętaj, że blockchain jest utrzymywany przez grupę węzłów. Ogólnie rzecz biorąc, istnieją dwa rodzaje węzłów: węzły pełne i węzły lekkie.

Węzeł pełnego archiwum synchronizuje blockchain poprzez pobranie pełnego łańcucha, od bloku genezy do bieżącego czołowego bloku, wykonując wszystkie transakcje zawarte wewnątrz. Zazwyczaj górnicy przechowują pełny węzeł archiwum, ponieważ są do tego zobowiązani w procesie wydobycia. Możliwe jest również pobranie pełnego węzła bez konieczności przeprowadzania każdej transakcji.

Niezależnie od tego, każdy pełny węzeł zawiera cały łańcuch. Ale dopóki węzeł nie musi wykonać każdej transakcji lub po prostu zapytać o dane historyczne, tak naprawdę nie ma potrzeby przechowywania całej sieci. Tu właśnie pojawia się koncepcja węzła lekkiego. Zamiast pobierania i przechowywania pełnego łańcucha i wykonywania wszystkich transakcji, węzły lekkie pobierają tylko łańcuch nagłówków, od bloku genezy do bieżącego nagłówka, bez wykonywania żadnych transakcji lub pobierania jakichkolwiek związanych z nimi stanów. Ponieważ węzły lekkie mają dostęp do nagłówków bloków, które zawierają hasze trzech drzew trie, mogą nadal łatwo generować i otrzymywać weryfikowalne odpowiedzi na temat transakcji, zdarzeń, sald itp.

Powodem tego jest fakt, że hasze w drzewie Merkle'a rozprzestrzeniają się w górę - jeśli złośliwy użytkownik spróbuje zamienić fałszywą transakcję na dno drzewa Merkle'a, zmiana ta spowoduje zmianę haszu węzła powyżej, co spowoduje zmianę haszu węzła powyżej i tak dalej, aż w końcu zmieni korzeń drzewa.

Każdy węzeł, który chce zweryfikować część danych, może użyć czegoś, co nazywa się "dowodem Merkle'a". Elementy dowodu Merkle'a:

  1. Kawałek danych do sprawdzenia i jego hasz
  2. Hasz korzenia drzewa
  3. „Gałąź” (wszystkie partnerskie hasze idące w górę ścieżką od kawałka do korzenia)

Każdy, kto czyta dowód, może sprawdzić, że haszowanie dla tej gałęzi jest spójne przez całą drogę w górę drzewa, a zatem, że dany kawałek jest rzeczywiście w tej lokalizacji w drzewie.

Podsumowując, zaletą korzystania z drzewa Merkle Patricia jest to, że węzeł korzeniowy tej struktury jest kryptograficznie zależny od danych przechowywanych w drzewie, a więc hasz węzła korzeniowego może być używany jako bezpieczna tożsamość dla tych danych.

Ponieważ nagłówek bloku zawiera hasz stanu, transakcje i potwierdzenia drzew, każdy węzeł może zatwierdzić niewielką część stanu Ethereum bez konieczności przechowywania całego stanu, który może być nieograniczonej wielkości.

Gaz i płatności

Jedną z bardzo ważnych koncepcji w Ethereum jest koncepcja opłat. Każde obliczenie, które następuje w wyniku transakcji w sieci Ethereum, wiąże się z opłatą - nie ma darmowego lunchu! Opłata ta jest płacona w nominale zwanym „gaz” (ang. gas). Gaz jest jednostką służącą do pomiaru opłat wymaganych dla danego obliczenia.

Cena gazu (ang. gas price) to ilość Ether, którą jesteś gotów wydać na każdą jednostkę gazu i jest mierzona w „gwei”. „Wei” jest najmniejszą jednostką Ether, gdzie 1⁰¹⁸ Wei reprezentuje 1 Ether. Jeden gwei to 1 000 000 000 Wei.

Z każdą transakcją nadawca ustawia limit gazu (ang. gas limit) i cenę gazu (ang. gas price). Iloczyn ceny gazu i limitu gazu stanowi maksymalną kwotę Wei, jaką nadawca jest gotów zapłacić za wykonanie transakcji.

Na przykład, powiedzmy że nadawca ustala limit gazu na 50 000, a cenę gazu na 20 gwei. Oznacza to, że nadawca jest skłonny wydać co najwyżej 50 000 x 20 gwei = 1 000 000 000 000 Wei = 0,001 Ether na wykonanie tej transakcji.

Należy pamiętać, że limit gazu stanowi maksymalną ilość gazu, na którą nadawca jest gotów wydać pieniądze. Jeśli ma wystarczająco dużo Etheru na swoim koncie, aby pokryć to maksimum, jest gotowy do wykonania transakcji. Nadawca otrzymuje zwrot za niewykorzystany gaz po zakończeniu transakcji, wymieniony po kursie pierwotnym.

W przypadku, gdy nadawca nie dostarczy gazu niezbędnego do realizacji transakcji, transakcja kończy się „wyczerpaniem gazu” i zostaje uznana za nieważną. W tym przypadku przetwarzanie transakcji zostaje przerwane, a wszelkie zmiany stanu, które nastąpiły, są odwracane, tak że wracamy do stanu Ethereum przed transakcją. Dodatkowo rejestrowana jest informacja o nieudanej transakcji, pokazująca jaka była próba i gdzie zakończyła się niepowodzeniem. A ponieważ maszyna wykonała już obliczenia przed wyczerpaniem się gazu, logicznie rzecz biorąc, żaden gaz nie jest zwracany do nadawcy.

Dokąd dokładnie idą te pieniądze z gazu? Wszystkie pieniądze wydane na gaz przez nadawcę są wysyłane na adres „beneficjenta”, który zazwyczaj jest adresem górnika. Ponieważ górnicy wkładają pracę w przeprowadzenie obliczeń i walidację transakcji, górnicy otrzymują w nagrodę opłatę za gaz.

Zazwyczaj im wyższa jest cena gazu, którą nadawca jest skłonny zapłacić, tym większą wartość górnik uzyskuje z transakcji. Tak więc, tym bardziej prawdopodobne, że górnicy będą go wybierać. W ten sposób górnicy mają swobodę wyboru, które transakcje chcą zatwierdzić lub zignorować. Aby pomóc nadawcom w ustaleniu ceny gazu, górnicy mają możliwość ogłoszenia minimalnej ceny gazu, za którą będą dokonywać transakcji.

Istnieją również opłaty za przechowywanie

Nie tylko gaz jest używany do płacenia za kroki obliczeniowe, ale również do płacenia za korzystanie z magazynu.Całkowita opłata za magazynowanie jest proporcjonalna do najmniejszej wielokrotności wykorzystanych 32 bajtów.

Opłaty za magazynowanie mają pewne zniuansowane aspekty. Na przykład, ponieważ zwiększona pamięć zwiększa rozmiar bazy danych stanu Ethereum na wszystkich węzłach, istnieje zachęta do przechowywania niewielkich ilości przechowywanych danych. Z tego powodu, jeżeli transakcja posiada krok, który usuwa wpis z pamięci, opłata za wykonanie tej operacji zostaje zniesiona i przyznawany jest zwrot za zwolnienie pamięci masowej.

Jaki jest cel opłat?

Jednym z ważnych aspektów sposobu działania Ethereum jest to, że każda pojedyncza operacja wykonywana przez sieć jest jednocześnie wykonywana przez każdy pełny węzeł. Jednak kroki obliczeniowe na maszynie wirtualnej Ethereum są bardzo kosztowne. Dlatego też inteligentne kontrakty Ethereum są najlepiej wykorzystywane do prostych zadań, takich jak prowadzenie prostej logiki biznesowej lub weryfikowanie podpisów i innych obiektów kryptograficznych, a nie do bardziej złożonych zastosowań, takich jak przechowywanie plików, e-mail czy uczenie maszynowe, które mogą obciążać sieć. Nałożenie opłat zapobiega nadmiernemu obciążeniu sieci.

Ethereum jest językiem kompletnym Turinga. (Krótko mówiąc, maszyna Turinga to maszyna, która może symulować dowolny algorytm komputerowy (ci, którzy nie znają maszyn Turinga, mogą sprawdzić to i to). Pozwala to na tworzenie pętli i sprawia, że Ethereum jest podatne na problem stopu, problem w którym nie można określić, czy program będzie działał w nieskończoność. Gdyby nie było opłat, złośliwy podmiot mógłby z łatwością spróbować zakłócić sieć poprzez wykonanie nieskończonej pętli w ramach transakcji, bez żadnych konsekwencji. Tak więc opłaty chronią sieć przed celowymi atakami.

Być może zastanawiasz się, „dlaczego my też musimy płacić za magazynowanie?” Cóż, podobnie jak obliczenia, przechowywanie danych w sieci Ethereum jest kosztem, który musi ponieść cała sieć.

Transakcja i komunikaty

Zauważyliśmy wcześniej, że Ethereum jest opartą o transakcje maszyną stanową. Innymi słowy, transakcje zachodzące pomiędzy różnymi kontami są tym, co przenosi globalny stan Ethereum z jednego stanu do drugiego.

W najbardziej podstawowym znaczeniu, transakcja jest kryptograficznie podpisaną instrukcją, która jest generowana przez zewnętrzne konto, szeregowana, a następnie przekazywana do łańcucha bloków.

Istnieją dwa rodzaje transakcji: wywołania komunikatu i tworzenia kontraktów (tzn. transakcje, które tworzą nowe kontrakty Ethereum).

Wszystkie transakcje zawierają następujące komponenty, niezależnie od ich rodzaju:

  • nonce: liczba transakcji wysłanych przez nadawcę.
  • gasPrice: liczba Wei, którą nadawca jest skłonny zapłacić za jednostkę gazu potrzebną do realizacji transakcji.
  • gasLimit: maksymalna ilość gazu, którą nadawca jest skłonny zapłacić za wykonanie tej transakcji.
  • Kwota ta jest ustalana i wypłacana z góry, przed dokonaniem jakichkolwiek obliczeń.
  • to: adres odbiorcy.
  • W transakcji tworzącej kontrakt, adres konta kontraktu jeszcze nie istnieje, a więc używana jest pusta wartość.
  • value: ilość Wei, która ma być przekazana od nadawcy do odbiorcy.
  • W transakcji tworzącej kontrakt wartość ta służy jako saldo początkowe w ramach nowo utworzonego konta kontraktu.
  • v, r, s: używane do wygenerowania sygnatury, która identyfikuje nadawcę transakcji.
  • init (istnieje tylko dla transakcji tworzących kontrakty): Fragment kodu EVM, który jest używany do inicjowania nowego konta kontraktu.
  • init jest uruchamiany tylko raz, a potem jest odrzucany.
  • Kiedy init jest uruchamiany po raz pierwszy, zwraca ciało kodu konta, które jest fragmentem kodu trwale związanym z kontem kontraktu.
  • data (opcjonalne pole, które istnieje tylko dla wywołań wiadomości): dane wejściowe (czyli parametry) wywołania wiadomości.
  • Na przykład, jeśli inteligentny kontrakt służy jako usługa rejestracji domeny, połączenie z tym kontraktem może wymagać wprowadzenia takich pól wejściowych jak domena i adres IP.

W rozdziale „Konta” dowiedzieliśmy się, że transakcje - zarówno wywołania wiadomości, jak i transakcje tworzące kontrakty - są zawsze inicjowane przez zewnętrzne konta i przekazywane do blockchainu. Innym sposobem myślenia o tym jest to, że transakcje są tym, co łączy świat zewnętrzny z wewnętrznym stanem Ethereum.

Ale to nie znaczy, że kontrakty nie mogą rozmawiać z innymi kontraktami. Kontrakty, które istnieją w globalnym zasięgu stanu Ethereum, mogą rozmawiać z innymi kontraktami w tym samym zasięgu. Komunikują się one z innymi kontraktami poprzez „wiadomości” lub „transakcje wewnętrzne”. Możemy myśleć o wiadomościach lub transakcjach wewnętrznych jako podobnych do transakcji, z tą główną różnicą, że NIE są one generowane przez zewnętrzne konta. Zamiast tego są one generowane przez kontrakty. Są to obiekty wirtualne, które, w przeciwieństwie do transakcji, nie są szeregowane i istnieją tylko w środowisku wykonania Ethereum.

Gdy jeden kontrakt wysyła wewnętrzną transakcję do innego kontraktu, jest wykonywany przypisany kod, który istnieje na koncie kontraktu odbiorcy.

Jedną z ważnych rzeczy do odnotowania jest to, że wewnętrzne transakcje lub wiadomości nie zawierają gasLimit. Jest to spowodowane tym, że limit gazu jest określany przez zewnętrznego twórcę oryginalnej transakcji (tj. niektóre z zewnętrznych kont). Limit gazu ustalony przez konto zewnętrzne musi być na tyle wysoki, aby można było przeprowadzić transakcję wraz ze wszystkimi podwykonaniami, które nastąpią w wyniku tej transakcji, takimi jak wiadomości przesyłane między kontraktem a kontraktem. Jeżeli w łańcuchu transakcji i wiadomości zabraknie gazu do wykonania danej wiadomości, wówczas wykonanie tej wiadomości zostanie cofnięte wraz z kolejnymi wiadomościami uruchamianymi w wyniku jego wykonania. Jednakże wykonania wyższego poziomu nie trzeba odwracać.

Bloki

Wszystkie transakcje są pogrupowane w „bloki”. Łańcuch bloków zawiera serię takich bloków, które są ze sobą połączone jak ogniwa łańcucha.

W Ethereum blok składa się z:

  • nagłówka bloku (ang. block header)
  • informacji o zestawie transakcji zawartych w tym bloku
  • zestawu innych nagłówków bloku dla wujków (ang. uncle/ommer) bieżącego bloku.

Wyjaśnienie pojęcia wujków

Czym, u diabła, jest „wujek”? Wujek (ang. uncle/ommer) jest blokiem, którego rodzic jest równy rodzicowi bieżącego bloku. Zobaczmy szybko, do czego służą wujkowie i dlaczego dany blok zawiera nagłówki bloków dla wujków.

Ze względu na sposób, w jaki Ethereum jest zbudowane, czasy bloków są znacznie niższe (~15 sekund) niż w przypadku innych blockchainów, takich jak Bitcoin (~10 minut). Umożliwia to szybsze przetwarzanie transakcji. Jedną z wad krótszych czasów tworzenia bloków jest jednak to, że górnicy znajdują bardziej konkurencyjne rozwiązania bloków. Te konkurencyjne bloki są również nazywane „blokami osieroconymi” (tzn. wydobyte bloki nie trafiają do głównego łańcucha).

Przeznaczeniem wujków jest pomaganie w nagradzaniu górników za włączenie tych osieroconych bloków. Wujkowie uwzględniani przez górników muszą być „ważni”, co oznacza że należą do szóstego lub młodszego pokolenia z obecnego bloku. Po szóstce dzieci nie można już odwoływać się do osieroconych bloków (ponieważ włączenie starszych transakcji trochę skomplikowałoby sprawę).

Bloki wujków otrzymują mniejszą nagrodę niż pełny blok. Niemniej jednak nadal istnieje pewna zachęta dla górników, aby uwzględnić te osierocone bloki i odebrać nagrodę.

Nagłówek bloku

Wróćmy na chwilę do bloków. Wspomnieliśmy wcześniej, że każdy blok ma „nagłówek”, ale co to dokładnie jest?

Nagłówek bloku jest częścią bloku składającą się z:

  • parentHash: haszu nagłówka bloku rodzica (to jest to, co sprawia, że blok ustawia „łańcuch”)
  • ommersHash: haszu aktualnej listy wujków bloku
  • beneficiary: adresu konta, które otrzymuje wynagrodzenie za wydobycie tego bloku
  • stateRoot: haszu węzła korzeniowego trie stanu (przypomnij sobie, jak uczyliśmy się, że drzewo stanu jest przechowywane w nagłówku i ułatwia lekkim klientom weryfikowanie wszystkiego o stanie)
  • transactionsRoot: haszu węzła korzeniowego drzewa trie, które zawiera wszystkie transakcje wyszczególnione w tym bloku
  • receiptsRoot: haszu węzła korzeniowego drzewa trie, które zawiera potwierdzenia wszystkich transakcji wyszczególnionych w tym bloku
  • logsBloom: Filtru Blooma (struktura danych) składającego się z informacji o rejestrach zdarzeń
  • difficulty: poziomu trudności tego bloku
  • numer: liczby bieżącego bloku (blok genezy ma numer bloku równy zero; numer bloku zwiększa się o 1 dla każdego kolejnego bloku)
  • gasLimit: aktualnego limitu gazu na blok
  • gasUsed: sumy łącznej ilości gazu zużytego przez transakcje w tym bloku
  • timestamp: uniksowego znacznika czasu utworzenia tego bloku
  • extraData: dodatkowych danych związanych z tym blokiem
  • mixHash: haszu, który w połączeniu z nonce dowodzi, że ten blok przeprowadził wystarczająco dużo obliczeń
  • nonce: haszu, który w połączeniu z mixHashem dowodzi, że ten blok przeprowadził wystarczająco dużo obliczeń

Zauważ, że każdy nagłówek bloku zawiera trzy struktury drzewa trie dla:

  • stanu (stateRoot)
  • transakcji (transactionsRoot)
  • potwierdzeń (receiptsRoot)

Te struktury trie to nic innego, jak drzewa Merkle Patricia, o których mówiliśmy wcześniej.

Dodatkowo w powyższym opisie istnieje kilka terminów wartych wyjaśnienia.

Przyjrzyjmy się im.

Rejestry zdarzeń (ang. logs)

Ethereum pozwala rejestrom na umożliwienie śledzenia różnych transakcji i komunikatów.

Kontrakt może jawnie wygenerować rejestr poprzez zdefiniowanie „zdarzeń”, które chce rejestrować.

Wpis w rejestrze zawiera:

  • adres konta rejestratora,
  • szereg tematów, które reprezentują różne zdarzenia zrealizowane w ramach tej transakcji oraz
  • wszelkie dane związane z tymi zdarzeniami.

Rejestry są przechowywane w filtrze Blooma, który efektywnie przechowuje niekończące się dane rejestru.

Potwierdzenie transakcji

Rejestry przechowywane w nagłówku pochodzą z informacji rejestru zawartej w potwierdzeniu transakcji. Tak jak otrzymujesz paragon przy zakupie czegoś w sklepie, tak Ethereum generuje potwierdzenie każdej transakcji. Jak możesz się spodziewać, każde potwierdzenie zawiera pewne informacje o transakcji. To potwierdzenie zawiera takie elementy jak:

  • numer bloku
  • hasz bloku
  • hasz transakcji
  • gaz wykorzystany przez bieżącą transakcję
  • skumulowany gaz wykorzystany w bieżącym bloku po wykonaniu bieżącej transakcji
  • rejestry tworzone podczas realizacji bieżącej transakcji
  • ...i tak dalej

Trudność bloku

„Trudność” bloku jest wykorzystywana do wymuszenia stałej ilości czasu potrzebnego do walidacji bloków. Blok genezy ma trudność 131 072 a specjalny wzór jest używany do obliczania trudności każdego kolejnego bloku. Jeśli dany blok jest walidowany szybciej niż poprzedni, protokół Ethereum zwiększa trudności tego bloku.

Trudność bloku wpływa na nonce - hasz, który musi być obliczony podczas wydobywania bloku przy użyciu algorytmu proof-of-work.

Związek pomiędzy trudnością bloku i nonce jest sformalizowany matematycznie jako:

gdzie Hd jest trudnością.

Jedynym sposobem na znalezienie nonce'a, który spełnia próg trudności jest użycie algorytmu proof-of-work do wyliczenia wszystkich możliwości. Oczekiwany czas na znalezienie rozwiązania jest proporcjonalny do trudności - im większa trudność, tym trudniej jest znaleźć nonce, a więc tym trudniej jest dokonać walidacji bloku, co z kolei zwiększa czas potrzebny na walidację nowego bloku. Dostosowując trudność bloku, protokół może więc dostosować czas trwania walidacji bloku.

Z drugiej strony, jeśli czas walidacji jest coraz wolniejszy, protokół zmniejsza trudność. W ten sposób czas walidacji dostosowuje się samoczynnie, aby utrzymać stałe tempo - średnio jeden blok co 15 sekund.

Wykonanie transakcji

Dotarliśmy do jednej z najbardziej złożonych części protokołu Ethereum: realizacji transakcji. Powiedzmy, że wysyłasz transakcję do przetworzenia w sieci Ethereum. Co się dzieje żeby zmienić stan Ethereum aby uwzględnić twoją transakcję?

Po pierwsze, wszystkie transakcje muszą spełniać podstawowy zestaw wymogów, aby mogły zostać wykonane.

Należą do niego następujące wymogi:

  • Transakcja musi być poprawnie sformatowana RLP. „RLP” oznacza „Recursive Length Prefix” i jest formatem danych stosowanym do kodowania zagnieżdżonych tablic danych binarnych.RLP jest formatem używanym przez Ethereum do serializacji obiektów.
  • Ważna sygnatura transakcji.
  • Ważny nonce transakcji. Przypomnijmy, że nonce konta to liczba transakcji wysłanych z tego konta.
  • Aby był ważny, nonce transakcji musi być równy nonce konta nadawcy. Limit gazu w transakcji musi być równy lub większy od własnego gazu wykorzystanego przez transakcję. Integralny gaz zawiera:
  1. predefiniowany koszt 21 000 gas za realizację transakcji
  2. opłatę gazową za dane przesyłane wraz z transakcją (4 gas za każdy bajt danych lub kodu równy zero oraz 68 gas za każdy niezerowy bajt danych lub kodu)
  3. jeśli transakcja jest transakcją kontraktotwórczą, dodatkowe 32 000 gas
  • Saldo konta nadawcy musi mieć wystarczającą ilość Ether, aby pokryć „z góry” koszty gazu, które nadawca musi zapłacić. Obliczenie początkowego kosztu gazu jest proste: Po pierwsze, limit gazu transakcji jest mnożony przez cenę gazu transakcji w celu określenia maksymalnego kosztu gazu. Następnie ten maksymalny koszt jest dodawany do całkowitej wartości przekazywanej od nadawcy do odbiorcy.

Jeśli transakcja spełnia wszystkie powyższe wymagania dotyczące ważności, przechodzimy do kolejnego kroku.

Po pierwsze, dedykujemy wstępny koszt wykonania od salda nadawcy i zwiększamy nonce konta nadawcy o 1 do rozliczenia bieżącej transakcji. W tym momencie możemy obliczyć pozostały gaz jako całkowity limit gazu dla transakcji minus użyty gaz integralny.

Następnie rozpoczyna się realizacja transakcji. Podczas realizacji transakcji Ethereum śledzi „podstan”. Ten podstan jest sposobem zapisu informacji zgromadzonych podczas transakcji, które będą potrzebne natychmiast po jej zakończeniu.

Konkretnie, zawiera on:

  • Self-destruct set: zestaw kont (jeśli istnieją), które zostaną odrzucone po zakończeniu transakcji.
  • Log series: zarchiwizowane i indeksowane punkty kontrolne wykonania kodu maszyny wirtualnej.
  • Refund balance: kwota do zwrotu na konto nadawcy po dokonaniu transakcji. Pamiętasz, jak wspomnieliśmy, że magazynowanie danych w Ethereum kosztuje i że nadawca otrzymuje zwrot pieniędzy za usuwanie zmagazynowanych danych? Ethereum śledzi to za pomocą licznika zwrotów. Licznik zwrotów zaczyna się od zera i zwiększa się za każdym razem, gdy kontrakt skasuje coś z pamięci masowej.

Następnie przetwarzane są różne obliczenia wymagane przez transakcję.

Po przetworzeniu wszystkich kroków wymaganych przez transakcję i przy założeniu braku stanu nieważnego, stan ten jest finalizowany poprzez określenie kwoty niewykorzystanego gazu do zwrotu nadawcy. Oprócz niewykorzystanego gazu, nadawca otrzymuje również zwrot pewnej kwoty z „salda refundacji (ang. refund balance)”, które opisaliśmy powyżej.

Po zwrocie pieniędzy nadawcy:

  • Ether za gaz jest przekazywany do górnika
  • gaz zużyty przez transakcję jest dodawany do licznika gazu w bloku (który śledzi łączną ilość gazu zużytego przez wszystkie transakcje w bloku i jest przydatny podczas walidacji bloku);
  • wszystkie konta w zestawie do samodestrukcji (ang. self-destruct set) (jeśli istnieją) są usuwane.

Ostatecznie pozostaje nam nowy stan i zestaw rejestrów utworzonych przez transakcję.

Teraz, gdy omówiliśmy już podstawy realizacji transakcji, przyjrzyjmy się niektórym różnicom pomiędzy transakcjami tworzącymi kontrakty a wywołaniami komunikatów.

Tworzenie kontraktów

Przypomnijmy, że w Ethereum istnieją dwa rodzaje kont: konta kontraktów i konta zewnętrzne. Kiedy mówimy, że transakcja jest „tworząca kontrakt”, mamy na myśli to, że celem transakcji jest utworzenie nowego konta kontraktu.

Aby utworzyć nowe konto kontraktu, najpierw deklarujemy adres nowego konta za pomocą specjalnej formuły. Następnie inicjujemy nowe konto przez:

  • Ustawienie nonce na zero;
  • Jeśli nadawca wysłał pewną ilość Ether jako wartość wraz z transakcją, saldo konta jest ustawiane na tę wartość;
  • Odjęcie wartości dodanej do salda tego nowego konta od salda nadawcy;
  • Ustawianie pamięci masowej na pustą;
  • Ustawienie codeHash kontraktu jako haszu z pustym ciągiem znaków (ang. string).

Po zainicjowaniu konta możemy faktycznie utworzyć konto, używając init code wysłanego wraz z transakcją (patrz rozdział „Transakcje i wiadomości” w celu odświeżania informacji na temat kodu init). To, co dzieje się podczas wykonywania tego kodu init, jest zróżnicowane. W zależności od konstruktora kontraktu, może on aktualizować pamięć masową konta, tworzyć inne konta kontraktu, wykonywać inne wywołania komunikatów itp.

Gdy kod do inicjowania kontraktu jest wykonywany, używany jest gaz. Transakcja nie może zużywać więcej gazu niż pozostały gaz. Jeśli tak się stanie, wykonanie trafi na wyjątek out-of-gas (OOG) i zakończy się. Jeżeli transakcja zakończy się z powodu wyjątku związanego z brakiem gazu, wówczas stan zostaje cofnięty do punktu bezpośrednio poprzedzającego transakcję. Nadawcy nie jest zwracany gaz, który został wydany przed wyczerpaniem.

Łeeeeeee ;(.

Jeśli jednak nadawca wysłał jakąkolwiek wartość Ether wraz z transakcją, to wartość Ether zostanie zwrócona, nawet jeśli zawarcie kontraktu nie powiedzie się.

Uff!

Jeśli kod inicjalizacyjny zostanie wykonany pomyślnie, płacony jest końcowy koszt utworzenia kontraktu. Jest to koszt magazynowania i jest proporcjonalny do wielkości utworzonego kodu kontraktu (ponownie, nie ma darmowego lunchu!) Jeśli nie ma wystarczającej ilości gazu pozostałego do zapłacenia tego ostatecznego kosztu, wtedy transakcja ponownie deklaruje wyjątek out-of-gas i zostaje przerwana.

Jeśli wszystko pójdzie dobrze i dotrzemy tak daleko bez żadnych wyjątków, to pozostały niewykorzystany gaz zostanie zwrócony pierwotnemu nadawcy transakcji, a zmieniony stan może się teraz utrzymać!

Hurra!

Wywołania komunikatów

Wykonanie wywołania komunikatu jest podobne do utworzenia kontraktu, z kilkoma różnicami.

Wykonanie wywołania komunikatu nie zawiera żadnego kodu init, ponieważ nie są tworzone żadne nowe konta. Może ona jednak zawierać dane wejściowe, jeżeli dane te zostały dostarczone przez nadawcę transakcji. Raz wykonane wywołania komunikatów posiadają również dodatkowy komponent zawierający dane wyjściowe, który jest używany, jeśli kolejne wykonanie wymaga tych danych.

Podobnie jak w przypadku tworzenia kontraktów, jeśli wykonanie wywołania komunikatu zakończy się z powodu braku gazu lub z powodu nieprawidłowej transakcji (np. przepełnienie stosu (ang. stack overflow), nieprawidłowe miejsce docelowe skoku lub nieprawidłowa instrukcja), nic z użytego gazu nie jest zwracane pierwotnemu wywołującemu. Zamiast tego cały pozostały niewykorzystany gaz jest zużywany, a stan jest przywracany do punktu bezpośrednio poprzedzającego transfer salda.

Aż do ostatniej aktualizacji Ethereum nie było możliwości wstrzymania lub odwrócenia realizacji transakcji bez tego, aby system zużywał cały dostarczony gaz. Na przykład, powiedzmy, że jesteś autorem kontraktu, który wyrzucił błąd, gdy wywołujący nie był upoważniony do wykonania jakiejś transakcji. W poprzednich wersjach Ethereum pozostały gaz byłby nadal zużywany, a nadawcy nie zwracano by żadnego gazu. Ale aktualizacja Bizancjum zawiera nowy kod „revert”, który umożliwia wstrzymanie realizacji kontraktu i przywrócenie zmian stanu, bez konieczności zużycia pozostałego gazu, z możliwością zwrotu przyczyny nieudanej transakcji.Jeżeli transakcja zakończy się z powodu revert, niewykorzystany gaz jest zwracany do nadawcy.

Model wykonania

Jak dotąd, nauczyliśmy się o serii kroków, które muszą się zdarzyć, aby transakcja została wykonana od początku do końca. Teraz przyjrzymy się jak transakcja jest faktycznie przeprowadzana w ramach VM.

Część protokołu, która faktycznie obsługuje przetwarzanie transakcji jest własną maszyną wirtualną Ethereum, znaną jako Ethereum Virtual Machine (EVM).

EVM jest kompletną maszyną wirtualną Turinga, zgodnie z wcześniejszą definicją. Jedynym ograniczeniem jakie ma EVM, a którego nie ma typowa kompletna maszyna Turinga, jest to, że EVM jest wewnętrznie związana gazem. Tak więc całkowita ilość obliczeń, które można wykonać, jest z natury rzeczy ograniczona przez ilość dostarczanego gazu.

Źródło: CMU Co więcej, EVM ma architekturę opartą na stosie. Maszyna stosowa to komputer, który używa stosu last-in, first-out do przechowywania tymczasowych wartości.

Rozmiar każdego elementu stosu w EVM wynosi 256 bitów, a maksymalny rozmiar stosu to 1024.

EVM posiada pamięć, w której elementy są przechowywane jako tablice bajtowe z adresami słownymi. Pamięć jest ulotna, co oznacza, że nie jest stała.

EVM posiada również pamięć masową. W przeciwieństwie do pamięci, pamięć masowa jest nieulotna i jest utrzymywana jako część stanu systemu. EVM przechowuje kod programu oddzielnie, w pamięci wirtualnej ROM, do której dostęp można uzyskać tylko za pomocą specjalnych instrukcji. W ten sposób EVM różni się od typowej architektury von Neumanna, w której kod programu jest przechowywany w pamięci podręcznej lub w pamięci masowej.

EVM ma również swój własny język: „EVM bytecode”. Kiedy programista taki jak ty czy ja pisze inteligentne kontrakty, które działają na Ethereum, zazwyczaj piszemy kod w języku wyższego poziomu, takim jak Solidity. Następnie możemy skompilować to do EVM bytecode, który EVM może zrozumieć.

OK, teraz do wykonania.

Przed wykonaniem konkretnego obliczenia procesor upewnia się, że następujące informacje są dostępne i ważne:

  • Stan systemu;
  • Pozostały gaz do obliczeń;
  • Adres konta będącego właścicielem kodu, który jest wykonywany;
  • Adres nadawcy transakcji, która zainicjowała to wykonanie;
  • Adres konta, który spowodował wykonanie kodu (może być inny niż pierwotny nadawca);
  • Cena gazu transakcji, która zainicjowała to wykonanie;
  • Dane wejściowe dla tego wykonania;
  • Wartość (w Wei) przekazana na to konto w ramach bieżącego wykonania;
  • Kod maszynowy do wykonania;
  • Nagłówek bieżącego bloku;
  • Głębokość obecnego wywołania komunikatu lub stosu tworzenia kontraktu.

Na początku wykonywania pamięć i stos są puste, a licznik programu wynosi zero.

PC: 0 STACK: [] MEM: [], STORAGE: {}


Następnie EVM wykonuje transakcję rekursywnie, obliczając stan systemu i stan maszyny dla każdej pętli. Stan systemu jest po prostu globalnym stanem Ethereum. Stan maszyny składa się z:

  • dostępnego gazu,
  • licznika programu,
  • zawartości pamięci,
  • aktywnej liczby słów w pamięci,
  • zawartości stosu.

Elementy stosu są dodawane lub usuwane z najbardziej lewej części serii.

W każdym cyklu odpowiednia ilość gazu jest redukowana z pozostałego gazu, a licznik programu zwiększa się.

Na końcu każdej pętli istnieją trzy możliwości:

  1. Maszyna osiąga stan wyjątkowy (np. niewystarczająca ilość gazu, nieprawidłowe instrukcje, niewystarczające elementy stosu, elementy stosu mogłyby ulec przepełnieniu powyżej 1024, nieprawidłowe miejsce docelowe JUMP/JUMPI itp.) i dlatego musi zostać zatrzymana, a wszelkie zmiany muszą zostać odrzucone;
  2. Sekwencja kontynuuje przetwarzanie do następnej pętli;
  3. Maszyna osiąga kontrolowane zatrzymanie (zakończenie procesu wykonania).

Zakładając, że wykonanie nie osiągnie stanu wyjątkowego i osiągnie „kontrolowane” lub normalne zatrzymanie, maszyna generuje stan wynikowy, gaz pozostały po wykonaniu, uzyskany podstan i wynik końcowy.

Uff. Przebrnęliśmy przez jedną z najbardziej skomplikowanych części Ethereum. Nawet jeśli nie do końca rozumiesz tę część, to jest w porządku. Nie musisz w pełni rozumieć drobiazgowych szczegółów wykonania, chyba że pracujesz na bardzo zaawansowanym poziomie.

Jak blok zostaje sfinalizowany

Na koniec przyjrzyjmy się, jak zostaje sfinalizowany blok wielu transakcji.

Kiedy mówimy „sfinalizowany”, może to oznaczać dwie różne rzeczy, w zależności od tego, czy blok jest nowy czy istniejący. Jeśli jest to nowy blok, odnosimy się do procesu wymaganego do jego wydobycia. Jeśli jest to istniejący blok, to mówimy o procesie walidacji bloku. W obu przypadkach, istnieją cztery wymagania, aby blok został „sfinalizowany”:

1) Walidacja (lub, w przypadku wydobywania, określenie) wujków

Każdy wujeczny blok w nagłówku bloku musi być ważnym nagłówkiem i znajdować się w szóstej generacji obecnego bloku.

2) Walidacja (lub, w przypadku wydobywania, określenie) transakcji

Ilość gasUsed w bloku musi być równa łącznej ilości gazu zużytego w transakcjach wymienionych w bloku. (Przypomnijmy, że dokonując transakcji śledzimy licznik gazu w bloku, który śledzi łączną ilość gazu zużytego przez wszystkie transakcje w bloku).

3) Zastosuj nagrody (tylko w przypadku wydobywania)

Adres beneficjenta otrzymuje 5 Ether za wydobycie bloku. (Zgodnie z propozycją Ethereum EIP-649, ta nagroda w wysokości 5 ETH zostanie wkrótce zredukowana do 3 ETH). Dodatkowo za każdego wujka beneficjent aktualnego bloku otrzymuje dodatkową 1/32 aktualnej nagrody bloku. Wreszcie, beneficjent bloku (bloków) otrzymuje również pewną kwotę (istnieje specjalny wzór na to, jak ją obliczyć).

4) Weryfikacja (lub w przypadku wydobywania obliczenie ważnego) stanu i nonce Upewnij się, że wszystkie transakcje i wynikające z nich zmiany stanu zostały zastosowane, a następnie zdefiniuj nowy blok jako stan po zastosowaniu nagrody za blok do końcowego stanu wynikowego transakcji. Weryfikacja odbywa się poprzez sprawdzenie tego stanu końcowego względem próby trie stanu zapisanego w nagłówku.

Wydobywanie algorytmu Proof of Work

W rozdziale „Bloki” pokrótce omówiono pojęcie trudności bloku. Algorytm, który nadaje znaczenie trudności bloku, nazywany jest Proof of Work (PoW).

Algorytm dowodu pracy (ang. proof-of-work) Ethereum nazywa się „Ethash” (wcześniej znany jako Dagger-Hashimoto).

Algorytm jest formalnie zdefiniowany jako:

gdzie m to mixHas, n to nonce, Hn to nagłówek nowego bloku (z wyjątkiem nonce i mixHash komponentów, które muszą być obliczone), Hn to nonce nagłówka bloku, a d to DAG, który jest dużym zbiorem danych. W rozdziale „Bloki” rozmawialiśmy o różnych elementach, które istnieją w nagłówku bloku.

Dwa z tych komponentów zostały nazwane **mixHash i nonce. Jak zapewne pamiętasz:

  • mixHash to ****hasz, który w połączeniu z nonce dowodzi, że ten blok przeprowadził wystarczające obliczenia;
  • nonce jest haszem, który w połączeniu z mixHashem dowodzi, że ten blok przeprowadził wystarczające obliczenia.

Funkcja PoW jest używana do oszacowania tych dwóch elementów.

To, jak dokładnie mixHash i nonce są obliczane za pomocą funkcji PoW, jest nieco skomplikowane i możemy to zgłębić w osobnym wpisie. Ale na wysokim poziomie działa to w ten sposób: Dla każdego bloku oblicza się „nasienie” (ang. seed).

To nasienie jest inne dla każdej „epoki”, gdzie każda epoka trwa przez 30 000 bloków. Dla pierwszej epoki nasienie jest haszem z serii 32 bajtów zer. Dla każdej kolejnej epoki jest to hasz haszu poprzedniego nasienia. Używając tego nasienia, węzeł może obliczyć pseudolosowy „cache”.

Ten cache jest niewiarygodnie przydatny, ponieważ umożliwia koncepcję „lekkich węzłów”, o których mówiliśmy wcześniej w tym dokumencie. Celem lekkich węzłów jest zapewnienie niektórym węzłom możliwości wydajnej weryfikacji transakcji bez obciążenia związanego z przechowywaniem całego zbioru danych blockchain. Lekki węzeł może zweryfikować ważność transakcji w oparciu wyłącznie o ten cache, ponieważ cache może regenerować konkretny blok, który musi zweryfikować.

Używając cache'a, węzeł może wygenerować „zbiór danych” DAG, gdzie każdy element w zbiorze danych zależy od małej liczby pseudolosowo wybranych elementów z cache'a.

Aby zostać górnikiem, musisz wygenerować ten pełny zestaw danych; wszyscy pełni klienci i górnicy przechowują ten zestaw danych, a zestaw danych rośnie liniowo z czasem.

Górnicy mogą następnie wziąć losowe kawałki zbioru danych i umieścić je przez funkcję matematyczną, aby je razem zhaszować do „mixHash”. Górnik będzie powtarzał generowanie mixHash do momentu, aż rezultat będzie poniżej pożądanego docelowego nonce. Gdy rezultat spełnia to wymaganie, to nonce jest uważane za ważne i blok może być dodany do łańcucha.

Wydobywanie jako mechanizm zabezpieczający

Ogólnie rzecz biorąc, celem PoW jest udowodnienie, w kryptograficznie bezpieczny sposób, że zużyto określoną ilość obliczeń na wygenerowanie pewnego rezultatu (tj. nonce). To dlatego, że nie ma lepszego sposobu, aby znaleźć nonce, który jest poniżej wymaganego progu, niż wymienić wszystkie możliwości. Wyniki wielokrotnego stosowania funkcji hasz mają jednolity rozkład, a więc możemy być pewni, że średnio czas potrzebny do znalezienia takiego nonce zależy od progu trudności. Im wyższa trudność, tym dłużej trwa rozwiązanie dla nonce. W ten sposób, algorytm PoW nadaje znaczenie pojęciu trudności, które jest używane do wzmocnienia bezpieczeństwa blockchainu.

Co rozumiemy przez bezpieczeństwo łańcucha bloków?To proste: chcemy stworzyć łańcuch blokowy, któremu WSZYSCY ufają. Jak mówiliśmy wcześniej w tym dokumencie, gdyby istniał więcej niż jeden łańcuch, użytkownicy straciliby zaufanie, ponieważ nie byliby w stanie rozsądnie określić, który łańcuch jest łańcuchem „prawidłowym”. Aby grupa użytkowników zaakceptowała bazowy stan, który jest przechowywany w łańcuchu bloków, potrzebujemy jednego kanonicznego łańcucha bloków, w który grupa ludzi wierzy.

To jest dokładnie to, co robi algorytm PoW: zapewnia, że konkretny łańcuch bloków pozostanie kanoniczny w przyszłości, co niezwykle utrudnia napastnikowi tworzenie nowych bloków, które nadpisują pewną część historii (np. poprzez kasowanie transakcji lub tworzenie fałszywych transakcji) lub utrzymają fork. Aby ich blok został najpierw zwalidowany, napastnik musiałby konsekwentnie rozwiązywać za nonce'a szybciej niż ktokolwiek inny w sieci, tak aby sieć uznała ich łańcuch za najcięższy (w oparciu o zasady protokołu GHOST, o których mówiliśmy wcześniej). To byłoby niemożliwe, chyba że napastnik miałby ponad połowę mocy wydobywczej sieci, scenariusz znany jako atak 51% większości.

Wydobywanie jako mechanizm dystrybucji bogactwa

Poza zapewnieniem bezpiecznego blockchaina, PoW jest również sposobem na dystrybucję bogactwa do tych, którzy wykorzystują swoją moc obliczeniową do zapewnienia tego bezpieczeństwa. Przypomnijmy, że górnik otrzymuje nagrodę za wydobycie bloku, w tym:

  • stałą nagrodę za blok w wysokości 5 etherów za „zwycięski” blok;
  • koszt gazu zużytego w ramach bloku przez transakcje zawarte w bloku;
  • dodatkową nagrodę za włączenie wujków jako części bloku.

W celu zapewnienia, że wykorzystanie mechanizmu konsensusu PoW dla bezpieczeństwa i dystrybucji bogactwa jest trwałe w dłuższej perspektywie, Ethereum stara się zaszczepić te dwie właściwości:

  • Udostępnienie go jak największej liczbie osób.Innymi słowy, ludzie nie powinni potrzebować specjalistycznego lub nietypowego sprzętu, aby uruchomić algorytm.Celem tego jest maksymalne otwarcie modelu dystrybucji bogactwa, tak aby każdy mógł dostarczyć dowolną ilość mocy obliczeniowej w zamian za Ether.
  • Zmniejszenie możliwości osiągnięcia nieproporcjonalnie dużego zysku przez każdy pojedynczy węzeł (lub mały zestaw). Węzeł, który może osiągnąć nieproporcjonalnie duży zysk, oznacza, że ma on duży wpływ na określenie kanonicznego łańcucha bloków.nJest to kłopotliwe, ponieważ zmniejsza bezpieczeństwo sieci.

W sieci blockchain Bitcoina jednym z problemów, który pojawia się w odniesieniu do dwóch powyższych właściwości jest to, że algorytm PoW jest funkcją hasz SHA256. Słabą stroną tego typu funkcji jest to, że można ją rozwiązać znacznie efektywniej przy użyciu specjalistycznego sprzętu, znanego również jako ASIC.

Aby złagodzić ten problem, Ethereum zdecydowało się uczynić swój algorytm PoW (Ethhash) sekwencyjnie mocno zależnym od pamięci RAM. Oznacza to, że algorytm jest tak skonstruowany, że obliczenie nonce wymaga dużej ilości pamięci i przepustowości. Duże zapotrzebowanie na pamięć utrudnia komputerowi równoległe korzystanie z niej w celu odkrycia wielu nonce'ów jednocześnie, a wymagania dotyczące dużej przepustowości utrudniają nawet superszybkiemu komputerowi odkrycie wielu nonce'ów jednocześnie. Zmniejsza to ryzyko centralizacji i stwarza bardziej wyrównane szanse dla węzłów dokonujących weryfikacji.

Należy zauważyć, że Ethereum przechodzi od mechanizmu konsensusu PoW do czegoś, co nazywa się „proof-of-stake”. Jest to temat wredny sam w sobie, który, miejmy nadzieję, będziemy mogli zgłębić w przyszłym poście.

☺️

Podsumowanie

…Uff! Dotarłeś do końca, mam nadzieję?

Jest wiele do przetrawienia w tym wpisie, wiem. Jeśli potrzeba wielokrotnego czytania, aby w pełni zrozumieć, co się dzieje, to jest całkowicie w porządku. Osobiście wielokrotnie czytałem Ethereum yellow paper, white paper i różne części podstawy kodu, zanim zacząłem rozumieć, co się dzieje.

Mimo to, mam nadzieję, że ten przegląd był dla ciebie pomocny. Jeśli znajdziesz jakieś błędy lub pomyłki, chciałbym, abyś napisał prywatną notatkę lub zamieścił ją bezpośrednio w komentarzu. Przyjrzę się każdemu, obiecuję ;)

I pamiętaj, jestem człowiekiem (tak, to prawda) i popełniam błędy. Poświęciłem czas, aby napisać ten wpis dla dobra społeczności, za darmo. Więc proszę bądź konstruktywny w swoich opiniach, bez zbędnego łajania.

☺️

[1] https://github.com/ethereum/yellowpaper

Story tags:

Why am I sharing my travel stories?

Founder & CEO of TruStory. I have a passion for understanding things at a fundamental level and sharing it as clearly as possible.

Preethi Kasireddy
LEARN MORE