Trustless L2
Trustless-взаимодействие между TON и Tycho L2 не требует доверия. Транзакция предъявляется смарт-контракту на принимающей стороне. Смарт-контракт проверяет доказательства и убеждается, что транзакция действительно была выполнена в исходной сети. Это работает в обе стороны, Tycho L2 может проверять события из TON, и TON может проверять события из Tycho L2.
Это возможно за счёт:
- общей структуры данных
- общей логики проверок, которая собирает цепочку от транзакции до подтвержденного блока
- использования доказательств Меркла и подписей валидаторов с порогом по весам
Данные, необходимые для проверки транзакции
Чтобы проверить транзакцию из TON внутри Tycho L2, контракту нужны две группы данных.
- Данные о наборе валидаторов
- Данные о транзакции
Данные о наборе валидаторов
Чтобы понять, какие именно данные нужны для проверки транзакции, нужно сначала разобраться, как устроена смена валидаторов в протоколе. В TON есть мастерчейн, где каждый мастер-блок подписывается валидаторами текущего набора.
Когда набор валидаторов меняется, в мастерчейне выпускается ключевой блок. В нём лежит информация о новом наборе валидаторов, включая их публичные ключи и веса. При этом сам ключевой блок подписан старыми валидаторами, поэтому новая конфигурация привязывается к уже подтверждённой истории.
На стороне L2 хранится цепочка данных, созданная из таких ключевых блоков, включая информацию о текущей эпохе валидаторов и о нескольких прошлых эпохах. Это нужно, чтобы для любого проверяемого мастер-блока можно было восстановить актуальный на тот момент набор валидаторов и понять, какие подписи требуется проверить.
Какие данные из ключевого блока переносятся в L2
Когда появляется новый ключевой блок, он переносится в L2 как пакет данных для проверки и обновления локального состояния. Эти данные включают:
- доказательство ключевого блока в виде доказательства Меркла для ячейки
file_hashкак отдельное поле, которое передаётся вместе с этим ключевым блоком- подписи этого ключевого блока
Нам не нужно переносить весь ключевой блок.
Передаются только нужные данные, а лишние заменяются на pruned-ячейки.
Передаются:
seq_no— порядковый номер мастер-блокаgen_utime— время генерации мастер-блокаprev_key_block— ссылка на предыдущий ключевой блок через его номер- конфигурация валидаторов из параметров 34 и при необходимости 32
Остальные части блока сворачиваются в pruned-ячейки.
Дальше контракт:
- Убеждается, что полученный блок действительно является ключевым блоком
- Извлекает
root_hashблока из доказательства Меркла - Проверяет подписи блока, используя
root_hash,file_hashи переданные подписи. Проверка делается относительно набора валидаторов, который был актуален при выпуске этого ключевого блока.
После успешной проверки контракт извлекает из McBlockExtra параметры конфигурации 32 и 34 и обновляет данные о валидаторах.
В L2 сохраняется только то, что нужно для будущих проверок мастер-блоков:
epoch_id— время начала действия этого набора валидаторов- период времени, в котором новый набор валидаторов считается актуальным
- список валидаторов с публичными ключами и весами в формате, удобном для перебора в контракте
- порог по суммарному весу подписей, который нужен, чтобы признать блок подтвержденным
Данные о транзакции
Для проверки транзакции необходимо предоставить пакет доказательств, который связывает транзакцию с мастер-блоком, подписанным валидаторами, и содержит данные для поиска транзакции в блоке.
check_transaction#ddab5b88
proof_chain:^(MERKLE_PROOF ProofChainRoot)
tx_proof:^TransactionProof
= InternalMsgBody;
Цепочка доказательств, связывающая транзакцию с мастер-блоком:
epoch_idмастер-блока, чтобы выбрать правильный набор валидаторов для проверки подписейfile_hashмастер-блока как отдельное полеmc_block— мастер-блокsignatures— подписи для мастер-блока- блок рабочей цепочки, если транзакция находится в нём, и при необходимости цепочка таких блоков, если мастер-блок ссылается на нужный блок рабочей цепочки через промежуточные блоки
proof_chain_root#_ {n:#}
file_hash:uint256
epoch_id:uint32
mc_block:^Cell
signatures:^(Hashmap 16 bits512)
head:(ProofChainHead ~n)
= ProofChainRoot;
proof_chain_head_empty#_ = ProofChainHead ~0;
proof_chain_head_1#_ sc_block1:^Cell = ProofChainHead ~1;
proof_chain_head_2#_ {n:#} sc_block1:^Cell tail:^(ProofChainTail ~n) = ProofChainHead ~2;
proof_chain_tail_empty#_ = ProofChainTail ~0;
proof_chain_tail_1#_ sc_block1:^Cell = ProofChainTail ~1;
proof_chain_tail_2#_ sc_block1:^Cell sc_block2:^Cell = ProofChainTail ~2;
proof_chain_tail_3#_ sc_block1:^Cell sc_block2:^Cell sc_block3:^Cell = ProofChainTail ~3;
proof_chain_tail_3tail#_ {n:#} sc_block1:^Cell sc_block2:^Cell sc_block3:^Cell tail:^(ProofChainTail ~n) = ProofChainTail ~4;
Информация о транзакции:
- идентификатор блока аккаунта, в котором лежат транзакции этого аккаунта
- логическое время транзакции
- хеш транзакции
tx_proof#_ account_block:uint256 lt:uint64 tx_hash:uint256 = TransactionProof;
Проверка транзакции
Контракт распаковывает цепочку доказательств в поисках блока, который содержит транзакцию.
- Если блоков рабочей цепочки нет, берётся
mc_block - Иначе выбирается самый последний из приложенных блоков рабочей цепочки.
В найденном блоке производим поиск искомой транзакции tx_hash, используя данные из TransactionProof.
- Из блока извлекается структура
account_blocks - По идентификатору выбирается нужный
account_blockдля конкретного аккаунта - Внутри
account_blockпо логическому времениltнаходится запись транзакции - Для найденной транзакции вычисляется хеш и сравнивается с переданным хешом
tx_hash
Последним шагом проверяется подпись мастер-блока.
- по
epoch_idполучаем публичные ключи и веса валидаторов этой эпохи - получаем
root_hashизmc_block - по
root_hashиfile_hashпроверяемsignatures, чтобы определить, был ли этот мастер-блок подписан правильным набором валидаторов
Нам не нужно переносить полные блоки. Передаются только нужные данные, а лишние заменяются на pruned-ячейки. Передаются:
- Для мастер-блока —
Block infoв минимальном виде иMcBlockExtraсshard-hashes - Для блока рабочей цепочки в связанной цепочке —
Block info prev_ref - Для блока с транзакцией — нужный
account_blockизBlock extra account_blocksи только одна запись транзакции поlt
Остальные части блока сворачиваются в pruned-ячейки.
Примеры реализации
- Реализация контрактов для trustless взаимодействия: tonred/tvm-bridge
- Инструменты для сбора пруфов: broxus/tycho-l2
- Trustless UI: ChainConnect Bridge