Basic proof concepts
A proof is a tree-based structure that contains the necessary data and can be verified by a proof requester. To prove something means to construct such a structure. The exact structure may differ depending on the use case. For example, suppose a proof is to be validated off-chain and requires multiple cell trees; the native TON approach is to construct a Bag of Cells (BoC) containing all the necessary cells. This is precisely what a lite server does when data is requested from the blockchain.
It is highly recommended to familiarize yourself with Cells and Exotic cells first. This article primarily covers situations where you want to verify a proof in a smart contract. However, the same techniques can be used to validate proofs off-chain.
There are several key points to consider when proving anything on-chain.
- The only trusted information available in a smart contract is a few recent MasterChain blocks.
- Some data is stored directly within blocks.
- Additional information is maintained within the WorkChain state.
- Blocks serve as diffs that reflect changes to the state over time. Think of blocks as Git commits and the state as your repository.
- Latest TL-B schemas can be found in the TON Monorepo. They may evolve, typically in backwards-compatible ways.
More about blocks
We need to examine the block layout to determine what we can prove and how to do it.
Each block (ShardChain block, MasterChain block) has a unique block ID:
block_id_ext$_ shard_id:ShardIdent seq_no:uint32
root_hash:bits256 file_hash:bits256 = BlockIdExt;
- ShardIdent contains information about the WorkChain and the shard the block belongs to.
- seq_no is the sequence number of the current block.
- root_hash is the hash of the block data (block header).
- file_hash helps validators optimize processes; typically, you don’t need it.
A full block structure is as follows:
block#11ef55aa global_id:int32
info:^BlockInfo value_flow:^ValueFlow
state_update:^(MERKLE_UPDATE ShardState)
extra:^BlockExtra = Block;
The most relevant field here is state_update. This MERKLE_UPDATE
cell stores the old and new hashes of the ShardChain state. Note that the MasterChain always consists of a single shard, so inspecting a MasterChain block reveals the MasterChain state hash.
Another relevant field is extra:
block_extra in_msg_descr:^InMsgDescr
out_msg_descr:^OutMsgDescr
account_blocks:^ShardAccountBlocks
rand_seed:bits256
created_by:bits256
custom:(Maybe ^McBlockExtra) = BlockExtra;
Inspecting a MasterChain block reveals the McBlockExtra field:
masterchain_block_extra#cca5
key_block:(## 1)
shard_hashes:ShardHashes
shard_fees:ShardFees
^[ prev_blk_signatures:(HashmapE 16 CryptoSignaturePair)
recover_create_msg:(Maybe ^InMsg)
mint_msg:(Maybe ^InMsg) ]
config:key_block?ConfigParams
= McBlockExtra;
The shard_hashes field is essential, as it holds the latest known ShardChain blocks, which are critical for BaseChain proofs.
For detailed inspections, it is convenient to use the official explorer.
High-level overview of proofs
Proving a transaction in MasterChain
To prove a transaction's existence in the MasterChain:
- Obtain a trusted MasterChain block
root_hash
using TVM instructions (PREVMCBLOCKS, PREVMCBLOCKS_100, PREVKEYBLOCKS). - User provides a complete MasterChain block that should be validated against the trusted hash.
- Parse the block to extract the transaction.
Proving a transaction in BaseChain
For BaseChain transactions:
- Follow steps 1-2 above to get a trusted MasterChain block.
- Extract the shard_hashes field from the MasterChain block.
- User provides the full ShardChain block that should be validated against the trusted hash.
- Parse the ShardChain block to find the transaction.