Wyjaśnienie białej księgi Ethereum. Część 2

Ethereum zostało zbudowane wokół centralnego celu stworzenia protokołu do budowy różnorodnych zdecentralizowanych aplikacji z licznymi przypadkami użycia.

Zapewniają pełny język programowania Turinga, w którym ważny jest czas programowania, bezpieczeństwo i interakcja między aplikacjami (zdecentralizowanymi aplikacjami). Kompletny programowalny blockchain firmy Turing pozwala na opracowanie szerokiej gamy inteligentnych kontraktów, które są znacznie bardziej wyrafinowane niż te oferowane przez Bitcoin.

Filozofia

Ethereum został zaprojektowany zgodnie z następującymi pięcioma zasadami.

Prostota

Ethereum jest zbudowany jako protokół, który jest prosty i ma wizję bycia otwartym dla wszystkich, nawet w Internecie

koszty przechowywania danych i nieefektywność czasu. Każdy przeciętny programista powinien być w stanie wybrać

z łatwością realizuj projekty i realizuj projekty. Pomaga to w pełnej realizacji tego, co niespotykane

potencjał Blockchain i Kryptowaluty.

Uniwersalność

Kompletność Ethereum w Turingu pomaga w stworzeniu każdego inteligentnego kontraktu, jaki może być

zdefiniowane matematycznie. Waluta, pochodne instrumenty finansowe lub własny Skynet - wszystko można zbudować. Jeśli jednak planujesz zbudować Skynet, być może będziesz musiał mieć szereg wielu powiązanych ze sobą kontraktów i zaopatrzyć ich w wystarczającą ilość gazu, aby utrzymać inteligentną umowę.

Modułowość

Ethereum jest tak zaprojektowane, że wszystkie części protokołu można podzielić na poszczególne jednostki. Nawet jeśli ktoś dokona niewielkiej modyfikacji protokołu w jednym miejscu, inne części stosu aplikacji będą pozornie niezmienione i będą działać bez dalszych modyfikacji.

Innowacje takie jak Ethash, zmodyfikowane drzewa Patricia i RLP (które zostaną omówione w przyszłych postach) są wdrażane jako osobne, wyposażone w kompletne biblioteki. Rozwój Ethereum odbywa się w taki sposób, aby przynieść korzyści całemu systemowi kryptowalut, a nie tylko samemu.

Zwinność

Konstrukty protokołu Ethereum nie są osadzone w kamieniu, chociaż modyfikacje konstrukcji wysokiego poziomu będą dokonywane tylko rozsądnie.

Niedyskryminacja i nieocenzura

Będąc prawdziwym otwartym na wszystkie protokoły, za pomocą Ethereum można tworzyć dowolne aplikacje. Mechanizmy regulacyjne stosowane w Ethereum służą raczej do ograniczenia i zminimalizowania szkód dla ekosystemu, a nie do ograniczenia określonej kategorii zastosowań.

Na przykład możesz uruchomić nieskończony skrypt pętli, o ile płacisz górnikom niezbędne i odpowiednie opłaty za uruchomienie kodu.

Rachunki Ethereum

W Ethereum stan składa się z obiektów zwanych „kontami”, w których każde konto ma 20-bajtowy adres publiczny. Przejścia stanowe to przekazywanie wartości i informacji między dwoma lub więcej kontami. Konto Ethereum zawiera następujące cztery pola.

  • Chwilowo; jest to licznik, który gwarantuje, że każdą transakcję można przetworzyć tylko raz
  • Aktualne saldo konta na rachunku
  • Kod kont konta (jeśli jest obecny, dotyczy inteligentnych umów)
  • Przestrzeń dyskowa konta (domyślnie pusta)

Eter jest głównym paliwem używanym w Ethereum i jest wykorzystywany do opłat transakcyjnych znanych również jako Gwei.

Istnieją dwa rodzaje kont, a mianowicie:

  1. Rachunki zewnętrzne; kontrolowane przez klucze prywatne: nie mają nieodłącznego kodu. Wiadomości są wysyłane poprzez utworzenie i podpisanie transakcji.
  2. Konta kontraktowe; kontrolowany przez kod kontraktu: kod aktywuje się w zależności od treści otrzymanej wiadomości i można aktywować dalsze procesy, takie jak odczyt i zapis do pamięci wewnętrznej, wysyłanie innych wiadomości lub tworzenie umów.

