Въведение в Bitcoin Script
Начало
Статии
Въведение в Bitcoin Script

Въведение в Bitcoin Script

Средно ниво
Публикувано Jul 10, 2020Актуализирано Jan 18, 2022
13m

Въведение

Биткойн понякога се нарича програмируеми пари. Поради своята дигитална природа, той позволява на потребителите голяма степен на гъвкавост, когато става въпрос за определяне на условията относно начина на изразходване на средствата. 
Когато обсъждаме Биткойн, говорим за портфейли и криптовалути. Но можем също да възприемем портфейлите като ключове, криптовалутите като чекове и блокчейна като ред след ред заключени сейфове. Всеки сейф има тънък слот, така че всеки може да депозира чекове или да погледне, за да види каква стойност съдържа сейфът. Въпреки това, само притежателят на ключа има достъп до вътрешността.

Когато притежател на ключа иска да даде пари на някого, той отключва кутията си. Прави се нова проверка, която препраща към по-старата (която след това се унищожава) и се заключва в кутия, която получателят може да отвори. За да го похарчи, новият получател повтаря процеса.

В тази статия ще разгледаме по-отблизо скрипта, езикът за програмиране, интерпретиран от възли в мрежата на Биткойн. Скриптът е това, което управлява механизма за заключване/отключване, споменат за сейфовете.


Как работи биткойн?

Продължавайки с нашата аналогия от по-горе, може да се каже, че всяка трансакция има две части – ключ (за отключване на кутията ви) и ключалка. Използвате своя ключ, за да отворите кутията, съдържаща чека, който искате да изпратите, и след това добавяте нов в нова кутия с различно заключване. За да харчите от новата кутия, ви трябва друг ключ.
Достатъчно просто. Можете също да получите малко вариации за видовете ключалки в системата. Възможно е някои сейфове да изискват да предоставите няколко ключа, а други - да докажете, че знаете тайна. Има куп условия, които хората могат да поставят. 
Нашият ключ е това, което наричаме scriptSig. Ключалката е нашият scriptPubKey. Ако разгледаме тези компоненти малко по-подробно, ще открием, че те всъщност са съставени от битове данни и блокове код. Когато се комбинират, те създават малка програма.

Когато правите трансакция, вие излъчвате тази комбинация към мрежата. Всеки възел, който я получава, ще провери програмата, която му казва дали трансакцията е валидна. Ако не, тя просто ще бъде отхвърлена и няма да можете да изразходвате заключените средства.

Чековете (криптовалутите), които държите, се наричат неизразходвани резултати от трансакции (UTXO). Средствата могат да бъдат използвани от всеки, който може да предостави ключа, пасващ на ключалката. По-конкретно, ключът е scriptSig, а ключалката е scriptPubKey.
Ако UTXO са във вашия портфейл, те вероятно ще имат условие, което казва, че само лицето, което може да докаже, че притежава този публичен ключ, може да отключи средствата. За да го отключите, предоставяте scriptSig, включващ дигитален подпис, като използвате частния ключ, който се свързва с публичния ключ, посочен в scriptPubKey. Всичко това скоро ще стане по-ясно.


Разбиране на стека на Биткойн

Скриптът представлява така известния език, базиран на стек. Всичко това означава, че когато четем набор от инструкции, ние ги поставяме в т.нар. вертикална колона. Списъкът A, B, C, например, би довел до стек с A в долната част и C в горната част. Когато инструкциите ни казват да направим нещо, ние оперираме с един или повече елементи, започващи от горната част на стека.


Елементи A, B и C се добавят и „изскачат“ от стека.


