Blockchain czyli sposób na zabezpieczenie danych

7 lat temu
Przez ostatnie miesiące omawiałem różne techniki kryptograficzne. Nadeszła pora aby pokazać w praktyce ich interesujące zastosowania. Już w tytule zdradziłem o czym będzie poniższy wpis. Zacznę od *blockchain* czyli *łańcucha bloków*. *Blockchain* kojarzy się głównie z *Bitcoinem* oraz innymi kryptowalutami, ale to mechanizm, który pozwala również realizować inne interesujące pomysły. Umożliwia on zabezpieczenie danych poprzez budowanie rozproszonego, integralnego rejestru wykorzystującego między innymi [funkcje skrótu](/blog/skracamy-czyli-integralnosc-w-praktyce/) oraz [podpisy cyfrowe](/blog/podpis-cyfrowy/).

## Łańcuch bloków i kopanie
Bardzo często tworzymy różne rejestry, czyli zapisy świadczące o kolejno wykonanych operacjach. jeżeli będziemy je zapisywali bez żadnych zabezpieczeń to w każdej chwili taki rejestr może zostać zmodyfikowany i nie będziemy w stanie wykryć takiego zakłócenia. Atakujący może zmienić dany wpis lub usunąć te z nich, które uzna za niewygodne. Dotyczy to na przykład logów systemowych. Włamywacz często usuwa z nich zapisy pozwalające na wykrycie ataku i identyfikację atakującego. Rozwiązaniem tego problemu może być taki sposób budowania rejestru, aby kolejno zapisywane bloki miały zapewnioną integralność. Wtedy nie będzie możliwości zmiany pojedynczego bloku. To jednak nie wystarczy, bo przez cały czas atakujący będzie mógł po prostu je usuwać. Integralność danego bloku należy więc zapewnić z uwzględnieniem wszystkich jego poprzedników. Przy wyliczaniu funkcji skrótu nie trzeba wyliczać jej dla całego rejestru, wystarczy skorzystać z ostatnio wyliczonej wartości ponieważ ona zapewnia integralność poprzedników.

Idea ta przedstawiona jest na poniższym rysunku. Pierwszy skrót wyliczany jest z danych pierwszego bloku. Kolejne skróty obejmują skrót poprzedniego bloku oraz nowe dane. Dzięki własności funkcji skrótu zapewniona jest integralność całego rejestru. Każde jego zakłócenie wpłynie na kolejne bloki. Danych nie da się również łatwo podrobić, czyli zastąpić innymi danymi dającymi ten sam skrót. Funkcje skrótu są odporne na takie ataki.

![blockchain.webp](/uploads/blockchain_300c132d06.webp)

Rejestr pokazany na rysunku to *blockchain*. jeżeli przechowujemy go wyłącznie lokalnie to atakujący może jeszcze przeliczyć funkcje skrótu po wykonaniu swoich zmian. Jednak, jeżeli rejestr będzie rozproszony, czyli jego kopia będzie przechowywana w wielu miejscach i na bieżąco synchronizowana to taka zmiana będzie utrudniona. Węzły sieci wykryją, iż coś, co już było rozpowszechnione, zostało zmienione. Właścicielem łańcucha bloków powinna być cała sieć i dzięki temu, iż przechowywany będzie w różnych miejscach jego modyfikacja będzie trudna. W razie ataku węzły sieci wykryją zmiany w poprzednich blokach i większość węzłów je odrzuci. Im więcej nowych bloków jest za blokiem, z którego dane nas interesują, tym trudniej taki atak przeprowadzić. Trzeba zakłócić więcej bloków i starać się je przekazać do innych węzłów.

