Мы уже давно интересуемся темой анонимности в криптовалютах и стараемся следить за развитием технологий в этой области. В своих статьях мы уже подробно разбирали принципы работы конфиденциальных транзакций в Monero, а также проводили сравнительный обзор технологий, существующих на этом поле. Однако же все анонимные криптовалюты на сегодняшний день построены на модели данных, предложенной Bitcoin — Unspent Transaction Output (далее UTXO). Для account-based блокчейнов как Ethereum существующие решения по реализации анонимности и конфиденциальности (например, Mobius или Aztec) пытались повторить модель UTXO в смарт-контрактах.
В феврале 2019 года группа исследователей из Стэнфордского университета и Visa Research выпустили препринт «Zether: Навстречу приватности в мире смарт-контрактов». Авторы впервые предложили подход к обеспечению анонимности в account-based блокчейнах и представили два варианта смарт-контракта: для конфиденциальных (сокрытие балансов и сумм переводов) и анонимных (сокрытие получателя и отправителя) транзакций. Мы находим предложенную технологию интересной и хотели бы поделиться ее устройством, а также поговорить о том, почему проблема анонимности в account-based блокчейнах считается очень сложной и удалось ли авторам решить ее в полной мере.
В UTXO-модели транзакция состоит из «входов» и «выходов». Прямой аналог «выходам» — купюры в вашем кошельке: каждый «выход» имеет какой-то номинал. Когда вы расплачиваетесь с кем-то (формируете транзакцию) вы тратите один или несколько «выходов», при этом они становятся «входами» транзакции, и блокчейн помечает их как потраченные. При этом получатель вашего платежа (или же вы сами, если вам нужна сдача) получает вновь сгенерированные «выходы». Схематично это можно изобразить так:
Account-based блокчейны устроены примерно как ваш банковский счет. Они оперируют только суммой на вашем счету и суммой перевода. Когда вы переводите какую-то сумму со своего счета, то не сжигаете никакие «выходы», сети не нужно запоминать, какие монеты потрачены, а какие нет. В простейшем случае проверка транзакции сводится к проверке подписи отправителя и суммы на его балансе:
Далее мы поговорим о том, как Zether скрывает сумму транзакций, получателя и отправителя. По ходу описания принципов его работы мы будем отмечать различия в конфиденциальном и анонимном варианте. Так как обеспечить конфиденциальность в account-based блокчейнах намного проще, то некоторые из ограничений, накладываемых анонимизацией, будут неактуальны для конфиденциальной версии технологии.
Для шифрования балансов и сумм переводов в Zether используется схема шифрования Эль-Гамаля. Работает она следующим образом. Когда Алиса хочет послать Бобу b монет по адресу (его публичному ключу) Y, она выбирает случайное число r и шифрует сумму:
где C — зашифрованная сумма, D — вспомогательное значение, необходимое для расшифровки этой суммы, G — фиксированная точка на эллиптической кривой, при умножении секретного ключа на которую получается публичный ключ.
Когда Боб получает эти значения, он просто прибавляет их к своему зашифрованному таким же способом балансу, чем и удобна эта схема.
Аналогичным образом Алиса вычитает из своего баланса такие же значения, только в качестве Y использует свой публичный ключ.
Замешивание «выходов» в UTXO появилось еще на заре криптовалют и помогает скрыть отправителя. Для этого сам отправитель при совершении перевода набирает случайные «выходы» в блокчейне и замешивает их со своими. Далее подписывает «выходы» кольцевой подписью — криптографическим механизмом, позволяющим убедить проверяющего в том, что среди замешанных «выходов» присутствуют монеты отправителя. Сами замешанные монеты, разумеется, не тратятся.
Однако для сокрытия получателя нам не удастся генерировать фальшивые «выходы». Поэтому в UTXO у каждого «выхода» свой уникальный адрес, и он криптографически связан с адресом получателя этих монет. На данный момент нет способа выявить связь между уникальным адресом «выхода» и адресом получателя, не зная его секретных ключей.
В account-based модели мы не можем использовать одноразовые адреса (иначе это уже будет модель «выходов»). Поэтому получателя и отправителя приходится замешивать среди других аккаунтов в блокчейне. При этом с замешиваемых аккаунтов списывается зашифрованный 0 монет (или прибавляется 0 — в случае замешивания получателя), фактически не меняя их реальный баланс.
Так как и отправитель, и получатель всегда имеют постоянный адрес, здесь возникает необходимость при переводах на одни и те же адреса использовать для замешивания одни и те же группы. Проще рассмотреть это на примере.
Допустим, Алиса решила сделать взнос в благотворительный фонд Боба, но предпочитает, чтобы этот перевод оставался анонимными для стороннего наблюдателя. Тогда, чтобы замаскировать себя в поле отправителя, она вписывает еще аккаунты Адама и Адель. А чтобы скрыть Боба — в поле получателя дополнительно аккаунты Бена и Билла. Делая следующий взнос, Алиса решила рядом с собой вписать Алекса и Аманду, а рядом с Бобом — Брюса и Бенджена. В этом случае при анализе блокчейна в этих двух транзакциях найдется всего лишь одна пересекающаяся пара участников — Алиса и Боб, что деанонимизирует эти транзакции.
Как мы уже упоминали, для сокрытия своего баланса в account-based системах пользователь шифрует свой баланс и сумму перевода. При этом он должен доказать, что остаток на его счете остается неотрицательным. Проблема заключается в том, что формируя транзакцию, пользователь строит доказательство относительно своего текущего состояния счета. А что будет, если Боб отправит Алисе транзакцию, и она будет принята раньше, чем отправленная Алисой? Тогда транзакция Алисы будет считаться невалидной, т. к. доказательство баланса было построено до принятия транзакции Боба.
Первое решение, которое приходит в такой ситуации — замораживать аккаунт до проведения транзакции. Но этот подход не годится, т. к. помимо сложности решения такой задачи в распределенной системе, в анонимной схеме будет непонятно, чей аккаунт блокировать.
Для решения этой проблемы технология разделяет входящие и исходящие транзакции: расходование средств имеет незамедлительный эффект на состояние баланса, а поступления — отложенный. Для этого вводится понятие «эпохи» — группы блоков фиксированного размера. Текущая «эпоха» определяется делением высоты блока на размер группы. Обрабатывая транзакцию, сеть обновляет баланс отправителя сразу, а средства получателя складывает в накопитель. Накопленные средства поступают в распоряжение получателя платежа только при наступлении новой «эпохи».
В результате пользователь может отправлять транзакции вне зависимости от того, как часто ему поступают средства (насколько позволяет его баланс, разумеется). Размер эпохи определяют исходя из того, насколько быстро распространяются блоки по сети и как быстро транзакция попадает в блок.
Это решение хорошо работает в случае конфиденциальных переводов, однако с анонимными транзакциями, как мы увидим далее, оно создает серьезные проблемы.
В account-based блокчейнах каждая транзакция подписывается приватным ключом отправителя, что убеждает проверяющего в том, что транзакция не была изменена и ее создал владелец этого ключа. Но что если злоумышленник, который прослушивал канал передачи, перехватит это сообщение и отправит точно такое же второе? Проверяющий сверит подпись транзакции и будет убежден в ее авторстве, и сеть спишет такую же сумму с баланса отправителя повторно.
Эту атаку называют replay-атакой. В UTXO-модели такие атаки не актуальны, т. к. злоумышленник будет пытаться использовать потраченные выходы, что само по себе не валидно и отвергается сетью.
Чтобы подобного не произошло, в транзакцию встраивают поле со случайными данными, которое называют nonce или просто «соль». При повторной отправке транзакции с «солью» проверяющий смотрит, использовался ли этот nonce ранее и, если нет, считает эту транзакцию валидной. Чтобы не хранить в блокчейне всю историю nonce-ов пользователей, обычно в самой первой транзакции его принимают равным нулю, а дальше увеличивают на единицу. Сети остается только проверить, что nonce новой транзакции отличается от прошлой на единицу.
В анонимной схеме переводов встает проблема валидации nonce-ов транзакций. Мы не можем привязывать nonce явным способом к адресу отправителя, т. к., очевидно, это деанонимизирует перевод. Мы также не можем прибавлять единицу к nonce-ам всех участвующих аккаунтов, т. к. это может конфликтовать с другими переводами, находящимися в обработке.
Авторы Zether предлагают генерировать nonce криптографически — в зависимости от «эпохи». Например:
Здесь x — секретный ключ отправителя, а Gepoch — дополнительный генератор для эпохи, получаемый хэшированием строки вида 'Zether + '. Теперь проблема, казалось бы, решается — мы не раскрываем nonce отправителя и не вмешиваемся в nonce-ы непричастных участников. Но такой подход накладывает серьезное ограничение: один аккаунт может отправить не более одной транзакции в «эпоху». Эта проблема, к сожалению, остается нерешенной, и на текущий момент делает анонимную версию Zether, на наш взгляд, едва ли пригодной для использования.
В UTXO отправитель должен доказать сети, что не тратит отрицательную сумму, иначе становится возможной генерация новых монет из воздуха (почему это возможно, мы писали в одной из предыдущих статей). А также подписать «входы» кольцевой подписью, чтобы доказать, что среди замешиваемых монет есть средства, принадлежащие ему.
В анонимной версии account-based блокчейна выражения для доказательства получаются намного сложнее. Отправитель доказывает, что:
Для такого сложного доказательства авторы используют смесь Bulletproof (один из авторов, к слову, принимал участие в ее создании) и Sigma protocol, которую называют Sigma-bullets. Формальное доказательство такого утверждения — довольно сложная задача, и оно сильно ограничивает число желающих заняться реализацией технологии.
На наш взгляд, часть Zether, привносящая конфиденциальность в account-based блокчейны, вполне может использоваться уже сейчас. Но на данный момент анонимная версия технологии накладывает серьезные ограничения на ее использование, а ее сложность — на реализацию. Однако не стоит сбрасывать со счетов, что авторы выпустили ее всего лишь несколько месяцев назад, и, возможно, кто-то другой найдет решение для имеющихся сегодня проблем. Ведь именно так делается наука.
В феврале 2019 года группа исследователей из Стэнфордского университета и Visa Research выпустили препринт «Zether: Навстречу приватности в мире смарт-контрактов». Авторы впервые предложили подход к обеспечению анонимности в account-based блокчейнах и представили два варианта смарт-контракта: для конфиденциальных (сокрытие балансов и сумм переводов) и анонимных (сокрытие получателя и отправителя) транзакций. Мы находим предложенную технологию интересной и хотели бы поделиться ее устройством, а также поговорить о том, почему проблема анонимности в account-based блокчейнах считается очень сложной и удалось ли авторам решить ее в полной мере.
Об устройстве этих моделей данных
В UTXO-модели транзакция состоит из «входов» и «выходов». Прямой аналог «выходам» — купюры в вашем кошельке: каждый «выход» имеет какой-то номинал. Когда вы расплачиваетесь с кем-то (формируете транзакцию) вы тратите один или несколько «выходов», при этом они становятся «входами» транзакции, и блокчейн помечает их как потраченные. При этом получатель вашего платежа (или же вы сами, если вам нужна сдача) получает вновь сгенерированные «выходы». Схематично это можно изобразить так:
Account-based блокчейны устроены примерно как ваш банковский счет. Они оперируют только суммой на вашем счету и суммой перевода. Когда вы переводите какую-то сумму со своего счета, то не сжигаете никакие «выходы», сети не нужно запоминать, какие монеты потрачены, а какие нет. В простейшем случае проверка транзакции сводится к проверке подписи отправителя и суммы на его балансе:
Разбор технологии
Далее мы поговорим о том, как Zether скрывает сумму транзакций, получателя и отправителя. По ходу описания принципов его работы мы будем отмечать различия в конфиденциальном и анонимном варианте. Так как обеспечить конфиденциальность в account-based блокчейнах намного проще, то некоторые из ограничений, накладываемых анонимизацией, будут неактуальны для конфиденциальной версии технологии.
Сокрытие балансов и сумм переводов
Для шифрования балансов и сумм переводов в Zether используется схема шифрования Эль-Гамаля. Работает она следующим образом. Когда Алиса хочет послать Бобу b монет по адресу (его публичному ключу) Y, она выбирает случайное число r и шифрует сумму:
где C — зашифрованная сумма, D — вспомогательное значение, необходимое для расшифровки этой суммы, G — фиксированная точка на эллиптической кривой, при умножении секретного ключа на которую получается публичный ключ.
Когда Боб получает эти значения, он просто прибавляет их к своему зашифрованному таким же способом балансу, чем и удобна эта схема.
Аналогичным образом Алиса вычитает из своего баланса такие же значения, только в качестве Y использует свой публичный ключ.
Сокрытие адресата и отправителя
Замешивание «выходов» в UTXO появилось еще на заре криптовалют и помогает скрыть отправителя. Для этого сам отправитель при совершении перевода набирает случайные «выходы» в блокчейне и замешивает их со своими. Далее подписывает «выходы» кольцевой подписью — криптографическим механизмом, позволяющим убедить проверяющего в том, что среди замешанных «выходов» присутствуют монеты отправителя. Сами замешанные монеты, разумеется, не тратятся.
Однако для сокрытия получателя нам не удастся генерировать фальшивые «выходы». Поэтому в UTXO у каждого «выхода» свой уникальный адрес, и он криптографически связан с адресом получателя этих монет. На данный момент нет способа выявить связь между уникальным адресом «выхода» и адресом получателя, не зная его секретных ключей.
В account-based модели мы не можем использовать одноразовые адреса (иначе это уже будет модель «выходов»). Поэтому получателя и отправителя приходится замешивать среди других аккаунтов в блокчейне. При этом с замешиваемых аккаунтов списывается зашифрованный 0 монет (или прибавляется 0 — в случае замешивания получателя), фактически не меняя их реальный баланс.
Так как и отправитель, и получатель всегда имеют постоянный адрес, здесь возникает необходимость при переводах на одни и те же адреса использовать для замешивания одни и те же группы. Проще рассмотреть это на примере.
Допустим, Алиса решила сделать взнос в благотворительный фонд Боба, но предпочитает, чтобы этот перевод оставался анонимными для стороннего наблюдателя. Тогда, чтобы замаскировать себя в поле отправителя, она вписывает еще аккаунты Адама и Адель. А чтобы скрыть Боба — в поле получателя дополнительно аккаунты Бена и Билла. Делая следующий взнос, Алиса решила рядом с собой вписать Алекса и Аманду, а рядом с Бобом — Брюса и Бенджена. В этом случае при анализе блокчейна в этих двух транзакциях найдется всего лишь одна пересекающаяся пара участников — Алиса и Боб, что деанонимизирует эти транзакции.
Гонки транзакций
Как мы уже упоминали, для сокрытия своего баланса в account-based системах пользователь шифрует свой баланс и сумму перевода. При этом он должен доказать, что остаток на его счете остается неотрицательным. Проблема заключается в том, что формируя транзакцию, пользователь строит доказательство относительно своего текущего состояния счета. А что будет, если Боб отправит Алисе транзакцию, и она будет принята раньше, чем отправленная Алисой? Тогда транзакция Алисы будет считаться невалидной, т. к. доказательство баланса было построено до принятия транзакции Боба.
Первое решение, которое приходит в такой ситуации — замораживать аккаунт до проведения транзакции. Но этот подход не годится, т. к. помимо сложности решения такой задачи в распределенной системе, в анонимной схеме будет непонятно, чей аккаунт блокировать.
Для решения этой проблемы технология разделяет входящие и исходящие транзакции: расходование средств имеет незамедлительный эффект на состояние баланса, а поступления — отложенный. Для этого вводится понятие «эпохи» — группы блоков фиксированного размера. Текущая «эпоха» определяется делением высоты блока на размер группы. Обрабатывая транзакцию, сеть обновляет баланс отправителя сразу, а средства получателя складывает в накопитель. Накопленные средства поступают в распоряжение получателя платежа только при наступлении новой «эпохи».
В результате пользователь может отправлять транзакции вне зависимости от того, как часто ему поступают средства (насколько позволяет его баланс, разумеется). Размер эпохи определяют исходя из того, насколько быстро распространяются блоки по сети и как быстро транзакция попадает в блок.
Это решение хорошо работает в случае конфиденциальных переводов, однако с анонимными транзакциями, как мы увидим далее, оно создает серьезные проблемы.
Защита от replay-атак
В account-based блокчейнах каждая транзакция подписывается приватным ключом отправителя, что убеждает проверяющего в том, что транзакция не была изменена и ее создал владелец этого ключа. Но что если злоумышленник, который прослушивал канал передачи, перехватит это сообщение и отправит точно такое же второе? Проверяющий сверит подпись транзакции и будет убежден в ее авторстве, и сеть спишет такую же сумму с баланса отправителя повторно.
Эту атаку называют replay-атакой. В UTXO-модели такие атаки не актуальны, т. к. злоумышленник будет пытаться использовать потраченные выходы, что само по себе не валидно и отвергается сетью.
Чтобы подобного не произошло, в транзакцию встраивают поле со случайными данными, которое называют nonce или просто «соль». При повторной отправке транзакции с «солью» проверяющий смотрит, использовался ли этот nonce ранее и, если нет, считает эту транзакцию валидной. Чтобы не хранить в блокчейне всю историю nonce-ов пользователей, обычно в самой первой транзакции его принимают равным нулю, а дальше увеличивают на единицу. Сети остается только проверить, что nonce новой транзакции отличается от прошлой на единицу.
В анонимной схеме переводов встает проблема валидации nonce-ов транзакций. Мы не можем привязывать nonce явным способом к адресу отправителя, т. к., очевидно, это деанонимизирует перевод. Мы также не можем прибавлять единицу к nonce-ам всех участвующих аккаунтов, т. к. это может конфликтовать с другими переводами, находящимися в обработке.
Авторы Zether предлагают генерировать nonce криптографически — в зависимости от «эпохи». Например:
Здесь x — секретный ключ отправителя, а Gepoch — дополнительный генератор для эпохи, получаемый хэшированием строки вида 'Zether + '. Теперь проблема, казалось бы, решается — мы не раскрываем nonce отправителя и не вмешиваемся в nonce-ы непричастных участников. Но такой подход накладывает серьезное ограничение: один аккаунт может отправить не более одной транзакции в «эпоху». Эта проблема, к сожалению, остается нерешенной, и на текущий момент делает анонимную версию Zether, на наш взгляд, едва ли пригодной для использования.
Сложность доказательств с нулевым разглашением
В UTXO отправитель должен доказать сети, что не тратит отрицательную сумму, иначе становится возможной генерация новых монет из воздуха (почему это возможно, мы писали в одной из предыдущих статей). А также подписать «входы» кольцевой подписью, чтобы доказать, что среди замешиваемых монет есть средства, принадлежащие ему.
В анонимной версии account-based блокчейна выражения для доказательства получаются намного сложнее. Отправитель доказывает, что:
- Отправляемая сумма положительна;
- Баланс остается неотрицательным;
- Отправитель правильно зашифровал суммы переводов (в том числе нулевые);
- Остаток на балансе изменяется только у отправителя и получателя;
- Отправитель владеет секретным ключом от своего аккаунта и он действительно присутствует в списке отправителей (среди замешанных);
- Nonce, используемый в транзакции, составлен правильно.
Для такого сложного доказательства авторы используют смесь Bulletproof (один из авторов, к слову, принимал участие в ее создании) и Sigma protocol, которую называют Sigma-bullets. Формальное доказательство такого утверждения — довольно сложная задача, и оно сильно ограничивает число желающих заняться реализацией технологии.
Что в итоге?
На наш взгляд, часть Zether, привносящая конфиденциальность в account-based блокчейны, вполне может использоваться уже сейчас. Но на данный момент анонимная версия технологии накладывает серьезные ограничения на ее использование, а ее сложность — на реализацию. Однако не стоит сбрасывать со счетов, что авторы выпустили ее всего лишь несколько месяцев назад, и, возможно, кто-то другой найдет решение для имеющихся сегодня проблем. Ведь именно так делается наука.