image


Мы внедрили прототип анонимных транзакций на базе zkSNARK, чтобы обеспечить конфиденциальные транзакции в блокчейне Waves. В своей реализации мы используем доказательную систему Groth16 на кривой BN254 и DSL circom.
Объясняем, как это работает.


zkSNARKs


zkSNARK – это криптографический примитив, подтверждающий знание специального набора данных (свидетельства), соответствующего множеству следующих уравнений (системы ограничений):


⟨ai,w⟩⟨bi,w⟩+⟨ci,w⟩=0

при этом часть данных свидетельства приватная. Эта конструкция позволяет доказать, например, знание прообраза хэша без разглашения прообраза. Также она может быть использована в механизме приватных транзакций для модели UTXO (Unspent Transaction (TX) Output), где публикуют только хэши UTXO, а валидность транзакции доказывают внутри zkSNARK (доказательство собственности, доказательство сохранения суммы).
zkSNARK является неинтерактивной технологией нулевого разглашения, то есть не подразумевающей протокол взаимодействия между участниками, который реализуют, чтобы доказать знание. В технологии zkSNARK доказывающий конструируют доказательство и отправляет его проверяющему – дополнительных взаимодействий не требуется. Проверяющий может проверить правильность и корректность использования данных по доказательству, не прибегая к дополнительной информации. Изначально zkSNARK-и создали как протокол “конфиденциального вычисления”: при вычислении результата данные, участвующие в расчетах, не раскрываются.
При помощи технологии zkSNARK можно реализовать схему коммит-раскрытие: доказывающий вычисляет хэш (hash), отдает его проверяющему и делает специальное доказательство, что он знает прообраз хеша x. При подстановке значений x и hash в формулу, и передаче этой формулы и доказательства проверяющему, проверяющий может удостовериться, что доказывающий знает x. На этом строятся анонимные транзакции: мы знаем приватный ключ и какой-то конкретный вход (непотраченный UTXO) с конкретной суммой, который пользователь создал на смарт-контракте. Не раскрывая этих данных, пользователь может подтвердить с помощью смарт-контракта, что это его вход, что он может им распоряжаться и отдать кому-то в пользование.
Сейчас технология не используется повсеместно, потому что пруф генерируется несколько минут, что не очень удобно для пользователя.
Узнайте больше о программировании zkSNARK-ов в статье Виталика Бутерина "Quadratic Arithmetic Programs: from Zero to Hero" и в статье Iden3 про разработку схем на circom.


Аккаунтная модель


Для транзакций в Waves обычно используют ключи и адреса на базе кривой curve25519. Эта кривая не является zkSNARK-friendly, поэтому для анонимных транзакций используем подгруппу скрученных кривых Эдвардса BabyJubJub. Кроме того, мы используем публичные ключи в качестве адресов, поскольку при отправке нужно шифровать данные для получателя.


UTXO-модель


В нашей модели UTXO представлен набором из 3 параметров: баланс, публичный ключ владельца и уникальный секрет. В блокчейне представлены только хэши без дополнительного шифрования. Владелец представлен публичным ключом, и, как отмечалось ранее, мы используем публичные ключи не на кривой curve25519, а на zkSNARK-friendly кривой BabyJubJub. Id UTXO должен быть сгенерирован случайным образом, так как если пользователь укажет два одинаковых id, то сможет забрать (потратить) UTXO только по одному из них. При этом заблокированными окажутся только UTXO с соответствующим id для текущего пользователя, но не для остальных. В интересах пользователя выбирать id, используя генератор случайных чисел (на id выделяется 253 битах, поэтому получить коллизию сложно).
Чтобы потратить UTXO, необходимо опубликовать в цепи обнулитель (nullifier) – детерминированную функцию от UTXO, определенную как hash(secret, owner_privkey). Это значение детерминировано и уникально для каждого UTXO, его знает только владелец. Кроме владельца, никто не может связать UTXO с соответствующим ему обнулителем.
UTXO хранятся внутри хэш-мапы dApp, то есть в стейте контракта. В блокчейне UTXO представлены в зашифрованном виде. Чтобы забрать свои средства, пользователь должен просканировать блокчейн и попытаться расшифровать каждый UTXO.