Skoro rejestr jest rozproszony to kto powinien zatwierdzać kolejne bloki czyli wyliczać ich funkcje skrótu? Ma do tego prawo każdy węzeł sieci. Ponieważ funkcja skrótu jest łatwa do wyliczenia to niemożliwa byłaby efektywna synchronizacja takiego rejestru z uwagi na zbyt często pojawiające się nowe bloki. Stosuje się nieco zmodyfikowane rozwiązanie. Najpopularniejszym jest zastosowanie tak zwanego *dowodu pracy* (ang. *proof of work*). Tylko węzeł, który wykona pewną pracę, zatwierdzoną przez inne węzły, ma prawo do potwierdzenia bloku. Popularnie wykonywanie tego zadania nazywa się *kopaniem* (ang. *mining*). Za wykonanie pracy (wykopanie bloku) węzeł sieci jest wynagradzany. Praca ta polega najczęściej na rozwiązaniu pewnego problemu, który jest skomplikowany obliczeniowo. Na przykład w sieci [*Bitcoin*](https://blockchain.info/) zadanie polega na zaatakowaniu funkcji skrótu. Tylko węzeł, który znajdzie liczbę dającą razem z zatwierdzanym blokiem wartość skrótu rozpoczynającą się od pewnej liczby zer jest uznawany za zwycięzcę w kopaniu. Zatem *blockchain* zrealizowany w praktyce to nie tylko sposób przechowywania danych ale również algorytmy, które zarządzają siecią węzłów.

## Transakcje
Rejestr to seria zapisów dowolnych informacji, które pozostaną w *blockchain* na zawsze. Można w nim umieścić na przykład swoją książkę czy opis wynalazku i jeżeli taki łańcuch bloków będzie rejestrem uznawanym przez wszystkich to otrzymamy strukturę zapewniającą *dowód autorstwa*. Rejestry kojarzą się głównie z zapisami księgowymi. Aby zrealizować zabezpieczone transakcje nie wystarczy przechowywać samych sald powiązanych z kontami, ponieważ każdy będzie mógł to saldo zmodyfikować. Użytkownik musi panować nad swoim kontem.

Wykorzystajmy do tego znany już mechanizm podpisu cyfrowego. Konto użytkownika będzie identyfikowane jego kluczem publicznym i z tym kluczem będzie powiązany stan jego środków. Zlecając transakcję tworzymy paczkę zawierającą:

- konto źródłowe (identyfikowane przez klucz publiczny konta nadawcy),
- konto docelowe (identyfikowane przez klucz publiczny konta odbiorcy),
- dane transakcji (na przykład kwota przelewu),
- podpis transakcji (wykonany dzięki klucza prywatnego nadawcy).

Analizując całą historię w łańcuchu bloków można na podstawie wpisów transakcyjnych wyliczyć saldo każdego z kont (saldo przypisane do kluczy
publicznych). Taka jest idea działania kryptowalut. Zauważmy, iż nie musimy nigdzie zakładać konta, wystarczy iż ktoś wykona przelew na nasz klucz publiczny i dopiero wtedy pojawimy się w *blockchain* z niezerowym saldem. Żeby skorzystać z tych środków niezbędne jest posiadanie odpowiadającego mu klucza prywatnego.

Bezpieczeństwo naszego konta związane jest z *bezpieczeństwem klucza prywatnego*. Oznacza to, iż o ile utracimy nasz klucz prywatny lub ktoś go nam wykradnie to tracimy pełną kontrolę nad naszymi aktywami. Podobnie jeżeli zlecimy przekaz i popełnimy błąd w docelowym kluczu publicznym. Nie ma wtedy możliwości odzyskania takich środków. Spójność bloków i jasne zasady są (a przynajmniej powinny być) na pierwszym miejscu.

## Blockchain w Ethereum
Sprawdźmy w praktyce jak działa *blockchain*. Wykorzystamy do tego jedną z jego implementacji jaką jest [Ethereum](https://www.ethereum.org/). Aby utworzyć prywatny łańcuch potrzebne będzie oprogramowanie realizujące zadania węzła. Program ten nazywa się `geth` i można go pobrać z [repozytorium Ethereum](https://geth.ethereum.org/downloads/). Do przygotowania poniższych przykładów używałem wersji 1.6.0. Może to być istotne, ponieważ projekt jest intensywnie rozwijany i pewne ustawienia konfiguracyjne mogą się zmieniać. Przyda nam się również klient [Mist](https://github.com/ethereum/mist/releases), pozwalający na wygodne tworzenie kont i zlecanie transakcji. Zalecam pobieranie wersji w pełni zgodnej z architekturą komputera jaki używamy (32 lub 64 bit).

Ethereum jest rozbudowanym łańcuchem bloków z wieloma funkcjami, ale na początek skorzystamy z możliwości przechowywania danych powiązanych z kontem użytkownika. W szczególności będą to zapisy o liczbie zgromadzonych *etherów* (w skrócie ETH). Jest to jednostka rozliczeniowa w tej sieci. Każdy *ether* to $$10^{18}$$ *wei*. Wei jest najmniejszą jednostką rozliczeniową w Ethereum. Wykorzystywaną funkcją skrótu w Ethereum jest Keccak-256.

W celu wystartowania prywatnego łańcucha niezbędne jest utworzenie pierwszego bloku. Poniżej prezentuję podstawowy plik konfiguracyjny dla bloku *genesis* w Ethereum (należy go zapisać do pliku `genesis.json`). Podane w nim opcje określają:

- trudność obliczeniową zadania wykonywanego przy zatwierdzaniu bloku - przykładowa wartość pozwala na zatwierdzanie ich przy stosunkowo niewielkich zasobach obliczeniowych w kilkanaście sekund, w razie potrzeby można ją zmniejszyć,
- limit wykonywanych obliczeń dla transakcji - założeniem Ethereum jest zużywanie przez każdą transakcję paliwa (ang. *gas*), które przeliczane jest na opłatę w *etherach*,
- konfigurację - identyfikator sieci (100) oraz wersję używanej struktury bloków,
- początkowe środki dla wybranych kont (brak).

```json
{
"difficulty": "0x400000",
"gasLimit": "0x8000000",
"config": {
"chainId": 100,
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0 },
"alloc": { }
}
```

Polecenie

```
geth --datadir ./eth init genesis.json
```

wygeneruje w katalogu `eth` nasz prywatny *blockchain* na podstawie bloku *genesis*. Będzie w nim tylko jeden blok.

W kolejnym etapie trzeba uruchomić lokalny węzeł. Należy użyć polecenia

```
geth --datadir ./eth --networkid 100
```

Po uruchomieniu węzła można się z nim komunikować dzięki poleceń RPC wysyłanych w formacie JSON. Dostępne są następujące kanały:

- lokalna komunikacja z węzłem dzięki IPC,
- zdalna i lokalna komunikacja z węzłem dzięki protokołu HTTP (domyślnie jest to port 8545).

Komunikując się z węzłem możemy odczytywać aktualny stan łańcucha bloków, przeglądać dane w nim zawarte oraz zlecać nowe transakcje. W szczególności możemy uruchomić konsolę `geth` dzięki polecenia `geth attach` oraz klienta Mist. Węzeł komunikuje się z innymi węzłami (tworzy sieć P2P) używając portu 30303. Dzięki sieci P2P synchronizowany jest budowany *blockchain* oraz przekazywane są informacje o zleconych transakcjach do innych węzłów.

Do rozpoczęcia korzystania z utworzonego *blockchain* konieczne będzie rozpoczęcie kopania. Nim to jednak zrobimy trzeba upewnić się, iż mamy na dysku co najmniej 2GB wolnego miejsca i utworzyć pierwsze konto, aby otrzymywać wynagrodzenie za wykopane bloki. Możemy to zrobić uruchamiając klienta Mist. Połączy się on z naszym lokalnym węzłem i wykryje, iż działamy w prywatnej sieci. Po wygenerowaniu nowego konta (wygenerowaniu pary kluczy algorytmu ECDSA) ekran aplikacji będzie prezentował się jak na poniższym rysunku.

![mist-accounts.webp](/uploads/mist_accounts_c605ffe8b5.webp)

Mając założone główne konto (jego identyfikator to wyliczona funkcja skrótu z klucza publicznego, klucze przechowywane są w katalogu `./eth/keystore`) możemy rozpocząć kopanie. Uruchamiamy konsolę `geth` (polecenie `geth attach`) i wykonujemy w niej polecenie `miner.start(1)`. Uruchomi to proces kopania z użyciem jednego wątku wykorzystując jeden rdzeń naszego procesora. Pierwsze uruchomienie wiąże się również z wygenerowaniem dużego grafu na którym będzie realizowany algorytm. Graf zostanie zapamiętany w pliku i regenerowany co kilka dni. Może to trwać kilkanaście minut. Potem co kilka sekund w konsoli zaczną pojawiać się wpisy oznaczające utworzenie nowego bloku, a na naszym koncie zacznie przybywać po pięć *etherów* za każdy blok.

```
Successfully sealed new block number=1 hash=0743ae3155b3
🔨 mined potential block number=1 hash=0743ae3155b3
Commit new mining work number=1 txs=0 uncles=0 elapsed=0s
...
```

Warto utworzyć teraz drugie konto i spróbować wykonać transakcję między nimi. Przy zlecaniu transakcji podajemy adresy kont źródłowego i docelowego, przekazywaną liczbę *etherów* oraz proponowaną opłatę za transakcję. Otrzyma ją węzeł, który wykopie blok, czyli w tym przypadku my sami. Węzły rozgłaszają między sobą zlecone transakcje oraz wykopane bloki. Będąc węzłem nie ma obowiązku kopania, ale w naszym przypadku jest to niezbędne aby tworzył się łańcuch - jesteśmy na razie jedynym węzłem w naszej sieci.

![mist-transaction.webp](/uploads/mist_transaction_0a488d8800.webp)

Ekran zatwierdzania zlecenia określa ile paliwa oraz w związku z tym ile *etherów* będzie kosztowała nasza transakcja. Ustawiony limit jest większy niż przewidywane zużycie, więc powinno wystarczyć paliwa aby transakcja dotarła do odbiorcy.

![mist-send-transaction.webp](/uploads/mist_send_transaction_dfa7485d3d.webp)

Po zatwierdzeniu transakcji i wykopaniu bloku, który będzie ją zawierał, środki pokażą się na koncie odbiorcy. Proszę zaobserwować, iż przy transakcji w Mist oznaczana jest na bieżąco liczba wykopanych kolejnych bloków. Przyjmuje się, iż jeżeli powstanie ich co najmniej sześć, to transakcja jest już *pewna* ponieważ *blockchain* z dużym prawdopodobieństwem został rozgłoszony w całej sieci.

Zachęcam do utworzenia jeszcze jednego konta i wykonania kilku transakcji. Proces kopania przerywamy dzięki `miner.stop()`. Węzeł jest przez cały czas aktywny, przyjmuje transakcje, ale nie są one zatwierdzane. Proces kopania można również uruchamiać łącznie z węzłem używając polecenia

```
geth --datadir ./eth --networkid 100 --mine --minerthreads=1 --etherbase=0x0000000000000000000000000000000000000000
```

w którym wstawiamy adres konta przyjmującego *ethery* za wykopanie bloku.

## Podsumowanie

Dotychczas w utworzonym rejestrze przechowywaliśmy zapisy związane ze stanem kont reprezentowanych przez klucze publiczne ich właścicieli. Można iść krok dalej - nie przechowywać w *blockchain* wyłącznie statycznych danych, ale umieścić tam aplikacje, które mogą zostać wykonane przy określonych warunkach. W ten sposób otrzymamy *kontrakt*, który zadziała tak jak został zaimplementowany i będzie niezależnym bytem żyjącym w łańcuchu. Przykłady takich kontraktów w Ethereum przedstawię w kolejnym wpisie.
Idź do oryginalnego materiału