В этой статье рассматриваются три способа аудита кошельков без передачи контроля в криптовалютах семейства CryptoNote.

Под аудитом кошельков понимается возможность для третьей стороны ("аудитора") видеть транзакции этого кошелька и рассчитывать его корректный актуальный баланс без передачи права на трату средств.

В статье рассматриваются различные способы обеспечения такой возможности в криптовалютном протоколе CryptoNote 2.0, где аудит реализован лишь частично: с помощью трекингового ключа можно распознавать входящие транзакции, но для распознавания исходящих нужен полный набор ключей.

Материал рассчитан на читателей, знакомых с темой блокчейна и “классических” криптовалют, а также с основами криптографии на эллиптических кривых.

Что такое CryptoNote?

Как это ни странно, но большинство интересующихся блокчейном и криптовалютами ничего не слышали о CryptoNote, несмотря на то, что эта технология явилась основой для более чем 300 форков, самым известным из которых стал Monero.

В 2014 году в криптовалютной среде появились упоминания о проекте, который назывался Bytecoin. Проект не являлся форком Bitcoin или другого открытого проекта и имел совершенно новую кодовую базу, что было крайне необычно для того времени. Его основная концепция сводилась к реализации privacy-технологии, которую называли CryptoNote. Приватность обеспечивалась двумя механизмами: стелс-адресами и подмешиванием входов с использованием кольцевой подписи (ее еще называли в то время “микшер на блокчейне”). Поскольку в то время Zcash существовал только в теории, технология оказалась весьма конкурентоспособной и вызвала большой резонанс в крипитовалютном сообществе.

Вскоре группа энтузиастов, проявившая интерес к одному из первых форков этой технологии, а затем и перехватившая инициативу в этом форке, своими энергичными действиями сумела привлечь наибольшее внимание к технологии как среди сообщества так и среди инвесторов. Этот форк назывался BitMonero, однако довольно скоро он был переименован в Monero.

В дальнейшем оба проекта — Bytecoin и Monero — развивались технологически разными путями: если Bytecoin оставался закрытым анонимным проектом, то Monero превратился в крупный community-driven проект с очень большим количеством участников и разработчиков. Тем не менее, они оба являются развитием технологии CryptoNote.

Понятие аудита и его приложения

В отличие от классического “псевдонимного” блокчейна типа Bitcoin, в котором любой желающий, сканируя блокчейн, может тривиально подсчитать балансы любого адреса, в приватном блокчейне, в частности, в CryptoNote, эта задача едва ли осуществима без дополнительных знаний. Во-первых, благодаря технологии стелс-адресов, в блокчейне вообще отсутствуют упоминания каких-либо адресов (это свойство обычно обозначается как anonymity). Во-вторых, в силу того, что благодаря кольцевой подписи вход транзакции не указывает на один конкретный выход родительской транзакции, а указывает на множество вероятных выходов, отследить движение средств также не представляется возможным (это свойство называется untraceability).

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

Говоря формально, под аудитом кошельков понимается возможность для третьей стороны ("аудитора") видеть транзакции этого кошелька и рассчитывать его корректный актуальный баланс без передачи права на трату средств. В оригинальной версии CryptoNote аудит был реализован лишь частично: с помощью трекингового ключа можно распознавать входящие транзакции, но для распознавания исходящих нужен полный набор ключей.

Об авторе и цели публикации

Автор статьи — один из основных разработчиков проекта Zano, основанного на CryptoNote, который развивался несколько лет теми же руками, которыми был написан оригинальный код технологии.

Команда сейчас рассматривает возможность добавления аудита кошельков в проект и проводит исследования этой темы с целью выбора оптимального варианта. С результатами некоторых из этих исследований автор хочет познакомить читателей.

2. Базовые сведения о криптографии на эллиптических кривых

Протокол CrypoNote использует эллиптическую кривую из схемы цифровой подписи с открытым ключом ed25519.

Напомним основные параметры этой кривой и дадим дополнительные определения.

Зафиксировано большое простое число q = 2255 - 19.

  1. Все операции происходят в целых числах в конечном поле Fq вычетов по модулю q.
  2. Задана эллиптическая кривая над полем Fq, обозначаемая E/Fq:

    −x2+y2= 1 +dx2 y2, где d = −121665/121666 (отношение вычисляется в Fq и является целым числом).

    Важным является факт ее симметрии относительно x и y.
  3. Над множеством точек кривой геометрически определена операция, для любых двух точек дающая третью: F(A, B) = C, условно называемая “сложением”, и задан нейтральный элемент, как точка, лежащая в бесконечности (это подробно рассмотрено здесь). Эта операция не выводит за пределы множества, обладает ассоциативностью, коммутативностью и наличием обратного элемента, благодаря чему все точки кривой вкупе с ней образуют абелеву группу, обозначаемую E(Fq).
    Порядок этой группы (число всех точек): #E(Fq)=2cl, где с = 3 (кофактор) и l=2252+27742317777372353535851937790883648493.
  4. Каждая точка кривой задается своими координатами (x, y). Поскольку координаты связаны уравнением кривой, это представление избыточно, и поэтому при реализации криптографических функций для экономии используется только координата y, кодируемая как 256-битное число.
    x-координата, в соответствии с симметричностью кривой (см. уравнение), может быть только одним из двух вариантов, выбор которого кодируется как старший бит числа, представляющего y-координату.
  5. Зафиксирована специальная точка кривой G = (x, −4/5). Точка задается y-координатой, из двух возможных вариантов x выбирается положительный.
  6. Сложение (см. п. 3) точки G с самой собой n раз определяет операцию умножения на целое число, образующую замкнутую мультипликативную группу G, порядок которой меньше, чем E(Fq):

    #G< #E(Fq), при этом #G=l=2252+27742317777372353535851937790883648493.

  7. Открытый ключ X — это точка эллиптической кривой, принадлежащая группе G: X ∈ G
  8. Секретный ключ x, или скаляр, соответствующий открытому ключу X — это такое целое число, что:

    X = x * G, x ∈ [1; l-1]

    Секретный ключ, также как и открытый (см. выше), кодируется как 256-битное число.
  9. Определена основная хеш-функция H (в коде и других источниках обозначается как cn_fast_hash). Она вычисляет 32-байтный хеш по произвольному набору данных:

    H : {0, 1} * → [0,2256-1]

  10. Хеш-функция Hs (s означает scalar) по произвольным данным выдает число, являющееся скаляром, т.е. валидным секретным ключом:

    H: {0,1} * → [1; l-1]

    Hs может быть реализована тривиально через H:

    Hs(x)= H(x) + 1 mod l

  11. Хеш-функция Hp (p означает point — точка кривой) по произвольным данным выдает элемент группы G, то есть, валидный открытый ключ:

    H: {0,1} * → G

    Реализация этой хеш-функции представляет определенную сложность по той причине, что во-первых, не любой набор 256 бит можно декодировать в точку эллиптической кривой (см. п. 4), а во-вторых, не любая точка кривой принадлежит группе G (см. п. 6).
    Одним из тривиальных способов реализации Hp является последовательное хеширование данных H(H(...H(x)...)) до тех пор, пока результат не станет возможным декодировать в точку XG.
    В CryptoNote используется более сложный и эффективный подход, реализованный в виде функции ge_fromfe_frombytes_vartime, который подробно рассмотрен в этой работе.
    Определим функцию, детерминированно преобразующую произвольные 256 бит в элемент группы G как:

    to_point :[0,2256-1] → G

    В CryptoNote хеш-функция Hp реализована так:

    Hp(x) = to_point( H(x) )

