Light client bridge


Light client bridge

Light client bridge will relay every block from source chain to target chain, normally for asset transfer just need to lock asset in some backing module or smart contract in source chain and mint the mapping asset in target chain

Components

Since the chain cannot directly access each other, the cross-chain data submission needs to be completed by a third party. This third party is the bridge relayers. Anyone can become a bridge relayer, and the bridge relayer obtains income by completing the relay task between the bridges. This incentive can promote the stable existence of bridge relayers to ensure the bridge’s regular operation.

Reference Implementation

Actually Parity officially provide a poc project illustrated in poa-eth guidance

Challenges

Merkle Mountain Ranges

Merkle mountain ranges are just merkle trees with an efficient append operation. leaf nodes contais block header in chain,super light client not nessessary to download all the block heades,e.g. only data marked as following is required to download from full node to verify transaction in leaf index 15 is valid

MMR proof

build merkle proof of leaf node including 3 steps:

MMR root

Node(p) = Blake2b(m | Node(left_child(p)) | Node(right_child(p)))

MMR in substrate

pub trait MmrApi<Hash: codec::Codec> {
    /// Generate MMR proof for a leaf under given index.
    #[skip_initialize_block]
    fn generate_proof(leaf_index: u64) -> Result<(EncodableOpaqueLeaf, Proof<Hash>), Error>;

    /// Verify MMR proof against on-chain MMR.
    ///
    /// Note this function will use on-chain MMR root hash and check if the proof
    /// matches the hash.
    /// See [Self::verify_proof_stateless] for a stateless verifier.
    #[skip_initialize_block]
    fn verify_proof(leaf: EncodableOpaqueLeaf, proof: Proof<Hash>) -> Result<(), Error>;

    /// Verify MMR proof against given root hash.
    ///
    /// Note this function does not require any on-chain storage - the
    /// proof is verified against given MMR root hash.
    ///
    /// The leaf data is expected to be encoded in it's compact form.
    #[skip_initialize_block]
    fn verify_proof_stateless(root: Hash, leaf: EncodableOpaqueLeaf, proof: Proof<Hash>)
        -> Result<(), Error>;
}
impl pallet_mmr::Config for Runtime {
    const INDEXING_PREFIX: &'static [u8] = b"mmr";
    type Hashing = Keccak256;
    type Hash = <Keccak256 as traits::Hash>::Output;
    type OnNewRoot = mmr_common::DepositBeefyDigest<Runtime>;
    type WeightInfo = ();
    type LeafData = mmr_common::Pallet<Runtime>;
}

Beefy protocol

To overcome the difficulty with GRANDPA finality proofs a separate round of BFT agreement is required where each voter will be voting on the MMR root of the latest block finalized by GRANDPA which using ECDSA for easier Ethereum compatibility and steps as following:

Beefy in polkadot runtime

    //start beefy(current only in rococo service)
    let beefy_params = beefy_gadget::BeefyParams {
    client,
    backend,
    key_store: keystore.clone(),
    network: network.clone(),
    signed_commitment_sender,
    min_block_delta: 4,
    prometheus_registry: prometheus_registry.clone(),
};

// Start the BEEFY bridge gadget.
task_manager.spawn_essential_handle().spawn_blocking(
    "beefy-gadget",
    beefy_gadget::start_beefy_gadget::<_, _, _, _>(beefy_params),
);

impl mmr_common::Config for Runtime {
	type BeefyAuthorityToMerkleLeaf = mmr_common::UncompressBeefyEcdsaKeys;
	type ParachainHeads = Paras;
}

Integrate MMR with Beefy

/// A BEEFY consensus digest item with MMR root hash.
pub struct DepositBeefyDigest<T>(sp_std::marker::PhantomData<T>);

