Key image
Отпечаток, который выход оставляет при трате — и только один раз
Key image I = x·Hp(P), где P — одноразовый ключ выхода, x — его приватный ключ. Для одного выхода key image всегда одинаковый, но по нему нельзя понять, какой это выход. Сеть хранит все key image и отклоняет транзакцию с повторяющимся.
Проблема двойной траты
В Bitcoin двойная трата ловится просто: каждый вход прямо указывает на выход, который тратит, и узел помнит, что выход уже потрачен (множество UTXO). В Monero вход указывает на 16 выходов, и какой из них настоящий — неизвестно. Значит, «отметить потраченный выход» невозможно. Нужен способ отметить трату, не раскрывая, что именно потрачено.
Решение
Почему это работает:
- Детерминированность.
xиPдля выхода фиксированы, значит иIодин. Потратить выход второй раз — значит опубликовать тот жеI. - Непривязываемость. Чтобы проверить, что
IсоответствуетP, нужно знатьx: посторонний видит две «случайные» точкиHp(P)иIи не может установить связь (это задача Диффи–Хеллмана). - Подделать нельзя. Линкуемая кольцевая подпись доказывает, что
Iвычислен из того жеx, что и один из ключей кольца.
Почему Hp(P), а не просто G? Тогда I = x·G = P — key image совпал бы с публичным ключом выхода, и трата была бы видна сразу. Hp даёт точку, логарифм которой никто не знает, — «вторую базу», уникальную для каждого выхода.
Проверка подгруппы: история бага 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++ нельзя.