Drugi typ konta jest wykorzystywany przez giełdę kryptowalut: Blockchain Board of Derivatives w nie-depozytowym systemie inteligentnych portfeli kontraktowych.

Inteligentne kontrakty są zatem niezależnymi agentami, które żyją w środowisku Ethereum i wykonują kod, gdy są przekazywane przez transakcję lub wiadomość. Takie kontrakty mają bezpośrednią kontrolę nad ich saldem eterowym i własnym magazynem kluczy.

Transakcje

Transakcja w Ethereum to zasadniczo podpisany i zaszyfrowany pakiet danych, który przechowuje wiadomość do wysłania z konta będącego własnością zewnętrzną.

Typowe transakcje zawierają:

  • Odbiorca wiadomości (klucz publiczny odbiorcy)
  • Podpis identyfikujący nadawcę (klucz prywatny nadawcy)
  • Ilość eteru do przesłania od nadawcy do odbiorcy
  • Opcjonalne pole danych
  • Wartość STARTGAS, reprezentująca maksymalną liczbę kroków obliczeniowych, jakie może wykonać wykonanie transakcji
  • Wartość GASPRICE, reprezentująca opłatę, którą nadawca płaci za krok obliczeniowy

Podzielmy te poszczególne punkty. Pierwsze trzy to standardowe pola obecne w każdej krypto-walucie. Pole danych nie ma domyślnej funkcji, ale może być wykorzystane przez umowę w celu uzyskania dostępu do danych. Na przykład, jeśli umowa działa jako usługa rejestracji domen, może zechcieć zinterpretować przekazywane dane jako zawierające dwa „pola”, przy czym pierwsze pole jest domeną do rejestracji, a drugie pole to adres IP zarejestruj domenę w. Umowa odczyta te wartości z danych wiadomości i odpowiednio umieści je w pamięci.

Pola STARTGAS i GASPRICE mają kluczowe znaczenie dla modelu anty-odmowy usługi Ethereum. Aby zapobiec nieskończonym pętlom lub innym marnotrawstwom obliczeniowym, każda transakcja musi ustalić limit liczby kroków obliczeniowych, które może zastosować. Podstawową jednostką obliczeniową jest „gaz”. Zwykle krok obliczeniowy kosztuje 1 gaz, ale niektóre operacje kosztują większe ilości gazu, ponieważ są one bardziej kosztowne obliczeniowo lub zwiększają ilość danych, które muszą być przechowywane jako część stanu.

Za każdy bajt w danych transakcji pobierana jest opłata w wysokości 5 gazu. System opłat powoduje, że osoba atakująca płaci proporcjonalnie za każde zużywane zasoby, w tym obliczenia, przepustowość i pamięć. Dlatego każda transakcja, która prowadzi do wysokiego zużycia sieci, naturalnie prowadzi do wyższej opłaty za gaz.

Mówiąc prościej, zapłacony gaz jest wprost proporcjonalny do liczby i złożoności obliczeń wykonanych w łańcuchu bloków.

Wiadomości

Umowy mogą wysyłać wiadomości do innych umów.

Typowe wiadomości zawierają:

  • Nadawca wiadomości
  • Odbiorca wiadomości
  • Ilość eteru do przesłania wraz z wiadomością
  • Opcjonalne pole danych
  • Wartość STARTGAS

Komunikat jest podobny do transakcji, z tą różnicą, że wiadomości są tworzone na podstawie umowy, a nie kont zewnętrznych. Komunikat jest generowany, gdy kod wykonujący kontrakt wykonuje kod operacji CALL, wytwarzając i wykonując komunikat.

Wiadomość jest odbierana przez konto odbiorcy, które następnie uruchamia swój kod. W ten sposób kontrakty mogą obowiązywać w relacjach z innymi umowami w sposób podobny do kont zewnętrznych.

Przydział gazu przypisany umową dotyczy zarówno gazu zużywanego przez transakcję, jak i wszystkich podwykonań.