Можем да направим разграничение между данните (неща като подписи, хешове и публични ключове) и инструкциите (или операционни кодове). Инструкциите премахват данни и правят нещо с тях. Ето един много прост пример за това как може да изглежда един скрипт:
<xyz> <md5 hasher> <d16fb36f0911f878998c136191af705e> <check if equal>
В червено имаме данни, а в синьо имаме операционни кодове. Четем отляво надясно, така че първо поставяме низа <xyz> в стека. Следва операционният код <md5 hasher>. Този не съществува в Биткойн, но да кажем, че премахва горния елемент на стека (<xyz>) и го хешира с помощта на алгоритъма MD5. След това резултатът се добавя обратно към стека. Резултатът тук е d16fb36f0911f878998c136191af705e.
Какво съвпадение! Следващият ни елемент за добавяне е <d16fb36f0911f878998c136191af705e>, така че сега нашият стек има два еднакви елемента. И накрая, <check if equal> изважда два елемента от горната част и проверява дали са равни. Ако са равни, добавя <1> към стека. Ако не са, добавя <0>
Стигнахме до края на нашия списък с инструкции. Нашият скрипт може да се провали по два начина – ако оставащият елемент е нула, или ако някой от операторите го направи неуспешен при неизпълнението на някои условия. В този пример нямахме такива оператори и в крайна сметка получаваме ненулев елемент (<1>), така че нашият скрипт е валиден. Тези правила са валидни и за реални биткойн трансакции.

Това беше просто измислена програма. Нека сега да разгледаме някои действителни програми.


Pay-to-Pubkey (P2PK)

Pay-to-Pubkey (P2PK) е изключително ясна. Тя включва заключване на средства към определен публичен ключ. Ако искате да получавате средства по този начин, трябва да предоставите на подателя своя публичен ключ, а не биткойн адрес. 

Първата трансакция между Сатоши Накамото и Хал Фини през 2009 г. беше P2PK. Структурата е била широко използвана в ранните дни на биткойн, но понастоящем до голяма степен е заменена от Pay-to-Pubkey-Hash (P2PKH). 
Скриптът за заключване на P2PK трансакция следва формата на <public key> OP_CHECKSIG. Достатъчно просто. Може би сте се досетили, че OP_CHECKSIG проверява за подпис срещу предоставения публичен ключ. Като такъв, нашият scriptSig ще бъде обикновен<signature> (подпис). Не забравяйте, че scriptSig е ключът към ключалката.



Не става много по-просто от това. Към стека се добавя подпис, последван от публичен ключ. OP_CHECKSIG извежда и двете и проверява подписа спрямо публичния ключ. Ако съвпадат, той добавя <1> към стека. В противен случай добавя <0>.

По причини, които ще разгледаме по-подробно в следващия раздел, P2PK вече не се използва.


Pay-to-Pubkey-Hash (P2PKH)

Pay-to-Pubkey-Hash (P2PKH) сега е най-често срещаният тип трансакция. Освен ако не полагате усилия да изтеглите архаичен софтуер, портфейлът ви вероятно прави това по подразбиране.

scriptPubKey в P2PKH е следният:

OP_DUP OP_HASH160 <public key hash> OP_EQUALVERIFY OP_CHECKSIG

Преди да представим scriptSig, нека да разберем какво ще правят новите операционни кодове:


OP_DUP

OP_DUP извежда първия елемент и го дублира. След това добавя и двете обратно към стека. Обикновено това се прави, за да можем да извършим операция върху дубликата, без да засягаме оригинала.


OP_HASH160

Това извежда първия елемент и го хешира два пъти. Първият кръг ще хешира с алгоритъма SHA-256. След това изходът на SHA-256 се хешира с алгоритъма RIPEMD-160. Полученият изход се добавя обратно към стека.


OP_EQUALVERIFY

OP_EQUALVERIFY комбинира два други оператора – OP_EQUAL и OP_VERIFY. OP_EQUAL извежда два елемента и проверява дали са идентични. Ако са идентични, добавя 1 към стека. Ако не са, добавя 0. OP_VERIFY извежда горния елемент и проверява дали е „Вярно“ (т.е. не е нула). Ако не е, трансакцията се проваля. Комбинирано, OP_EQUALVERIFY причинява неуспех на трансакцията, ако горните два елемента не съвпадат.

Този път scriptSig изглежда така:

<signature> <public key>

Трябва да предоставите подпис и съответния публичен ключ, за да отключите P2PKH изходи.



Можете да видите какво се случва в горния GIF. Не се различава твърде много от скрипт P2PK. Просто добавяме допълнителна стъпка, за да проверим дали публичният ключ съответства на хеша в скрипта.

