Wprowadzenie do Bitcoin Script
Strona Główna
Artykuły
Wprowadzenie do Bitcoin Script

Wprowadzenie do Bitcoin Script

Zaawansowany
Opublikowane Jul 10, 2020Zaktualizowane Jan 18, 2022
13m

Spis Treści


Wprowadzenie

Bitcoin jest czasem określany jako programowalny pieniądz. Ze względu na swój cyfrowy charakter pozwala użytkownikom na dużą elastyczność, jeśli chodzi o ustalanie warunków wydawania środków. 
Mówiąc o bitcoinach, mówimy o portfelach i monetach, ale moglibyśmy również myśleć o portfelach jak o kluczach, monetach jako czekach, a blockchainie jako o rzędzie zamkniętych sejfów. Każdy sejf ma małą dziurkę, dzięki czemu każdy może zdeponować czeki lub sprawdzić, jak wiele gotówki jest w sejfie. Jednak tylko posiadacz klucza będzie mógł uzyskać dostęp do wnętrza.

Kiedy posiadacz klucza chce dać pieniądze komuś innemu, odblokowuje swoje "pudełko". Tworzą nowy czek odwołujący się do starszego (który jest następnie niszczony) i zamyka go w pudle, które odbiorca może otworzyć. Aby go wydać, nowy odbiorca powtarza proces.

W tym artykule przyjrzymy się Script'owi, językowi programowania interpretowanemu przez węzły w sieci Bitcoina. Script rządzi mechanizmem blokowania/odblokowywania wyżej wymienionych sejfów.


Jak działa Bitcoin?

Korzystając z naszej analogii, można powiedzieć, że każda transakcja składa się z dwóch części - klucza (do odblokowania skrzynki) i blokady. Klucz służy do otwierania pola zawierającego czek, który chcesz wysłać, a następnie dodajesz nowy do nowego pudełka z inną blokadą. Aby wydać fundusze z nowego pudełka, potrzebujesz innego klucza.
Dosyć proste. Istnieje także pewna różnorodność blokad w systemie. Być może niektóre sejfy wymagają podania wielu kluczy, a inne mogą wymagać udowodnienia, że znasz określony sekret. Istnieje wiele warunków, które ludzie mogą ustawić. 
Naszym kluczem nazywamy scriptSig. Blokada to nasz scriptPubKey. Jeśli spojrzymy na te komponenty nieco bardziej szczegółowo, przekonamy się, że składają się one z kawałków danych i bloków kodu. Po połączeniu tworzą niewielki program.

Dokonując transakcji, transmitujesz tę kombinację do sieci. Każdy otrzymujący ją węzeł sprawdzi program, który poinformuje go, czy transakcja jest prawidłowa. Jeśli nie, zostanie ona po prostu odrzucona i nie będziesz mógł wydać zablokowanych środków.

Czeki (monety), które posiadasz, nazywane są niewydanymi wynikami transakcji (UTXO). Z funduszy może korzystać każdy, kto może dostarczyć klucz pasujący do blokady. Dokładniej kluczem jest scriptSig, a blokada to scriptPubKey.
Jeśli UTXO znajdują się w twoim portfelu, prawdopodobnie będą one miały warunek, że tylko osoba, która może udowodnić posiadanie tego klucza publicznego, jest w stanie odblokować te fundusze. Aby je odblokować, udostępniasz scriptSig, który zawiera podpis cyfrowy, za pomocą klucza prywatnego, będącego odwzorowaniem klucza publicznego określonego w scriptPubKey. Brzmi zawile? Nie martw się, wszystko to wkrótce stanie się jasne.


Zrozumienie stosu Bitcoina

Script to tak zwany język oparty na stosie. Wszystko to oznacza, że kiedy czytamy zestaw instrukcji, umieszczamy je w czymś, co można uznać za pionową kolumnę. Na przykład lista A, B, C spowoduje utworzenie stosu z A na dole i C na górze. Gdy instrukcje mówią nam, abyśmy coś zrobili, działamy na jednym lub kilku elementach leżących na górze stosu.


