Введение

Биткойн иногда называют программируемыми деньгами. Благодаря своей цифровой природе он предоставляет пользователям большую степень гибкости, когда дело доходит до установления условий расходования средств.

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

Когда владелец ключа хочет передать деньги кому-то другому, он отпирает свою шкатулку. Они оформляют новый чек, ссылающийся на старый (который затем уничтожается), и помещают его в ящик, который может открыть получатель. Чтобы потратить их, новый получатель повторяет процесс.

В этой статье мы подробнее рассмотрим Script, язык программирования, интерпретируемый узлами сети Биткойн. Скрипт — это то, что управляет механизмом блокировки/разблокировки сейфов.


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

Если воспользоваться нашей аналогией, приведенной выше, можно сказать, что каждая транзакция состоит из двух частей — ключа (для разблокировки вашего ящика) и замка. Вы используете свой ключ, чтобы открыть ящик, содержащий чек, который хотите отправить, а затем добавляете новый в новый ящик с другим замком. Чтобы потратить из нового ящика, нужен еще один ключ.

Достаточно просто. Вы также можете немного изменить типы замков в системе. Возможно, для некоторых сейфов требуется предоставить несколько ключей, а для других требуется, чтобы вы доказали, что знаете секрет. Есть куча условий, которые люди могут поставить.

Наш ключ — это то, что мы называем scriptSig. Замок — это наш скриптPubKey. Если мы посмотрим на эти компоненты более подробно, мы обнаружим, что они на самом деле состоят из битов данных и блоков кода. В сочетании они создают крошечную программу.

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

Чеки (монеты), которые вы держите, называются неизрасходованными выходами транзакций (UTXO). Средствами может воспользоваться любой, кто сможет предоставить ключ, подходящий к замку. В частности, ключ — это scriptSig, а блокировка — scriptPubKey.

Если UTXO находятся в вашем кошельке, они, вероятно, будут иметь условие, согласно которому только человек, который может доказать право собственности на этот открытый ключ, может разблокировать эти средства. Чтобы разблокировать его, вы предоставляете scriptSig, включающий цифровую подпись, используя закрытый ключ, который сопоставляется с открытым ключом, указанным в scriptPubKey. Вскоре все станет яснее.


Понимание стека Биткойн

Сценарий — это так называемый стековой язык. Все это означает, что когда мы читаем набор инструкций, мы помещаем их в вертикальный столбец. Например, список A, B, C приведет к созданию стека с A внизу и C вверху. Когда инструкции говорят нам что-то сделать, мы работаем с одним или несколькими элементами, начиная с вершины стека.


Elements A, B, and C being added and “popped” from the stack.

Элементы A, B и C добавляются и «извлекаются» из стека.


Мы можем различать данные (такие как подписи, хеши и открытые ключи) и инструкции (или коды операций). По инструкции удалить данные и что-то с ними сделать. Вот очень простой пример того, как может выглядеть скрипт:

<xyz> <md5 hasher> <d16fb36f0911f878998c136191af705e> <проверить равенство>

Красным цветом показаны данные, а синим — коды операций. Мы читаем слева направо, поэтому сначала помещаем строку <xyz> в стек. Далее идет код операции <md5 hasher>. В Биткойне такого не существует, но, скажем, он удаляет верхний элемент стека (<xyz>) и хеширует его с помощью алгоритма MD5. Затем выходные данные добавляются обратно в стек. Выход здесь будет d16fb36f0911f878998c136191af705e.

Какое совпадение! Следующий элемент, который мы добавим, — <d16fb36f0911f878998c136191af705e>, так что теперь в нашем стеке есть два одинаковых элемента. Наконец, <check if равно> извлекает два элемента сверху и проверяет, равны ли они. Если да, то в стек добавляется <1>. Если нет, то добавляется <0>.

Мы подошли к концу нашего списка инструкций. Наш скрипт мог потерпеть неудачу по двум причинам: если бы оставшийся элемент был нулевым, или если бы один из операторов вызвал его сбой, когда некоторые условия не были выполнены. В этом примере таких операторов не было, и в итоге мы получили ненулевой элемент (<1>), поэтому наш скрипт был валидным. Эти правила справедливы и для реальных транзакций биткойнов.

Это была всего лишь выдуманная программа. Давайте теперь посмотрим на некоторые реальные.


Оплата по ключу (P2PK)

Оплата по Pubkey (P2PK) невероятно проста. Он предполагает привязку средств к определенному открытому ключу. Если вы хотите получить средства таким образом, вы должны предоставить отправителю свой открытый ключ, а не биткойн-адрес.

Самая первая транзакция между Сатоши Накамото и Хэлом Финни в 2009 году была P2PK. Эта структура активно использовалась на заре существования Биткойна, но в настоящее время ее в значительной степени заменила Pay-to-Pubkey-Hash (P2PKH).

Скрипт блокировки транзакции P2PK имеет формат <public key> OP_CHECKSIG. Достаточно просто. Вы могли догадаться, что OP_CHECKSIG проверяет подпись по предоставленному открытому ключу. Таким образом, наш скриптSig будет простой <signature>. Помните, что scriptSig — это ключ к замку.