Все пак има какво да се отбележи. В скрипт за заключване на P2PKH публичният ключ не се вижда – можем да видим само неговия хеш. Ако отидем в блокчейн търсачка и погледнем P2PKH изход, който не е изразходван, не можем да определим публичния ключ. Това се разкрива само когато получателят реши да преведе средствата.
Това има няколко предимства. Първо, хеширането на публичния ключ е просто по-лесно за предаване, отколкото пълен публичен ключ. Сатоши го стартира през 2009 г. точно по тази причина. Хешът на публичния ключ е това, което днес познаваме като биткойн адрес.
Второто предимство е, че хешовете на публичния ключ могат да осигурят допълнителен слой сигурност срещу квантовите изчисления. Тъй като нашият публичен ключ не е известен, докато не изразходваме средствата, за другите е още по-трудно да изчислят частния ключ. Те ще трябва да обърнат двата кръга на хеширане (RIPEMD-160 и SHA-256), за да го получат.



Pay-to-Script-Hash (P2SH)

Pay-to-Script-Hash (P2SH) беше много интересно развитие за Биткойн. Позволява на подателя да заключи средства към хеша на скрипт – не е необходимо да знае какво всъщност прави скриптът. Вземете следния хеш SHA-256:

e145fe9ed5c23aa71fdb443de00c7d9b4a69f8a27a2e4fbb1fe1d0dbfb6583f1

Не е нужно да знаете входа на хеша, за да заключите средства към него. Но лицето, което ща изразходва средствата, трябва да предостави скрипта, който е бил използван за хеширането му, и трябва да отговаря на условията на този скрипт.

Горният хеш е създаден от следния скрипт:

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

Ако искате да похарчите криптовалутите, свързани с този scriptPubKey, вие не само предоставяте тези команди. Необходим ви е scriptSig, който кара завършения скрипт да се оценява на „Вярно“. В този пример това е елемент, който <се умножава по 2>, за да даде резултат <4>. Разбира се, това означава, че нашият scriptSig е само <2>.

В реалния живот scriptPubKey за изход P2SH е:

OP_HASH160 <redeemScript hash> OP_EQUAL

Тук няма нови оператори. Но ние имаме <redeemScript hash> като нов елемент. Както подсказва името, това е хеш на скрипта, който трябва да предоставим, за да осребрим средствата (наречен redeemScript). scriptSig ще се промени в зависимост от това какво има в redeemScript. Като цяло обаче ще откриете, че това е някаква комбинация от подписи и прикачените публични ключове, последвани от (задължителния) redeemScript:

<signature> <public key> <redeemScript>

Нашата оценка се различава малко от изпълнението на стека, което сме виждали досега. Случва се в две части. Първият просто проверява дали сте предоставили правилния хеш. 



Ще забележите, че ние не правим нищо с елементите, предхождащи redeemScript. На този етап не се използват. Стигнахме до края на тази мини програма и горният елемент е различен от нула. Това означава, че е валидно.

Все още обаче не сме готови. Мрежовите възли разпознават тази структура като P2SH, така че всъщност имат елементите на scriptSig, които чакат в друг стек. Там ще се използват подписът и публичният ключ.
Досега третирахме redeemScript като елемент. Но сега това ще се тълкува като инструкции, които могат да бъдат всякакви. Да вземем например скрипт за заключване на P2PKH, на който трябва да предоставим <signature> (подпис) и <public key> (публичен ключ), които съответстват на <public key hash> (хеш на публичен ключ) вътре в <redeemScript>.



След като вашият redeemScript бъде разширен, можете да видите, че имаме ситуация, която изглежда точно като обикновена P2PKH трансакция. Оттам нататък просто го стартирате, както бихте направили с обикновен.

Тук демонстрирахме това, което се нарича P2SH(P2PKH) скрипт, но е малко вероятно да намерите такъв в реална ситуация. Нищо не ви пречи да направите такъв, но това не ви дава допълнителни предимства и в крайна сметка заема повече място в блок (и следователно струва повече).

P2SH обикновено е полезен за трансакции с много подписи или съвместими със SegWit. Трансакциите с много подписи могат да бъдат значителни по размер, тъй като може да изискват множество ключове. Преди внедряването на Pay-to-Script-Hash, подателят ще трябва да изброи всички възможни публични ключове в своя скрипт за заключване. 