Elementy A, B i C dodawane i usuwane ze stosu.


Możemy rozróżnić dane (takie jak podpisy, hashe i klucze publiczne) od instrukcji (lub kodów operacyjnych). Instrukcje usuwają dane i w jakiś sposób je obsługują. Oto bardzo prosty przykład tego, jak mógłby wyglądać skrypt:
<xyz> <md5 hasher> <d16fb36f0911f878998c136191af705e> <check if equal>
Na czerwono mamy dane, a na niebiesko kody. Czytamy od lewej do prawej, więc najpierw umieszczamy ciąg <xyz> na stosie. Następny mamy kod operacji <md5 hasher>. Ten nie istnieje w Bitcoinie, ale powiedzmy, że usuwa górny element stosu (<xyz>) i hashuje go za pomocą algorytmu MD5. Następnie dane wyjściowe są ponownie dodawane do stosu. Dane wyjściowe to w tym wypadku d16fb36f0911f878998c136191af705e.
Co za zbieg okoliczności! Nasz następny element do dodania to <d16fb36f0911f878998c136191af705e>, więc teraz nasz stos ma dwa identyczne elementy. Na koniec <check if equal> wysuwa dwa elementy z góry i sprawdza, czy są one równe. Jeśli tak, dodaje <1> do stosu. Jeśli nie, dodaje <0>
Doszliśmy do końca naszej listy instrukcji. Nasz skrypt mógł zawieść na dwa sposoby - jeśli pozostały element był zerem lub jeśli jeden z operatorów spowodowałby awarię, gdy niektóre warunki nie zostały spełnione. W tym przykładzie nie mieliśmy takich operatorów i otrzymaliśmy element niezerowy (<1>), więc nasz skrypt był prawidłowy. Te zasady obowiązują również w przypadku prawdziwych transakcji Bitcoina.

To był tylko wymyślony program. Przyjrzyjmy się teraz realnym przykładom.


Pay-to-Pubkey (P2PK)

Pay-to-Pubkey (P2PK) jest niezwykle prosty. Polega na zablokowaniu środków na określonym kluczu publicznym. Jeśli chcesz otrzymać środki w ten sposób, przekaż nadawcy swój klucz publiczny, a nie adres Bitcoin.

Pierwszą transakcją między Satoshi Nakamoto i Halem Finneyem w 2009 roku była transakcja P2PK. Struktura była intensywnie używana na początku istnienia Bitcoina, ale obecnie w dużej mierze zastąpiła ją metoda Pay-to-Pubkey-Hash (P2PKH). 
Skrypt blokujący dla transakcji P2PK ma format <public key>OP_CHECKSIG. To proste. Być może zgadłeś, że OP_CHECKSIG sprawdza podpis pod podanym kluczem publicznym. W związku z tym nasz scriptSig będzie prostym <signature>. Pamiętaj, scriptSig jest kluczem do zamka.



Nie ma nic prostszego. Do stosu dodawany jest podpis, a następnie klucz publiczny. OP_CHECKSIG usuwa je oba i weryfikuje podpis względem klucza publicznego. Jeśli się zgadzają, dodaje <1> do stosu. W przeciwnym razie dodaje <0>.

Z powodów, które omówimy w następnej sekcji, P2PK nie jest już tak naprawdę używany.


Pay-to-Pubkey-Hash (P2PKH)

Pay-to-Pubkey-Hash (P2PKH) jest obecnie najpopularniejszym rodzajem transakcji. O ile nie robisz nic, aby pobrać archaiczne oprogramowanie, Twój portfel prawdopodobnie obsługuje go domyślnie.

ScriptPubKey w P2PKH jest następujący:

OP_DUP OP_HASH160 <public key hash> (hash klucza publicznego) OP_EQUALVERIFY OP_CHECKSIG