Rozumiemy to samo na przykładzie.

@A jest kontem zewnętrznym

@B jest umową

@A wysyła @B transakcję z 1000 gazami.

@B zużywa 600 gazu i wysyła wiadomość do @C.

Wewnętrzne wykonanie @C zużywa 300 gazu.

1000–600–300 = 100

Oznacza to, że kontrakt @B może wydać kolejne 100 gazu na obliczenia / wiadomość / transakcję przed wyczerpaniem się gazu.

Ethereum State Transition Function

Jak wspomniano w części 1 serii, możesz przywołać funkcję zmiany stanu

ZASTOSUJ (S, TX) -> S ’

Dalsze kroki zostały zaczerpnięte z białej księgi i są dość oczywiste:

  1. Transakcja musi mieć odpowiednią liczbę wartości, podpis musi być ważny, a wartość jednorazowa powinna być zgodna z wartością jednorazową na koncie nadawcy. Jeśli nie jest zgodny, zgłoś błąd.
  2. Opłata transakcyjna jest obliczana jako STARTGAS * GASPRICE, adres wysyłkowy można ustalić na podstawie podpisu. Odejmij opłatę od salda nadawcy i zwiększ wartość nonce nadawcy. Jeśli saldo nie jest wystarczające do wydania, wyrzuć błąd.
  3. Zainicjuj GAS = STARTGAS, a pewna ilość gazu na bajt jest pobierana, aby zapłacić za bajty w transakcji.
  4. Przenieś wartość transakcji z konta nadawcy na konto odbiorcy. Jeśli konto odbiorcze jeszcze nie istnieje, utwórz je. Jeśli konto odbiorcze jest umową, uruchom kod umowy albo do końca, albo do momentu wyczerpania paliwa w trakcie realizacji.
  5. Jeśli transfer wartości nie powiódł się, ponieważ nadawcy nie było wystarczającej ilości pieniędzy lub w wykonaniu kodu zabrakło gazu, cofnij wszystkie zmiany stanu z wyjątkiem uiszczenia opłat i dodaj opłaty do konta górnika. Opłaty nie można cofnąć, ponieważ górnik zużywa energię w celu ułatwienia transakcji.
  6. W przeciwnym razie zwróć opłatę za cały pozostały gaz nadawcy i prześlij opłaty zapłacone za gaz zużyty górnikowi.

Załóżmy, że kod umowy jest następujący:

if! self.storage [calldataload (0)]:
self.storage [calldataload (0)] = calldataload (32)

Umowa jest faktycznie napisana niskim kodem EVM, ale powyższy przykład jest napisany w Serpent.

Rozważmy teraz przykład:

Pamięć umowy jest początkowo pusta, a transakcja jest wysyłana z 10 wartościami eteru, 2000 gazu, 0,001 ceny gazu eterowego i 64 bajtami danych, przy czym bajty 0–31 oznaczają liczbę 2, a bajty 32–63 zawierają ciąg CHARLIE.

Proces funkcji zmiany stanu w tym scenariuszu jest następujący. Kroki te są podobne do tych wymienionych w ogólnym przykładzie powyżej.

  1. Sprawdź, czy transakcja jest ważna i poprawnie sformułowana.
  2. Sprawdź, czy nadawca transakcji ma co najmniej 2000 * 0,001 = 2 eteru. Jeśli tak, odejmij 2 etery z konta nadawcy. (Ponieważ musimy użyć formuły STARTGAS * GASPRICE)
  3. Zainicjuj gaz = 2000; zakładając, że transakcja ma długość 170 bajtów, a opłata za bajt wynosi 5, odejmij 850 (170 * 5), aby pozostało 1150 (2000–850) gazu.
  4. Odejmij 10 więcej eteru z konta nadawcy i dodaj go do konta umowy.
  5. Uruchom kod. W tym przypadku jest to proste: sprawdza, czy pamięć kontraktowa jest używana w indeksie 2, zauważa, że ​​nie jest używana, i ustawia pamięć w indeksie 2 na wartość CHARLIE. Załóżmy, że wymaga to 187 gazu, więc pozostała ilość gazu wynosi 1150–187 = 963
  6. Dodaj 963 * 0,001 = 0,963 eteru z powrotem do konta nadawcy i zwróć wynikowy stan.

