Home About John Resume/CV References Writing Research

Ethereum Bridging using Light Clients - Rainbow Costing

A review of how Bridging costs can be reduced using light clients. Taking the near Rainbow Bridge as an example.
Feb 23rd, 2022
John Whitton
Thanks to Aaron Li for their helpful suggestions.

Table of Content


In this article we review the use of light clients and how they can improve trust and costing for bridges.

Bridge Transaction Walk Through

Following is a walkthough of a funds transfer from Ethereum to a target chain (In this example Near), complete with light client updates, block propogation and proofs to ensure the transaction validity.

Ethereum to Near Funds Transfer


From the diagram above you’ll notice that there are many actors involved, below is an overview of the actors and the operations they perform.

Sample TransactionFlow

  1. Block Propogation
    1. Get the Latest Slot: The relayer loops polling Ethereum every 12 seconds to get the latest slot. It then checks if it is for a new epoch and if so (every 6 minutes) submits an execution header (with 32 blocks in it) and a light client update with the latest approved epochs and updated sync_comittee. Relayer source code for the loop is here for retrieving the latest slot is here, for submitting execution blocks is here and for sending light client updates is here.
      1. Send Block Headers (submit_execution_header): Batch transaction which submits 32 block headers to client-eth2.bridge.near for Ethereum Blocks 16493592 to 16493623. (The second slot in epoch 176,936 to the first slot in epoch 176,937). Executed every 6 minutes when the first slot of a new epoch is found.
      2. Create Light Client update proposal(add_proposal): calls bridge-validator.sputnik-dao.near to add proposal 17410 for slot 5,661,984 in epoch 176,937.
    2. Approve Proposal (act_proposal): sends a VoteApprove action for proposal 17410 from a bridge validator to the Validator DAO Contract.
      1. act_proposal in contract bridge-validator.sputnik-dao.near
      2. submit_beacon_chain_light_client_update in client-eth2.bridge.near
      3. on_proposal_callback in contract bridge-validator.sputnik-dao.near
  2. Funds Transfer Transaction Flow
    1. Lock Funds On Ethereum: Locking 10,000 USDT to send to user on NEAR.
    2. Deposit Funds on Target Chain Bridge Contract (deposit)
      1. deposit in contract factory.bridge.near
      2. verify_log_entry in contract prover.bridge.near
      3. block_hash_safe in contract client-eth2.bridge.near
      4. finish_deposit in contract factory.bridge.near : mint of 10,000 USDT.


Bridging Resources Required

Here is the storage and compuational costs per component.

Component Data Storage Notes
Ethereum 2 Client
Prover not applicable 0 bytes  
DAO Contract      

TODO Review the following data structure and elements and move into the table above commenting on any mandatory requirements and structures that can be improved.

pub struct Eth2Client {
    /// If set, only light client updates by the trusted signer will be accepted
    trusted_signer: Option<AccountId>,
    /// Mask determining all paused functions
    paused: Mask,
    /// Whether the client validates the updates.
    /// Should only be set to `false` for debugging, testing, and diagnostic purposes
    validate_updates: bool,
    /// Whether the client verifies BLS signatures.
    verify_bls_signatures: bool,
    /// We store the hashes of the blocks for the past `hashes_gc_threshold` headers.
    /// Events that happen past this threshold cannot be verified by the client.
    /// It is desirable that this number is larger than 7 days' worth of headers, which is roughly
    /// 51k Ethereum blocks. So this number should be 51k in production.
    hashes_gc_threshold: u64,
    /// Network. e.g. mainnet, kiln
    network: Network,
    /// Hashes of the finalized execution blocks mapped to their numbers. Stores up to `hashes_gc_threshold` entries.
    /// Execution block number -> execution block hash
    finalized_execution_blocks: LookupMap<u64, H256>,
    /// All unfinalized execution blocks' headers hashes mapped to their `HeaderInfo`.
    /// Execution block hash -> ExecutionHeaderInfo object
    unfinalized_headers: UnorderedMap<H256, ExecutionHeaderInfo>,
    /// `AccountId`s mapped to their number of submitted headers.
    /// Submitter account -> Num of submitted headers
    submitters: LookupMap<AccountId, u32>,
    /// Max number of unfinalized blocks allowed to be stored by one submitter account
    /// This value should be at least 32 blocks (1 epoch), but the recommended value is 1024 (32 epochs)
    max_submitted_blocks_by_account: u32,
    // The minimum balance that should be attached to register a new submitter account
    min_storage_balance_for_submitter: Balance,
    /// Light client state
    finalized_beacon_header: ExtendedBeaconBlockHeader,
    finalized_execution_header: LazyOption<ExecutionHeaderInfo>,
    current_sync_committee: LazyOption<SyncCommittee>,
    next_sync_committee: LazyOption<SyncCommittee>,


Explorer and Interactive Links