Zanim wprowadzimy scriptSig, zobaczmy, co zrobią nowe kody operacyjne:


OP_DUP

OP_DUP wyciąga pierwszy element i kopiuje go. Następnie dodaje oba z powrotem do stosu. Zazwyczaj robi się to, abyśmy mogli wykonać operację na duplikacie, nie wpływając na oryginał.


OP_HASH160

Wyciąga pierwszy element i dwa razy go hashuje. W pierwszej rundzie zostanie zaszyfrowany algorytmem SHA-256. Wyjście SHA-256 jest następnie hashowane za pomocą algorytmu RIPEMD-160. Końcowy wynik jest dodawany z powrotem na stos.


OP_EQUALVERIFY

OP_EQUALVERIFY łączy dwa inne operatory - OP_EQUAL i OP_VERIFY. OP_EQUAL wyciąga dwa elementy i sprawdza, czy są one identyczne. Jeśli tak, dodaje 1 do stosu. Jeśli nie, dodaje 0. OP_VERIFY wyciąga górny element i sprawdza, czy zwraca Prawdę (tzn. jest niezerowy). Jeśli tak nie jest, transakcja kończy się niepowodzeniem. Złączone, OP_EQUALVERIFY powoduje, że transakcja kończy się niepowodzeniem, jeśli dwa górne elementy nie pasują do siebie.

Tym razem scriptSig wygląda tak:

<signature> <public key>

Musisz podać podpis i odpowiedni klucz publiczny, aby odblokować wyjścia P2PKH.



Możesz zobaczyć, co się dzieje w powyższym GIF-ie. Nie różni się to zbytnio od skryptu P2PK. Dodajemy tylko kolejny krok, aby sprawdzić, czy klucz publiczny pasuje do hashu w skrypcie.

Jest jednak coś wartego zauważenia. W skrypcie blokującym P2PKH klucz publiczny nie jest widoczny - widzimy tylko jego hash. Jeśli przejdziemy do eksploratora blockchain i spojrzymy na dane wyjściowe P2PKH, które nie zostały wydane, nie możemy ustalić klucza publicznego. Jest ujawniany tylko wtedy, gdy odbiorca decyduje się na przekazanie środków.
Ma to kilka zalet. Po pierwsze, hash klucza publicznego jest po prostu łatwiejszy do przekazania niż pełny klucz publiczny. Właśnie z tego powodu Satoshi uruchomił go w 2009 roku. Hash klucza publicznego to ta rzecz którą dzisiaj nazywamy adresem Bitcoin.
Drugą korzyścią jest to, że hashe klucza publicznego mogą zapewnić dodatkową warstwę zabezpieczeń przed obliczeniami kwantowymi. Ponieważ nasz klucz publiczny nie jest znany, dopóki nie wydamy funduszy, innym osobom jeszcze trudniej jest odgadnąć klucz prywatny. Musieliby odwrócić dwie rundy hashowania (RIPEMD-160 i SHA-256), aby je zdobyć.



Pay-to-Script-Hash (P2SH)

Pay-to-Script-Hash (P2SH) był bardzo interesującym rozwinięciem dla Bitcoina. Pozwala on nadawcy zablokować środki na hashu skryptu - nie muszą oni wiedzieć, co faktycznie robi skrypt. Weźmy następujący hash SHA-256:

e145fe9ed5c23aa71fdb443de00c7d9b4a69f8a27a2e4fbb1fe1d0dbfb6583f1

Nie musisz znać wejścia hashu, aby zablokować fundusze. Wydający musi jednak dostarczyć skrypt użyty do tego hashowania i spełnić warunki tego skryptu.

Powyższy hash został utworzony z następującego skryptu:

<multiply by 2> <4> <check if equal>