To kończy kroki, które są podejmowane w całym procesie.

Jeśli po otrzymującym końcu transakcji nie było umowy, całkowita opłata za transakcję byłaby po prostu równa podanej GASPRICE pomnożonej przez długość transakcji w bajtach, a dane przesyłane obok transakcji byłyby nieistotne.

W takim przypadku górnik wykorzystałby cały gaz, aby nie przynieść żadnych rezultatów, ponieważ żaden kontrakt nie istnieje.

Wiadomości i transakcje działają na podobnych warunkach, jeśli chodzi o przywracanie: jeśli wykonanie komunikatu zabraknie gazu, wówczas wykonanie tego komunikatu i wszystkie inne wykonania wywołane przez to wykonanie zostaną przywrócone, ale wykonania nadrzędne nie muszą zostać przywrócone.

Oznacza to, że „bezpieczne” jest, aby kontrakt zadzwonił do innego kontraktu, tak jakby A zadzwonił do B z gazem G, a wykonanie A gwarantuje utratę co najwyżej gazu G. Egzekucje rodzicielskie poza umowami nie są jednak cofane.

Ponadto istnieje kod operacji, CREATE, który tworzy umowę. Jego mechanizmy wykonawcze są zasadniczo podobne do CALL, z tym wyjątkiem, że dane wyjściowe wykonania określają kod nowo utworzonej umowy.

Zagłębimy się w opcode bardziej szczegółowo w naszych przyszłych szczegółowych blogach technicznych.

Wykonanie kodu

Kod w umowach Ethereum jest napisany w niskopoziomowym języku bajtecode opartym na stosie, zwanym „kodem wirtualnej maszyny Ethereum” lub „kodem EVM”. Kod EVM jest zasadniczo serią bajtów, a każdy bajt jest operacją.

„Wykonywanie kodu jest nieskończoną pętlą, która polega na wielokrotnym wykonywaniu operacji na bieżącym liczniku programu (który zaczyna się od zera), a następnie zwiększaniu licznika programu o jeden, aż do osiągnięcia końca kodu lub błędu, STOP lub POWRÓT instrukcja została wykryta. ”

Operacje mają dostęp do trzech rodzajów przestrzeni do przechowywania danych:

  1. Stack, ostatni na wejściu pierwszy na wyjściu kontener, do którego wartości można wypychać i wyskakiwać jak typowy stos.
  2. Pamięć, nieskończenie rozszerzalna tablica bajtów.
  3. Storage, magazyn kluczy / wartości. W przeciwieństwie do stosu i pamięci, które resetują się po zakończeniu obliczeń, pamięć trwa długo.

Kod ma również dostęp do wartości, nadawcy, danych wiadomości przychodzącej oraz nagłówka bloku. Kod może również zwrócić tablicę bajtów danych jako dane wyjściowe.

Model wykonania kodu EVM jest dość prosty. Będziemy dalej badać w poniższych krokach.

Gdy maszyna wirtualna Ethereum jest uruchomiona, jej pełny stan obliczeniowy może zostać zdefiniowany przez krotkę. Krotka składa się z analizy, transakcji, wiadomości, kodu, pamięci, stosu, komputera i gazu.

Tutaj, stanate jest stanem globalnym zawierającym wszystkie konta i obejmującym salda i przechowywanie.

Na początku każdej rundy wykonywania bieżąca instrukcja zostaje znaleziona przez pobranie bajtu pc-tego kodu (lub 0, jeśli pc> = len (kod)), co oznacza, że ​​pc jest uważane za zero, gdy jest większe lub równe do długości kodu.

Każda instrukcja ma własną definicję wpływu na krotkę.

ADD zrzuca dwa elementy ze stosu, odsuwa ich sumę, zmniejsza gaz o 1 i zwiększa liczbę komputerów o 1 (typowe działanie stosu)

SSTORE zrzuca dwa górne elementy ze stosu i wstawia drugi element do magazynu kontraktu zgodnie z indeksem określonym przez pierwszy element.

