TON Trustless Bridge Challenge Assessment

Regularly, when discussing various high-frequency trading, SocialFi, and other tasks, the question of creating a TVM-sidechain comes up. Previously, such development stumbled over the fact that those who need a sidechain do not have the expertise to create it, it is unclear where to start looking for a team with expertise, and how to assess the scale of development.

In this context, this task at the competition had several goals at once:

  • Highlight the bright developers in the ecosystem capable of creating secure sidechains and other complex developments that require a profound understanding of how TON works, in particular for subsequent recruitment to various teams, including TON Core.
  • Show that even one of the most difficult parts of such a sidechain, the trustless bridge, has finite complexity and can be implemented in a few weeks.
  • Raise the level of expertise in proving mechanisms in TON.
  • Lower the threshold for creating secure and fast sidechains.
    We were surprised and very happy to receive many good solutions. Thus, the competition between top-tier solutions was based not only on simple verification but also on additional criteria such as clarity of code, scripts, tests, and documentation.

At the same time, competing in the contest is not the same task as creating a production-ready solution. There were requirements that may not be expedient for a real bridge but were introduced for unification and simplicity of assessment. For instance, interaction between the lite-client and tx-checker may be implemented in many ways, with the most convenient way yet to be found. Thus, the only part of the interaction we checked for was the authorization of Liteclient on the tx-checker side (thus, dictionary-based query-remembering mechanisms, as well as using the block as proof, etc., are acceptable).
The same stands for the lack of convenient tools for end users, the lack of UX optimization, and the verification of additional parameters (for example, transactions in workchains), etc.: having them is nice, but not having them is not a disadvantage.

There were a few common mistakes present in the solutions. For the sake of understanding our assessment, we will present them here in detail:

Max_main_validators logic: Masterchain blocks are signed not by all validators but only by main validators, which are the top (by weight) N validators from the whole validator set. N can be parsed from config34. This logic has two consequences: first, you need to only reach 2/3 of main_weight, and second, you should only accept signatures from main validators (signatures of validators below top N should be discarded).

Signature deduplication: The contract should not allow the same signature (from the same validator) to be counted in weight two or more times.

Signature absence resistance: You should expect that some validators may sometimes misbehave and not generate a signature. The absence of less than 1/3 (by weight) of signatures (even if the signature from the top validator is absent) should not affect block validation.

Checking prev_key_block, global_id, validator_set, etc.: Theoretically, proper checks of some additional parameters don’t increase the security of the system compared to just checking that “current validators generated enough signatures.” In practice, such checks are helpful during operation. So, the absence of these checks was taken into account but considered a very minor issue. The same stands for check_block operations.

In the transaction checker, there was a clever-but-insecure optimization. We call it a “hints”-based search, where instead of properly looking up accounts-blocks to find the target account block and then searching inside for the target lt, the contract accepts “hints” about the path to the target transaction (e.g., in the first cell, use the 0th ref; in the next cell, use the 1st ref, and so on) and then just compares the hash of the cell reached with the hash of the transaction.
The problem with such a solution is that (without additional checks) it doesn't guarantee that the path leads to the transaction cell. An attacker can craft a custom transaction cell (never executed on-chain) and then just send it from his wallet somewhere else. This way, the cell containing the attacking transaction will be included in the block as content of the message in one of the transactions. So, the existence of such a cell in an arbitrary place in the BoC does not guarantee that the transaction was executed.
The same issue is relevant for a full-cell scan of the block when we just recursively compare the hashes of all cells with a given transaction cell.

Another common issue with the transaction checker is that the contract checks the proof for a transaction and asks the LC about the correctness of the block but never checks that the proof and block correspond to each other.

Two solutions tried to implement proving shard-transactions. Unfortunately, it requires a more complicated approach. The thing is that not every shard block is committed to the masterchain directly. Some blocks are committed through their children. There is a maximal length of a chain of blocks not committed to the masterchain, and this length is equal to 8.
Proper proof of a shardchain transaction requires up to 5 parts:

  • Proof of the transaction's existence in some shard block.
  • Optionally, a chain of shard blocks referencing each other until some block that is committed to the masterchain (can be empty if the block itself is committed but can also contain up to 8 blocks).
  • Proof that the tip shard-block is committed to some masterchain block.
  • Proof that this masterchain block is a child of the latest known masterchain block (this can be done since the masterchain state has the full list of all masterchain blocks in OldMcBlocksInfo).
  • Optionally, proof for the latest block (can be empty if it is already proven).

Since this logic is quite complicated, we decided not to include it in the task and mentioned that only transactions in the masterchain should be proven. Kudos to those who dared, though.