Jeśli chcesz wydać monety powiązane z tym scriptPubKey, nie tylko udostępniasz te polecenia. Potrzebujesz także scriptSig, który sprawi, że wartość ukończonego skryptu będzie Prawdą. W tym przykładzie jest to element który musisz (pomnożyć przez 2) <multiply by 2>, aby otrzymać wynik <4>. Oczywiście oznacza to, że nasz scriptSig to po prostu <2>.

W rzeczywistości skrypt scriptPubKey dla wyniku P2SH to:

OP_HASH160 <redeemScript hash> OP_EQUAL

Nie ma tu nowych operatorów, ale mamy <redeemScript hash> jako nowy element. Jak sama nazwa wskazuje, jest to hash skryptu, który musimy dostarczyć, aby zrealizować fundusze (zwany redeemScript). ScriptSig zmieni się w zależności od tego, co jest w redeemScript. Ogólnie jednak okaże się, że jest to kombinacja podpisów i dołączonych kluczy publicznych, po których następuje (obowiązkowy) redeemScript:

<signature> <public key> <redeemScript>

Nasza ewaluacja różni się teraz nieco od "wykonania" stosu, które widzieliśmy do tej pory. Dzieje się to w dwóch częściach. W pierwszej sprawdzamy po prostu, czy podałeś poprawny hash.



Zauważ, że nie robimy nic z elementami poprzedzającymi redeemScript. W tym momencie nie są używane. Dotarliśmy do końca tego miniprogramu, a najwyższy element jest niezerowy. To znaczy, że jest poprawny.

Jednak jeszcze nie skończyliśmy. Węzły sieciowe rozpoznają tę strukturę jako P2SH, więc faktycznie posiadają elementy scriptSig oczekujące na innym stosie. Tam właśnie będzie używany podpis i klucz publiczny.
Jak dotąd potraktowaliśmy redeemScript jako element, lecz teraz będzie on interpretowany jako instrukcje, które mogą być czymkolwiek. Weźmy przykład skryptu blokującego P2PKH, do którego musimy podać <signature> i <public key> pasujące do <public key hash> wewnątrz <redeemScript>.



Po rozszerzeniu redeemScriptu możesz zauważyć, że mamy sytuację, która wygląda dokładnie jak zwykła transakcja P2PKH. Z tego miejsca po prostu uruchamiamy go tak, jak normalnie.

Pokazaliśmy tutaj tak zwany skrypt P2SH (P2PKH), ale jest mało prawdopodobne, abyś znalazł go "na wolności". Nic nie stoi na przeszkodzie, abyś go stworzył, ale nie daje on żadnych dodatkowych korzyści i ostatecznie zajmuje więcej miejsca w bloku (a zatem kosztuje więcej).

P2SH zazwyczaj przydaje się w takich transakcjach, jak multisignature lub kompatybilnych z SegWit. Transakcje Multisig mogą być bardzo duże, ponieważ czasem wymagają wielu kluczy. Przed wdrożeniem funkcji Pay-to-Script-Hash nadawca musiałby wymienić wszystkie możliwe klucze publiczne w swoim skrypcie blokującym. 

W przypadku P2SH nie ma znaczenia, jak skomplikowane są warunki wydatkowania. Hash redeemScriptu ma zawsze stały rozmiar. Koszty są zatem przenoszone na użytkowników, którzy chcą odblokować skrypt blokujący.

Kompatybilność z SegWit to kolejny przypadek, w którym przydaje się P2SH (omówimy szczegółowo różnice w strukturze transakcji w następnej sekcji). SegWit był soft-forkiem, który spowodował zmianę formatów bloków/transakcji. Ponieważ jest to aktualizacja opcjonalna, nie każdy software'owy portfel rozpoznaje zmiany.

To nie ma znaczenia, czy klienci zwijają hash skryptu SegWit w P2SH. Podobnie jak w przypadku wszystkich tego typu transakcji, nie muszą wiedzieć, jaki będzie odblokowujący redeemScript.


Transakcje SegWit (P2WPKH i P2WSH)

