Курс Технология блокчейн и Биткоин Лекция 1. Раздел: Сравнение процедуры традиционной банковской онлайн транзакции и транзакции в сети Биткоин. Сравнение не убедительное. В приведенном примере оплаты за ритейл-услугу (покупка чашки кофе) получается, что подтверждения транзакции в сети Биткойн нужно ждать час, а при использовании традиционного POS-терминала подтверждение мгновенное (а при использовании банком системы мгновенных платежей, средства будут зачислены на счет продавца в другой банк почти мгновенно). При этом, покупатель при блок-чейне оплачивает комиссии сам, т.е. сумма на ценике и сумма перевода разные, а при использовании POS-терминала сколько на ценике, столько и списывается. Чем в приведенном примере для участников транзакции использование блок-чейн отличается от использования простых электронных кошельков? Не могу отделаться от ощущения, что блк-чейн - очередная "мода", позвляющая ИТшникам полчать финансирование.
|
Транзакция – это запись в распределенном реестре
Цель: Сформировать начальное представление об основных этапах жизненного цикла транзакций.
Все представляют, что такое Википедия? А теперь вообразите эту электронную энциклопедию без информационных статей. Вообще без единой. Будет ли от такого ресурса хоть какая-нибудь польза? Конечно нет. Подобную параллель можно провести между блокчейном и учтенными в нем транзакциями. Концептуально блокчейн – это организационная структура, обеспечивающая удобный, прозрачный и безопасный способ реализации жизненного цикла транзакций. Правда, способ настолько оригинальный и неожиданный, что вызвал целую технологическую революцию и, вообще, обещает перевернуть весь мир. Тем не менее, основную ценность представляет контент, т.е., транзакции. Именно в них отражается движение денежных средств. Поэтому блокчейн Биткоина часто называют глобальной бухгалтерской книгой. Итак, мы выяснили, что транзакции являются наиболее важной частью системы Биткоин. Остальные элементы платформы выполняют инфраструктурные функции и задачи. Достаточный повод, чтобы серьезно во всем этом разобраться.
Транзакции - это специальные структуры данных, фиксирующие процессы передачи ценности (токенов) между участниками криптоплатформы.
Жизненный цикл транзакции представлен на рисунке 3.1.
Подписание транзакции валидной электронной подписью – обязательный шаг, снимающий блокировку средств, замороженных где-то в глубинах блокчейна. Можно сказать, что механизм распределенных реестров является вдвойне прозрачным. По распределенному реестру каждый пользователь сети всегда может отследить любую цепочку транзакций, фиксирующих движение конкретных электронных монет. Но кроме записей, уже внесенных в блокчейн, клиенты платформы имеют возможность анализировать транзакции (сделки), которые еще даже не включены в блоки. Практически любой узел в сети через какое-то время получает все подписанные, но еще не оприходованные транзакции. Ноды проверяют полученные транзакции и передают их дальше. Примерно так работает пиринговая сеть Биткоина. На плечи майнеров ложится очень нелегкая задача реализации принятого в платформе Биткоин механизма поддержания консенсуса. Настолько нелегкая, что в настоящее время майнеров-одиночек практически не осталось – хешрейт не позволяет. Итогом майнинга является включение очередного блока, содержащего среди прочих интересующую нас транзакцию, в блокчейн. На этом активная фаза жизненного цикла транзакции завершается. Новый транзакционный цикл начнется в тот момент, когда владелец полученных средств решит ими воспользоваться, удлиняя тем самым цепочку владения, отраженную в реестре.
Когда речь идет о крупных сделках рекомендуется несколько отложить момент ее завершения. Следует дождаться включения в блокчейн нескольких блоков дополнительно. Добавление очередного блока называется подтверждением. В настоящее время считается вполне достаточно шести подтверждений, т.е., ждать придется недолго – около часа. Программное обеспечение большинства кошельков настроено именно таким образом и будет отображать транзакции как неподтвержденные вплоть до момента получения шести подтверждений.
Создание транзакций
Есть пара аспектов, которые роднит транзакции в сети Биткоин и традиционные чеки. На самом деле не важно кто создает эти финансовые документы, выражающие намерение передать определенные ценности. В платежной системе они никак не проявляются до тех пор, пока не будут предъявлены для исполнения. А для этого они должны быть подписаны непосредственно владельцем средств. Так же как чек указывает некий счет в качестве источника средств, транзакция ссылается на одну или несколько транзакций, записанных в блокчейне, и черпает ценности с их выходов.
Должным образом сформированная и подписанная, транзакция содержит всю информацию, необходимую для перевода денежных средств. Такая транзакция программным обеспечением кошелька отправляется в путешествие по сети Биткоин, чтобы майнеры могли ее проверить и включить в свои блоки.
Трансляция транзакций по сети Биткоин
Среднестатистический размер транзакции составляет от 300 до 400 байт. Поскольку каждый узел в сети Биткоин связан с несколькими соседними узлами, проблема доверия между ними не возникает (пока мы не принимаем в расчет возможность атаки Сивиллы (Sybil attack) — вид атаки, характерной для одноранговой сети, в результате которой жертва ограничена коммуникациями с узлами, контролируемыми злоумышленником). Аутентификация отправителей транзакций получающими их нодами также не предусмотрена. Напомним, что пока расчеты осуществляются в рамках сети Биткоин поддерживается анонимность их участников.
Чуть позже мы убедимся в том, что транзакции не содержат секретной информации (закрытые ключи, учетные данные). Благодаря этому для их трансляции можно без опасений использовать любой удобный сетевой транспорт, чего ни в коем случае нельзя делать, например, в платежных системах на основе кредитных карт, содержащих конфиденциальную информацию и использующих для коммуникаций исключительно шифрованные каналы. Для отправки транзакции подходят самые простые и незащищенные средства, включая Wi-Fi, Bluetooth, NFC, Chirp, штрих-коды, копирование и вставку в веб-формах. Могут использоваться каналы спутниковой или коротковолновой радиосвязи и т.д.
Каждый узел сети, получив транзакцию, осуществляет ее проверку. Такой подход позволяет предотвратить распространение спама, атаки на отказ в обслуживании (DoS или DDoS-атаки), уменьшить другие угрозы системе. Если транзакция оказывается валидной, узел ретранслирует ее другим, известным ему нодам. Соответствующее сообщение получит также непосредственный отправитель транзакции. Таким образом, в течение непродолжительного времени (речь идет о секундах) валидная транзакция распространится в виде круговой волны (подобно волне на поверхности воды от брошенного камня) со скоростью, растущей в геометрической прогрессии.
Если результат проверки окажется отрицательным, узел отклоняет транзакцию и извещает об этом отправителя.
Сеть Биткоин является пиринговой. Т.е., любой узел связан с несколькими другими узлами, обнаруженными им непосредственно после запуска при помощи особого децентрализованного протокола. Транспортная система Биткоин образует слабо связанную сеть без фиксированной топологии, или какой-либо структуры, т.е., является одноранговой.
Являясь неотъемлемым инфраструктурным компонентом платформы Биткоин, одноранговые (пиринговые, P2P - от английского "peer-to-peer") сети заслуживают определенного внимания.
Основная цель пиринговых сетей – организация эффективного обмена файлами практически любого размера. Однако, известны и другие сферы использования концепции одноранговых систем, например, в области распределенных вычислений – технологии, позволяющей задействовать удаленные вычислительные системы для решения сложных, ресурсоемких задач. Еще одно название подобных сетевых объединений -децентрализованные. Для наших целей достаточно рассмотреть основные особенности технических решений на основе пиринговых сетей для построения сетевых систем файлообмена.
Одноранговый характер сети свидетельствует о том, что все ее участники имеют одинаковые права и выполняют одни и те же функции, то есть, могут, как принимать информацию, выступая в роли сервера, так ее и отдавать, выступая в роли клиента. Здесь нет выделенных серверов, предоставляющих в общее пользование свои ресурсы или информационные сервисы. Недаром сочетание "peer-to-peer" можно перевести как "равный к равному". Еще такие сетевые объединения называют децентрализованными.
Как работают пиринговые сети?
Как и любая другая сеть, пиринговая представляет собой свободное объединение компьютеров. Отличительной особенностью является логика организации его работы, основанная исключительно на равноправии всех участников. Кстати, принято участников такой сети называть пирами. Мы все очень любим глобальную сеть Интернет, ориентированную на клиент-серверную архитектуру. Что же такого полезного есть в одноранговой сети и что не может обеспечить Интернет? Прежде всего, потрясающая работоспособность, демонстрируемая независимо от числа доступных пиров, при любом возможном их сочетании. В одноранговой системе пропускная способность серверов не является критичным местом (типичная уязвимость обычных сетей). Их там просто нет.
Когда мы используем клиент-серверную модель, качество информационного сервиса (скорость выдачи информации, время обслуживания) полностью зависит от возможностей соответствующего сервера. Если сервер выходит из строя, информация или оказываемые им услуги становятся полностью недоступными.
Пиринговая модель передачи данных позволяет выстроить иную логику работы. Информация реплицируется на множестве узлов сети. Мало того, что пропадает зависимость от ограниченного в ресурсах единственного сервера, способного удовлетворить Ваши информационные потребности, Вы еще и получаете принципиальную возможность получать необходимую информацию одновременно из разных источников. Теперь скорость получения информации будет определяться только Вашей готовностью ее принимать (пропускной способностью Вашего канала). Надежность такой системы безусловно выше чем в централизованном решении.
Конечно мы нарисовали слегка идеальную картину пиринговой сети. Практические реализации одноранговых файлообменных систем сталкиваются с рядом трудностей, главной из которых является поиск таких пользователей, которые обладают интересующим Вас файлом и одновременно находятся в активном состоянии. На практике подобные проблемы решаются с помощью гибридных систем, допускающих присутствие выделенных серверов, координирующих работу, процессы поиска зарегистрированных в сети компьютеров, определения их текущего статуса ("активен" или "не активен").
Доступ к сервисам одноранговых сетей осуществляется с помощью соответствующего клиента.
И в заключении небольшого экскурса в мир пиринговых сетей приведем список наиболее известных файлообменных P2P-сетей:
- ED2K (eDonkey2000). Работает по протоколу MFTP. В качестве клиента используется приложение eMule (Edonkey – его устаревшая версия). Сеть продолжает действовать и в настоящее время, хотя разработчики прекратили его поддержку еще в 2005 году. Этот факт лишний раз подчеркивает потрясающую живучесть такого рода систем.
- BitTorrent – самая популярный файлообменник, обеспечивающий высокую скорость передачи данных. Популярные клиенты: uTorrent, BitComet, BitSpirit, Azureusи и т.д.
- Direct Connect – представляет собой набор связанных между собой небольших хабов (серверов), предназначенных для поиска информации на компьютерах участников этих сетей. Информацию об активных хабах концентрируется на специальных серверах (хаблистах). Яркий пример частично децентрализованной сети. Может использоваться для организации файлового обмена в крупных районных или городских локальных сетях. Основной клиент - DC++.
- Gnutella и Gnutella2 – одноранговые сети в чистом виде, использующие для передачи данных свой собственный протокол, разработанный фирмой Nullsoft. Основные клиенты: Shareaza, LimeWire, Phex, Morpheusи и т.д.
- FastTrack. Использует классическую версию протокола P2P, правда в передаче информации участвуют только те источники, которые имеют полные версии файлов. Основные клиенты – KaZaA, giFT(KCeasy) и mlDonkey.
Структура транзакции
Цель: Сформировать комплекс знаний о структуре транзакции, составе и взаимосвязи ее компонентов, процедуре фиксации факта передачи цифрового актива от одного владельца другому с помощью записи в реестре.
Разберемся каким образом в транзакции записывается инструкция передать средства из конкретного источника (определяется входом транзакции) заданному получателю (определяется выходом транзакции).
Как уже упоминалось ранее, блокчейн сети Биткоин не содержит балансовые счета или данные их владельцев. В связи с этим входы и выходы транзакций проще ассоциировать с некоторым числом токенов, заблокированных посредством криптографических операций. Система организована так, что только владелец закрытого ключа способен их разблокировать.
Наконец-то заглянем внутрь транзакции (Таблица 3.1).
Размер | Поле | Описание |
---|---|---|
4 байта | Версия | Описывает правила управления транзакцией |
1-9 байт | Число входов | Показывает число входов транзакции |
Без ограничения | Входы | Структура, включающая один или несколько входов транзакции |
1-9 байт | Число выходов | Показывает число выходов транзакции |
Без ограничения | Выходы | Структура, включающая один или несколько выходов транзакции |
4 байта | Locktime | Время (в формате ОС Unix) или номер блока |
Locktime
Сначала разберемся с полем Locktime (Время связывания транзакции). Очень похоже на функционал обычного чека с отсрочкой платежа. Трактуется в платформе как временная точка, до наступления которой транзакция не может стать действительной, распространяться по сети или включаться в блоки.
В большинстве транзакций поле Locktime устанавливается в 0, что означает немедленное распространение и исполнение. Если его содержимое отлично от 0 и меньше 500 000 000, то его значение интерпретируется как высота блока, до достижения которой транзакция не считается действительной, не распространяется по сети и не включается в блокчейн. Если же оно больше 500 000 000, система интерпретирует его как Unix-время (количество секунд, прошедших с 1 января 1970-го года), до наступления которого транзакция не считается действительной.
Входы и выходы транзакции
Цель: Сформировать комплекс знаний о строении входов и выходов транзакции, составе, функциях и взаимосвязи их структурных компонентов.
Безусловно, это самые интересные и важные части транзакции. Концептуальным элементом схемы являются неизрасходованные выходы транзакций (Unspent Transaction Output) или UTXO.
Биткоин был первой валютой, реализующей модель UTXO для отслеживания состояния реестра. Каждая очередная транзакция расходует выходы предыдущих и создает новые выходы, которые в свою очередь будут израсходованы последующими транзакциями. Каждый выход может использоваться только один раз. Такая структура обладает множеством очень полезных математических свойств, включая конструктивное доказательство невозможности двойных трат при условии, что каждая транзакция доказывает факт того, что сумма ее входов больше, чем сумма ее выходов.
Основная причина успеха UTXO - это присущий ей естественный параллелизм в виду того, что каждая транзакция может обрабатываться параллельно, так как все они относятся к независимым неконфликтующим выходам (поскольку у каждого выхода ровно один владелец). С точки зрения теоретической информатики, UTXO – очень элегантная и легко доказуемая схема.
Отметим, что модель UTXO подвергается определенной критике. Оппоненты утверждают, что технические решения, основанные на подобной схеме, подходят только для тех предметных областей, где каждый выход имеет строго одного владельца. Данное обстоятельство хорошо стыкуется с моделью валюты, и поэтому работает в случае с платформой Биткоин. А вот для приложений общего назначения, допускающих притязание на один выход нескольких владельцев (конфликтующие выходы), использование UTXO может привести к неуправляемому шторму неподтвержденных транзакций, и даже к комбинаторному взрыву блокчейна.
К счастью, в этом разделе книги мы рассматриваем именно криптовалюту. Даже самые ярые критики UTXO признают, что для данной цели модель UTXO подходит наилучшим образом.
Итак, платформа Биткоин отслеживает все доступные (неизрасходованные) выходы UTXO (на февраль 2019 года общая масса выпущенной криптовалюты превысила 16 840 000 BTC). Каждый раз, когда пользователь так или иначе получает биткоин, эта сумма учитывается в платформе как нерастраченный выход. Таким образом, средства конкретного пользователя могут быть раскиданы по пулу UTXO среди тысяч транзакций и сотен блоков. Именно поэтому платформа исключает такое понятие как баланс биткоин-адреса или счета и оперирует только отдельными нерастраченными выходами, привязанными к конкретному владельцу. Впрочем, для удобства пользователей программное обеспечение кошельков эмулирует балансы счетов пользователя, которые прекрасно отражаются в интерфейсной части приложения. Для этого кошельки сканируют блокчейн и суммируют все нерастраченные выходы, принадлежащие данному пользователю.
Неизрасходованный выход транзакции может блокировать любую произвольную сумму в биткоинах (или сатоши). При этом он является неделимым (не станете же Вы отрывать кусочки от пятитысячной купюры для оплаты покупки в 500 рублей). Аналогия тут совершенно полная. Это означает, что для осуществления сделки требующей купюры меньшего номинала, чем имеющийся в Вашем распоряжении неизрасходованный выход, Вам все равно придется потратить его целиком. На самом деле все не так печально. Сдачу самому себе никто не отменял. Т.е., транзакция кроме одного выхода, отправляющего требуемую сумму по адресу получателя платежа, будет содержать еще один выход – сдачу самому себе (аналогичным образом, если средств в одном нерастраченном выходе недостаточно, следует аккумулировать несколько UTXO, т.е., транзакция может включать несколько входов). Ну и о вознаграждении майнерам не стоит забывать. Все ровно так как показано на рисунке 4.
Приложение кошелька пользователя, как правило, формирует платеж из имеющихся в распоряжении различных неизрасходованных выходов, так чтобы итоговая сумма превышала или равнялось величине перевода. Могут использоваться различные стратегии в зависимости от предпочтений владельца.
Почему модель UTXO называют симметричной? Нерастраченные выходы в блокчейне, которые транзакция потребляет являются ее входами. Одновременно с этим создаются новые нерастраченные выходы, которые становятся выходами данной транзакции. Фраза конечно звучит не очень благозвучно, но по сути здесь все верно.
Созданная транзакция тратит выход предшествующей транзакции, разблокируя его подписью текущего владельца денежных средств. В тоже самое время указанная в транзакции сумма сразу же блокируется механизмом, использующим биткоин-адрес нового обладателя.
А теперь попробуйте ответить на вопрос: что является первичным вход или выход транзакции?
Есть только одно исключение из сформулированного правила в модели UTXO. Речь идет о так называемых coinbase-транзакциях, являющихся источником эмиссии криптовалюты Биткоин. Эти токены отправляются майнеру, которому удалось сформировать очередной валидный блок, включенный в блокчейн. Первой транзакцией любого блока является такая coinbase-транзакция, фиксирующая факт перевода майнеру, сгенерировавшему этот блок, гонорара за его труды. Разумеется, майнер сам эту транзакцию и формирует в процессе создания блока. Он даже может в качестве получателя новеньких биткоинов указать любого другого участника сети (не думаю, что на практике майнеры часто поступают таким образом). Особенностью этой цифровой записи является отсутствие входов. Ее выход создается из воздуха.
Признаться, в блокчейне Биткоина есть еще одна особая, единственная транзакция, входящая в его генезис-блок. Первые 50 BTC были отправлены Сатоши Накамото по адресу 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa. Никаких входов у этой транзакции конечно же нет. Но поскольку блок генезиса жестко зашит в программном обеспечении всех приложений (кстати это типичная ситуация для генезис-блоков практически всех криптовадют) непонятно каким образом эти монеты вообще можно потратить. Впрочем, Сатоши Накамото такой же большой оригинал в отношении использования собственных криптокапиталов как и во всем остальном.
Как мы уже знаем, передача биткоинов - это создание неизрасходованного выхода транзакции (UTXO), зарегистрированного на получателя платежа. Каждая полная нода в сети Биткоин отслеживает множество (пул) нерастраченных выходов (UTXO).
Выходы транзакции
Выход транзакции состоит из двух основных частей: суммы платежа (в сатоши) и блокирующего сценария, также называемого "обременением" - скрипта, блокирующего указанную сумму вплоть до выполнения условий, соблюдение которых снимает обременение.
Как именно создаются сценарии мы подробно рассмотрим немного позже, а пока в таблице 7 представим подробное строение выхода транзакции.
Размер | Поле | Описание |
---|---|---|
8 байт | Сумма | Отправляемая новому владельцу сумма. Неотрицательное целое число. Сумма номинирована в сатоши (1 BTC = 108 сатоши). |
От 1 до 9 байт | Размер скрипта обременения | Неотрицательное число, в байтах. |
Без ограничения | ScriptPubKey Обременение | Сценарий, определяющий условия, после удовлетворения которых средства можно будет потратить |
Обременение
Выходы транзакций инкапсулируют определенную сумму и фиксированное обременение, определяющее условие вывода средств. В большинстве случаев, блокирующий скрипт ассоциирует обременение с определенным адресом в сети Биткоин – адресом получателя платежа. Таким образом право собственности на выделенные средства передается новому владельцу. Если мы воспользуемся ставшим классическим примером оплаты, адресованной хозяину кофейни Сергею, чашки кофе стоимостью 0,025 BTC, выпитой Мариной, то после окончания всех инфраструктурных процессов в блокчейне появится транзакция на сумму 2 500 000 сатоши с обременением, блокирующем средства на биткоин-адрес Сергея. Недешевый вышел кофе. Выход этой транзакции на сумму 2 500 000 сатоши становится частью пула неизрасходованных выходов транзакций, а кошелек Сергея отметит пополнение суммы доступных средств. Когда Сергей решит потратить эту сумму, его новая транзакция снимет блокировку с выхода (UTXO), предоставив соответствующий скрипт, содержащий открытый ключ и электронную подпись, произведенную с помощью закрытого ключа Сергея.
Входы транзакции
В самой простой интерпретации входы транзакций можно рассматривать как указатели на неизрасходованные выходы (UTXO). Они ссылаются на конкретный нерастраченный выход, используя хеш транзакции и порядковый номер (в одной транзакции может содержаться несколько выходов). Но просто так нельзя обратиться к понравившемуся Вам выходу, хранящему привлекательную сумму. Такой вход не будет принят системой, и, честно говоря, вообще не может появиться. Вы также должны доказать платформе право владения конкретным неизрасходованным выходом. Поэтому вход транзакции должен включать отпирающий сценарий, удовлетворяющий условиям снятия обременения, установленным данным выходом. Как правило это электронная подпись, доказывающая факт владения биткоин-адресом, фигурирующим в сценарии блокировки. А, если быть совсем точным, то скрипт состоит из двух компонент: электронной подписи и открытого ключа пользователя. Открытый ключ служит подтверждением того, что создатель транзакции имеет право распоряжаться суммой с указанных выходов. Второй компонент – это ECDSA-подпись хеша упрощенной версии транзакции. Совместно с открытым ключом, подпись подтверждает, что транзакция была создана истинным владельцем данного биткоин- адреса.
Разработчики программного обеспечения, если не желают связываться со всей структурой блокчейна, могут получить пул неизрасходованные выходов (pool UTXO) или набор выходов, ассоциированных с конкретным биткоин-адресом с помощью RPC-вызова кошелька Bitcoin Core или API сторонних приложений, например, blockchain.info.
Когда пользователь совершает перевод средств, его клиент собирает входы транзакции путем выбора из нерастраченных выходов. Например, чтобы провести платеж суммой 0,025 BTC, программное обеспечение кошелька может выбрать два выхода с 0,02 BTC и 0,005 BTC и сложить их балансы для получения требуемой суммы. После того, как выходы выбраны, кошелек для каждого из них генерирует разблокирующие скрипты, содержащие подписи. Поскольку условия обременения выходов требовали именно этого, все прекрасно сработает.
Формальные характеристики структуры данных для хранения входа транзакции представлены в таблице 3.3.
Размер | Поле | Описание |
---|---|---|
32 байта | Хеш-дайджест более ранней транзакции | Указатель на более раннюю транзакцию, содержащую выход, который будет использован. Значение представлено в виде двойного SHA-256 хеша более ранней транзакции. |
4 байта | Номер выхода | Индекс выхода более ранней транзакции, начиная с 0. Неотрицательное целое. |
От 1 до 9 байт | Размер разблокирующего скрипта | Неотрицательное число, в байтах. |
Без ограничения | ScriptSig Снятие блокировки | Сценарий, удовлетворяющий условиям снятия обременения, установленным данным выходом |
4 байта | Номер последовательности | В настоящее время отключенная функция замены транзакции. Установлено в 0xFFFFFFFF. |
Поле "Номер последовательности" предназначено для переопределения транзакции до истечения момента времени, установленного в ее поле Locktime - возможность, которая в настоящее время отключена. В большинстве транзакций в это поле записано максимальное значение (0xFFFFFFFF), которое просто игнорируется платформой Биткоин. Если в транзакции поле Locktime установлено в ненулевое значение, тогда, как минимум, один из ее входов должен иметь порядковый номер меньше чем 0xFFFFFFFF.
Комиссия за транзакцию
Цель: Сформировать представление о функциях комиссии за транзакции, правилах их вычисления.
В большинстве транзакций номинальная сумма включает в себя комиссионные майнерам. На этапе становления сети Биткоин число транзакций было относительно не большим, поэтому майнеры, собирая блоки, буквально дрались за них, подбирая даже бесплатные. Сейчас ситуация коренным образом поменялась. Без достаточной мотивации Ваша транзакция рискует долго оставаться вне поля внимания майнеров.
Большинство кошельков учитывают комиссионные автоматически. Однако, если Вы предпочитаете интерфейс командной строки, или создаете транзакции программно, Вам придется рассчитывать и включать комиссионные самостоятельно.
Кроме стимулирования майнеров, комиссия выполняет важнейшую системную функцию – избавляет сеть Биткоина от спам-транзакций – за них тоже придется платить, причем столько же сколько и за нормальные. Готовы потратиться?
Величина комиссионных рассчитывается на основе размера транзакции в килобайтах и не зависит от переводимой суммы. На самой заре биткоина величина комиссии была фиксированной и постоянной по всей сети. Но постепенно в благородном деле майнинга установились вполне себе рыночные отношения. В приоритете у современных майнеров - транзакции с повышенной комиссией. Стоит отметить, что часть цены комиссии обусловлена системными причинами и регулируется на основе пропускной способности сети и числа транзакций, ожидающих обслуживания. Все, что касается пропускной способности платформы Биткоин, всегда подвергалось самой решительной критике. А решение критичных технических вопросов имеет тенденцию со временем переходить в финансовую плоскость.
Но это не значит, что бесплатная транзакция никогда не будет обработана. Логика у пользователей здесь совершенно очевидная. Если Вы очень торопитесь, то повысьте вознаграждение за свою транзакцию, и она очень быстро попадет в один из ближайших блоков. Согласно протокола Биткоин комиссионные не являются обязательными, и транзакции без вознаграждения в итоге могут быть обработаны. Но с достаточными комиссионными это произойдет наверняка намного быстрее.
Теоретически, комиссия за транзакцию может составить 0%, при соблюдении следующих условиях:
- размер транзакции менее 1000 байт;
- сумма на каждом выходе не менее чем 0.01 BTC;
- высокий приоритет.
Программное обеспечение современных кошельков вполне адекватно рассчитывают оптимальный размер комиссии. Заодно они предложат прогноз скорости проведения транзакции. Но если Вы попытаетесь рассчитать оптимальную комиссию самостоятельно, учтите, что это многофакторная задача. А многие рекомендации, транслируемые из одного издания в другое, безнадежно устарели.
Добавление вознаграждения к транзакции
В структуре данных транзакции не предусмотрено поля для комиссии. Майнер забирает себе разность между суммой входов и суммой выходов. Любое количество сатошей, оставшееся после вычитания суммы выходов из суммы входов транзакции, признается комиссией. Если Вы создаете свои транзакции собственноручно, обязательно убедитесь в том, что случайно не установили очень большие комиссионные, например, забыв включить в транзакцию дополнительный выход со сдачей самому себе.
Итак, величина комиссии за транзакцию напрямую зависит от ее размера. А что же влияет на размеры транзакции? Расклад здесь примерно такой:
- каждый адрес, с которого получены средства, добавляет 148 байт;
- каждый адрес, на который отправляют средства, добавляет 34 байта;
- структура транзакции занимает 10 байт (независимо от числа входящих в нее адресов).
Сложные схемы транзакций и осиротевшие транзакции
Мы уже рассмотрели механизмы, благодаря которым транзакции образуют собственные, внутренние по отношению к блокчейну цепочки, в результате чего одна транзакция тратит выходы предшествующей транзакции и создает выходы для последующей транзакции. Модель UTXO отлично справляется с регулированием таких потоков денежных средств.
А теперь представим, что все транзакции такой цепочки поступили в сеть почти одновременно. Такие сложные транзакционные схемы – отнюдь не беспочвенные фантазии.
В качестве примера упомянем CoinJoin-транзакции. CoinJoin — это протокол, призванный усилить уровень конфиденциальности транзакций в сети Биткоин. С его помощью несколько пользователей после согласования друг с другом могут создавать совместно подписанную транзакцию, смешивая личные балансы в один общий. Впервые был предложен в 2013 году.
Когда цепочка транзакций передается по сети, изначальный хронологический порядок легко может нарушаться. В этом случае некоторая нода сначала получает дочернюю транзакции и лишь через какое-то время – родительскую, выходы которой были задействованы в дочерней. Получив подобную, казалось бы, непригодную транзакцию с неправильными входами, нода вместо того, чтобы признать ее недействительной, помещает ее в специальный, временный пул, где дочерняя транзакция будет ожидать прибытия родительской. Поскольку транзакция не была признана недействительной, она также будет ретранслирована другим узлам.
Подобный временный пул называют пулом (множеством) сиротских транзакций. После поступления родительской транзакции, все сироты, ссылавшиеся на ее выходы, освобождаются из сиротского пула, еще раз проверяются, после чего переводятся на уровень рабочего пространства сети Биткоин, где они могут быть включены в очередной блок.
Таким образом, наличие пула для сирот гарантирует, что валидные транзакции не будут отброшены только потому, что их родитель задерживается в пути и что, в конечном итоге, цепочка, которой они принадлежат, будет реконструирована в правильном порядке, независимо от хронологии поступления ее составных частей.
На количество транзакций потеряшек, хранимых в памяти, имеется ограничение, что позволяет предотвратить DoS или DDoS-атаки. Этот лимит отражается как параметр MAX_ORPHAN_TRANSACTIONS в исходном коде биткоин-клиентов. Если число бесхозных транзакций в пуле превышает критическое значение, лишние будут удалены. Выбор кандидатов на ликвидацию происходит случайным образом.
Язык сценариев транзакций
Цель: Сформировать представление о языке сценариев транзакций Script в необходимом объеме.
Проверка валидности транзакции в сети Биткоин осуществляется на основе выполнения скрипта, записанного на специальном языке сценариев, называемым Script. Это Форт (Forth) – подобный стековый язык программирования с обратной польской нотацией, позволяющей избежать присутствия скобок и приоритетов в выражениях, что чрезвычайно удобно для вычисления формул в ЭВМ на основе стеков. Важная особенность таких языков - использование стека для передачи параметров между термами, что позволяет очень гибко и просто реализовывать достаточно сложные конструкции. Отличительная особенность обратной польской нотации заключается в том, что аргументы располагаются перед знаком операции. Стек - это коллекция, элементы которой доступны согласно принципа "последний вошел, первый вышел" ("Last-In-First-Out", LIFO). Т.е., в любой момент времени мы будем иметь дело только с элементом, добавленным последним (лежащим на вершине стека). Как известно, структура данных стек поддерживает две операции: добавление (push) и удаление (pop).
Неполнота по Тьюрингу для языка Script в первую очередь означает отсутствие циклов и сложных инструментов управления ходом выполнения кода. Для сети Биткоин данное свойство означает ограничение сложности сценариев и предсказуемое время их выполнения. Это решение принято, исходя из соображений безопасности системы. Ограничения языка Script гарантируют отсутствие бесконечных циклов или других форм так называемых "логических бомб", способных привести к атакам на отказ в обслуживании. Таким образом, механизм проверки транзакций становится практически неуязвимым. Эта характеристика особенно важна, если учесть, что каждая транзакция в сети Биткоин проверяется каждой полной нодой.
Почему в данном случае речь не идет о SMART-контрактах? Хотя некоторые принципы умных контрактов впервые были заложены именно в протоколе Биткоин, его платформа не может реализовать их в клиентском программном обеспечении по соображениям безопасности. Поэтому большинство разработчиков создают цифровые контракты либо на новых платформах, таких как Ethereum, либо экспериментируют с сайдчейнами (вилками основной сети биткоина). Язык Script подходит для программирования операций, содержащих только простые условия. Он не является Тьюринг-полным, а сама платформа не имеет маркеров состояния, чтобы на их основе можно было проектировать приложения. К тому же пропускная способность сети Биткоин сильно ограничена, что также не позволяет создать систему многофункциональных контрактов.
Выполнение скрипта, как и положено в системах с постфиксной нотацией, осуществляется по одному элементу слева направо. Обрабатываемые данные добавляются в стек. Операторы добавляют или извлекают одно, или несколько значений из стека, производят над ними запланированное действие, результат которого также может добавляться в стек. Инструкции языка (всего их порядка 80) подразделяются на категории (управляющие, для работы со стеком, для работы со строковыми фрагментами, побитовые логические операции, арифметические, криптографические).
Например, оператор OP_ADD извлечет два операнда из стека, вычислит их сумму, и поместит полученный результат на вершину стека. А оператор OP_EQUAL извлекает два операнда из стека, сравнивает их и добавляет в стек результат сравнения (ИСТИНА (кодируется как цифра 1) – если операнды равны и ЛОЖЬ (кодируется как цифра 0) – в противном случае).
Рассмотрим пример скрипта для решения следующей задачи: проверить равна или нет числу 5 сумма двух чисел 2 и 3.
Сценарий решения этой простой задачи будет выглядеть следующим образом:
2 3 OP_ADD 5 OP_EQUAL
Логика выполнения сценария биткоин-клиентом демонстрируется на рисунке 3.2.
Транзакция признается действительной, если указатель стека принимает в итоге значение TRUE (OP_TRUE), т.е. равен 1, или после выполнения скрипта, стек окажется пустым, или на его вершине окажется любое другое ненулевое значение.
Транзакция признается недействительными, если после выполнения скрипта на вершине стека окажется значение FALSE (OP_FALSE), или выполнение сценария будет прервано оператором (например, OP_VERIFY, OP_RETURN), операторной скобкой (например, OP_ENDIF) и др.
В большинстве случаев блокирующие сценарии ссылаются на биткоин-адреса или публичные ключи, требуя предоставления доказательства их владением для преодоления обременения. Однако, сценарий совсем не обязательно должен быть таким сложным. Подойдет любая конкатенация из блокирующего и разблокирующего сценариев, результатом которой является значение TRUE (OP_TRUE).
Возвращаясь к последнему примеру, мы может использовать в качестве блокирующего скрипта набор инструкций:
3 OP_ADD 5 OP_EQUAL
Разблокировать такой выход можно с помощью транзакции, вход которой содержит следующий скрипт разблокировки:
2
Программное обеспечение клиента объединяет блокирующий и разблокирующий сценарии, в результате чего получается уже знакомый нам скрипт:
2 3 OP_ADD 5 OP_EQUAL
Результат исполнения этого сценария равен OP_TRUE, следовательно, транзакцию следует считать действительной. Наверное, это не совсем надежная защита для заблокированных средств, поскольку выход с таким обременением может потратить любой, кто знает, что 2+3=5.
Независимость от состояния системы
Язык сценариев не требует восстановления состояния до выполнения скрипта и сохранения состояния после. Все данные, необходимые для работы сценария, содержатся в нем самом. Любой сценарий единообразным образом будет исполнен в любом клиенте. Если Ваша система выполнила сценарий с положительным результатом, можно не сомневаться в том, что любая другая система в сети Биткоин завершит сценарий с точно таким же результатом. Таким образом, проверка транзакции инвариантна относительно инфраструктуры сети и любых внешних условий.
Сценарии транзакций
Цель: Сформировать комплекс знаний о схеме выполнения блокирующего и разблокирующего сценария в рамках реализации механизма проверки действительности транзакций, а также всей системы передачи ценности от одного владельца другому в блокчейне платформы Биткоин.
IT-специалисты давно занимались проблемой хранения связанных данных. Разрабатывались разные модели: реляционные; иерархические; сетевые. Однако, идея, реализованная в платформе Биткоин, была поистине революционной. Семантическая связь внутри цепочек транзакций реализуется на основе программного кода. Элегантная, прозрачная и абсолютно надежная схема. В 2009 году в свет вышли не просто электронные монеты. Биткоин – это программируемые деньги.
Создание сценария
Механизм проверки действительности транзакций, а заодно, и вся система передачи ценности от одного владельца другому в блокчейне платформы Биткоин опирается на два типа сценариев: блокирующий сценарий и разблокирующий. Блокирующий скрипт воплощает налагаемое на выход транзакции обременение. Здесь определяются условия, выполнение которых откроет в будущем доступ к средствам, замороженным на выходе транзакции. Исторически сложилось так, что сценарий блокировки назывался scriptPubKey (сценарий открытого ключа). В большинстве случаев этот скрипт действительно хранил открытый ключ или биткоин-адрес. Однако, мы уже не раз упоминали, что возможный спектр блокирующих сценариев гораздо шире. При этом традициям (особенно в том, что касается терминологии) тоже необходимо следовать. Аналогично, за разблокирующими скриптами закрепилось название scriptSig, поскольку, являясь частью каждого входа транзакции, они, как правило, содержат электронную подпись пользователя, сделанную с помощью его закрытого ключа.
Программное обеспечение любого клиента платформы Биткоин проверяет правомочность и соответствие стандартам каждой транзакции, выполняя ее блокирующий и разблокирующий сценарии вместе. При этом проверяются все входы транзакции, для чего сначала из пула извлекаются все нерастраченные выходы, на которые эти входы ссылаются. Каждый анализируемый выход содержит соответствующий блокирующий сценарий, определяющий условия доступа к средствам. После этого, для каждого входа оба сценария выполняются вместе. Схематично данная процедура отображена на рисунке 3.3.
А на рисунке 3.4 представлен пример записи объединенного сценария для самого популярного вида платежа на хеш открытого ключа (Pay-to-Public-Key-Hash).
Отметим, что вплоть до 2010 года платформа Биткоин работала в полном соответствии с изложенным алгоритмом. Скрипты сначала извлекались, потом склеивались и на исполнение поступал уже единый, объединенный сценарий. Однако соображение безопасности (при определенных условиях разблокирующий сценарий мог повредить блокирующий) заставили внести в эту процедуру определенные поправки. В настоящее время схема немного изменилась. Каждый из скриптов выполняется отдельно, по очереди, а полученный после выполнения первого скрипта результат передается через стек. Рассмотрим подробнее каким образом это реализуется.
Сначала выполняется разблокирующий скрипт. Если он выполнился без ошибок (например, не осталось лишних операторов), основной (не альтернативный) стек копируется. Затем запускается на исполнение блокирующий скрипт. Если результат выполнения блокирующего скрипта с ранее скопированным из стека результатом разблокирующего скрипта дает значение "TRUE", то данный вход транзакции признается действительным (в случае получения такого же результата проверок для остальных входов, транзакция считается полностью готовой (валидной) к включению в блокчейн). Т.е., разблокирующий скрипт смог полностью удовлетворить условиям обременения, закодированным в блокирующем скрипте и, следовательно, данный вход успешно подтвердил полномочия на трату конкретного неизрасходованного выхода. Если же в результате выполнения объединенного сценария получен иной результат (не "TRUE"), тогда вход признается недействительным, как не удовлетворившим условия доступа к средствам, закодированные в соответствующем нерастраченном выходе. Поскольку нерастраченный выход вместе с остальными частями транзакции уже записан в блокчейн (т.е., никогда не меняется), никакие недействительные (неудачные, или злонамеренные) попытки израсходовать его по ссылке из новой транзакции никак не могут на него повлиять. Только действительная транзакция, удовлетворяющая условиям ограничения, изменит состояние нерастраченного выхода на "растраченный" и вызовет его удаление из пула доступных (нерастраченных) выходов.
Да, конечно, Script язык с очень скромными возможностями, которому ох как далеко до современных инструментов разработчиков умных контрактов (да ему эти возможности и не нужны, если честно). Однако сама идея хранить код обработки непосредственно в самих структурах данных (прообраз умных контарктов) безусловно впервые была успешно реализована именно в сети криптовалюты Биткоин. К тому же история развития этой платформы демонстрирует иную тенденцию – сужение возможностей и вариабельности платежных конструкций и схем в целях безопасности. Об этом мы поговорим в следующем параграфе.
Разрешенные в платформе Биткоин типы транзакций
Цель: Сформировать комплекс знаний о типах разрешенных в платформе Биткоин транзакций и реализуемых с их помощью различных схемах платежей в сети Биткоин.
Следует сразу предупредить читателя, что мы ступаем на довольно скользкую почву, в том плане, что никаких догм, жестко заданных правил в блокчейн-платформах обычно не существует (кроме содержимого самого блокчейна – он неизменен). Технология настолько молодая, что изменения стандартов идет перманентно. Поэтому содержимое данного параграфа актуально в момент написания и вполне возможно уже не полностью будет отвечать действительности при прочтении. Хотя тот набор, который мы сейчас разберем не менялся в течении нескольких последних лет. И для реализации многих приложений его не вполне достаточно. Поэтому работа в плане увеличения гибкости транзакционной модели сети Биткоин никогда не прекращалась. Правда, чаще всего подобные истории заканчивались с противоположным результатом – вводились новые ограничения и запреты.
Итак, что мы сегодня имеем в плане разнообразия типов биткоин-транзакций?
Почти сразу же разработчики внесли весьма серьезные ограничения на типы сценариев, которые должны поддерживаться эталонным клиентом. Всего было определено ровно пять возможных вариантов. Транзакции, выполненные только по таким лекалам, будут восприниматься программным обеспечением клиентов, и, самое главное, майнеров – без их участия транзакция никогда не попадет в блокчейн. Вы конечно можете создать нестандартную транзакцию, не относящуюся ни к одному задекларированных типов, но тогда Вам придется искать майнера, который признает ее валидной и включит в блок.
Разрешенными являются следующие типы сценариев транзакций:
- pay-to-public-key-hash (P2PKH);
- public-key;
- multisignature (с ограничением на 15 ключей);
- pay-to-script-hash (P2SH);
- выход данных (OP_RETURN).
Поговорим о них подробнее.
Pay-to-Public-Key-Hash сценарии
Большинство транзакций, наполняющих сегодня сеть Биткоин, используют один и тот же сценарий проверки валидности - Pay-to-Public-Key-Hash (P2PKH, платеж на хеш открытого ключа), проецирующий на криптовалютную платформу простейшую операцию перевода денежных средств от одного лица другому, но только в обезличенной форме.
Такие транзакции включают блокирующие сценарии, обременяющие выходы хешами открытых ключей, известных также как биткоин-адрес. Транзакции, реализуемые по такой схеме, содержат скрипты P2PKH. Нерастраченный выход, блокированный с помощью сценария P2PKH, можно открыть, предоставив открытый ключ и электронную подпись, сделанную с помощью соответствующего закрытого ключа.
Вернемся к рассмотренному ранее примеру с Мариной, оплатившей биткоинами чашку кофе, выпитую в кофейне Сергея. Выход соответствующей транзакции будет заблокирован скриптом scriptPubKey следующей конструкции:
OP_DUP OP_HASH160 <Public-Key-Hash Сергея> OP_EQUALVERIFY OP_CHECKSIG
"Public-Key-Hash Сергея" представляет собой биткоин-адрес кошелька владельца кофейни в шестнадцатеричном формате (т.е., без перевода в кодировку Base58Check с характерной для этого случая "1" вначале). Это вполне естественно, если вспомнить, что формат Base58Check придуман для лучшего восприятия больших чисел людьми. А компьютеры легко разберутся и с шестнадцатеричным представлением данных.
Когда Сергею понадобятся эти средства, он легко сможет снять с них обременение с помощью следующего разблокирующего скрипта (scriptSig), входящего в состав новой транзакции (в качестве одного из ее входов):
<Signature Сергея> <Public-Key Сергея>
Здесь под термом Signature понимается электронная подпись владельца криптопары.
Если объединить оба скрипта в один сценарий, мы получим уже отчасти знакомый нам программный код на языке Script:
<Signature Сергея> <Public-Key Сергея> OP_DUP OP_HASH160 <Public-Key-Hash Сергея> OP_EQUALVERIFY OP_CHECKSIG
После выполнения этого комбинированного сценария, мы получим на вершине стека единственное значение "ИСТИНА" тогда и только тогда, когда разблокирующий скрипт подойдет условиям, закодированным в блокирующем скрипте. Воспользоваться средствами может только обладатель закрытого ключа Сергея, представив для снятия обременения свою электронную подпись. В штатной ситуации это и есть сам Сергей.
Ход выполнения сценария в случае P2PKH-транзакции проиллюстрирован рисунком 3.5 (в целях улучшения восприятия у операторов языка Script на рисунке опущены префиксы OP_). Причину разделения процесса на две части Вы уже знаете. Скрипты объединенного сценария выполняются последовательно, а результат первого передается во второй посредством стека.
Выполнение сценария начинается с разблокирующего скрипта scriptSig, включающего всего два шага. Сначала на вершину стека добавляется электронная подпись владельца биткоин-адреса, на хеш которого был совершен платеж. Затем – на вершину стека добавляется соответствующий открытый ключ.
В самом начале второй части комбинированного сценария - скрипта обременения scriptPubKey выполняется дублирование указателя стека, т.е., самого верхнего значения - открытого ключа. Инструкция OP_HASH160 на самом деле вычисляет двойной хеш, применяя последовательно хеш-функции SHA-256 и RIPEMD-160 к указателю стека (значению на вершине), а полученный результат вновь добавляется в стек. Т.е., на вершине стека окажется величина, равная RIPEMD-160(SHA-256(<отрытый ключ>)). Мы с Вами помним, что это формула вычисления биткоин-адреса. Аналогичная по смыслу величина у нас вшита в самом скрипте обременения – биткоин-адрес, на который собственно и были в свое время отправлены средства. Это следующий операнд скрипта, появление которого в нем означает, что биткоин-адрес получателя платежа размещается на вершине стека. Осталось их сравнить. Что и делает инструкция OP_EQUALVERIFY. С ее помощью из стека выбираются два верхних значения (биткоин-адрес действительного получателя средств и биткоин-адрес претендента на эти средства) и осуществляется их сравнение. Результат сравнения добавляется к стеку. На самом деле команда OP_EQUALVERIFY немного мощнее, чем простой оператор сравнения, который также в синтаксисе языка Script присутствует - OP_EQUAL. Инструкция OP_EQUALVERIFY подразумевает также в конце исполнения вызов команды OP_VERIFY, отмечающую транзакцию как недействительную, если на вершине стека оказывается значение не равное TRUE. Если сравнение подтвердило совпадение биткоин-адресов, осталось убедиться в реальности прав пользователя на фигурирующий в сценарии биткоин-адрес. Поскольку последний рассчитывался через открытый ключ, достаточно проверить действительно ли предъявитель данного открытого ключа является его владельцем. А эту задачу легко можно решить достаточно традиционным способом. Имея пару электронная подпись и открытый ключ, мы всегда можем проверить подходят ли они друг к другу. Именно эту функцию и выполняет последняя инструкция сценария OP_CHECKSIG. Если проверка пройдет успешно, на вершине опустевшего к моменту завершения сценария стека окажется значение 1 (TRUE). В противном случае указатель стека будет возвращать значение 0 – транзакция недействительная.
Хотя относительно реализации последнего шага и был употреблен эпитет "традиционно", это утверждение не является справедливым. Традиционной можно назвать процедуру использования открытого ключа для проверки электронной подписи. А вот идея проверки принадлежности публичного ключа на основе электронной подписи является весьма оригинальной. Впрочем, так можно охарактеризовать практически любой компонент платформы Биткоин.
Осталось рассмотреть всего один вопрос. Подпись всегда ставится под документом. Электронная подпись не исключение. На основании какого электронного документа создается электронная подпись в скрипте scriptSig? Электронная подпись генерируется на основе дайджеста (результата хеширования) некоторых полей данной транзакции. Стоит отметить, что подпись никогда не формируется на основе целиком всей транзакции, и пользователи могут указывать, какая часть транзакции подписывается (конечно, кроме поля scriptSig).
Pay-to-Public-Key сценарии
Эта модель транзакций появилась раньше остальных и использовалась еще на заре рождения платформы Биткоин, когда в скрипте в качестве адреса поначалу указывался IP-адрес получателя, а в последствии более универсальный идентификатор - открытый ключ. Минусами такого похода являются раздутые транзакции (из-за длинных адресов) и передача по сети открытых ключей в незашифрованном виде, что может с появлением квантовых компьютеров обернуться очень серьезными неприятностями для сети Биткоин, поскольку вероятно появится возможность получить закрытые ключи по открытым.
Pay-to-public-key — является слегка упрощенной формой платежа по сравнению с моделью pay-to-public-key-hash. Сегодня этот тип транзакций чаще всего используется в coinbase-транзакциях, создаваемых старым программным обеспечением ортодоксальных майнеров.
Блокирующий скрипт scriptPubkey записывается следующим образом:
<Public Key A> OP_CHECKSIG
А в качестве разблокирующего скрипта scriptSig используется только подпись получателя:
< Signature А >
Догадались откуда пошли названия блокирующего и разблокирующего скриптов?
Комбинированный сценарий, проверяемый программным обеспечением систем платформы Биткоин имеет очень вид:
<Signature A> <Public Key A> OP_CHECKSIG
Вся проверка ограничивается добавлением в стек двух значений (подписи и открытого ключа) и вызовом для их сравнения единственного хорошо нам знакомого оператора OP_CHECKSIG.
Сценарии с мульти-подписью
Для чего используется мульти-подпись? Основные цели — разделение ответственности или повышение безопасности.
Принцип разделения ответственности призывает к тому, чтобы несколько человек коллегиально принимали решения по любому критически важному вопросу. Решение каждого из менеджеров фиксируется с помощью применения персональной электронной подписи. Примером может послужить ситуация, когда компания имеет двух и более владельцев. Например, чтобы потратить существенную сумму, владельцы должны прийти к консенсусу (знакомая для нас тема), при этом территориально они могут быть удалены друг от друга.
Безопасность с помощью мульти-подписи можно повысить, используя стратегию раздельного хранения ключей, необходимых для подписи. Как вариант - на разных устройствах. Например, один ключ записан на персональном компьютере, а другой — на мобильном устройстве. В такой ситуации задача злоумышленника, стремящегося похитить криптовалюту пользователя, существенно усложняется.
Еще один вариант использования мульти-подписи — это безопасность торговых сделок. В случае, когда продавец не доверяет покупателю (и наоборот), сделка происходит на условиях, когда оба участника операции должны подтвердить то, что она состоялась. Обычно для этого используют депозитный счет. Он может принадлежать третьему лицу, выполняющему роль арбитра, или обоим участникам сделки. В случае, когда стороны не привлекают третью сторону, процесс может быть организован созданием общего счета с необходимостью подписать сделку всеми владельцами счета. На этот счет каждая сторона заранее отправляет залог (желательно превышающий стоимость сделки). После успешного завершения сделки залоги возвращаются. Если сделка сорвана, то залоги теряются.
В общем, имеются самые разные схемы использования мульти-подписи и механизмы ее реализации. Например, популярнейшие блокчейны Биткоин и Ethereum используют разные технологии. У Биткоина имеется решение на основе P2SH - транзакции. Ethereum использует дополнительные адреса с одной подписью, контролируемые умными контрактами. Вторая технология сложнее, но при этом достигается большая гибкость приложений.
Возвращаемся к рассмотрению особенностей скриптов различных типов транзакций сети Биткоин.
Скрипт в транзакции с мульти-подписью вводит обременение, согласно которому из N публичных ключей, упомянутых в сценарии, по меньшей мере М должны быть использованы при создании подписи. Только после этого обременение будет преодолено.
Ранее уже упоминалось, что эта схема называется M-из-N, где N - общее количество ключей, а М - пороговое число подписей, необходимых для признания транзакции действительной.
Блокирующий скрипт scriptPubkey записывается следующим образом:
M<pubKey А> <pubKey B> … <pubKey N> N OP_CHECKMULTISIG
А в качестве разблокирующего скрипта scriptSig используется только подпись получателя:
OP_0 < Signature А > < Signature B > … < Signature M >
Префикс OP_0 необходим для того, чтобы обойти ошибку в оригинальной реализации OP_CHECKMULTISIG.
Комбинированный сценарий мульти-подписи 2-из-3 выглядит следующим образом:
OP_0 <Signature A> <Signature C> 2 <pubKey A> <pubKey B> <pubKey C> 3 OP_CHECKMULTISIG
При выполнении этот комбинированный сценарий закончится успешно (в стеке окажется единственное значение TRUE) тогда и только тогда, когда разблокирующий скрипт будет содержать действительные электронные подписи на основе двух любых закрытых ключей, соответствующих двум из трех открытых ключей.
Сценарии с выводом данных (OP_RETURN)
Безусловно, сеть Биткоин является криптовалютной платформой, предназначенной для проведения платежей и транзакций. Т.е., ее основная функция – это реализация покупной способности. Однако, на самом деле ее возможности далеко выходят за рамки исключительно платежной системы. Многие разработчики пробовали использовать язык сценариев транзакций, чтобы привнести такие преимуществами децентрализованных приложений как безопасность и устойчивость в системы, автоматизирующие самые разные области профессиональной деятельности. В первую очередь подобные технологии проникли в сферы цифровых нотариальных услуг, сертификации, государственных кадастров и реестров, умных контрактов.
Для подобных технических решений на начальном этапе характерной особенностью являлось создание выходов транзакций, записывающих данные непосредственно в блокчейн. Например, распределенный реестр может хранить цифровые отпечатки файлов (финансовых документов или произведений искусства), что позволит в любой момент времени очень оперативно доказать их существование, подтвердить право собственности, или получить необходимую информацию.
Откровенно говоря, вопрос применения блокчейна платформы Биткоин для хранения посторонних (не связанных с платежами) данных всегда вызывал бурную дискуссию. Копий на этот счет было сломано немало. Часть разработчиков не могла даже мысли допустить о таком варварском использовании поистине бесценных ресурсов блокчейна. Остальные считают своим долгом продвигать технологии распределенных реестров, демонстрируя уникальные возможности платформы Биткоин.
Отметим, что обе точки зрения вполне обоснованы. Дополнительные "непрофильные" данные неизбежно приведут к "раздуванию" блокчейна, увеличению нагрузки на полные ноды, повышению стоимости хранения данных. Негативным образом такой механизм скажется на состоянии пула нерастраченных выходов, поскольку для записи данных в блокчейн понадобятся транзакции, которые никогда нельзя будет потратить (выходы транзакций будут заблокированы несуществующими адресами, для которых просто не существуют секретные ключи). Т.е., база данных UTXO со временем тоже будет перегружена. В отношении последней угрозы со временем было найдено решение.
Наконец, в версии 0.9 эталонного клиента Bitcoin Core между противоборствующими был достигнут определенный компромисс. Разработчики ввели оператор OP_RETURN. Инструкция OP_RETURN позволяет добавить 80 байт данных к выходу транзакции. Тем не менее, в отличие от недействительных выходов оператор OP_RETURN явно создает выход, который доказуемо нельзя потратить. Но при этом такой выход не помещается в пул нерастраченных выходов (UTXO). Собственно, в этом и заключался компромисс. Выходы OP_RETURN записываются в блокчейн, провоцируя увеличение его размера, но не хранятся в пуле нерастраченных выходов, экономя оперативную память ЭВМ, реализующих функционал полных нод.
Блокирующие скрипты с оператором OP_RETURN выглядят следующим образом:
OP_RETURN <data>
Максимальный размер данных ограничен 80-ю байтами (установлен в феврале 2015 года в Bitcoin Core версии 0.10, первоначально предел составлял 40 байт). Чаще всего это результаты хеширования, например, с помощью алгоритма SHA-256 (длина = 32 байта). Многие приложения помечают данные специальными префиксами. Например, служба цифровых нотариусов (Proof of Existence) применяет 8-ми байтный префикс DOCPROOF (0х44f4350524f4f46 в шестнадцатеричном формате).
Разблокирующих сценариев для выходов, содержащих инструкцию OP_RETURN, не существует, т.е., такие выходы доказуемо не могут быть растрачены. Поэтому их не нужно держать в пуле нерастраченных выходов. Балансы транзакций с OP_RETURN-выходами обычно нулевые. Вряд ли кому-то придет в голову навсегда и безвозвратно загнать в блокчейн некоторую сумму биткоинов.
Также запрещено указывать в качестве входа транзакции выход, содержащий инструкцию OP_RETURN. Такая транзакция сразу же будет помечена как недействительная любым программным обеспечением в платформе Биткоин.
Согласно стандарта сети Биткоин транзакция может иметь только один выход с инструкцией OP_RETURN. При этом остальные выходы транзакции могут быть любых других типов.
Pay-to-Script-Hash (P2SH) сценарии
Модель Pay-to-script-hash (P2SH) была введена в 2012 году в качестве нового типа транзакций, существенно расширяющего спектр поддерживаемых платформой финансовых схем. Появилась возможность использовать сложные сценарий транзакций. Эту модель часто путают с мульти-подписью, однако сфера применения P2SH-скриптов гораздо шире. Если отталкиваться только от скриптов обременения, так вариабельность условий обременения вообще становится практически безграничной. Достичь это позволило весьма элегантное решение. Средства отправляются на адрес скрипта. При этом что из себя представляет скрипт заранее никак не оговаривается – он может быть любым. Еще одна особенность этой модели – в поле транзакции scriptPubKey (где у нас обычно размещаются условия обременения) ограничения в виде скрипта больше не фигурируют. Акцент смещается в сторону пользователя, который впоследствии осуществит трату средств. Именно ему придется представить скрипт в качестве доказательства своего права на заблокированные в выходе P2SH-транзакции средства. Согласитесь, логика проверки по сравнению с предыдущими моделями иная. Очень мощное с точки зрения масштабируемости функционала платформы решение.
Итак, в P2SH-транзакции биткоины традиционно блокируются в скрипте, но сам скрипт не добавляется в выход транзакции (в поле scriptPubKey). Вместо этого, скрипт обременения хешируется. Полученный дайджест никак не может быть использован для восстановления исходного скрипта. Однако, имея на руках исходный скрипт, можно легко получить точно такой же дайджест путем повторного хеширования скрипта. Хеш сценария — это и есть та информация, которая включается в выход транзакции (в поле scriptPubKey). Для того, чтобы такой выход в следующей транзакции, недостаточно просто выполнить условия, определенные в сценарии, так как ноды сети Биткоин видят только дайджест хеша скрипта и поэтому о самом скрипте им ничего не известно. Следовательно, ноды не могут удостовериться в том, что условия, определенные в комбинированном сценарии выполнены. То есть, они не могут подтвердить транзакцию. Поэтому, чтобы разблокировать и растратить средства, транзакция должна включать в себя, вместе с условиями, определенными в скрипте, и сам скрипт. В этом случае ноды сети Биткоин, вычислив хеш приложенного сценарий, могут сравнить его с хешем, размещенным в выходе предыдущей транзакции. В случае совпадения нодам остается только проверить правильность выполнения условий, определенных в сценарии, после чего транзакция признается действительной. Рисунок 3.6 иллюстрирует изложенный процесс.
Таким образом, P2SH-модель по сути реализует следующее финансовое предписание "заплатить при предъявлении скрипта, соответствующего данному хешу".
Чем же модель P2SH отличается от мульти-подписи? Рассмотрим простой пример схемы с корпоративным управлением доступа к средствам, обеспечивающим высокую степень безопасности. Пусть какая-то компания использует механизм мульти-подписи для всех платежей, поступивших от клиентов. Для конкретики примем, что разблокировать средства можно при наличии подписей минимум двух владельцев компании. Всего компанией владеют пять человек.
Комбинированный сценарий траты средств в этом случае выглядит следующим образом:
OP_0 <Signature A> <Signature B> 2 < pubKey A> < pubKey B> < pubKey C> < pubKey D> < pubKey E> 5 OP_CHECKMULTISIG
Недостаток такого сценария очевиден – он слишком длинный. Мы помним, что в распределенных системах следует экономить на каждом байте блокчейна. К тому же вторая часть сценария (скрипт scriptPubKey) должен предварительно быть доведен до каждого клиента, чтобы они смогли его использовать в своих платежных транзакциях. Организационно это не самое удобное решение. Клиенты должны пользоваться специальными кошельками, быть в курсе процедуры создания мульти-подписных транзакций и платить повышенную комиссию майнерам за обработку огромных транзакций (из-за большой длины открытых ключей, которых в блокирующем скрипте стало намного больше, чем в традиционном P2PKH-выходе). Даже не будем вспоминать о дополнительной нагрузке на оперативную память каждой полной ноды, в которой должны храниться все подобные нерастраченные выходы (в пуле UTXO). Все перечисленные проблемы серьезно осложняют применение сложных скриптов обременения на практике.
Pay-to-script-hash модель как раз и была разработана для решения указанных проблем.
В P2SH-транзакциях, блокирующий скрипт заменяется процедурой проверки хеша скрипта, называемого погашающим, поскольку его код будет представлен системе в момент траты выхода (погашения), а отнюдь не в качестве предварительно записанных условий обременения. В таблице 3.4 представлены форматы скриптов для сценария с мульти-подписью и P2SH-схемы.
Сценарий с мульти-подписью | P2SH-сценарий |
---|---|
Блокирующий скрипт: | Погашающий скрипт: |
2 PubKeyA PubKeyB PubKeyC PubKeyD PubKeyE 5 OP_CHECKMULTISIG | 2 PubKey A PubKey B PubKey C PubKey D PubKey E 5 OP_CHECKMULTISIG |
Разблокирующий скрипт: | Блокирующий скрипт: |
Signature A Signature В | OP_HASH160 <20-байтный хеш погашающего скрипта> OP_EQUAL |
Разблокирующий скрипт: | |
Signature A Signature В Погашающий скрипт |
Биктоин-адрес формата P2SH всегда начинается с 3, а не 1, как в P2PKH-адресах. Это связано с тем, что P2SH-адреса перед кодированием по стандарту base58check дополняются префиксом байта версии, равным 0x05 (в шестнадцатеричном формате), а не 0x00 (в шестнадцатеричном формате), характерным для P2PKH-адресов.
А теперь вместо мнемонических обозначений в блокирующий скрипт подставим реальные открытые ключи (520-разрядные двоичные числа, начинающиеся с 0х04):
2 04C16B8698A9ABF84250A7C3EA7EEDEF9897D1C8C6ADF47F06CF73370D74DCCA01CDCA79DCC5C395D7EEC6984D83F1F50C900A24DD47F569FD4193AF5DE762C58704A2192968D8655D6A935BEAF2CA23E3FB87A3495E7AF308EDF08DAC3C1FCBFC2C75B4B0F4D0B1B70CD2423657738C0C2B1D5CE65C97D78D0E34224858008E8B49047E63248B75DB7379BE9CDA8CE5751D16485F431E46117B9D0C1837C9D5737812F393DA7D4420D7E1A9162F0279CFC10F1E8E8F3020DECDBC3C0DD389D99779650421D65CBD7149B255382ED7F78E946580657EE6FDA162A187543A9D85BAAA93A4AB3A8F044DADA618D087227440645ABE8A35DA8C5B73997AD343BE5C2AFD94A5043752580AFA1ECED3C68D446BCAB69AC0BA7DF50D56231BE0AABF1FDEEC78A6A45E394BA29A1EDF518C022DD618DA774D207D137AAB59E0B000EB7ED238F4D800 5 OP_CHECKMULTISIG
Пугает, правда? Зато в P2SH-сценарии весь этот ужас представляется 20-ти байтным дайджестом - результатом хеширования блокирующего скрипта с помощью последовательного применения алгоритмов SHA-256 и RIPEMD-160, т.е., RIPEMD-160(SHA-256(<код блокирующего скрипта>)). Для нашего конкретного случая это всего-то:
54c557e07dde5bb6cb791c7a540e0a4796f5e97e
Выход P2SH-транзакция будет обременен с помощью следующего блокирующего скрипта:
OP_HASH160 54c557e07dde5bb6cb791c7a540e0a4796f5e97e\ OP_EQUAL
Это все, что нужно знать клиенту для проведения платежа в адрес рассматриваемой в примере компании. Размер такой транзакции не будет превышать размеров обычных транзакций (по крайней мере по причине раздутого скрипта scriptPubKey) и проведение платежа не потребует от клиента затрат на повышенную комиссию майнерам.
Как же можно снять обременение с такого нерастраченного выхода? Ведь биткоин-адреса получателя средств, в привычном для нас с Вами понимании, в P2SH-сценарии больше нет. Необходимо создать вход, в котором представлен оригинальный код погашающего скрипта (того самого, хеш которого использовался при обременении средств) и подписи двух или более владельцев компании - в качестве удовлетворения условий обременения выхода - погашающего скрипта. Например:
< Signature A> < Signature B> < 2 <PubKey A> <PubKey B> <PubKey C> <PubKey D> <PubKey E> 5 OP_CHECKMULTISIG >
Комбинированный сценарий проверки притязаний на заблокированные средства, выраженных приведенным выше входом транзакции, выполняется в два этапа. Во-первых, погашающий сценарий проверяется разблокирующим скриптом на предмет совпадения хеша:
2 <PubKey A> <PubKey B> <PubKey C> <PubKey D> <PubKey E> 5 OP_CHECKMULTISIG OP_HASH160 54c557e07dde5bb6cb791c7a540e0a4796f5e97e OP_EQUAL
Если проверочный сценарий заканчивается успешно (в стеке окажется единственное значение ИСТИНА), переходим ко второму этапу, на котором мы фактически имеем дело с обычной процедурой проверки комбинированного сценария, включающего модифицированный разблокирующий скрипт (<Условия снятия обременения> + <Погашающий скрипт>):
OP_0 < Signature A> < Signature B> 2 <PubKey A> <PubKey B> <PubKey C> <PubKey D> <PubKey E> 5 OP_CHECKMULTISIG
Далее все происходит точно также как и в случае проверки обычной транзакции.
Pay-to-script-hash адреса
Последним аспектом P2SH-модели, на котором мы остановимся, является возможность использовать хеш скрипта в качестве адреса (впервые такая возможность была определена в BIP0013 - Bitcoin Improvement Proposal "Address Format for pay-to-script-hash"). Адресом в P2SH выходе транзакции является 20-ти байтный хеш сценария в кодировке Base58Check, точно так же, как традиционный биткоин-адрес - это 20-ти байтный хеш открытого ключа в кодировке Base58Check. Шестнадцатеричные P2SH-адреса используют префикс версии "5". Это значит, что после кодирования в формате Base58Check такие адреса будут начинаться с 3. Именно по этой тройке вначале все участниками сети Биткоин легко смогут распознать P2SH-адреса. Например, упомянутый выше шестнадцатеричный P2SH-адрес 54c557e07dde5bb6cb791c7a540e0a4796f5e97e в формате Base58Check будет записан так:
39RF6JqABiHdYHkfChV6USGMe6Nsr66Gzw
P2SH-адреса могут распространяться традиционными способами аналогично биткоин-адресам и аналогично последним содержат всю необходимую информацию для создания транзакций. Клиенты нашей компании из примера могут воспользоваться практически любым Биткоин-кошельком для проведения платежей по описанной выше, достаточно сложной схеме. Единственное, что им для этого нужно 20-байтный шестнадцатеричный хеш скрипта, воспринимаемого как некоторый абстрактный адрес, или его 34-символьная запись в формате Base58Check. Ну и без некоторого числа нерастраченных биткоинов они конечно тоже заплатить не смогут.
В модели pay-to-script-hash в качестве погашающего скрипта разрешено использовать любой не содержащий ошибок сценарий, включая стандартные типы: P2PK, P2PKH, мульти-подпись. Запрещается применять сценарии типа OP_RETURN (как OP_RETURN-скрипт не может быть погашен по определению) и рекурсивные обращения, т.е., те же скрипты P2SH.
Поскольку погашающий скрипт изначально в сети не представлен (блокирующий скрипт содержит только его хеш), надо быть особенно внимательным с P2SH-транзакциями. Любая ошибка в сценарии обременения, во-первых, будет обнаружена только при попытке разблокировать денежные средства, а во-вторых, уже никак не сможет быть исправлена. И биткоины с таким обременением для владельца окажутся навсегда потерянными.
Обновление Segregated Witness
Цель: Сформировать представление о сути и задачах, выполняемых обновлением Segregated Witness (SegWit).
В 2017 году был активирован протокол Segregated Witness (SegWit) – самый крупный на сегодняшний день софтварный апгрейд в сети Биткоин, целью которого было дальнейшее масштабирование, снижение транзакционных комиссий и увеличение лимита блоков.
Для понимания проблемы, которую решает SegWit, необходимо внимательнее рассмотреть, как устроены биткоин-транзакции. Состоят они из двух главных частей: основных данных о транзакции, к которым, например, относится информация о том, какие именно перемещаются монеты и в каком направлении, и, так называемого, "свидетеля" (witness). Речь идет о части кода (которую называют scriptSig, witness или же разблокирующий скрипт) с данными электронной подписи, служащей доказательством того, что владелец монет действительно хочет их потратить. Именно в этих данных заключена небольшая проблема, получившая название пластичности транзакции. Ее суть состоит в том, что подписи могут быть изменены даже после их создания и при этом они остаются действительными. Это означает, что те, кто транслируют транзакцию, или майнеры, которые включают ее в блок, могут изменить внешний вид этой транзакции, или, если говорить более предметно, ее идентификатор. Проблема на первый взгляд кажется не столь значимой: транзакции остаются действительными, а монеты перемещаются на нужные адреса. Сложность возникает при создании новых транзакций, которым необходимо знать идентификатор прежней транзакции, на который они опираются. Это в свою очередь значительно затрудняет создание поверх сети Биткоин определенных протоколов второго слоя, например, двунаправленных платежных каналов.
Разберемся в этих проблемах более основательно. Напомним, что идентификатор транзакции (он же хеш транзакции - txid) – это 64-разрядное шестнадцатеричное число (256 бит), которое представляет собой идентификационный номер, уникальный для каждой транзакции в блокчейне Биткоин. Такие же идентификаторы используются и в других платформах.
Например, идентификатор транзакции в сети Биткоин выглядит так:
a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d
Идентификатор транзакции в платформе Ethereum выглядит практически также:
b4bc263278d3f77a652a8d73a6bfd8ec0ba1a63923bbb4f38147fb8a943da26d
Дискуссии о решении проблемы пластичности транзакций посредством "отделения" данных о подписи от остальных данных транзакции начались еще в январе 2012 года. Среди прочих в них принимали участие разработчики Bitcoin Core Расселл О'Коннор, Мэтт Коралло, Люк Дэш-младший и Грегори Максвелл, а также модератор форума Bitcointalk Theymos. Однако подходящего решения тогда так найдено и не было.
Segregated Witness (сокращенно SegWit) — обновление платформы Биткоин, призванное решить две основных проблемы данной криптовалюты. Речь идет об обеспечении пластичности транзакций блокчейна Биткоин, а также об увеличении пропускной способности системы. Аналогичное по сути техническое решение реализовано и для целого ряда альткоинов, таких как Litecoin, DigiByte, Groestlcoin и Vertcoin. Обновление позволяет уменьшить размер транзакций, делая блоки более вместительными, а также снимает проблему пластичности ("изменчивости" transaction malleability), что очень важно для технологий наподобие платежных каналов или лайтнинга, полагающихся на строение транзакции сети Биткоин.
SegWit является "софт форком" (т.е. изменения сделаны на уровне программного обеспечения клиентов и не затронули сам блокчейн) и позволяет сети функционировать в прежнем режиме. При этом, он предусматривает изменение структуры, используемой для хранения информации в блоке и механизма валидации транзакций полными нодами. Для достижения цели данные, инкапсулирующие электронные подписи и блокирующие скрипты выделены в обособленную структуру под названием "отдельный свидетель" (англ. segregated witness). Поскольку размеры каждой транзакции стали существенно меньше, основные блоки способны вместить большее число транзакций. Одновременно такой механизм обеспечивает инвариантность идентификатора одной и той же транзакции в рамках всего жизненного цикла.
Форк (от англ. Fork - вилка) - процесс, в результета которого создается альтернативная успешная версия блокчейна. Подобное может произойти умышленно - в случае сосредоточения под контролем группы майнеров вычислительных мощностей, достаточных для влияния на консенсус сети (атака 51%), случайно (когда нескольким майнерам одновременно удается записать новые блоки, что приведет к ветвлению блокчейна, или в результате ошибки системы), а также целенаправленно при решении команды разработчиков расширить функционал системы (или исправить ранее допущенные ошибки) в новых версиях клиентского программного обеспечения.
Форк признается успешным, если его ветвь становится цепочкой блоков с максимальной суммарной сложностью доказательств консенсуса. В этом случае альтернативная ветка блокчейна отвергается и в дальнейшем в системе не развивается.
Также форком называют изменения протокола криптовалюты, приводящие к созданию двух различных версий блокчейна с единой историей.
Часто форком называют новую криптовалюту, основанную на протоколе действующей платежной платформы. Например, большинство известных криптовалют являются форками сети Биткоин.
Проблема масштабируемости
В 2010 Сатоши Накамото (по крайней мере, так себя называл создатель (группа разработчиков) Биткоина) ограничил размер блока одним мегабайтом. Такая мера благотворно повлияла на совместимость узлов сети и позволила существенно уменьшить эффективность DDoS-атак. Однако, одновременно с этим снизилась максимальная пропускная способность платформы, в среднем до 3-7 транзакций в секунду. В дальнейшем это ограничение оказало очень негативное влияние на возможность масштабирования сети. Рост числа участников и количества переводов привел к заметному увеличению времени задержки платежа — некоторые транзакции "зависали" в подвешенном состоянии на несколько дней. Синхронно с задержками выросла комиссия за обработку транзакций, взимаемая майнерами. Данное обстоятельство резко снизило возможности применения платформы Биткоин для проведения мелких платежей – ниши, которая изначально предполагалась идеальной для использования криптовалюты.
Преодолеть негативные последствия разработчики пытались не один раз. Самым очевидным и известным из предложенных решений является увеличение размера блока. Сразу несколько форков Биткойна, такие как Bitcoin XT, Bitcoin Classic, Bitcoin Unlimited, Lightning Bitcoin и самый успешный Bitcoin Cash, пошли именно по этому пути.
Пластичность транзакции
Еще одной проблемной точкой сети Биткоин является, так называемая, пластичность транзакции. Обычная платежная транзакция содержит электронную подпись, которая позволяет другим участникам сети проверить правомочность намерений создателя транзакции. Подпись формируется на основе закрытого ключа для каждой транзакции, что позволяет впоследствии легко обнаружить малейшее изменение контента транзакции. Любое изменение критичных данных транзакции вызывает изменение ее идентификатора. На самом деле, можно изменить транзакцию, сохранив при этом ее статус как действительной (например, можно без проблем добавить дополнительные служебные константы в подпись, не меняющие смысла сценария проверки транзакции). Однако, подобные незначительные правки приводят к кардинальному изменению ее идентификатора. В результате, модифицированная транзакция будет считаться совершенно новой, но при этом, сможет пройти проверку другими узлами сети.
Критичной является ситуация, когда модифицированная транзакция оказывается в блокчейне раньше исходной или в дальнейшем окажется в его более длинной ветви. В этом случае оригинальная транзакция будет признана недействительной, поскольку ее вход ссылается на уже использованный выход (так же будут признаны недействительными все транзакции, ссылающиеся на нее). Это создает множество проблем, в виду того, что большое число систем проверяет состоятельность финансовых сделок по их идентификатору. Также это обстоятельство существенно затрудняет внедрение технических решений более высокого уровня, основанных на блокчейне платформы Биткоин.
Атака "дней рождения"
Сценарии с мульти-подписью теоретически могут быть атакованы. Если злоумышленник владеет хотя бы одним ключом из представленного в транзакции списка, то с учетом коллизии хеша он может уменьшить число комбинаторных вариантов до 280, перебор которых представляется вполне осуществимой задачей для современных вычислительных систем.
Указанные выше проблемы обострились в последнее время в связи с ростом числа пользователей и развертыванием поверх сети Биткоин большого числа распределенных приложений. Разработчики и раньше пытались с ними бороться, но, пожалуй, только обновление Segregated Witness реально предоставляет механизм выхода из тупиковой ситуации. Хотя бы на какое-то время.
Подписи
Подписи – это криптографический прием, в котором для вычисления уникальной последовательности чисел используется закрытый ключ в сочетании с любыми другими данными. Соответствующий открытый ключ может использоваться для верификации того, что данная подпись была создана с использованием данного приватного ключа. Таким образом, подписи доказывают, как владение закрытым ключом, так и подтверждение определенной части данных владельцем закрытого ключа – и всё это без его разглашения. В случае платформы Биткоин закрытые ключи обычно используются для подписи данных транзакции (за исключением входов, scriptPubKeys, и некоторых других данных). В результате подпись и открытый ключ, по которому расходуются биткоины добавляются к транзакции в поле входа. Это доказывает то, что владелец ключа действительно желал совершить транзакцию, и гарантирует, что ее нельзя было подделать.
Далее все данные транзакции, включая на этот раз и входы, совместно хешируются, полученный результат впоследствии служит её идентификатором (txid транзакции). Если транзакция в конце концов попадает в блок, майнер хеширует ее txid вместе с txid другой транзакции, получая новый хеш. Этот хеш также хешируется, на этот раз с хешем двух других txid транзакций. Процесс продолжается, пока не останется всего один хеш. Эта система хешей называется деревом Меркла, а оставшийся хеш – корнем Меркла. Корень объединяется с дополнительными данными, которые используются для идентификации конкретного блока, формируя его заголовок. Хеш заголовка блока в итоге включается в заголовок следующего блока, образуя цепочку связанных блоков. Блокчейн криптовалюты является неизменяемым, поскольку редактирование части любой транзакции задним числом поменяет txid транзакции, что приведёт к изменению заголовка блока – однако такой блок уже не будет соответствовать установленным требованиям. И поскольку заголовок блока влияет на структуру последующих заголовков блока, они также не будут им соответствовать.
SegWit основан на концепции сайдчейнов, разработанной компанией Blockstream, и дополняет идею разработчика ядра платформы Биткоин Люка Дэша. Общая концепция была разработана спустя несколько месяцев в сотрудничестве с разработчиками ядра Грегори Максвеллом и Эриком Ломброзо.
С точки зрения нод, которые не используют SegWit (условно назовём их ортодоксальными) некоторые вновь созданные выводы могут начать использовать странный тип scriptPubKeys. Странность заключается в том, что их едва ли можно считать блокирующими или запирающими. Именуемые как (Anyone Can Spend - "тратят все"), эти выходы в целом заявляют, что подписи им не нужны. Кроме того, в них еще и присутствует совершенно бессмысленный (но не запрещенный протоколом) текст.
Старые ноды сочтут эти транзакции бессмысленными. Они будут уверены в том, что любой пользователь может создать новый scriptSig, высвобождая эти выводы – а это значит, что они практически полностью незащищены. В то же время, старые ноды технически не смогут не принимать новые транзакции. Текст ScriptPubKeys покажется им не имеющим смысла, но технически вполне допустимым. Поэтому ортодоксальные ноды определят в итоге транзакции как действительные, и ретранслируют их дальше по сети.
А вот ноды с SegWit (назовем их новыми) поведут себя немного иначе. Текст ScriptPubKeys приобретет для них вполне конкретный смысл и будет идентифицирован как весьма специфический тип выхода.
Подобно "доперестроечным" выходам, эти новые выходы будут нуждаться в валидных подписях для высвобождения биткоина – однако в отличие от них, для этого им не нужна будет подпись, включённая в scriptSig следующей транзакции. Вместо этого подпись должна содержаться в ранее отсутствующей части транзакции – SegWit.
SegWit по сути является параллельной структурой данных, содержащей подписи и некоторые другие данные. Главное здесь то, что SegWit полностью игнорируется старыми нодами, но признается новыми. К тому же, теперь подписи не хешируются совместно с другими частями транзакции для создания txid.
Таким образом, и ортодоксальные, и новые ноды будут считать транзакции с SegWit валидными. Ортодоксальные ноды признают их действительными, поскольку с их точки зрения им вовсе не нужны подписи, а новые – потому, что нужная подпись находится в SegWit. Поскольку и те, и другие ноды хешируют данные транзакции, получая один и тот же идентификатор, консенсус по компоновке блоков будет достигнут, а, следовательно, к состоянию блокчейна также не будет возникать никаких вопросов.
Есть, однако, небольшая проблема: если подписи не влияют на компоновку блокчейна, он уже не может являться доказательством того, что в транзакции включены корректные подписи.
Чтобы подписи всё равно включались в блокчейн, майнер с SegWit выполняет дополнительное действие – создаёт дерево Меркла не только из транзакций, но и из SegWit, причём последнее полностью соответствует дереву транзакций. Корень дерева SegWit включается в поле ввода транзакции coinbase. Таким образом корень дерева SegWit меняет данные транзакции coinbase, её идентификатор, а значит и заголовок – в результате меняется вся компоновка блокчейна. Обновление Segregated Witness позволяет удалить подписи из транзакций биткоина, сохранив его неизменяемость и не нарушая ни одного из принятых правил консенсуса.
Обновление Segregated Witness
Суть обновления Segregated Witness состоит в образовании одноименной динамической структуры вне основного блока и переносе в нее подписей транзакций. Такой механизм позволяет значительно уменьшить нагрузку на блок, оказываемую каждой транзакцией, поскольку на электронную подпись приходится более половины ее размера. Автоматически решается и проблема пластичности транзакций, поскольку подписи больше не влияют на идентификатор транзакции.
При этом, безусловно, меняется процесс проверки транзакции. Теперь ноде нужно загрузить расширенный блок (основной блок + данные "отделенного свидетеля"). О своей готовности обработать расширенную структуру блока узел специально извещает соседей. Узлы, не поддерживающие SegWit, продолжают принимать стандартные блоки размером в 1МБ, полагая, что транзакции не требуют доказательства владения в виде подписи.
Контейнер подписей для организации связи с основной цепочкой использует дерево Меркла, корень которого хранится в заголовке блока. Для всех электронных подписей и транзакций рассчитываются значения хеш-функции, которые заносятся в дерево Меркла. Суммарный хеш подписей присоединяется к хешу первой транзакции (coinbase-транзакции) в дереве Меркла.
Расширенный блок теоретически ограничен 4 мегабайтами, но фактически максимальный размер блока составляет чуть менее 2 мегабайт.
В SegWit для защиты кошельков с мульти-подписью вместо модели P2SH используются сценарии P2WSH, защищенные хеш-функцией SHA-256. Данное обстоятельство усложняет атаку "дней рождения".
На самом деле, Segregated Witness меняет не только структуру транзакции, но и строение ее выходов. Это, однако, не значит, что в одной и той же транзакции не могут быть потрачены как традиционные неизрасходованные выходы (UTXO), так и новые SegWit типа — просто первые будут искать "доказательство" внутри входа (поле scriptSig), а вторые — снаружи. Также вводится отдельный идентификатор wtxid — он хеширует не только транзакцию, но и всю witness часть, так что если транзакция передается без witness данных, то ID равен wtxid.
Так как Segregated Witness все-таки является софт-форком, его обновления могут быть проигнорированы, а значит более старые системы должны как-то обрабатывать SegWit выходы. Дело в том, что для старых нод или кошельков эти выходы выглядят как доступные всем, то есть они могут быть потрачены с пустой подписью, что все еще допустимо. Принявшие обновление ноды и кошельки конечно же будут искать все подписи вне пространства входов в специальном поле witness.
Pay-to-Witness-Public-Key-Hash
Теперь давайте взглянем на примеры транзакций и на то, как они изменятся с обновлением Segregated Witness. Начнем со стандартной Pay-to-Public-Key-Hash (P2PKH) транзакции. Нас интересуют выходы, а именно их поля "scriptPubKey". Типичный блокирующий скрипт выглядит следующим образом:
OP_DUP OP_HASH160 <Public-Key-Hash> OP_EQUALVERIFY OP_CHECKSIG
Модель Segregated Witness низводит его до уровня "Anyone Can Spend":
0 <Public-Key-Hash>
SegWit-выход существенно проще традиционного — он состоит из двух значений, которые будут помещены в стек сценария. Как уже упоминалось, для старых версий клиентов платформы Биткоин такой выход будет виден как доступный любому, поскольку он не требует подписи. А вот для обновленного клиента первое число интерпретируется как номер версии, а второе как аналог запирающего скрипта (witness program). На самом деле, здесь должен использоваться хеш сжатого публичного ключа, об этом мы расскажем немного позже.
Теперь давайте сравним транзакции, в которых выход будет потрачен. В традиционном случае это выглядело бы так:
[...] "Vin" : [ { "txid": "8adbca5e652c68f8f3c30ac658115bc4af395d0cc7e6beaea18168295c29d011", "vout": 0, "scriptSig": "<Signature> <Public-Key>" } ] [...]
Для траты SegWit-выхода, транзакция должна иметь пустое поле sriptSig и содержать все подписи в отдельном месте:
[...] "Vin" : [ { "txid": "8adbca5e652c68f8f3c30ac658115bc4af395d0cc7e6beaea18168295c29d011", "vout": 0, "scriptSig": "" } ] [...] "witness": "<Signature> <Public-Key>" [...]
Несмотря на то, что ортодоксальные клиенты могут обрабатывать SegWit-транзакции, они не могут тратить их выходы, так как они просто не знают, как это сделать: кошелек старого типа попытается потратить SegWit-выход с пустой подписью, однако эта транзакция на самом деле не будет считаться действительной (ноды, поддерживающие Segregated Witness, ее не пропустят). В частности, из этого следует, что отправитель, как минимум, должен убедиться в том, что кошелек получателя реализует SegWit.
Согласно BIP1432 (Transaction Signature Verification for Version 0 Witness Program) в настоящее время выходы должны создаваться с помощью хеша сжатого открытого ключа. Если выход будет создан на основе обычного адреса или несжатого открытого ключа, его нельзя будет потратить.
Pay-to-Witness-Script-Hash
Следующим важнейшим типом транзакции является P2SH-модель. Чтобы потратить выход P2SH-транзакции нужно предоставить погашающий скрипт и условия траты, определенные этим скриптом. Используя такой подход можно отправлять биткоины на адрес, защищенный способом, о котором нам вообще ничего не известно, а также сильно экономить место — в случае, например, кошелька с мульти-подписью блокирующий скрипт был бы действительно большим, если бы мы хранили в нем все "замки" полностью, а не только хеш скрипта.
Если вспомнить пример, ранее нами уже разобранный, то традиционный блокирующий скрипт для случая с мульти-подписью, требующей наличия 2-ух подписей из 5-ти выглядит так:
OP_HASH160 54c557e07dde5bb6cb791c7a540e0a4796f5e97e OP_EQUAL
Для траты средств, нужно предоставить погашающий скрипт, который собственно определяет необходимость предоставления 2-х подписей из 5-ти возможных, а также любые 2 подписи из списка и все это должно содержаться во входе транзакции:
[...] "Vin" : [ "txid": "8adbca5e652c68f8f3c30ac658115bc4af395d0cc7e6beaea18168295c29d055", "vout": 0, "scriptSig": "<SigA> <SigB> <2 PubA PubB PubC PubD PubE 5 CHECKMULTISIG>", ]
Рассмотрим технику реализации этой модели в системах с поддержкой Segregated Witness.
Начнем с блокирующего скрипта выхода:
0 9592d601848d04b172905e0ddb0adde59f1590f1e553ffc81ddc4b0ed927dd73
Как и в предыдущем случае блокирующий скрипт намного проще. Здесь первое значение является номером версии, а второе — это 32-х байтный SHA-256-дайджест погашающего скрипта (witness program).
Обратим внимание на то, что здесь хеш 32-х байтный. Сделано это для того, чтобы можно было по длине хеша легко отличить witness-программы для P2WPKH /20 байт RIPEMD-160(SHA-256(script))/ и P2WSH /32 байта SHA-256(script)/.
Транзакция, способная потратить этот выход, выглядит следующим образом:
[...] "Vin" : [ "txid": "8adbca5e652c68f8f3c30ac658115bc4af395d0cc7e6beaea18168295c29d077", "vout": 0, "scriptSig": "", ] [...] "witness": "<SigA> <SigB> <2 PubA PubB PubC PubD PubE 5 CHECKMULTISIG>" [...]
Можно ли использовать Segregated Witness в случае, когда один из партнеров не имеет обновленного кошелька? Оказывается, можно. Скажем, восприимчивый к инновациям владелец кофейни Сергей уже обзавелся кошельком с поддержкой Segregated Witness. В то время как у его постоянной клиентки Марины старый, ортодоксальный кошелек. Разумеется, можно прибегнуть к транзакции стандартного типа. Однако, Сергей любит все новое, и оба очень хотят сэкономить на комиссионных майнерам.
В такой ситуации Сергей может создать P2SH сценарий, содержащий SegWit-скрипт. Программное обеспечение кошелька Марины опознает хеш этого сценария как самый обычный P2SH-адрес, что позволит ей без каких-либо проблем отправить туда средства. В свою очередь Сергей так же легко сможет потратить выход транзакции Марины, используя SegWit- транзакцию.
Таким образом, оба типа SegWit-транзакций и P2WSH, и P2WPKH могут быть реализованы в рамках P2SH-модели.
P2SH(P2WPKH) сценарии
Точно также любой P2WSH скрипт может быть реализован внутри P2SH. Возьмем multisig скрипт 2 из 5, рассмотренный ранее. Все шаги будут практически идентичны случаю P2SH(P2WPKH):
Начинаем снова с создания witness- программы:
0 9592d601848d04b172905e0ddb0adde59f1590f1e553ffc81ddc4b0ed927dd73
Первое число — версия, второе число — 32-х байтный SHA-256 хеш нашего скрипта мульти-подписи. Далее шаги повторяются — находим RIPEMD-160 хеш от witness-программы и преобразуем в обычный P2SH-адрес. Для использования выхода, отправленного на этот адрес, в scriptSig нужно записать п-программу, а все подписи и полный скрипт мультиподписи в поле witness.
Снижение размера комиссионных
За счет скидки на хранение witness-данных SegWit-транзакции обходятся дешевле, по сравнению с традиционными. Фактически было изменено само понятие "размера" для SegWit-транзакций. Вместо традиционного подхода для них используется концепция "виртуального размера" (virtual size) — все данные, хранящиеся в witness, учитываются с коэффициентом 0.25, что также позволяет разместить в блоке больше транзакций. Рассмотрим на примере. Пусть у нас есть традиционная транзакция размером в 200 байт. В блок размера 1 МB поместится 5000 таких структур данных. Теперь возьмем эквивалентный ей SegWit вариант, где примерно 120 байт это witness-данные. Тогда ее виртуальный размер = 80 + 0.25 * 120 = 110 и теперь уже 9090 таких транзакций поместятся в тот же самый блок. Также при комиссии, скажем, в 40 сатоши/байт для первой транзакции мы получим комиссию в 8000 сатоши, а для SegWit структуры - 4400 сатоши, что практически в два раза меньше.
Версия скрипта
Каждый блокирующий скрипт содержит байт, отвечающий за версию скрипта. Использование механизма версий позволяет вносить дополнения и правки (изменения в синтаксисе, новые операторы и т.д.) в виде софт-форков.
Оптимизация процессов проверки электронных подписей
В обновлении Segregated Witness были оптимизированы алгоритмы обработки электронных подписей (OP_CHECKSIG, OP_CHECKMULTISIG и т.д.). До реализации обновления число хеш-вычислений увеличивалось квадратично относительно количества подписей. В системе, использующей Segregated Witness, сложность алгоритмов валидации понижена до O(n).
Рис. 3.7. Сравнение времени проверки электронных подписей до (зеленая линия) и после (красная линия) реализации Segregated Witness
Активация
Segregated Witness был предложен Питером Вюлле (Pieter Wuille) в конце 2015 года. Выпуск состоялся в октябре 2016 года — на 6 месяцев раньше запланированного срока. Активация должна была произойти после преодоления 95%-порога участников, сигнализирующих о поддержке обновления. Но некоторые участники сети заявили, что поддержат обновление, только если в него будет добавлено увеличение размера основного блока (китайские пулы могли заблокировать введение SegWit). 23 мая 2017 года майнеры и разработчики подписали Нью-Йоркское соглашение, предполагавшее увеличение размера основного блока до 2 МБ в течение 6 месяцев (это обновление назвали SegWit2x). SegWit был активирован 24 августа 2017 года.
Некоторые альткойны тоже решили реализовать SegWit. Так как многие альткойны основаны на коде платформы Биткоин, это не составило для разработчиков особых трудностей. Первым из них активировал обновление Groestlcoin в январе 2017 года.
Преимущества Segregated Witness:
- Совместим с предыдущими версиями программного обеспечения.
- Увеличивает количество транзакций в блоке.
- Снижает комиссионные сборы.
- За счет количества транзакций в блоке общие сборы майнеров могут увеличиться.
- Уменьшает время ожидания в очереди.
- Способствует масштабируемости платформы Биткоин.
- Устраняет пластичность транзакций.
- Облегчает разработку и увеличивает эффективность и безопасность дополнительных надстроек (смарт-контракты, Lightning Network и т. д.).
- Устраняет проблему квадратичного роста времени проверки транзакций.
- Повышает надежность кошельков с мульти-подписью.
Недостатки:
- Увеличивает нагрузку на узлы сети.
- Несколько усложняется проверка транзакций.
- Сборы майнеров могут сократится.
- Некоторые участники считают SegWit лишь временной мерой и настаивают на увеличении размера основного блока.
- Дополнительная цепочка тоже требует обслуживания, в чем майнеры не особо заинтересованы. В сети Биткоин не предусмотрено вознаграждение за проверку транзакций (в отличие от Dash). Майнеров сдерживает только высокая вероятность майнинга ошибочных блоков при высокой концентрации облегченных клиентов (SPV-нод).
- Так как SegWit является софт-форком, обновлены будут далеко не все клиенты, а значит в сети будут находится одновременно два вида нерастраченных выходов, и такие важные изменения как устранение уязвимости идентификаторов транзакций и хеширование за линейное время не будут применены к ортодоксальным выходам, а значит сеть все еще будет уязвима к атакам, основанным на изменяемости идентификаторов транзакций, а также к проблеме квадратичного времени хеширования.
- SegWit может уменьшить безопасность сети. Количество нод, проводящих полную валидацию, сильно уменьшится, так как только принявшие SegWit смогут проверять witness часть транзакций.
- SegWit не может быть отменен. Если его отменить и откатить все изменения обратно, все SegWit-выходы станут доступными каждому.
- SegWit пытается решить все проблемы сразу и, как следствие, огромное количество кода изменено. Это усложняет дальнейшую работу и увеличивает вероятность появления багов, которые будет сложнее устранить.
Перспективы развития
8 ноября 2018 "хард-форк" SegWit2x был отложен на неопределенное время из-за отсутствия консенсуса.
Благодаря обновлению SegWit существенно облегчается разработка и внедрение надстроек, а также увеличивается их безопасность и эффективность. В ближайшее время планируется запуск Lightning Network. Разрабатывается решение для увеличения гибкости смарт-контрактов Merklized Abstract Syntax Tree (MAST), которое также улучшает масштабируемость и повышает конфиденциальность.
Начиная с версии 0.16.0 эталонного клиента Bitcoin Core, опубликованной 15-го февраля 2018 года, программное обеспечение этого кошелька обеспечивает полную поддержку технологии Segregated Witness. Базовая поддержка SegWit появилась еще в версии 0.13, но содержала много недоработок. Начиная с Bitcoin Core 0.16.0, адреса и транзакции SegWit поддерживаются полностью и используются по умолчанию.
В октябре 2018 года число SegWit-транзакций в сети Биткоин превысило 50%. Как следует из рисунка 25, рост SegWit-транзакций хотя и не был стремительным, но проявил высокую стабильность. Спустя всего семь месяцев после реализации обновления (т.е., начиная с марта 2018 года) процент SegWit-транзакций не опускался ниже отметки 30%. На начало 2019 года их доля составляет половину общего пула.
Краткие итоги
Транзакции - это специальные структуры данных, фиксирующие процессы передачи ценности (токенов) между участниками криптоплатформы. Ноды проверяют полученные от других узлов по пиринговой сети транзакции и передают их дальше. Жизненный цикл транзакции включает следующие этапы: создание; подписание обычной или мульти-подписью; распространение по сети; проверка майнером и включение в блок; включение блока с транзакцией в блокчейн.
Пиринговая (одноранговая) сеть представляет собой свободное объединение абсолютно равноправных компьютеров. Такая сеть характеризуется высокой степенью отказоустойчивости и принципиальной возможностью получения необходимой информацию одновременно из разных источников.
Транзакция согласно протокола Биткоин содержит следующие поля: версия; число входов; входы; число выходов; выходы; Locktime.
Концептуальным элементом протокола Биткоин являются неизрасходованные выходы транзакций (Unspent Transaction Output), позволяющие отследить состояния реестра. Каждая очередная транзакция расходует выходы предыдущих и создает новые выходы, которые в свою очередь будут израсходованы последующими транзакциями. Причем каждый выход может использоваться только один раз. Выходы транзакций инкапсулируют определенную сумму и фиксированное обременение, определяющее условие вывода средств. В большинстве случаев, блокирующий скрипт (ScriptPubKey) ассоциирует обременение с адресом получателя платежа. Поэтому вход транзакции должен включать отпирающий сценарий (ScriptSig), удовлетворяющий условиям снятия обременения, установленным данным выходом. Как правило это электронная подпись, доказывающая факт владения биткоин-адресом, фигурирующим в сценарии блокировки.
Особенностью coinbase-транзакций является отсутствие входов.
Кроме стимулирования майнеров, комиссия за транзакцию выполняет важнейшую системную функцию – избавляет сеть Биткоина от спам-транзакций. Стандартная величина комиссионных рассчитывается на основе размера транзакции в килобайтах и не зависит от переводимой суммы.
Проверка валидности транзакции в сети Биткоин осуществляется на основе выполнения скрипта, записанного на специальном языке сценариев, называемым Script. Это Forth – подобный стековый язык программирования с обратной польской нотацией, неполный по Тьюрингу. Важная особенность таких языков - использование стека для передачи параметров между термами, что позволяет очень гибко и просто реализовывать достаточно сложные конструкции.
Разрешенными в платформе Биткоин являются следующие типы сценариев транзакций:
- pay-to-public-key-hash (P2PKH);
- public-key;
- multisignature (с ограничением на 15 ключей);
- pay-to-script-hash (P2SH);
- выход данных (OP_RETURN).
Суть обновления Segregated Witness состоит в образовании одноименной динамической структуры вне основного блока и переносе в нее подписей транзакций. Такой механизм позволяет значительно уменьшить нагрузку на блок, оказываемую каждой транзакцией, поскольку на электронную подпись приходится более половины ее размера. Автоматически решается и проблема пластичности транзакций, поскольку подписи больше не влияют на идентификатор транзакции.