3. Учет средств и расчет баланса в CryptoNote 2.0

Вспомним, как происходит отправка средств и расчет баланса в оригинальном версии протокола.

Алиса, отправляя деньги Бобу, формирует выходы своей транзакции следующим образом.

Рис. 3.1. Алиса формирует выходы транзакции, отправляя деньги Бобу
Рис. 3.1. Алиса формирует выходы транзакции, отправляя деньги Бобу

  1. У Боба есть пара закрытых ключей (v, s). Он вычисляет свой адрес как пару соответствующих открытых ключей (V, S) и опубликовывает его или отправляет Алисе.
  2. Алиса выбирает случайный секретный ключ транзакции r и вычисляет открытый ключ R = r * G, который записывает в специальное поле extra транзакции.
  3. Для каждого выхода Алиса вычисляет стелс-адрес (one-time destination key):

    Pi = Hs(r * V, i) * G + S, где i — порядковый номер выхода.

  4. Алиса подписывает и отправляет транзакцию.

Сторонний наблюдатель, анализируя стелс-адреса Pi, не может сказать, предназначен ли конкретный выход Бобу, и даже не может определить, предназначены ли разные выходы с ключами Pi и Pj одному получателю.

Чтобы принять деньги, Боб анализирует все транзакции в блокчейне следующим образом.

Рис. 3.2. Боб проверяет выходы транзакции, чтобы определить входящие переводы
Рис. 3.2. Боб проверяет выходы транзакции, чтобы определить входящие переводы

  1. Используя свой секретный ключ v, Боб вычисляет для каждого выхода P’i = Hs(v * R, i) * G + S (где i — порядковый номер выхода, S — открытый spend-ключ Боба).

Если P’i == Pi, то это означает, что он является получателем данного выхода и может его потратить, вычислив соответствующий секретный ключ.
Поэтому Боб увеличивает баланса своего кошелька на номинал данного выхода.

Чтобы потратить выход, получателем которого Боб является, и отправить монеты Кэрол, он действует следующим образом.

Рис. 3.3. Боб формирует вход для новой транзакции, используя принадлежащий ему выход
Рис. 3.3. Боб формирует вход для новой транзакции, используя принадлежащий ему выход

  1. Боб, используя свои секретные ключи (v, s), вычисляет закрытый ключ xi = Hs(v * R, i) + s к стелс-адресу Pi, то есть Pi = xi * G.

  2. Боб рассчитывает key image: I = xi * Hp(Pi) и записывает его, номинал и ссылку на соответствующий выход во вход своей транзакции для Кэрол.
    Следует обратить внимание на то, что, во-первых, вычислить key image может только владелец секретного spend-ключа s (корректность этого будет удостоверена кольцевой подписью), и во-вторых, третья сторона не сможет связать key image I и соответствующий стелс-адрес Pi.

  3. Боб уменьшает баланс своего кошелька на номинал используемого выхода в п. 6.

  4. Боб формирует выходы в транзакции для Кэрол в соответствии с п.п. 2-3. После чего подписывает транзакцию и отправляет.

Если предположить, что Боб утратил всю историю своих операций, но по-прежнему владеет своими секретными ключами (v, s), то он сможет восстановить историю транзакций и рассчитать свой текущий баланс следующим образом.

Рис. 3.4. Боб определяет свои входящие и исходящие платежи и рассчитывает баланс
Рис. 3.4. Боб определяет свои входящие и исходящие платежи и рассчитывает баланс

  1. Боб сканирует весь блокчейн и анализирует все транзакции на предмет наличия выходов, адресованных ему (см. п. 5).

  2. При нахождении такого выхода Pi, Боб увеличивает баланс своего кошелька на величину номинала.
    Затем, используя свой секретный spend-ключ s вычисляет соответствующий секретный ключ выхода xi (п. 6) и key image I (п. 7). После чего записывает key image, Pi и другие данные платежа в таблицу.

  3. Если при дальнейшем сканировании блокчейна Боб обнаружит во входе некоторой транзакции key image, имеющийся в таблице, это будет означать, что данная транзакция была когда-то сформирована Бобом. Поэтому он уменьшит баланс своего кошелька на величину номинала входа.

Действуя таким образом, Боб восстановит актуальный баланс своего кошелька.

Обратите внимание, что если сторонний аудитор Ден получит от Боба секретный ключ v, то он сможет распознать входящие транзакции Боба в блокчейне, однако, не обладая секретным ключом s, он не сможет распознать исходящие транзакции Боба, а значит и рассчитать верный баланс. Как будет показано далее, в случае непосредственной траты выхода Бобом без подмешиваний Ден сможет опознать такую транзакцию, как исходящий перевод Боба, однако в общем случае этого сделать нельзя.

Таким образом, для расчета баланса кошелька Боба необходимо знать оба секретных ключа (v, s).

Если Боб передаст аудитору Дену оба своих секретных ключа (v, s), это будет равносильно передаче самих средств, так как Ден, обладая s, сможет их потратить. Поэтому полноценный аудит кошельков в рамках оригинального протокола CryptoNote 2.0 невозможен.

В следующих разделах мы рассмотрим варианты модификации протокола, обладающие такой возможностью.

Отметим, что секретный ключ транзакции r Алисе имеет смысл сохранять (что и происходит в некоторых реализациях протокола). Любой, кто знает r для некоторой транзакции, может проверить, относится ли выход i этой транзакции к заданному адресу (V, S), просто повторяя вычисления из п.3:

P’i = Hs(r * V, i) * G + S

и сравнивая результат с Pi.

Этот факт Алиса может, например, использовать, чтобы доказать, что она перевела деньги именно Бобу.

Вычислить целевой адрес (V, S) по выходу, даже зная r, нельзя.

4. Вариант A: Bytecoin Auditable Coins

В момент своего появления Bytecoin был первой и единственной реализацией протокола CryptoNote, поэтому ему были свойственны все особенности и ограничения, рассмотренные в предыдущем разделе.

7 февраля 2018 года разработчики выпустили версию 3.4.0 Amethyst, содержащую ряд улучшений и изменений CryptoNote, которые мы сейчас рассмотрим.

В рамках тематики этой статьи, наиболее интересным изменением стала возможность для обычных кошельков создавать специальные копии — auditable wallet (AW), обладающие двумя свойствами:

  1. AW не может потратить средства из основного кошелька;
  2. баланс AW всегда совпадает с балансом основного кошелька. Невозможно изменить баланс основного кошелька так, чтобы это не отразилось на балансе AW.

