Lot o zachodzie słońca

Protokół Bitcoina od środka: Jak autoryzowane są transakcje?

Ogłosić nową transakcję w sieci Bitcoin może każdy. Wystarczy zbudować odpowiedni ciąg bajtów zawierający podstawowe informacje o płatności i wysłać go w świat. Jednak nie każda płatność zostanie przyjęta, bowiem trzeba w jakiś sposób udowodnić, że mamy prawo dysponować środkami, które są w naszym posiadaniu. Zobaczmy jak reguluje to protokół Bitcoina 🙂

Nadawca decyduje

W klasycznym podejściu do płatności sprawa jest dość prosta – albo posiadamy gotówkę i możemy swobodnie z niej korzystać, albo pieniądze znajdują się w banku i to on autoryzuje swoimi metodami dostęp do naszych środków. W świecie Bitcoina sprawa jest nieco odwrócona, bowiem to nadawca wyznacza warunki, jakie musi spełnić odbiorca, aby wykorzystać otrzymane środki. Ba, może nawet wykonać taką transakcję, która uniemożliwi wydanie wychodzących środków, a nawet taką, która umożliwi skorzystanie z nich każdemu. Czyli pierwszemu, który taką transakcję zauważy. A to wszystko dzięki skryptom.

Język skryptowy

Do podpisywania transakcji w sieci Bitcoin wykorzystuje się skrypty napisane w specjalnie utworzonym do tego celu języku. Składa się on z szeregu instrukcji, które wykonywane są kolejno od lewej do prawej strony. Co ciekawe, język ten nie jest kompletny w sensie Turinga – nie utworzymy w nim żadnej pętli. Tak na wszelki wypadek, aby przy próbie sprawdzania poprawności sprytnie skonstruowanej transakcji nie zapętlić weryfikującego na zawsze 🙂

Na język składa się ponad 80 komend, aczkolwiek w praktyce używa się zaledwie kilku z nich. Ponadto język oparty jest na stosie – większość komend właśnie na nim operuje. Poniżej lista kliku poleceń, które najczęściej można spotkać w skryptach autoryzujących transakcję.

Komenda Komentarz
PUSHDATA Umieszczenie na stosie ciągu bajtów następującego po komendzie.
OP_DUP Duplikacja wartości na szczycie stosu.
OP_HASH160 Wyliczenie wartości algorytmu RIPEMD-160 na podstawie wartości znajdującej się na szczycie stosu.
OP_EQUALVERIFY Sprawdzenie, czy dwie kolejne wartości na stosie są sobie równe.
OP_CHECKSIG Sprawdzenie, czy znajdujące się na stosie klucz publiczny oraz wynik podpisania transakcji kluczem prywatnym są zgodne. Poprawna weryfikacja zostawia na stosie wartość true, zaś negatywna false. Więcej na temat działania OP_CHECKSIG opisałem w dalszej części wpisu.

„scriptSig” i „scriptPubKey”

Skrypt autoryzujący transakcję dzieli się na dwie części: „scriptSig” i „scriptPubKey”. O tym, gdzie dokładnie obie części umiejscowione są w surowej transakcji, można przeczytać w poprzednim wpisie na temat budowy wiadomości „tx”. „scriptPubKey” jest fragmentem kodu znajdującym się w części transakcji środków wychodzących. Określa warunki, jakie musi spełnić transakcja po niej następująca. „scriptSig” jest częścią transakcji środków przychodzących i zawiera takie komendy, które sprawią, że po połączeniu ze skryptem nadawcy całość zakończy się sukcesem. W standardowych transakcjach w sieci Bitcoin skrypty transakcji śledzą ten sam ustalony schemat. Co nie przeszkadza w wymyśleniu czegoś bardziej wyrafinowanego 🙂 Poniższy przykład ilustruje krok po kroku weryfikację przykładowej transakcji.

1. Pobierz poprzednią transakcję

Do zweryfikowania przykładowej transakcji – nazwijmy ją B – będziemy potrzebować poprzednią transakcję – nazwijmy ją A.

2. Wyłuskaj z A „scriptPubKey”

Z części transakcji A dotyczącej środków wychodzących potrzebujemy pobrać warunki, które postawił nadawca A, aby środki mogły zostać wydane w przyszłych transakcjach. Warunki te znajdują się w części „scriptPubKey” reprezentowanej przez komendy w specjalnym języju skryptowym.

3. Wyłuskaj z B „scriptSig”

Z części transakcji B dotyczącej środków przychodzących potrzebujemy pobrać skrypt, który nadawca B pozostawił, aby spełnić warunki postawione przez nadawcę A.

4. Wykonaj skrypt

Po pobraniu obu skyptów, łączymy je w jeden, wykonując po kolei instrukcje z „scriptSig”, a następnie „scriptPubKey”.

5. Sprawdź rezultat

Jeżeli skrypt wykona się bez żadnych błędów, oznacza to, że transakcja została autoryzowana.

Weryfikacja standardowej transakcji

Pomimo że w budowaniu warunków, które musi spełnić odbiorca naszej transakcji mamy teoretycznie pewną dowolność, większość transakcji w sieci Bitcoin śledzi jeden ustalony schemat. Zobaczmy to na przykładzie.

Budowa klasycznych skryptów

Standardowy „scriptPubKey” zbudowany jest w następujący sposób:

OP_DUP OP_HASH160 PublicKeyHash OP_EQUALVERIFY OP_CHECKSIG

PublicKeyHash to wartość odnosząca się do adresu Bitcoin odbiorcy. Z PublicKeyHash powstaje klasyczny adres Bitcoin, lecz operacja ta jest w pełni odwracalna. Posiadając adres Bitcoin odbiorcy, jesteśmy w stanie wyliczyć z niego PublicKeyHash. Więcej szczegółów na temat adresów można przeczytać we wpisie „Generujemy adresy kont”.