Istnieje wiele sposobów na optymalizację wykonania EVM poprzez kompilację just-in-time, podstawową implementację Ethereum można wykonać w kilkuset wierszach kodu.

Blockchain and Mining

Blockchain Ethereum jest mniej więcej podobny do blockchaina Bitcoin z kilkoma subtelnymi różnicami.

Główna różnica między Ethereum i Bitcoinem w odniesieniu do architektury blockchain polega na tym, że w przeciwieństwie do Bitcoin (który zawiera tylko kopię listy transakcji), bloki Ethereum zawierają kopię listy transakcji, najnowszy stan, numer bloku i trudność.

Podstawowy algorytm sprawdzania poprawności bloku w Ethereum można wyjaśnić w następujących krokach:

  1. Sprawdź, czy poprzedni wymieniony blok istnieje i jest poprawny.
  2. Sprawdź, czy znacznik czasu bloku jest większy niż czas odniesienia poprzedniego bloku i mniej niż 15 minut w przyszłość.
  3. Sprawdź, czy numer bloku, trudność, root transakcji, root wujek i limit gazu (różne koncepcje niskiego poziomu Ethereum, które zostaną omówione później) są prawidłowe.
  4. Sprawdź, czy dowód pracy na bloku jest ważny.
  5. Niech S [0] będzie stanem na końcu poprzedniego bloku. (pamiętaj, że zostało to omówione i wyjaśnione w poprzednim poście na blogu)
  6. Niech TX będzie listą transakcji bloku, zawierającą n transakcji. Dla wszystkich i w 0… n-1 ustaw S [i + 1] = ZASTOSUJ (S [i], TX [i]). Jeśli którakolwiek aplikacja zwróci błąd lub jeśli całkowita ilość gazu zużytego w bloku aż do tego momentu przekroczy GASLIMIT, zwróć błąd.
  7. Niech S_FINAL będzie S [n], ale dodanie nagrody blokowej wypłaconej górnikowi (S_FINAL = S [n] + nagroda blokowa). Nagroda jest przyznawana, gdy górnik zakończy wydobywanie bloku.
  8. Sprawdź, czy korzeń drzewa Merkle stanu S_FINAL jest równy końcowemu pierwiastkowi stanu podanemu w nagłówku bloku. Jeśli tak, blok jest ważny; w przeciwnym razie nie jest poprawny. (Drzewo Merkle i walidacja za pomocą nagłówka bloku wyjaśniono odpowiednimi zdjęciami w poprzednim poście na blogu)

Podejście polegające na zapisywaniu całego stanu w każdym bloku może początkowo wydawać się nieefektywne, ale jest porównywalne z Bitcoinem.

Stan jest przechowywany w strukturze drzewa i po każdym bloku tylko niewielka część drzewa musi zostać zmieniona. Oznacza to, że między dwoma sąsiadującymi blokami zdecydowana większość drzewa powinna być taka sama. Dane mogą być przechowywane raz i przywoływane dwukrotnie za pomocą wskaźników (skrótów poddrzewa).

Aby to osiągnąć, zastosowano specjalny rodzaj drzewa zwany „drzewem Patricia”, w tym modyfikację koncepcji drzewa Merkle, która umożliwia wydajne wstawianie i usuwanie węzłów.

Ponadto, ponieważ wszystkie informacje o stanie są częścią ostatniego bloku, nie ma potrzeby przechowywania całej historii łańcucha bloków.

Często zadawane pytanie brzmi „gdzie” jest wykonywany kod umowy, pod względem fizycznym sprzętu.

Proces wykonywania kodu kontraktu jest zdefiniowany w samej funkcji przejścia stanu, która jest częścią algorytmu sprawdzania poprawności bloku. Jeśli transakcja zostanie dodana do bloku B, wykonanie kodu odrodzonego przez tę transakcję zostanie wykonane przez wszystkie węzły, które pobiorą i sprawdzą poprawność bloku B, teraz lub w przyszłości.

Oznacza to koniec części 2 białej księgi Ethereum. W następnej części omówimy zastosowania protokołu Ethereum i ekosystemu w czasie rzeczywistym.