A signature gets added to the stack, followed by a public key. OP_CHECKSIG pops them both and verifies the signature against the public key. If they match, it adds a <1> to the stack. Otherwise, it adds a <0>


Это не может быть намного проще, чем это. В стек добавляется подпись, за которой следует открытый ключ. OP_CHECKSIG извлекает их обоих и сверяет подпись с открытым ключом. Если они совпадают, в стек добавляется <1>. В противном случае добавляется <0>.

По причинам, которые мы подробно рассмотрим в следующем разделе, P2PK больше не используется.


Оплата по Pubkey-Hash (P2PKH)

Pay-to-Pubkey-Hash (P2PKH) в настоящее время является наиболее распространенным типом транзакции. Если вы не собираетесь загружать устаревшее программное обеспечение, ваш кошелек, скорее всего, делает это по умолчанию.

scriptPubKey в P2PKH выглядит следующим образом:

OP_DUP OP_HASH160 <хеш открытого ключа> OP_EQUALVERIFY OP_CHECKSIG

Прежде чем представить scriptSig, давайте разберем, что будут делать новые коды операций:


ОП_ДУП

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 приводит к сбою транзакции, если два верхних элемента не совпадают.

На этот раз скриптSig выглядит так:

<signature> <public key>

Вам необходимо предоставить подпись и соответствующий открытый ключ для разблокировки выходов P2PKH.


We’re just adding an extra step to check that the public key matches the hash in the script


Вы можете увидеть, что происходит на GIF-изображении выше. Он не слишком отличается от сценария P2PK. Мы просто добавляем дополнительный шаг для проверки соответствия открытого ключа хешу в скрипте.

Однако есть что отметить. В сценарии блокировки P2PKH открытый ключ не виден — мы можем видеть только его хэш. Если мы обратимся к обозревателю блокчейна и посмотрим на неизрасходованные выходные данные P2PKH, мы не сможем определить открытый ключ. Это становится известно только тогда, когда получатель решает перевести средства.

Это имеет несколько преимуществ. Во-первых, хеш открытого ключа проще передать, чем полный открытый ключ. Сатоши запустил его в 2009 году именно по этой причине. Хэш открытого ключа — это то, что мы сегодня знаем как биткойн-адрес.

Второе преимущество заключается в том, что хэши открытых ключей могут обеспечить дополнительный уровень безопасности от квантовых вычислений. Поскольку наш открытый ключ неизвестен, пока мы не потратим средства, другим еще сложнее вычислить закрытый ключ. Чтобы получить его, им пришлось бы поменять местами два раунда хеширования (RIPEMD-160 и SHA-256).


➠ Хотите начать работу с криптовалютой? Купите биткойны на Binance!


Хэш оплаты по сценарию (P2SH)

Pay-to-Script-Hash (P2SH) стал очень интересной разработкой для Биткойна. Это позволяет отправителю привязывать средства к хешу скрипта – ему не нужно знать, что на самом деле делает скрипт. Возьмите следующий хэш SHA-256:

e145fe9ed5c23aa71fdb443de00c7d9b4a69f8a27a2e4fbb1fe1d0dbfb6583f1

Вам не нужно знать входные данные хеша, чтобы зафиксировать на нем средства. Однако отправитель должен предоставить сценарий, который использовался для его хэширования, и должен удовлетворить условия этого сценария.

Приведенный выше хеш был создан на основе следующего скрипта:

<умножить на 2> <4> <проверить равенство>

Если вы хотите потратить монеты, привязанные к этому scriptPubKey, вы не только предоставляете эти команды. Вам также понадобится scriptSig, который придаст завершенному сценарию значение True. В этом примере это элемент, который вы <умножаете на 2>, чтобы получить результат <4>. Конечно, это означает, что наш scriptSig — это просто <2>.

В реальной жизни скриптPubKey для вывода P2SH выглядит так:

OP_HASH160 Хэш <redeemScript> OP_EQUAL

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

<signature> <public key> <redeemScript>

Наша оценка немного отличается от выполнения стека, которое мы видели до сих пор. Это происходит в двух частях. Первый просто проверяет, что вы предоставили правильный хэш.


We’ve reached the end of this mini-program, and the top element is non-zero. That means it’s valid


Вы заметите, что мы ничего не делаем с элементами, предшествующими redeemScript. На данный момент они не используются. Мы подошли к концу этой мини-программы, и верхний элемент ненулевой. Это означает, что это действительно.

Однако мы еще не закончили. Сетевые узлы распознают эту структуру как P2SH, поэтому элементы scriptSig фактически находятся в другом стеке. Здесь будут использоваться подпись и открытый ключ.

До сих пор мы рассматривали redeemScript как элемент. Но теперь это будет интерпретировано как инструкция, которая может быть чем угодно. Давайте возьмем пример скрипта блокировки P2PKH, которому мы должны предоставить <signature> и <public key>, соответствующие хешу <public ключа> внутри <redeemScript>.