Однако, такой функциональностью обладают только адреса кошельков новых версий, так называемые, amethyst-адреса. С существующими адресами и аккаунтами обеспечивается обратная совместимость, они теперь называются legacy-адреса. Реализация новой функциональности стала возможна только в транзакциях новой версии, поскольку разработчики поменяли формат выходов, поэтому в сети Bytecoin в данный момент циркулируют как транзакции старого формата ?, так и новые. Транзакции нового формата также поддерживают два типа адресов: amethyst и legacy, поэтому в конечном счете мы имеем три варианта криптографических схем:

  1. tx.version < amethyst, legacy address;
  2. tx.version >= amethyst, legacy address;
  3. tx.version >= amethyst, amethyst address.

Рассмотрим их более подробно.

4.1. tx.version < amethyst, legacy address

Это оригинальная схема CryptoNote, но с детерминированной генерацией r.

Аккаунт кошелька представлен секретным ключом s и хешем vs. Секретный view-ключ v детерминированно генерируется как функция от vs.

Секретный ключ r транзакции теперь выбирается не случайно, а вычисляется при помощи vs:

r = Hs(ht, vs), где ht = H(tx.inputs, tx.version)

Таким образом отпадает необходимость хранить секретные ключи r в локальном хранилище для будущих нужд, так как для любой своей транзакции, когда-либо отправленной в блокчейн, такой ключ может быть вычислен с помощью vs.

Баланс кошелька вычисляется аналогично оригинальному CryptoNote (см. раздел 3), то есть, для учета исходящих платежей необходим секретный spend-ключ s.

В случае, если в локальном хранилище кошелька будут сохранены все адреса, на которые когда-либо осуществлялся перевод средств, то существует возможность рассчитать верный баланс без привлечения секретного spend-ключа s следующим образом:

  1. Алиса сканирует транзакции в блокчейне и ведет учет входящих платежей при помощи секретного view-ключа v, как было описано в разделе 3, и увеличивает баланс.
  2. Также для каждого выхода каждой транзакции Алиса перебирает все адреса (V, S) на которые когда-либо осуществлялись переводы и вычисляет:

    P’i = Hs(r * V, i) * G + S

  3. Если P’i = Pi, то данный выход является исходящим платежом Алисы на адрес (V, S) и она уменьшает баланс.

Так Алиса рассчитает баланс используя только vs и v.

Очевидно, что данный способ ненадежен и непрактичен, так как отсутствие в указанном локальном хранилище адреса, на который перевод фактически был осуществлен, приведет к завышению баланса. Поэтому он представляет, скорее, теоретический интерес.

4.2. tx.version >= amethyst

Как уже было отмечено выше, начиная с версии 3.4.0 Amethyst в Bytecoin изменился формат транзакций. Если tx.version >= amethyst, то выходы транзакции имеют другой формат.

Теперь каждый выход, помимо номинала amount и открытого ключа Pi, содержит также дополнительный открытый ключ Qi (обозначаемый в коде как encrypted_secret) и дополнительный байт, содержащий зашифрованный тип адреса, amethyst или legacy (именуемый в коде encrypted_address_type). Эти структуры схематично показаны на рис. 4.2.

Рис. 4.2. Сравнение структуры данных для выходов транзакций CryptoNote и Bytecoin Amethyst
Рис. 4.2. Сравнение структуры данных для выходов транзакций CryptoNote и Bytecoin Amethyst

Таким образом, размер каждого выхода увеличился на 33 байта.

Для каждого выхода i тип адреса кодируется и декодируется следующим образом:

encrypted_address_type(i) = (H(oi) & 255) xor address_tag

где:

oi = H(ht, vs, i) (обозначается в коде как output_seed),

address_tag равен 0 для legacy-адресов и 1 для ametyst-адресов.

4.3. tx.version >= amethyst, legacy address

Аккаунт кошелька также представлен секретным ключом s и хешем vs, на основе которого детерминированно генерируется секретный view-ключ v.

Алиса, отправляя деньги Бобу на его адрес (V, S) формирует выходы своей транзакции следующим образом:

  1. Вычисляет ht = H(tx.inputs, tx.version), затем для каждого выхода i:
  2. Di = Hs(ht, vs, i) * G (в коде обозначается как output_shared_secret)
  3. Pi = S + Hs(Di, ht, i) * G
  4. Qi = Hs(ht, vs, i) * V (при этом Qi = v * Di, однако view-ключ v получателя неизвестен)
  5. Пара (Pi, Qi) — открытые ключи выхода i.

Чтобы принять деньги, Боб анализирует все транзакции следующим образом.

6. Для каждой транзакции вычисляет ht = H(tx.inputs, tx.version), затем для каждого выхода i:

7. Di = v-1 * Qi

8. S’ = Pi - Hs(Di, ht, i) * G

9. Если S’ совпадает с открытым ключом S Боба, то этот выход предназначен ему и он увеличивает баланс своего кошелька на соответствующий номинал.

Чтобы потратить выход, получателем которого Боб является, и отправить монеты Кэрол, он действует следующим образом.

10. Используя свои секретные ключи v и s, вычисляет секретную часть Pi:
Di = v-1 * Qi
xi = s + Hs(Di, ht, i), при этом легко видеть, что Pi = xi * G (см. п. 3)

11. Боб рассчитывает key image: I = xi * Hp(Pi) и записывает его, номинал и ссылку на соответствующий выход во вход своей транзакции для Кэрол.

12.Боб уменьшает баланс своего кошелька на номинал используемого выхода.

13. Боб формирует выходы в транзакции для Кэрол в соответствии с п.п. 1-5. После чего подписывает транзакцию и отправляет.

Особенностью данной схемы, в отличии от CryptoNote, является то, что любой, кому Алиса передаст свой секретный хеш vs, сможет вычислить адрес получателя (V, S) для любой транзакции Алисы:

14. Di = Hs(ht, vs, i) * G

15. S = Pi - Hs(Di, ht, i) * G

16. V = Hs(ht, vs, i)-1 * Qi

Однако проблемой в данном случае является задача определения, какие транзакции сформировала и отправила Алиса. Без этой информации в п.п. 14-16 для произвольных транзакций будут получатся произвольные бесполезные данные, неотличимые от настоящих адресов (V, S). Некоторую помощь в этом может оказать алгоритм кодирования encrypted_address_type, так как для транзакций Алисы это поле после декодирования будет иметь только допустимые значения {0, 1}. Но, к сожалению, допустимые значения также могут получится и произвольно, и такой случай нельзя будет отличить.

Поскольку в данной схеме вычисление key image также требует знания секретного spend-ключа s, то проблема аудита, то есть, вычисления точного баланса кошелька без передачи прав на трату средств, остается не решенной.

4.4. tx.version >= amethyst, amethyst address

Константа H

Для работы этой криптографической схемы требуется введение новой константы — точки H, элемента группы G, причем порядок точки H неизвестен. Иными словами, это некоторый зафиксированный открытый ключ, секретная часть которого гарантировано неизвестна и вероятность ее нахождения пренебрежима мала:

