Технический › Кто отправитель?

Key image

Отпечаток, который выход оставляет при трате — и только один раз

Коротко

Key image I = x·Hp(P), где P — одноразовый ключ выхода, x — его приватный ключ. Для одного выхода key image всегда одинаковый, но по нему нельзя понять, какой это выход. Сеть хранит все key image и отклоняет транзакцию с повторяющимся.

Проблема двойной траты

В Bitcoin двойная трата ловится просто: каждый вход прямо указывает на выход, который тратит, и узел помнит, что выход уже потрачен (множество UTXO). В Monero вход указывает на 16 выходов, и какой из них настоящий — неизвестно. Значит, «отметить потраченный выход» невозможно. Нужен способ отметить трату, не раскрывая, что именно потрачено.

Решение

I = x · Hp(P)

Почему это работает:

  • Детерминированность. x и P для выхода фиксированы, значит и I один. Потратить выход второй раз — значит опубликовать тот же I.
  • Непривязываемость. Чтобы проверить, что I соответствует P, нужно знать x: посторонний видит две «случайные» точки Hp(P) и I и не может установить связь (это задача Диффи–Хеллмана).
  • Подделать нельзя. Линкуемая кольцевая подпись доказывает, что I вычислен из того же x, что и один из ключей кольца.

Почему Hp(P), а не просто G? Тогда I = x·G = P — key image совпал бы с публичным ключом выхода, и трата была бы видна сразу. Hp даёт точку, логарифм которой никто не знает, — «вторую базу», уникальную для каждого выхода.

новая транзакцияkey image I = 3f9a… база узла a17c… 0be2… 3f9a… ← уже есть! … ✗ отклонить: двойная трата узел не знает, какой выход потрачен — только что этот отпечаток уже был

Проверка подгруппы: история бага 2017 года

На кривой Ed25519 точек в 8 раз больше, чем в подгруппе G. Если к I добавить точку малого порядка T (такую, что 8·T = 0), получится другая точка I' = I + T, а кольцевая подпись при определённых условиях всё равно сходилась. Один выход — до 8 разных key image — до 8 трат. В 2017 году эту ошибку нашли в исходном коде CryptoNote; в Monero её закрыли до эксплуатации, в Bytecoin — эксплуатировали.

Исправление: узел проверяет, что l·I = 0, то есть I лежит в правильной подгруппе.

Key image и view-only кошелёк

Чтобы понять, потрачен ли ваш выход, кошелёк вычисляет его key image и ищет его в блокчейне. Но для этого нужен x = Hs(8aR ‖ i) + b — то есть ключ траты. Поэтому кошелёк только с ключом просмотра не видит трат и показывает завышенный баланс. Обходной путь — экспортировать key images из полного кошелька. В Carrot для этого будет отдельный ключ.

Key image в FCMP++

В FCMP++ key image остаётся: L = x·Hp(O). Меняется то, как доказывается его связь с выходом, — вместо кольца это делает доказательство членства в дереве всех выходов. Важное следствие: все key image, уже опубликованные в RingCT-транзакциях, сохраняют смысл — потратить старый выход второй раз после перехода на FCMP++ нельзя.