Once your redeemScript has been expanded, you can see that we have a situation that looks exactly like a regular P2PKH transaction.


Как только ваш redeemScript будет расширен, вы увидите, что ситуация выглядит точно так же, как обычная транзакция P2PKH. После этого вы просто запускаете его, как обычно.

Здесь мы продемонстрировали так называемый сценарий P2SH(P2PKH), но вы вряд ли найдете один из них в реальной жизни. Ничто не мешает вам создать его, но это не дает вам дополнительных преимуществ и в конечном итоге занимает больше места в блоке (и, следовательно, стоит дороже).

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

Но в случае с P2SH не имеет значения, насколько сложны условия расходов. Хэш redeemScript всегда имеет фиксированный размер. Таким образом, затраты перекладываются на пользователей, которые хотят разблокировать сценарий блокировки.

Совместимость с SegWit — еще один случай, когда P2SH может пригодиться (подробнее о том, чем отличается структура транзакций, мы поговорим в следующем разделе). SegWit был софт-форком, который привел к изменению форматов блоков/транзакций. Поскольку это обновление осуществляется по желанию, не все программное обеспечение кошельков распознает изменения. Не имеет значения, помещают ли клиенты хэш сценария SegWit в P2SH. Как и во всех транзакциях этого типа, им не нужно знать, каким будет разблокирующий сценарий погашения.


Транзакции SegWit (P2WPKH и P2WSH)

Более полное введение в SegWit можно найти в Руководстве для начинающих по раздельному свидетелю.

Чтобы понять формат транзакции в SegWit, вам просто нужно знать, что у нас больше нет просто scriptSig и scriptPubKey. Теперь у нас также есть новое поле, называемое свидетелем. Данные, которые мы использовали для хранения в scriptSig, перемещаются в свидетель, поэтому scriptSig пуст.

Если вы столкнулись с адресами, начинающимися с «bc1», это то, что мы называем «родными для SegWit» (в отличие от просто «SegWit-совместимых», которые начинаются с «3», поскольку это P2SH-адреса).


Плата-свидетелю-Pubkey-Hash (P2WPKH)

Pay-to-Witness-Pubkey-Hash (P2WPKH) — это SegWit-версия P2PKH. Наш свидетель выглядит так:

<signature> <public key>

Вы заметите, что это то же самое, что и скриптSig из P2PKH. Здесь scriptSig пуст. Между тем, скриптPubKey выглядит следующим образом:

<OP_0> <хеш открытого ключа>

Это выглядит немного странно, не так ли? Где находятся коды операций, позволяющие нам сравнить подпись, открытый ключ и его хэш?

Мы не показываем здесь дополнительные операторы, поскольку узлы, получающие транзакцию, знают, что с ней делать, основываясь на длине <public key hash>. Они рассчитают длину и поймут, что она должна выполняться в том же стиле, что и старая добрая транзакция P2PKH.

Необновленные узлы не знают, как интерпретировать транзакцию таким образом, но это не имеет значения. По старым правилам свидетеля нет, поэтому читают пустой скриптSig и какие-то данные. Они оценивают это и отмечают как действительное — по их мнению, любой может потратить полученные результаты. Вот почему SegWit считается обратно совместимым софт-форком.


Хэш сценария оплаты свидетелю (P2WSH)

Хэш сценария оплаты свидетелю (P2WSH) — это новый P2SH. Если вы зашли так далеко, вы, вероятно, сможете понять, как это будет выглядеть, но мы все равно пробежимся по этому вопросу. Наш свидетель — это то, что мы обычно помещаем в скриптSig. Например, в P2WSH, который обертывает транзакцию P2PKH, это может выглядеть примерно так:

<подпись 1> <открытый ключ>

Вот наш скриптPubKey:

<OP_0> <script хэш>

Действуют те же правила. Узлы SegWit считывают длину хеша скрипта и определяют, что это выход P2WSH, который оценивается аналогично P2SH. Между тем, старые узлы рассматривают это просто как выход, который может потратить каждый.


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

В этой статье мы немного узнали о строительных блоках Биткойна. Давайте кратко суммируем их:


Тип сценария

Описание

Оплата по ключу (P2PK)

Блокирует средства на определенном открытом ключе

Оплата по Pubkey-Hash (P2PKH)

Привязывает средства к определенному хешу открытого ключа (т. е. адресу).

Хэш оплаты по сценарию (P2SH)

Блокирует средства по хешу скрипта, который может предоставить получатель.

Плата-свидетелю-Pubkey-Hash (P2WPKH)

Версия P2PK для SegWit

Хэш сценария оплаты свидетелю (P2WSH)

Версия P2SH для SegWit


Как только вы углубитесь в Биткойн, вы начнете понимать, почему у него такой большой потенциал. Транзакции могут состоять из множества различных компонентов. Манипулируя этими строительными блоками, пользователи получают большую гибкость, когда дело доходит до установления условий того, как и когда можно тратить средства.


➠ Вопросы о сценарии? Отправляйтесь в Академию Ask!