H = y * G, где y — неизвестное число.

Например, можно задать H так, как предлагается тут:

H = Hp(G) = to_point( cn_fast_hash(G) )

Разработчики Bytecoin не вычисляют константу, а задают ее численно, без каких-либо указаний на природу ее происхождения:

constexpr P3 H{ge_p3{{7329926, -15101362, 31411471, 7614783, 27996851, -3197071, -11157635, -6878293, 466949, -7986503},

{5858699, 5096796, 21321203, -7536921, -5553480, -11439507, -5627669, 15045946, 19977121, 5275251},

{1, 0, 0, 0, 0, 0, 0, 0, 0, 0},

{23443568, -5110398, -8776029, -4345135, 6889568, -14710814, 7474843, 3279062, 14550766, -7453428}}};

Однако поиск этой числовой последовательности показал, что ими используется та же константа, что и в Monero для RingCT, способ вычисления которой и его обоснование рассмотрены тут.

Поскольку H принадлежит группе G, то можно сказать, что H так же порождает группу G, как и базовая точка G.

Это означает, что ∀ x ∈ [1,p - 1], x *H ∈ G 

Множественные несвязываемые адреса (unlinkable addresses)

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

Рассматриваемая схема позволяет на одном и том же наборе секретных ключей генерировать неограниченное количество публичных адресов. При этом:

  1. адреса генерируются детерминированно из первоначального набора секретов;
  2. эти адреса не могут быть связаны между собой, т.е. сторонний наблюдатель не сможет вычислить, что они принадлежат одному аккаунту;
  3. проверка входящих транзакций и учет баланса для N множественных адресов будет вычислительно проще, чем проверка N аккаунтов в обычной схеме.

Аккаунт кошелька представлен секретным ключом s и хешем vs, также, как и в предыдущей схеме. Однако на основе хеша vs теперь детерминированно генерируется не только секретный view-ключ v, но и дополнительный секретный audit-ключ a.

Процесс генерации i-го адреса происходит следующим образом:

Рис. 4.4.2. Генерация amethyst-адресов для аккаунта Bytecoin
Рис. 4.4.2. Генерация amethyst-адресов для аккаунта Bytecoin (желтым цветом обведены предрассчитанные величины, раскрытие которых не угрожает раскрытию секретных ключей v, s и vs)
  1. Вычисляется δ = Hs(A + s * H, i), Δ = δ * G
  2. S = A + s * H + Δ
  3. V = v * S = v * (A + s * H + Δ) = v * (A + s * H) + δ * V
  4. пара (V, S) = (v * S, S) является i-ым публичным адресом данного аккаунта.

Заметим, что для расчета достаточно знать следующие величины:

  • A
  • V
  • s * H
  • v * (A + s * H)

Величины s * H и v * (A + s * H) предрассчитываются при создании аккаунта и рассматриваются как безопасные в том смысле, что зная их, нельзя вычислить s или v.

Сгенерированные адреса хранятся локально в контейнере, оптимизированном для поиска по полю S.

Формирование выходов при отправке средств

Алиса, отправляя деньги Бобу на его адрес (V, S) формирует выходы своей транзакции следующим образом (рис. 4.4.3).

Рис. 4.4.3. Формирование выходов транзакции при отправке средств на Amethyst-адрес
Рис. 4.4.3. Формирование выходов транзакции при отправке средств на Amethyst-адрес

Вычисляет ht = H(tx.inputs, tx.version), затем для каждого выхода i:

  1. Pi = Hs( Hp(ht, vs, i), ht, i )-1 * S
  2. Qi = Hs( Hp(ht, vs, i), ht, i )-1 * V + Hp(ht, vs, i)
  3. Пара (Pi, Qi) — открытые ключи выхода i.

Учет входящих платежей

Чтобы принять деньги, Боб анализирует все транзакции следующим образом (рис. 4.4.4.).

Рис. 4.4.4. Анализ выходов транзакции на предмет входящих переводов
Рис. 4.4.4. Анализ выходов транзакции на предмет входящих переводов
  1. Для каждого выхода i, используя секретный view-ключ v, Боб вычисляет:

    K = Hp(ht, vs, i) = Qi - v * Pi (output_shared_secret в коде)

  2. S’ = Hs(K, ht, i) * Pi
  3. После чего пытается найти S’ в списке своих адресов. Если находит, то это означает, что данный выход переводит деньги Бобу. Он увеличивает баланс для соответствующего адреса.

Это означает, что для того, чтобы правильно учитывать входящие платежи, в данной схеме, помимо списка адресов или ключей для их генерации, необходимо знать секретный view-ключ v.

Формирование исходящих платежей

Чтобы потратить выход i, получателем которого Боб является, и отправить монеты Кэрол, он действует следующим образом.

  1. K = Hp(ht, vs, i) = Qi - v * Pi (output_shared_secret в коде)
  2. xi = Hs(K, ht, i)-1 * (a + δ)
  3. Xi = xi * G + Hs(K, ht, i)-1 * s * H
  4. Вычисляет key image I = xi * Hp(Xi)
  5. Боб уменьшает баланс, относящийся к его адресу S = Hs(K, ht, i) * Pi на величину номинала соответствующего выхода.
  6. Боб формирует выходы транзакций, подписывает ее и отправляет.

Видно, что для вычисления key image требуется знание не только секретного view-ключа v, но и a.

Важной особенностью данной схемы является возможность вычисления key image (а значит, и учета своих исходящих платежей, следовательно — расчет баланса) без использования секретного spend-ключа s.

Раскрытие адресов получателей для аудита

Также, как и предыдущая, данная криптографическая схема позволяет извлекать из выходов транзакций адреса получателей в случае, если известен секретный хеш отправителя vs.

Происходит это следующим образом:

Рис. 4.4.5
Рис. 4.4.5. Аудитор, обладая vs, восстанавливает информацию о входящих/исходящих платежах, балансе и адресах получателей

Предположим, что Алиса передала vs и s * H Кэрол. Тогда Кэрол:

  1. Восстановит секретные ключи v и a, восстановит список адресов Алисы.
  2. Будет сканировать блокчейн и для каждого выхода i каждой транзакции будет определять, является ли он входящим платежом, пытаясь найти

    S’ = Hs(Qi - v * Pi, ht, i) * Pi


    среди адресов Алисы.
  3. Если найдет, то Кэрол увеличит баланс соответствующего адреса и, при помощи a и v, вычислит key image (см. выше) и сохранит его локально.
  4. Если среди входов транзакций встретится key image Алисы, то это будет означать, что данная транзакция — исходящий платеж Алисы. Кэрол восстановит адреса получателей для всех выходов этой транзакции:

    S = Pi * Hs(Hp(ht, vs, i), ht, i)
    V = (Qi - Hp(ht, vs, i)) * Hs(Hp(ht, vs, i), ht, i)

    и уменьшит баланс соответствующего адреса Алисы на величину номинала входа.

