Bitcoin Script — язык условий расходования средств. Каждый выход транзакции, то есть сумма с условиями её следующего использования, содержит эти условия. Тот, кто хочет его потратить, должен их выполнить. Script помогает ответить на простой вопрос: вправе ли именно эта транзакция использовать именно эти средства?
Алиса получает 20 000 sat — двадцать тысяч наименьших единиц биткоина. Сеть не открывает счёт на её имя. Она создаёт выход транзакции: сумму и условие её дальнейшего расходования. Пока выход не потрачен, он называется UTXO. При обычном платеже условие требует подписи соответствующим ключом. Когда Алиса впоследствии платит, её кошелёк выбирает этот выход, ссылается на него новым входом транзакции и создаёт цифровую подпись. Для этого он использует закрытый ключ, никуда его не отправляя; остальные проверяют подпись открытым ключом. Знать адрес Алисы для получения платежей недостаточно. Условия исходного выхода записаны в поле scriptPubKey, а Bitcoin Script определяет, как проверять их выполнение. Узел — не сотрудник, узнающий Алису, а компьютер, проверяющий правила. Помимо подписи, он должен, например, проверить, не был ли тот же выход уже потрачен. Транзакция действительна лишь при выполнении всех применимых правил. [4] [2]
При обычных платежах условия составляет кошелёк. Создавайте резервную копию по его инструкции: обычное восстановление по словам в совместимом кошельке не означает, что нужно вручную сохранять каждый скрипт. Но в более сложной конфигурации, например Multisig, требующей нескольких подписей, слова одного участника могут не описывать весь порядок расходования. Для повседневного использования лучше выбрать кошелёк с проверенными процедурами, чем вручную собирать скрипты в шестнадцатеричной записи. Дескрипторы выходов описывают тип скрипта, ключи и их выведение; это помогает определить, что должен искать восстановленный кошелёк. Одного закрытого ключа может не хватить для восстановления более сложной конфигурации. Сохраняйте также нужные скрипты и сведения о происхождении ключей, то есть откуда и как они были выведены, и проверяйте предусмотренные пути расходования. [11]
Следующие главы показывают, как условия записываются внутри транзакции; для повседневных платежей знать это необязательно. Поле scriptSig и часть под названием witness передают данные для проверки. Хеш — это отпечаток данных, а временная блокировка запрещает расходование до заданной границы. Стек можно представить как стопку элементов, с которыми работают сверху. Script обрабатывает данные с помощью стека: помещает в него значения, а инструкции, называемые кодами операций, извлекают их, сравнивают или обрабатывают иным образом. Инструкции подписи проверяют подписи; закрытые ключи и создание подписей в эту проверку не входят. Язык позволяет сочетать подписи, условия на основе хешей и временные блокировки. В нём нет циклов общего назначения, и он не является полным по Тьюрингу: на нём нельзя описать все вычисления, доступные в универсальном языке программирования. Узел должен применять точные правила для соответствующего типа выхода и версии скрипта. [1] [3]
Выход P2PKH (Pay to Public Key Hash) обычно имеет вид OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG. Вход предоставляет подпись и открытый ключ в своём scriptSig. При проверке устанавливается, соответствует ли хеш открытого ключа значению в выходе, а затем проверяется подпись. Поэтому одного знания адреса или открытого ключа для такого расходования недостаточно. Закрытый ключ использует подписывающий кошелёк, а не узел, который проверяет транзакцию. [1]
P2SH согласно BIP16 сохраняет в выходе хеш скрипта. Вход, расходующий этот выход, предоставляет соответствующий redeemScript, то есть скрипт, раскрываемый при расходовании, вместе с нужными данными. Его scriptSig может содержать только инструкции, помещающие данные в стек. Узел сначала проверяет совпадение хеша, а затем выполняет redeemScript с оставшимися элементами стека. Одного правильного хеша для расходования недостаточно: необходимо также выполнить условия этого скрипта. [5]
В SegWit версии 0 данные для проверки перенесены в часть транзакции, называемую witness. Это список элементов данных, а не скрипт сам по себе. P2WPKH использует подпись и открытый ключ; P2WSH также содержит witnessScript, хеш SHA256 которого должен соответствовать выходу. У нативного SegWit scriptSig пуст. При использовании обёртки P2SH он содержит только инструкцию помещения соответствующей программы witness в стек; сами проверочные данные остаются в witness. [6]
Проверка подписи использует сообщение, полученное из транзакции по установленной процедуре. Режим SIGHASH определяет, какие её части подпись защищает от изменений. Поэтому нельзя автоматически считать, что каждая подпись охватывает всю транзакцию. BIP143 для операций подписи в SegWit версии 0 всегда включает и сумму расходуемого выхода. Кошелёк должен знать правильные данные и выбранный режим; Script при проверке не оценивает человеческое намерение подписывающего. [7]
Taproot согласно BIP341 позволяет израсходовать выход P2TR (Pay to Taproot) двумя способами. Путь через ключ проверяет подпись Шнорра относительно открытого ключа выхода, не раскрывая скрипт. Путь через скрипт, напротив, раскрывает выбранный скрипт и контрольный блок, называемый control block, который доказывает его связь с выходом. Остальные скрипты в дереве раскрывать не требуется. Правила для раскрытого скрипта зависят также от версии соответствующего листа. [8]
BIP342 определяет Tapscript для листьев Taproot версии 0xc0. Инструкции подписи здесь используют подписи Шнорра, а OP_CHECKSIGADD позволяет построить условие, требующее нескольких подписей. Старый OP_CHECKMULTISIG запрещён при исполнении. Прежний предел длины скрипта в 10 000 байт и лимит в 201 код операции по прежним правилам подсчёта не применяются. Число операций подписи ограничивается бюджетом, определяемым размером witness; ограничения стека сохраняются. Поэтому пределы одной версии нельзя представлять как общие свойства всего языка Script. [9]
Правила консенсуса определяют действительность расходования в блоке. Стандартность означает дополнительные локальные правила узла для приёма неподтверждённых транзакций в Mempool и их распространения; это не процесс стандартизации. Поэтому действительная по консенсусу транзакция не обязательно будет распространяться обычным образом. Особого внимания требует OP_SUCCESSx в Tapscript: после успешного прохождения предыдущих проверок его распознавание при декодировании завершает проверку скрипта успехом ещё до обычного исполнения и проверок стека. Это задел для будущих расширений, а не обычное безопасное условие расходования. [10] [9]
Для полной картины прочитайте эту статью вместе с Транзакция, Multisig, Timelock, Taproot. На эту статью также ссылаются Hash, SHA-256, Правила консенсуса, Timelock.