impl<T> pallet_mmr::primitives::OnNewRoot<beefy_primitives::MmrRootHash> for DepositBeefyDigest<T> where
	T: pallet_mmr::Config<Hash = beefy_primitives::MmrRootHash>,
	T: pallet_beefy::Config,
{
	fn on_new_root(root: &<T as pallet_mmr::Config>::Hash) {
		let digest = sp_runtime::generic::DigestItem::Consensus(
			beefy_primitives::BEEFY_ENGINE_ID,
			parity_scale_codec::Encode::encode(
				&beefy_primitives::ConsensusLog::<<T as pallet_beefy::Config>::BeefyId>::MmrRoot(*root)
			),
		);
		<frame_system::Pallet<T>>::deposit_log(digest);
	}
}

Snowfork Bridge

Snowbridge has a layered architecture with a clear seperation between low level bridge functionality, mid level trust functionality and high level application functionality.

Trust Layer

Ethereum MPT verification in Substrate

// Validate an Ethereum header&ethash proof for import
fn validate_header_to_import(header: &EthereumHeader, proof: &[EthashProofData]) -> DispatchResult {
  ...
}

Beefy light client smart contract in Ethereum

Bridge Layer

guarantee basic deliverability and replay protection and with incentivized bridge adding a strict message ordering channels in both directions.

Ethereum → Substrate

There is a channel for sending Polkadot RPCs out from Ethereum to Polkadot via events. It consists of OutboundChannel contract on the Ethereum side and a corresponding InboudChannel on the parachain side, workflow as following:

Substrate → Ethereum

There is a OutboundChannel on the parachain side for sending Ethereum RPCs out from the parachain to Ethereum. It is responsible for accepting requests from other pallets and parachains for messages to be sent over to the correspongding InboundChannel smart contract

App layer and Relayer Implementation

Ethereum → Substrate Relayer is pretty straight forward so just skip and jump to Substrate → Ethereum part

start from invoking app requests (e.g lock polkadot dot asset ) into Parachain Message Commitments that will be included in the parachain header. With the help of Commitment Relayer Worker The ethereum channel then processes those commitments and verifies them via the Polkadot and Parachain Light Client Verifier to extract Ethereum RPCs. Those Ethereum RPCs are then routed to their target contract by calling that contract.

Workflow as following:

1. Following MMR Roots from Polkadot Relay Chain

The first step for trustless verification of our bridge on Ethereum starts with following the Polkadot relay chain via following new BEEFY MMR roots (as mentioned above) as they are produced and verifying their validity. Their validity is verified by checking that they are signed by the correct set of Polkadot validators.

Beefy Relayer Worker

func (li *BeefyEthereumListener) pollEventsAndHeaders(ctx context.Context, descendantsUntilFinal uint64) error {
	headers := make(chan *gethTypes.Header, 5)

	li.ethereumConn.GetClient().SubscribeNewHead(ctx, headers)

	for {
		select {
		case <-ctx.Done():
			li.log.Info("Shutting down listener...")
			return ctx.Err()
		case gethheader := <-headers:
			blockNumber := gethheader.Number.Uint64()

                    //submit initial beefy witness verification 
                       li.forwardWitnessedBeefyJustifications()
			li.processInitialVerificationSuccessfulEvents(ctx, blockNumber)
 
                     //submit CompleteSignatureCommitment            
			li.forwardReadyToCompleteItems(ctx, blockNumber, descendantsUntilFinal)
			li.processFinalVerificationSuccessfulEvents(ctx, blockNumber)
		}
	}
}
        case msg := <-wr.beefyMessages:
        switch msg.Status {
        case store.CommitmentWitnessed:
            err := wr.WriteNewSignatureCommitment(ctx, msg)
            if err != nil {
                wr.log.WithError(err).Error("Error submitting message to ethereum")
            }
        case store.ReadyToComplete:
            err := wr.WriteCompleteSignatureCommitment(ctx, msg)
            if err != nil {
                wr.log.WithError(err).Error("Error submitting message to ethereum")
            }
        }
    }

Beefy light client smart contract

2. Applying New Relay Chain MMR Updates

These verified relay chain MMR updates contain validator set updates and parachain header updates. Then use them to update knowledge about Polkadot validators and to extract and follow new headers of Snowbridge parachain blocks.

3. Bridge Messages Verification

Lastly, with these verified parachain blocks, using a Parachain light client with the previous Parachain Commitments to verify individual bridge messages.