Таким образом, раскрывая часть данных аккаунта, можно предоставить третьей стороне различные уровни доступа к информации.

Чтобы сгенерировать список своих адресов:

  • A
  • V
  • s * H
  • v * (A + s * H)

Чтобы распознать только входящие платежи:

  • A
  • v
  • s * H

Чтобы произвести аудит, т.е. рассчитать баланс по своим адресам, без раскрытия адресов получателей:

  • a
  • v
  • s * H

Чтобы произвести аудит, т.е. рассчитать баланс по своим адресам, и раскрыть при этом адреса получателей:

  • vs
  • s * H

4.5. Сравнение подписей транзакций CryptoNote и Bytecoin Amethyst

Структуры данных и размеры подписей

Как было отмечено выше, реализация возможности проводить аудит кошельков, не раскрывая секретный spend-ключ, потребовала от разработчиков Bytecoin Amethyst увеличения размера данных, передаваемых в каждом выходе транзакции: по сравнению с оригинальным протоколом передается один открытый ключ и 1 байт для идентификации типа адреса. Итого, 33 байта на выход.

В CryptoNote у транзакции подписывается отдельно каждый ее вход. Для каждого открытого ключа Pi некоторого выхода, на который ссылается данный вход, в подпись записывается пара скаляров (r,c) общим размером 64 байта. (Вход может ссылаться на несколько выходов, лишь один из которых — настоящий, а остальные служат для анонимизации перевода.)

Таким образом, если в транзакции Ninputs входов, каждый из которых ссылается на Nmixins выходов, то общий размер подписи в байтах можно выразить как:

S = 32 * 2 * Nmixins * Ninputs

Минимальный размер подписи для транзакции составляет 64 байта (один вход, который тратит один выход напрямую).

В Bytecoin Amethyst подписывается вся транзакция целиком, и соответствующая структура данных усложнена (рис. 4.5.1).

Рис. 4.5.1. Сравнение структуры подписи транзакции в CryptoNote и Bytecoin
Рис. 4.5.1. Сравнение структуры подписи транзакции в CryptoNote и Bytecoin

Для каждого входа транзакции в подпись записывается точка, два скаляра и еще Nmixins скаляров. Ко всей подписи также добавляется еще один скаляр. Итого, общий размер подписи в байтах можно выразить как:

S = 32 * ((3 + Nmixins) * Ninputs + 1)

Минимальный размер подписи для транзакции составляет 160 байт (один вход, который тратит один выход напрямую).

Видно, что обе функции растут пропорционально произведению Nmixins * Ninputs

Чтобы сравнить различие размеров подписи транзакции более наглядно, рассчитаем их для наиболее характерных величин Nmixins и Ninputs и составим таблицу (рис. 4.5.2).

Рис. 4.5.2
Рис. 4.5.2. Различие в размере подписи транзакции Bytecoin по сравнению с CryptoNote (в процентах относительно размера подписи CryptoNote; зеленым отмечен выигрыш в размере подписи Bytecoin)

Как легко можно видеть, в ситуации, когда подмешивание выходов отсутствует и происходит прямая трата средств (Nmixins = 1), либо когда подмешивается один выход (Nmixins = 2), размер подписи Bytecoin больше, чем CryptoNote до 150%.

При подмешивание двух выходов (Nmixins = 3) размеры подписей в той и другой схемах практически совпадают.

При дальнейшем увеличении числа подмешиваемых выходов, размер подписи Bytecoin оказывается меньше.

Стоит отметить, что разработчики Bytecoin с выходом версии 3.4.0 Amethyst в целях увеличения анонимности установили минимальное число подмешиваемых выходов равным 3. В таких условиях подпись Bytecoin будет обладать меньшим размером.

Трудоемкость проверки подписи

Помимо размера кольцевой подписи, прямо влияющего на размер блокчейна, другой важной характеристикой является вычислительная сложность ее проверки. Она определяет такие важные параметры криптовалютной системы как, например, скорость синхронизации с сетью новых узлов и вычислительную нагрузку на сеть при большом потоке транзакций.

Трудоемкость проверки подписи для CryptoNote и Bytecoin можно было бы легко сравнить практически, просто написав тест, генерирующий, а затем проверяющий большое число подписей с заданными Nmixins и Ninputs, однако, поскольку далее в статье будут рассмотрены еще не реализованные на практике, а лишь предлагаемые в теории схемы, будет логично трудоемкость и этих схем оценивать эмпирически, по числу используемых криптографических операций.

В CryptoNote и Bytecoin используется несколько основных примитивов (см. раздел 2). В таблице на рис. 4.5.3. приведено характерное время выполнения самых часто используемых примитивов на современном middle-end компьютере с процессором Core i5-6500 (для сравнения использовался оригинальный исходный код, скомпилированный в Microsoft Visual Studio 2017 со всеми возможными оптимизациями по скорости).

Рис. 4.5.3. Характерное время основных криптографических операций
Рис. 4.5.3. Характерное время основных криптографических операций

Результаты, полученные из тестов в Bytecoin и CryptoNote хорошо согласуются между собой. Легко видеть, что наибольший вклад будет давать операция умножения скаляра на число, и, в меньшей степени, процедура вычисления хеш-функции Hp, при этом операции сложения двух точек и вычисления хеш-функции Hs не будут существенно влиять на сложность.

Рассмотрим процедуру проверки кольцевой подписи CryptoNote (рис. 4.5.4, процедура генерации подписи подробно рассмотрена в Белой книге и здесь рассматриваться не будет).

Рис. 4.5.4. Схема проверки кольцевой подписи CryptoNote
Рис. 4.5.4. Схема проверки кольцевой подписи CryptoNote

Как уже было отмечено, в CryptoNote подписывается отдельно каждый вход транзакции, соответственно, и проверяется также каждый вход отдельно. Поэтому, проверяющий для каждого входа транзакции проверяет соответствующую строку (на рисунке) подписи следующим образом.

  1. Для каждой пары значений rj и cj при помощи key-image входа I и открытого ключа Pj очередного выхода, на который ссылается данный вход, вычисляются величины:

    Lj = rj * G + cj * Pj
    Rj = rj * Hp(Pj) + cj * I

    (при этом индекс j пробегает значение от 0 до Nmixins)
  2. Вычисляется сумма c всех cj.
  3. Вычисляется хеш c`=Hs(tx_prefix_hash, L0 .. Ln, R0 .. Rn)
    где tx_prefix_hash — хеш префиксной части транзакции (без подписи).
  4. Проверяется равенство c` = c. Если оно выполняется, то кольцевая подпись считается действительной.

Оценим число операций умножения скаляра на точку и вычисления хеша Hp.

Каждое вычисление Lj и Rj требует по два скалярных умножения. Число пар Lj, Rj соответствует числу подмешиваемых выходов Nmixins для каждого входа. Поэтому имеем:

O(*) = Ninputs * 4 * Nmixins

При этом хеш-функция Hp используется однократно при каждом вычислении Rj, следовательно:

O(Hp) = Ninputs * Nmixins

