Активация протокола Segwit в 2017 году стала одним из значимых моментов в развитии сети Биткоин. Это обновление позволило пользователям сократить комиссию и открыло дорогу к созданию Lightning Network - протокола offchain платежей, который позволил пользователям совершать микроплатежи без огромных комиссий и долгого ожидания подтверждения транзакции. О Lightning Network мы ещё успеем с Вами поговорить в одной из следующих статей. Сегодня же я бы хотел обсудить проблему, с которой столкнулись пользователи сразу после активации протокола Segwit. После его введения индустрии понадобилось некоторое количество времени, чтобы перейти на программное обеспечение, поддерживающее новые типы адресов: P2WPKH и P2WSH.
Поэтому возникла необходимость в промежуточном решении, чтобы кошельки и сервисы, не обновившие программное обеспечение для работы с Segwit, получили возможность отправлять средства на bech32 адреса. Решение оказалось простым и изящным: завёрнутый в P2SH Segwit(Wrapped или Nested Segwit).
Адреса типа Wrapped Segwit начинаются с '3' и до момента первой траты монет с этих адресов ничем не отличаются от обычных P2SH адресов, на базе которых они строятся. Таким образом, для достижения обратной совместимости(backward compatibility) вместо P2WPKH и P2WSH используются P2SH-P2WPKH и P2SH-P2WSH адреса. Сегодня мы подробно рассмотрим один из этих форматов: P2SH-P2WPKH.
Напомню, чтобы потратить некоторый utxo(не потраченный выход) необходимо предоставить полный скрипт. Его элементы могут содержаться в трёх полях данных транзакции: scriptSig, scriptPubKey и witness. В адресах старых форматах (P2PKH,P2SH) полный скрипт составлялся из двух частей(на схеме 1 выделены красным): запирающего скрипта, расположенного в поле scriptPubKey транзакции(Транзакция 1), которой был пополнен данный адрес, и отпирающего скрипта, содержащегося в поле scriptSig транзакции которой мы тратим средства с адреса(Транзакция 2).
После появления Native Segwit адресов(bech32) отпирающий скрипт фактически был перенесён из поля scriptSig в новое поле witness. scriptSig поле при этом осталось пустым.
Wrapped Segwit использует все три поля для составления и проверки скрипта. sctiptPubKey по-прежнему находится в транзакции, пополняющей адрес(Транзакция 1), а scriptSig и Witness в транзакции, тратящей соответствующий utxo с адреса(Транзакция 2).
P2SH-P2WPKH
В случае использования P2WPKH адреса части скрипта по трём вышеупомянутым полям распределены следующим образом.
То есть поле scriptSig у адресов типа P2WPKH остаётся пустым. Поэтому первый полный скрипт фактически состоит только из поля scriptPubKey.
В стек попадают данные из поля scriptPubKey, т.е. 0 и 20 байтный хэш публичного ключа.
После этого старые клиенты, видя, что в вершине стека находится не 0, будут воспринимать скрипт как валидный. Обновлённые клиенты, распознающее Segwit, считывают в scriptPubKey поле P2WPKH скрипт следующим образом. Первый элемент - 0 определяет версию witness программы. Второй элемент - 20 байтный хэш witness программы. В случае P2WPKH адреса witness программа представляет из себя хэш публичного ключа. После этого P2WPKH скрипт преобразуется в P2PKH скрипт.
Полученный новый скрипт далее обрабатывается по обычным правилам P2PKH скрипт, про которые Вы можете подробно прочитать в одной из моих предыдущих статей.
Теперь посмотрим, как работает аналог P2WPKH адреса, обёрнутый Segwit P2SH-P2WPKH. Первое, на что следует обратить внимание, - это scriptSig. В отличие от P2WPKH адреса, он не пустой. scriptSig P2SH-P2WPKH содержит выкупающий скрипт(redeem script), состоящий из двух элементов: 0 и 20-байтного хеш-значения публичного ключа. То есть фактически скрипт, содержащийся в scriptPubKey P2WPKH адреса, в случае P2SH-P2WPKH находится в поле scriptSig. В scriptPubKey, в свою очередь, находится стандартный P2SH скрипт, а в witness содержится подпись и открытый ключ.
Проверка скрипта начинается по обычным правилам P2SH адресов. Берётся выкупающий скрипт(redeem script) из scriptSig, являющийся первым отпирающим скриптом, и запирающий скрипт из scriptPubKey.
В стек сначала помещается полный выкупающий скрипт(redeem script). C помощью опкода OP_HASH160 он хешируется. Результатом данной операции будет 20 байтное хеш-значение, лежащее на вершине стека. Далее в стек помещается HASH160(Redeem script), и два верхних элемента стека сравниваются с помощью опкода OP_EQUAL. Если они равны, то далее выкупающий скрипт(redeem script) десериализуется(разбивается на отдельные части). В нашем случае выкупающий скрипт состоит из двух элементов: 0 и хеш-значения публичного ключа. Фактически, начиная с этого момента, повторяются все вышеописанные шаги для P2WPKH адреса.
То есть в стек последовательно помещается сначала 0, затем 20-байтное хеш значение от открытого ключа.
После этого не обновлённые клиенты, видя, что в вершине стека находится не 0, будут воспринимать скрипт как валидный. В свою очередь, клиенты, которые были обновлены, распознают Segwit, развернут выкупающий скрипт в P2PKH скрипт и будут обрабатывать его по обычным правилам P2PKH скрипта.
Таким образом, старые клиенты получили возможность использовать преимущества Segwit протокола, включая сниженные комиссии. Однако стоит отметить, что по размерам вход, ассоциированный с P2SH-P2WPKH адресом, весит больше входа P2WPKH в силу того, что у первого заполнено поле scriptSig, в то время как у второго оно остаётся пустым. Тем не менее использование P2SH-P2WPKH адресов позволяет сократить комиссию на 25-30% по сравнению с классическими P2PKH адресами.
На сегодня вышеописанная проблема и её решение не так актуальны, как в 2017 году, так как с момента активации протокола Segwit все сервисы и кошельки давно обновили своё ПО и могут работать с bech32 адресами. Тем не менее механизм, использованный в качестве промежуточного варианта, позволяет лучше понять принципы работы скриптов Биткоина. Поэтому в ближайший четверг мы с Вами продолжим говорить о Nested Segwit и обсудим P2SH-P2WSH адреса, которые являлись альтернативой P2WSH для не обновлённых клиентов. А пока на сегодня всё. Всем криптобобра и оптимизма)!