Klasyczny „scriptSig” zbudowany jest następująco:

TransactionSignature PublicKey

Wyjaśnienie powyższych elementów skryptu także wymaga cofnięcia się do tematu generowania adresów kont w sieci Bitcoin. W lekkim uproszczeniu, na adres Bitcoin składa się klucz prywatny oraz odpowiadający mu klucz publiczny. Klucz publiczny ulega kilku przekształceniom, po których powstaje adres Bitcoin. Klucz prywatny zna tylko właściciel adresu, klucz publiczny nie jest znany do momentu wykonania pierwszej transakcji przez właściciela adresu, zaś adres Bitcoin jest publicznie udostępniany i na niego przyjmowane są wpłaty. TransactionSignature to podpis cyfrowy transakcji. Uzyskuje się go poprzez wygenerowanie ciągu bajtów reprezentujących transakcję (o szczegółach budowy transakcji można przeczytać w poprzednim wpisie), a następnie podpisanie jego hasha (podwójne zastosowanie algorytmu SHA-256) kluczem prywatnym. PublicKey to nic innego jak wspomniany powyżej klucz publiczny odpowiadający prywatnemu.

Połączone ze sobą powyższe skrypty układają się w poniższy ciąg poleceń:

TransactionSignature PublicKey OP_DUP OP_HASH160 PublicKeyHash 
OP_EQUALVERIFY OP_CHECKSIG

Przeanalizujmy krok po kroku co tutaj ma miejsce 🙂

Komenda Komentarz
Zawartość stosu po wykonaniu
TransactionSignature Umieszczenie na stosie podpisu transakcji.
TransactionSignature
PublicKey Umieszczenie na stosie klucza publicznego.
TransactionSignature PublicKey
OP_DUP Duplikacja ostatniego elementu na stosie, w tym przypadku PublicKey.
TransactionSignature PublicKey PublicKey
OP_HASH160 Wykonanie na elemencie na szczycie stosu operacji SHA-256, a następnie RIPEM-160. Wynikiem takiej operacji jest PublicKeyHash.
TransactionSignature PublicKey PublicKeyHash
PublicKeyHash Wrzucenie na stos PublicKeyHash.
TransactionSignature PublicKey PublicKeyHash PublicKeyHash
OP_EQUALVERIFY Ściągnięcie ze stosu dwóch wartości i sprawdzenie, czy są sobie równe. Ta operacja wykazuje, że odbiorca transakcji przedstawia się tym samym adresem, na który nadawca wysłał środki.
TransactionSignature PublicKey
OP_CHECKSIG Operacja ta polega na wyliczeniu hasha transakcji poprzez podwójne zastosowanie algorytmu SHA-256, następnie podpisaniu wyniku kluczem PublicKey. Jeżeli wartość ta równa jest przedstawionej wartości TransactionSignature wyliczonej przez właściciela adresu odbiorcy, oznacza to, że udowodnił, że jest w posiadaniu klucza prywatnego i autoryzacja transakcji przebiegła pomyślnie.
true

I to wszystko 🙂 Wartość true na szczycie stosu oznacza, że skrypt wykonał się poprawnie i transakcję można uznać za autoryzowaną.

Sekrety zaszyte w blockchainie

Istnieje możliwość skonstruowania transakcji w taki sposób, aby środki z niej wychodzące nie trafiły do nikogo. Aby uzyskać taki efekt, wystarczy w skrypcie ScriptPubKey użyć słowa kluczowego OP_RETURN. Przerywa ono wykonanie skryptu z rezultatem niepowodzenia. Przykład takiej transakcji można znaleźć tutaj. W praktyce środki przeznaczone na wyjście mogą zostać wykorzystane jedynie przez kopalnię potwierdzającą transakcję.

Skonstruowanie skryptu z użyciem słowa OP_RETURN pozwala w ramach limitu 80 bajtów na umieszczenie dodatkowej informacji w transakcji. Może to być cokolwiek, co przyjdzie nam do głowy. Tak wysłana wiadomość pozostanie w obiegu dopóty, dopóki Bitcoin istnieje. Póki nic nie wskazuje na jego upadek – taka możliwość pobudza wyobraźnię. Można powiedzieć coś, co nigdy nie zostanie zamazane. Można zostawić dowód na istnienie stworzonego przez nas dokumentu (np. poprzez wygenerowanie jego skrótu kryptograficznego). Powstało już kilka serwisów, które zajmują się umieszczaniem takich informacji w sieci Bitcoina, np. Proof of Existence – strona ta po przesłaniu pliku wyliczy dla nas jego skrót i umieści w blockchainie ślad o jego istnieniu. Serwis Coin Secrets szuka na bieżąco transakcji zawierających OP_RETURN i wypisuje wiadomości w nich zaszyte. Jeżeli znajdziesz tam interesujący sekret – podziel się koniecznie w komentarzu 🙂

Implementacja

Implementacja skryptów jest stosunkowo prosta, bowiem każde polecenie w tym języku odpowiada konkretnej wartości reprezentowanej przez bajt. Więcej szczegółów na temat implementacji skryptów jak i całej transakcji pojawią się w następnych wpisach.


Źródła, z których korzystałem przygotowując wpis:
1. https://bitcoin.org/en/developer-documentation
2. https://en.bitcoin.it/wiki/Protocol_documentation
3. https://en.bitcoin.it/wiki/Script
4. http://www.slideshare.net/coinspark/bitcoin-2-and-opreturns-the-blockchain-as-tcpip