Стейт dApp-а


В стейте dApp-а хранятся hash-map-ы, представляющие два множества:


  • хэши UTXO
  • обнулители

Таким образом, dApp может проверить существование UTXO анонимного множества и уникальность обнулителей. Этого достаточно, чтобы обрабатывать анонимные переводы с защитой от подделки новых ассетов и двойного расходования.
В dApp – 3 метода, соответствующие базовым типам транзакций:


  • Депозит
  • Трансфер
  • Вывод

Для перевода и вывода средств используем собственные верификаторы, обеспечивающие взаимодействие dApp со специальными анонимными аккаунтами, основанными на кривой BabyJubJub. Депозиты обрабатываются с обычных аккаунтов Waves.


Комиссии


За пополнение счета комиссия взимается с curve25519 аккаунта. За переводы и снятие средств комиссия снимается с анонимного счета. На уровне dApp это выглядит так:
dApp оплачивает транзакцию сам, то есть с его баланса списывается нативный токен, который тратится на оплату комиссии
Между входами и выходами часть комиссии сжигается, чтобы аннулировать активы, соответствующие реальным активам на смарт-контракте
На уровне UTXO сжигаем некоторую сумму в качестве комиссии при обработке транзакции.


Транзакции


Депозит является простой операцией, каждый депозит добавляет новый элемент в UTXO.


image


Трансферы основываются на примитиве перевода 2-to-2.


image


Некоторые входы и выходы могут быть нулевыми. В качестве частичного примера такой конструкции может быть представлен любой вид простого перевода (join, split и другие переводы, за исключением атомик свопов).


Выводы работают как и другие транзакции, только вместо создания второго UTXO позволяют пользователю выводить свои UTXO из dApp. На входе также может быть два UTXO, на выходе UTXO с остатком средств и withdraw, которые публикуются в блокчейне.


image


При выводе или трансфере dApp проверяет, что соответствующие обнулители еще не встречались в его стейте.
При помощи zkSNARK мы можем посчитать, что сумма входов транзакции равна сумме выходов. При исполнении транзакции мы тратим её UTXO и еще какой-то нулевой UTXO, которого нет в merkle-дереве. Чтобы потратить UTXO, нужно доказать знание приватного ключа его владельца. Проверяем, что приватный ключ при умножении на генератор группы дает в результате публичный ключ. Соответственно, не зная приватный ключ транзакцию совершить нельзя.


Анонимное множество


Используем анонимное множество из 8 элементов. Целевой элемент приватно выбирается из множества, представленного в публичном входе zkSNARK. Этот метод позволяет обфусцировать граф транзакций (если существует возможность восстановления графа взаимодействия UTXO, то существует возможность деанонимизации транзакций).
В дальнейшем, простой сборщик 8 элементов можно заменить на сборщик Merkle-деревьев. Подход скрывает граф транзакций.
Чтобы создать валидную транзакцию, доказываем, что тратим какой-то UTXO из множества UTXO. Помещаем в приватные входы zkSNARK merkle proof-ы и данные UTXO, а в публичный вход – root hash. Таким образом, при помощи SNARK-а доказываем, что знаем UTXO.


image


Защита от двойного расходования


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


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


Если контракт dApp получает не уникальное значение обнулителя, транзакция отклоняется. Таким образом, гарантируется, что каждый UTXO тратится по одному разу.


После того, как потратили UTXO, обнулитель публикуется в списке потраченных обнулителей в стейте dApp. То есть в hash-map напротив данного обнулителя ставится "true". Перед публикацией обнулителя в стейте проверяем, что данный обнулитель еще не использован, а значит UTXO с таким id можно тратить.

Комментарии (0)