Теперь рассмотрим алгоритм проверки кольцевой подписи в Bytecoin Amethyst (рис. 4.5.5).

Рис. 4.5.5. Схема проверка кольцевой подписи в Bytecoin Amethyst
Рис. 4.5.5. Схема проверка кольцевой подписи в Bytecoin Amethyst

Проверяется целиком вся подпись, для всех входов сразу. Происходит это так:

  1. В хеш-буфер записывается префиксный хеш транзакции (без подписи).
  2. Для каждого входа i (строка подписи на рисунке):
    1. Вычисляется X.
    2. Последовательно вычисляются значения пар (Y0, Z0) ... (Yn, Zn), где n = Nmixins
    3. Набор (pp, X, Yn, Zn, P0 ... Pn), где Pj — открытые ключи выходов, на которые ссылается данный вход, добавляется в хеш-буфер.
  3. Вычисляется Hs по всем данным хеш-буфера и результат сравнивается с величиной c0.
    Подпись считается действительной, если равенство соблюдается.

Оценим число операций умножения скаляра на точку и вычисления хеша Hp.

Каждое вычисление Yj и Zj требует по два скалярных умножения, плюс вычисление X требует трех скалярных умножений. Число пар Yj, Zj соответствует числу подмешиваемых выходов Nmixins для каждого входа. Поэтому имеем:

O(*) = Ninputs * (3 + 4 * Nmixins)

При этом хеш-функция Hp используется однократно при каждом вычислении Zj и однократно для вычисления B для каждого из входов, следовательно:

O(Hp) = Ninputs * (Nmixins + 1)

Чтобы наглядно сравнить вычислительную сложность обоих алгоритмов на типовых данных, введем следующую метрику. Сложим число операций скалярного умножения и операций вычисления Hp с весами, пропорциональными характерному времени выполнения этих операций:

O(total) = 130 * O(*) + 15 * O(Hp)

Затем сравним относительные результаты для CryptoNote и Byteсoin в процентах (рис. 4.5.6).

Рис. 4.5.6
Рис. 4.5.6. Вычислительная сложность проверки кольцевой подписи Bytecoin Amethyst по сравнению с CryptoNote (зависимость от Ninputs отсутствует)

Как видно, проверка подписи Bytecoin является значительно более трудоемкой операцией.

Однако, как уже было отмечено выше, в Bytecoin с версии 3.4.0 Amethyst в целях увеличения анонимности установлено минимальное число подмешиваемых выходов равным 3, поэтому худшее практическое значение не превысит 25% (в теории).

Подытожим:

  1. Размер каждого выхода увеличивается на открытый ключ Q — 32 байта.
  2. Размер кольцевой подписи варьирует в сравнении с размером подписи CryptoNote в зависимости от числа подмешиваемых выходов (при большом их числе оказывается меньше):

    S = 32 * ((3 + Nmixins) * Ninputs + 1)

  3. Вычислительная сложность выше, чем в CryptoNote и значительно зависит от числа подмешиваемых выходов:

    O(*) = Ninputs * (3 + 4 * Nmixins)
    O(Hp) = Ninputs * (Nmixins + 1)

5. Исследования аудита в CryptoNote от Anton Sokolov

Осенью 2019 года на Medium.com была опубликована серия эссе (1, 2, 3, 4, 5, 6) о проблеме аудита в CryptoNote и возможных способах ее решения под авторством Anton Sokolov. В ней с теоретических позиций рассматриваются несколько вариантов модификации оригинального протокола таким образом, чтобы решить задачу аудита для третьей стороны.

Рассмотрим последнюю из них как наиболее оптимизированную. Будем сокращенно обозначать ее как "AS".

Примечание: для единообразия изложения spend-ключи и далее будут обозначаться буквами s и S, view-ключи — буквами v и V, несмотря на то, что в оригинальных работах используются b, B и a, A соответственно.

Формирование выходов

Аккаунт кошелька представлен набором не двух, как в CryptoNote, а трех секретных ключей {v, s, d}: view-, spend- и audit-ключи соответственно.

Совокупность трех открытых ключей {V, S, D} представляет публичный адрес этого аккаунта.

Алиса, отправляя деньги бобу, действует следующим образом (рис. 5.1).

Рис. 5.1. Формирование выходов транзакций в схеме AS
Рис. 5.1. Формирование выходов транзакций в схеме AS
  1. Боб опубликовывает свой адрес, поэтому Алиса знает его открытые ключи V, S и D.

  2. Так же, как и в CryptoNote, Алиса выбирает случайный секретный ключ транзакции r и вычисляет открытый ключ R = r * G, который записывает в специальное поле extra транзакции.

  3. Для каждого выхода Алиса вычисляет не один, а два стелс-адреса P и T. Первый рассчитывается аналогично CryptoNote:

    P = Hs(r * V) * G + S

    Второй иначе:

    T = Hs(r * D) * D

    Порядковый номер выхода в оригинальной работе здесь не используется, однако, разумно предположить, что это сделано для упрощения и на практике он будет одним из аргументов функции Hs по аналогии с CryptoNote (см. раздел 3).

  4. Алиса подписывает и отправляет транзакцию.

Сторонний наблюдатель не сможет связать P и T с адресом Боба.

Распознавание входящих платежей и формирование входов

Чтобы принять и потратить деньги, Боб действует следующим образом (рис. 5.2).

Рис. 5.2. Определение входящих переводов и формирование входов для тратящей их транзакции
Рис. 5.2. Определение входящих переводов и формирование входов для тратящей их транзакции

5. Используя свой секретный view-ключ v, Боб стелс-адрес P каждого выхода сравнивает с точкой:
P’ = Hs(v*R)*G+S
(этот шаг аналогичен CryptoNote)

6. В случае, если равенство выполняется, это означает, что данный выход адресован Бобу. Он увеличивает свой баланс на величину номинала выхода.

7. При необходимости перевести полученные средства Кэрол, Боб, используя свои секретные spend- и audit-ключи s и d, вычисляет два key image: I и Ï. Первый — аналогично CryptoNote:
I = x * Hp(P)
и дополнительный:
t = Hs(d*R)*d
Ï = t * Hp(P)
Затем записывает их, номинал и ссылку на соответствующий выход во вход своей транзакции для Кэрол.

8. Затем Боб формирует выходы транзакции для Кэрол, уменьшает свой баланс пропорционально потраченным выходам, подписывает транзакцию и отправляет.

Как видно, здесь так же, как и в CryptoNote третья сторона — Аудитор — обладая только секретным view-ключом v, сможет распознать только входящие переводы.

Аудит

Если же Аудитор располагает также секретным audit-ключом d, то он сможет распознать исходящие платежи Боба и рассчитать его баланс следующим образом:

9. Для каждого входящего платежа Аудитор будет рассчитывать и запоминать дополнительный key image Ï в локальном хранилище:
t = Hs(d*R)*d
Ï = t * Hp(P)