Bardziej kompleksowe wprowadzenie do SegWit znajduje się w Przewodniku dla Początkujących Dotyczącym Segregated Witness.
Aby zrozumieć format transakcji w SegWit, musisz wiedzieć, że nie mamy już tylko scriptSig i scriptPubKey. Teraz pojawia się nam nowe pole zwane świadkiem (witness). Dane, które przechowywaliśmy w scriptSig, są przenoszone do świadka, więc scriptSig jest pusty.

Jeśli natrafisz na adresy zaczynające się na „bc1”, nazywamy je rodzimymi SegWit (w przeciwieństwie do tylko zgodnych z SegWit, które zaczynają się na „3”, ponieważ są to adresy P2SH).


Pay-to-Witness-Pubkey-Hash (P2WPKH)

Pay-to-Witness-Pubkey-Hash (P2WPKH) to SegWit'owa wersja P2PKH. Nasz świadek wygląda następująco:

<signature> <public key>

Zauważ, że jest to dokładnie to samo, co scriptSig z P2PKH. Tutaj scriptSig jest pusty. Tymczasem scriptPubKey przypomina:

<OP_0> <public key hash>

Wygląda to nieco dziwnie, prawda? Gdzie są kody, które pozwalają nam porównać podpis, klucz publiczny i jego hash?

Nie pokazujemy tutaj dodatkowych operatorów, ponieważ węzły, które otrzymują transakcję, wiedzą, co z nią zrobić na podstawie długości <public key hash>. Obliczą długość i zrozumieją, że musi być prowadzona w tym samym stylu, co dobra, tradycyjna transakcja P2PKH.
Niezaktualizowane węzły nie wiedzą, jak interpretować transakcję w ten sposób, ale to nie ma znaczenia. Zgodnie ze starymi zasadami nie ma świadka, więc czytają pusty scriptSig i niektóre dane. Oceniają to i określają jako ważne - jeśli o to chodzi, każdy może wydać otrzymany wynik (output). Dlatego SegWit jest uważany za soft fork ze wsteczną kompatybilnością.


Pay-to-Witness-Script-Hash (P2WSH)

Hash Pay-to-Witness-Script (P2WSH) to nowy P2SH. Jeśli dotarłeś tak daleko, prawdopodobnie możesz się domyślić, jak to będzie wyglądać, niemniej i tak przez to przejdziemy. Naszym świadkiem jest to, co zwykle umieszczamy w scriptSigu. Na przykład w P2WSH, który zwija transakcję P2PKH, może wyglądać mniej więcej tak:

<signature 1> <public key>

Oto nasz scriptPubKey:

<OP_0> <script hash>

Obowiązują te same zasady. Węzły SegWit odczytują długość hashu skryptu i określają, że jest to wyjście P2WSH, które ewaluuje się podobnie jak P2SH. Tymczasem stare węzły postrzegają to jako wynik, który każdy może wydać.


Przemyślenia końcowe

W tym artykule dowiedzieliśmy się trochę o elementach składowych Bitcoina. Podsumujmy je szybko:


Typ skryptuOpis

Pay-to-Pubkey (P2PK)

Zamyka fundusze na określonym kluczu publicznym

Pay-to-Pubkey-Hash (P2PKH)

Zamyka fundusze na określonym hashu klucza publicznego (np. adresie)

Pay-to-Script-Hash (P2SH)

Zamyka fundusze na hashu skryptu, który może dostarczyć odbiorca

Pay-to-Witness-Pubkey-Hash (P2WPKH)

SegWit'owa wersja P2PK

Pay-to-Witness-Script-Hash (P2WSH)

SegWit'owa wersja P2SH


Gdy brniesz coraz głębiej w Bitcoina, zaczynasz rozumieć, dlaczego ma on tak duży potencjał. Transakcje mogą składać się z wielu różnych składników. Manipulując tymi elementami, użytkownicy mają dużą elastyczność, jeśli chodzi o ustalanie warunków, w jaki sposób i kiedy można wydawać środki.