Но при P2SH няма значение колко сложни са условията за разход. Хешът на redeemScript винаги е с фиксиран размер. Следователно разходите се прехвърлят върху потребителя(ите), които искат да отключат скрипта за заключване.

Съвместимостта със SegWit е друг случай, в който P2SH е полезен (подробностите за различието на структурата на трансакцията ще разгледаме в следващия раздел). SegWit беше мека разклонение, която доведе до промяна на форматите за блок/трансакция. Тъй като това е надстройка с възможност за включване, не всички софтуерни портфейли разпознават промените.

Няма значение дали клиентите обвиват хеша на скрипта SegWit в P2SH. Както при всички трансакции от този тип, не е нужно да се знае какъв ще бъде отключващият redeemScript. 


SegWit трансакции (P2WPKH и P2WSH)

За по-изчерпателно въведение в SegWit, вижте Ръководство за начинаещи за сегрегирани свидетели.
За да разберете формата на трансакцията в SegWit, просто трябва да знаете, че вече нямаме само scriptSig и scriptPubKey. Сега имаме и ново поле, наречено свидетелят. Данните, които съхранявахме в scriptSig, се преместват в свидетеля, така че scriptSig е празен.

Ако сте срещали адреси, започващи с „bc1“, те са това, което наричаме SegWit-native (за разлика от само съвместимите със SegWit, които започват с „3“, тъй като са адреси P2SH).


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

Pay-to-Witness-Pubkey-Hash (P2WPKH) е SegWit версията на P2PKH. Нашият свидетел изглежда така:

<signature> <public key>

Ще забележите, че това е същото като scriptSig от P2PKH. Тук scriptSig е празен. Междувременно scriptPubKey прилича на следното:

<OP_0> <public key hash>

Това изглежда малко странно, нали? Къде са операционните кодове, за да можем да сравним подписа, публичния ключ и неговия хеш?

Тук не показваме допълнителни оператори, защото възлите, които получават трансакцията, знаят какво да правят с нея въз основа на дължината на <хеш на публичен ключ>. Те ще изчислят дължината и ще разберат, че тя трябва да се изпълнява в същия стил като добрата стара трансакция P2PKH.
Ненадстроените възли не знаят как да интерпретират трансакцията по този начин, но това няма значение. Според старите правила няма свидетел, така че се чете празен scriptSig и някои данни. Те оценяват това и го отбелязват като валидно – що се отнася до тях, всеки може да изразходва резултата. Ето защо SegWit се счита за обратно съвместимо меко разклонение.


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

Pay-to-Witness-Script Hash (P2WSH) е новият P2SH. Ако сте стигнали дотук, вероятно можете да разберете как ще изглежда това, но все пак ще го разгледаме. Нашият свидетел е това, което обикновено поставяме в scriptSig. В P2WSH, който обвива P2PKH трансакция, например, може да изглежда така:

<signature 1> <public key>

Ето нашия scriptPubKey:

<OP_0> <script hash>

Важат същите правила. SegWit възлите четат дължината на хеша на скрипта и определят, че това е P2WSH резултат, който се оценява подобно на P2SH. Междувременно старите възли просто го виждат като резултат, който всеки може да изразходва.


Заключителни мисли

В тази статия научихме малко за градивните елементи на Биткойн. Нека ги обобщим набързо:


Тип на скриптаОписание

Pay-to-Pubkey (P2PK)

Заключва средства към конкретен публичен ключ

Pay-to-Pubkey-Hash (P2PKH)

Заключва средства към конкретен хеш на публичен ключ (т.е. адрес)

Pay-to-Script-Hash (P2SH)

Заключва средства към хеша на скрипт, който получателят може да предостави

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

SegWit версията на P2PK

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

SegWit версията на P2SH


След като се задълбочите в Биткойн, започвате да разбирате защо той има толкова голям потенциал. Трансакциите могат да бъдат съставени от много различни компоненти. Чрез манипулиране на тези градивни елементи, потребителите имат голяма гъвкавост по отношение на определянето на условията за начина и времето на изразходване на средствата.