10. Если во входах какой-либо из транзакций блокчейна дополнительный key image Ï совпадёт с одним из сохраненных, то это будет означать, что данная транзакция является исходящим платежом Боба. Аудитор уменьшит баланс на номинал соответствующего входа.

Таким образом, Аудитор, обладая набором ключей {v, S, d} сможет распознать в блокчейне входящие и исходящие переводы Боба и восстановить его корректный баланс. При этом потратить средства Аудитор не сможет, т.к. без секретного spend-ключа s он не сможет вычислить основной key image I.

Задача решена.

Кольцевая подпись

Применив идеи из этой работы, автору удалось уменьшить размер подписи по сравнению с CryptoNote: теперь, для каждого входа в подписи хранится только Nmixins + 1 скаляр (рис. 5.3).

Рис. 5.3. Сравнение размеров кольцевой подписи в схеме AS и CryptoNote
Рис. 5.3. Сравнение размеров кольцевой подписи в схеме AS и CryptoNote

Таким образом, размер подписи уменьшается почти вдвое.

Рассмотрим вычислительную сложность ее проверки. Схема проверки подписи представлена на рис. 5.4.

Рис. 5.4. Схема проверки кольцевой подписи в AS
Рис. 5.4. Схема проверки кольцевой подписи в AS

Этот алгоритм очень похож по структуре на используемый в CryptoNote (см. рис. 4.5.4). Проверка осуществляется раздельно для каждого входа и состоит в последовательном вычислении значений uj, Lj, Rj, cj для всех выходов, использованных для подмешивания в данном входе. В результате, цикл из cj должен замкнуться и величина cn+1 должна совпасть с c0, в этом случае подпись считается действительной.

Вычислительная сложность алгоритма определяется шестью операциями скалярного умножения и одним вычислением хеш-фукнции Hp на итерацию, число который есть произведение Ninputs * Nmixins.

Сравнение с CryptoNote по данным и вычислительной нагрузке

  1. Размер адреса увеличивается на 50%, т.к. адрес теперь представляет совокупность трех открытых ключей: {V, S, D}, а не двух {V, S}.
    Кодированное представление адреса увеличится примерно на столько же: например, стандартный Zano-адрес, содержащий два открытых ключа, занимает 97 символов:
    ZxD5UBX5PM3RTsEtTRd9ATUFxXyocoQzDRk3baVBahuWQJRK8QHTUT9GQM7jk7GoedK5B2
    nP4HxSPDpuLHvizpwj2q99bmz7t

    Аналогичный Zano-адрес, содержащий три открытых ключа будет иметь длину порядка 141 символа:
    ZxD5UBX5PM3RTsEtTRd9ATUFxXyocoQzDRk3baVBahuWQJRK8QHTUT9GQM7jk7GoedK5B2
    nP4HxSPDpuLHvizpwjcenhnGbhpJFLk8vkhJywHCcht4d9EKA7CHHav1H6QPpB1cLsTvPfj

  2. Размер каждого выхода увеличивается на дополнительный стелс-адрес T — 32 байта.
  3. Размер каждого входа увеличивается на дополнительный key image Ï — 32 байта.
  4. Размер кольцевой подписи меньше, чем в CryptoNote:

    S = 32 * (Nmixins + 1) * Ninputs

  5. Вычислительная сложность выше, чем в CryptoNote:

    O(*) = Ninputs * 6 * Nmixins

    O(Hp) = Ninputs * Nmixins

6. Аудит через ограничение на подмешивание выходов

Рассмотрим еще один способ реализации аудита в CryptoNote.

Атрибут выхода mix_attr

В проекте Zano, являющимся наследником CryptoNote, у выхода транзакции появился дополнительный атрибут mix_attr размером 8 бит, и правило ядра, ограничивающее использование выходов в подмешивании, в зависимости от его значения.

Структуру выходов CryptoNote и Bytecoin (рис. 4.2) мы можем теперь дополнить таким образом (рис. 6.1.).

Рис. 6.1. Сравнение структуры данных для выходов транзакций CryptoNote, Zano и Bytecoin Amethyst
Рис. 6.1. Сравнение структуры данных для выходов транзакций CryptoNote, Zano и Bytecoin Amethyst

Правило ядра, ограничивающее подмешивание в зависимости от mix_attr, таково:

  1. Если mix_attr = 0, то ограничений нет. Данный выход может использоваться для подмешивания с другими выходами в любых комбинациях. Это — значение по-умолчанию.
  2. Если mix_attr = 1, то данный выход может быть потрачен только непосредственно, без подмешивания, и не может быть использован для подмешивания к другим выходам.
  3. Если mix_attr ≥ 2, то данный выход может быть потрачен только при подмешивании других, при этом общее число использованных выходов не должно быть меньше величины mix_attr.

Главной особенностью этого нововведения является п. 3, что позволяет увеличить untraceability (неотслеживаемость связей) за счет исключения ситуаций, когда выход, уже использованный в подмешивании, тратится его владельцем непосредственно (это подробно рассмотрено в [19]).

Однако, в контексте данной статьи, нас будет интересовать п. 2, то есть ситуация, когда mix_attr = 1 и выход, помеченный таким образом, может быть потрачен только непосредственно. Это ограничение проиллюстрировано на рис. 6.2.

Рис. 6.2. Иллюстрация ограничения.
Рис. 6.2. Иллюстрация ограничения.
Сверху: input #0 ссылается только на output #0 с mix_attr = 1 (непосредственная трата) — допустимая ситуация.
Снизу: input #1 ссылается на три выхода: output #2 с mix_attr = 1 и еще на output #1 и output #3 — недопустимая ситуация

Так как output #2 имеет mix_attr = 1, то он не может быть смешан с другими выходами, вне зависимости от их значений атрибута mix_attr. Он может быть только потрачен непосредственно, как output #0.

Эту особенность можно использовать для организации аудита.

Аудит при помощи mix_attr = 1

Как было отмечено в разделе 3, если в рамках оригинального протокола CryptoNote аудитор Ден получит от Боба секретный ключ v, он сможет распознать входящие транзакции, но не сможет распознать исходящие, поэтому точный расчет баланса невозможен.

Тем не менее, поскольку Ден будет обладать полной информацией о не потраченных выходах (UTXO) Боба, то он сможет отследить тот факт, что какая-то транзакция в своих входах ссылается на UTXO Боба. При использовании подмешивания других выходов Ден не сможет определить, тратит ли данная транзакция именно выход Боба, что достигается благодаря использованию кольцевой подписи. Однако, если подмешивания нет и UTXO Боба тратится непосредственно, то аудитор Ден может быть уверен, что данная транзакция является исходящим платежом Боба. В этом и состоит идея данной схемы.

Предположим, что Алиса отправляет Бобу транзакцию, и Боб хочет, чтобы аудитор Ден мог видеть факт прихода средств от Алисы и факт их траты, когда Боб решит их потратить.

Схема работы будет выглядеть следующим образом (рис. 6.3).

