Транзакция по байтам
Полный разбор hex транзакции RingCT: каждое поле, каждая функция — на настоящих и случайных данных
Транзакция Monero — это три подряд идущих куска байтов: префикс (входы, выходы, extra), база RingCT (комиссия, зашифрованные суммы, обязательства) и prunable (Bulletproofs+, CLSAG, псевдовыходы). Ниже — инструмент, который разбирает любую v2-транзакцию побайтно, считает txid, проверяет баланс и, если дать ключ просмотра, находит «свои» выходы и расшифровывает суммы. Для случайной транзакции он показывает все секреты, чтобы было видно, как каждая функция применяется к числам.
Как читать байты
Сериализация Monero простая — никаких заголовков и имён полей, только значения подряд. Два вида кирпичиков:
- varint — целое число переменной длины: по 7 бит в байте, старший бит = «дальше есть ещё байт».
0x02= 2,0x80 0x01= 128,0x80 0xb5 0x18= 400000. - ключ / точка / скаляр — ровно 32 байта, без длины. Массивы точек, длина которых известна из контекста (например,
sв CLSAG — по размеру кольца), тоже идут без длины.
Поэтому парсер обязан знать формат заранее: тип RingCT, прочитанный в середине, определяет, как читать всё остальное.
Какие функции где применяются
| Поле | Кто вычисляет | Формула | Подробнее |
|---|---|---|---|
| extra: R | отправитель | R = r·G | stealth-адреса |
| выход: P | отправитель | P = Hs(8·r·A ‖ i)·G + B | stealth-адреса |
| view tag | отправитель | Keccak("view_tag" ‖ 8rA ‖ i)[0] | view tags |
| ecdhInfo | отправитель | v ⊕ Keccak("amount" ‖ Hs(8rA ‖ i))[0..8] | Педерсен |
| outPk | отправитель | C = y·G + v·H, y = Hs("commitment_mask" ‖ Hs(8rA ‖ i)) | Педерсен |
| кольцо | кошелёк отправителя | 16 глобальных индексов, гамма-распределение по возрасту | приманки |
| key image | владелец входа | I = x·Hp(P) | key image |
| pseudoOuts | отправитель | C̃ = y'·G + v·H, маски так, что Σ C̃ = Σ C + fee·H | RingCT |
| CLSAG | владелец входа | кольцо вызовов по P_i и C_i − C̃ | кольцевые подписи |
| Bulletproofs+ | отправитель | доказательство 0 ≤ v < 2⁶⁴ для всех выходов | range proofs |
Что делает узел, получив эти байты
- Разбирает байты; любой лишний или недостающий байт — отказ.
- Проверяет, что key images новые и лежат в правильной подгруппе.
- По смещениям восстанавливает 16 глобальных индексов и достаёт из базы ключи и обязательства этих выходов.
- Проверяет баланс
Σ C̃ = Σ outPk + fee·H— это делает и инструмент ниже. - Проверяет Bulletproofs+ и CLSAG каждого входа.
Что делает кошелёк получателя
- Достаёт
Rиз extra и считает общий секрет8·a·R(одна операция на транзакцию). - Для каждого выхода: хеш → сравнивает view tag; не совпало — дальше не считает.
- Совпало — сравнивает
Hs(8aR ‖ i)·G + Bс ключом выхода. - Выход наш — расшифровывает сумму, восстанавливает маску и сверяет
y·G + v·HсoutPk.
Все эти шаги инструмент показывает с конкретными числами.
Парсер сверен с JSON-расшифровкой monerod на настоящих транзакциях типов 5 и 6 и на coinbase-транзакции: совпадают все поля и txid. Кнопка «настоящая tx из mainnet» загружает одну из них — сравните txid с любым обозревателем блоков.
А как будет выглядеть транзакция FCMP++?
Формат транзакций FCMP++ ещё может поменяться до хардфорка, поэтому байтового парсера для него здесь нет. Но по текущей реализации общая структура понятна — и она удивительно похожа на нынешнюю:
| Что было в RingCT | Что в FCMP++ | Функции |
|---|---|---|
| 16 смещений на вход | ссылка на блок, по дереву которого строится доказательство | корень Curve Tree из всех выходов |
key image I = x·Hp(P) | тот же L = x·Hp(O) | двойная трата ловится как раньше |
псевдовыход C̃ | C̃ = C + r_c·G — часть перерандомизированного кортежа | перерандомизация |
| CLSAG на вход | SA+L на вход (несколько точек и скаляров) | обобщённый Шнорр |
| — | membership proof на всю транзакцию | Generalized Bulletproofs + дивизоры |
| view tag 1 байт, ECDH на Ed25519 | view tag 3 байта, X25519, якорь от Janus | Carrot |
Посмотреть перерандомизацию на числах можно в инструменте. Когда формат зафиксируют, сюда добавится и байтовый разбор FCMP++-транзакций.