Рис. 6.3. Аудитор с помощью секретного view-ключа v Боба определяет входящие и исходящие транзакции
Рис. 6.3. Аудитор с помощью секретного view-ключа v Боба определяет входящие и исходящие транзакции
  1. Боб сообщает Алисе свой публичный адрес (V, S), а также просит ее во всех выходах, адресованных ему, поставить атрибут mix_attr в значение 1 (ниже рассмотрим, как это можно реализовать технически).
  2. Боб передает аудитору Дену свой секретный view-ключ v.
  3. Алиса отправляет Бобу транзакцию по обычной схеме CryptoNote. В выходах, адресованных Бобу она ставит mix_attr = 1.
  4. Ден сканирует все транзакции в блокчейне и с помощью секретного view-ключа Боба проверяет для каждого выхода Pi == P’i = Hs(v * R, i) * G + S (где i — порядковый номер выхода, S — открытый spend-ключ Боба). Если равенство выполняется, то данный выход адресован Бобу.
    Ден убеждается, что mix_attr == 1, добавляет данный выход в список и увеличивает баланс на величину номинала выхода.
  5. Ден также проверяет все входы всех транзакций, и если один из входов ссылается на UTXO Боба из списка, то это означает, что данная транзакция — исходящий платеж Боба. Благодаря ограничению mix_attr = 1, данных вход не может подмешивать другие выходы, а значит он тратит выход Боба непосредственно, и Ден может быть уверен, что это исходящий платеж Боба. Ден уменьшает баланс на величину номинала входа.

Таким образом, Ден сможет рассчитать корректный баланс кошелька Боба, не получая доступа к его секретному spend-ключу s.

Особенности схемы

Особенность 1. Важно, чтобы отправляющая сторона (Алиса) поставила mix_attr в значение 1 при отправке транзакции. Если Алиса этого не сделает, то средства придут Бобу, однако в дальнейшем Ден не сможет однозначно сказать, потратил их Боб или нет, так как любой другой пользователь сможет использовать данных выход для подмешивания.

Один из вариантов решения — введение адресов особого типа, содержащих те же два открытых ключа (V, S), но упакованных отличным от стандартного образом. Алиса, отправляя средства на адрес такого типа, будет знать о необходимости установить mix_attr = 1 для соответствующих выходов.

Особенность 2. Использование адресов особого типа не обязывает Алису устанавливать нужное значение атрибута для выходов. Она может отправить средства не сделав этого, однако, это будет сразу же выявлено как Бобом, так и аудитором Деном.

Особенность 3. В данной схеме возможность аудита достигается ценой предельного уменьшения untraceability (неотслеживаемости связей). Это не представляет особой проблемы в случае, если возможность аудита предоставлена неограниченному кругу лиц. Например, благотворительный фонд может опубликовать адрес для пожертвований вместе с секретным view-ключом, благодаря чему любой желающий сможет выступить в качестве аудитора и рассчитать актуальный баланс его кошелька. Важно, что при этом анонимность входящих переводов остается стандартной для CryptoNote (можно использовать большое число Nmixins). Однако, анонимность исходящих платежей будет ограничена невозможностью подмешивать произвольные выходы.

Стоит отметить, что в случае, когда возможность аудита предоставляется узкому кругу лиц, в отличии от примера выше, уменьшение untraceability может быть нежелательным эффектом.

Большими достоинствами этого варианта являются простота реализации и отсутствие дополнительной нагрузки по вычислениям и данным.

7. Заключение

Мы рассмотрели три варианта добавления возможности проведения аудита в протокол CryptoNote: Bytecoin Amethyst, теоретическая схема AS от Anton Sokolov и использование mix_attr (обозначим как MA).

Относительное изменение размера кольцевой подписи для всех вариантов, кроме Bytecoin (BCN), не зависит от Ninputs, поэтому рассмотрим Ninputs = 1 (минимальный) и Ninputs = 3 (рис. 7.1).

Рис. 7.1. Сравнение размеров кольцевой подписи
Рис. 7.1. Сравнение размеров кольцевой подписи при Ninputs = 1 и Ninputs= 3 для всех рассмотренных вариантов

Примечание. Ninputs — число входов в транзакции, Nmixins — число выходов, ассоциированных с каждым входом транзакции. Если Nmixins > 1 то это означает, что для увеличения untraceability каждый вход ссылается более чем на один выход какой-то другой транзакции и при этом нельзя установить, какой из них реальный.

Лучший результат показывает схема AS, давая уменьшение размера кольцевой подписи в широком диапазоне числа подмешиваемых выходов (Nmixins). Bytecoin также дает хороший результат при Nmixins >= 3 (как уже было отмечено, в Bytecoin Amethyst для повышения untraceability было введено ограничение на минимальное число Nmixins равное 3).

Схема MA никаких улучшений в части размера кольцевой подписи не предлагает.

Сравним теперь трудоемкость процедуры проверки кольцевой подписи. Для этого будем использовать метрику, упоминавшуюся выше, а именно возьмем сумму операций скалярного произведения и вычисления хеш-функции Hp с весами, пропорциональными их времени выполнения на типичном современном процессоре:

O(total) = 130 * O(*) + 15 * O(Hp)

Имеем следующую картину (рис. 7.2).

Рис. 7.2. Сравнение увеличения вычислительной сложности проверки кольцевой подписи
Рис. 7.2. Сравнение увеличения вычислительной сложности проверки кольцевой подписи (Ninputs = 1)

При прямой трате средств (Nmixins = 1) схемы AS и Bytecoin требуют существенно большего объема вычислений для проверки подписи.

При подмешивании выходов (Nmixins > 1) схема Bytecoin Amethyst выглядит предпочтительнее AS, однако последний рассмотренный вариант MA остается непревзойденным.

Подведем окончательный итог, приняв во внимание такие факторы, как увеличение размера адреса, входов и выходов транзакций (рис. 7.3).

Рис. 7.3. Сравнение всех рассмотренных схем
Рис. 7.3. Сравнение всех рассмотренных схем

Bytecoin сохраняет удобную для конечного пользователя длину адреса, в то время как схема Anton Sokolov увеличивает его на 50%. Это не влияет непосредственно на размер блокчейна (хотя адрес отправителя в зашифрованном виде может передаваться в extra транзакции при желании последнего, например) и вычислительную сложность, однако незначительно влияет на удобство использования. В схеме MA размер адреса остается равным 64 байтам, однако требуется способ отличать адреса, предназначенные для аудита, от обычных.

Варианты реализации аудита Bytecoin и AS подразумевают добавление одной точки (32 байт) к каждому выходу транзакции, однако, размер входа остается неизменным только для Bytecoin.

Стоит также отметить тот факт, что схема Bytecoin Amethyst достаточно давно реализована в коде и, судя по отсутствию сообщений о проблемах за прошедшее с момента ее воплощения время, хорошо проверена на практике. Однако, ее публичное описание в строгом виде найти не удалось, поэтому и формальные доказательства ее корректности пока отсутствуют.

Схема AS, напротив, описана строго и предложена к обсуждению в криптосообществе.

Схема MA не имеет строго описанного формального доказательства, однако, в силу ее крайней простоты, оно представляется излишним.