Skip to content

Ethereum to Ethereum Architecture

Shirikatsu edited this page Feb 4, 2019 · 1 revision

Introduction

As we move towards a universal model of blockchain interoperability, the core design of Ion revolves around continuous execution of functions across turing-complete blockchains. This component of interoperability focuses on the tying-together of two different Ethereum blockchains. This follows the general Ion architecture of:

  • Block storage
  • Block validation
  • Functional smart contracts

This document describes the Ethereum - Ethereum interoperability reference architecture that has advised the specification.

Block storage

For two chains to interact, data about one chain must travel to the second. With Ethereum, we must be able to transmit blocks from one chain to another in order to facilitate general state-based interoperability. Thus our first issues revolve around storing an Ethereum block on the EVM and how such data can be used. We envisage that contract function execution can be triggered conditional to some other contract function execution on another chain and for state to be able to be used across different chains. Since each Ethereum block does not store full world state, we use an event-consumption model instead.

This model focuses on the consumption of an event emitted on one chain by a contract call on another and making use of the event parameters in the subsequent execution. This allows us to perform cross-chain interaction.

To consume an event that was emitted on chain A we must be able to verify the successful execution of the specific transaction on chain B. Proving on chain B that a transaction occurred on chain A requires three separate but linked steps:

  1. Propagation of state from chain A to chain B
  2. Prove that a specific state transition occurred successfully
  3. Execution of a function conditional on this expected event parameters

Proof of state transition

To know how and what data to store about the state of another chain, we must first know what data we need in order to achieve cross-chain interactions. Here we need to be able to make assertions that a given transaction in a block was executed successfully and that the parameters in the event we expected exist.

As noted above, since world state is a product of applied state transitions of executed transactions, we cannot directly use this to construct a state proof. Instead we use merkle patricia trie proofs and tangibly extract a single transaction or receipt by hash and generate a proof that the item exists in its respective trie.

Each block header in Ethereum contains TxTrieRoot and ReceiptTrieRoot. These are the root hashes of the merkle patricia trie of the transactions and receipts in each block respectively. By generating a merkle proof for a transaction in a block and a respective receipt in the block we can assert that a transaction did exist and that an event was emitted by it.

We must construct a proof that can verify against the stored data. A correct proof must assert that:

  1. The specified item exists in the supplied trie (tx in txtrieroot, receipt in receipttrieroot)
  2. The supplied trie hashes to the correct root hash and therefore the the correct block hash

There are therefore multiple pieces of data that must be submitted to prove a state transition:

  • The item being proven (transaction or receipt)
  • An RLP-encoded array of items of the respective trie
  • A merkle patricia trie path to the item
  • The root hash of the trie that the item belongs to
  • The m_blockhash that the trie belongs to (to get the root hash)

There is a multi-stage verification process of the proof. First the transaction is verified in the transaction trie, then the receipt of that transaction is verified in the receipt trie. Once these two have been verified, we must verify that the supplied RLP-encoded tries hashes to the correct root hash.

Transaction Trie Proof

Requires:

  • Transaction to prove
  • RLP-encoded array of transaction trie
  • Path to transaction in trie

Using the RLP-encoded array of transactions, we can reform the transactions by encoding the bytes and subsequently using a merkle patricia trie proof with the path provided, we can verify whether the provided transaction exists in the trie. As long as we have a reference to the expected root hash of the transaction trie, we can verify if the constructed trie hashes to the expected root hash.

Transaction Receipt Trie Proof

Requires:

  • Receipt to prove
  • RLP-encoded array of receipt trie
  • Path to receipt in trie

Using the RLP-encoded array of Transaction receipts, we can reform the receipts by decoding the bytes and subsequently using a merkle patricia trie proof with the path provided, we can verify whether the provided receipt exists in the trie. As long as we have a reference to the expected root hash of the receipt trie, we can verify if the constructed trie hashes to the expected root hash.

Trie Hash Proof

Requires:

  • RLP-encoded array of transaction trie
  • RLP-encoded array of receipt trie

Similarly to the above, we reconstruct our tries using the RLP-encoded versions of them. We then use these tries to produce the root hashes of them respectively and verify via RLP encoding that they are valid constituents of the stored block header.

We define a contract interface for verification of state here.

Although the mechanism above describes a way for a native Ethereum chain to assert a state transition of a foreign Ethereum chain, we must allow a way for any action that requires a proof of a state transition on another chain to define an explicit dependence on the exact state transition expected. This is to prevent the execution of an action via the submission of a state proof of an unrelated state transition on the foreign chain. In essence, a single contract may emit multiple instances of a single event signature invoked by different people and we must facilitate differentiating them.

Event Verification

For any event emitted in a given transaction, we must verify the contains of the event to ensure the relevancy of each emission to the consumer. Once the state transition of the transaction has been proven, the receipt of the proof can be deconstructed to extract any event emitted from it. Each transaction may emit multiple events, however we can use knowledge of a specific event signature to extract the one we are interested in.

Procedure:

  • Extract the relevant event by filtering the receipt object by event signature.
  • Deconstruct the event into it's constituents.
  • Check the event parameters match the expected values

We define a contract interface for event verification here.

Data persistence

Now that we know what data we need for event consumption, we know how we'll store this data. We'll need to be able to submit data as individual blocks however Solidity restricts how complex this can be achieved. We use RLP-encoding to help alleviate this problem by submitting each block a single RLP-encoded byte blob and decode and store this on-chain.

We only require 3 pieces of information to be stored:

  • Block hash
  • TxTrieRoot
  • ReceiptTrieRoot

Once blocks have been validated, they can be submitted for storage here.

Block Validation

To prevent malicious or erroneous submission of blocks, the validation layer must implement a method to verify the outcome of the consensus mechanism of the originating chain. When a block is retrieved from one chain and submitted to another, we must be able to ensure that the block did originate from the purported chain before it is passed on for storage. Once a block is stored it can be used for interoperation, as such the validation also becomes the teller of finality.

Deterministic finality consensus mechanisms can be implemented to pass on blocks as and when they are verified as finality is guaranteed.

Probabilistic finality consensus mechanisms, however, are more arbitrary. The implementation of a validation layer to verify blocks from a probabilistic chain must decide the level of finality, and thus the level of risk associated with transactions that build from state from a probabilistic state transition. For example, one could build an EthHash validation layer that uses block height as a finality coefficient and only passing blocks to storage after n blocks.

This modular validation architecture allows interoperability between any chains without making any finality assumptions. We allow the development of arbitrary validation modules that are appropriate for the user's level of certainty in the finality of the blocks from a given chain.

We have implemented a validation module based on the Clique Proof-of-Authority consensus mechanism here.

Generic Flow

Overview

The following sequence diagrams shows an example flow of the Ion Interoperability Framework where an event is emitted in a transaction on one chain that is then used as the conditional predicate upon which a secondary function is called on another chain in response.

This example is based on the Ion tutorial showing interoperation between Rinkeby testnet and another Ethereum chain using a very simple functional use-case smart contract.

Setup

Below is a sequence diagram illustrating the deployment flow of the Ion stack.

Deployment

Step 1. Deploy Ion to the receiving chain

Deploy the Ion gateway contract to the chain that will be receiving blocks.

Step 2. Deploy validation and storage contracts to the receiving chain

Deploy the relevant validation contract and the storage contract to the receiving chain supplying with the deployed Ion address.

Step 3. Register validation to Ion

Register the validation contract to Ion to grant block submission access.

Step 4. Register Chain B to Chain A

Register the chain to be interoperated with.

Procedure

Generic flow

Step 1. Emit event through tx

Alice makes a function call that emits an event on Chain B. She intends to consume this event to execute another function on Chain A.

Step 2. Submit Block to Validation

Alice generates the encoded block via the CLI and submits it to Chain A. The block is then decoded and validated against the validation of the originating chain and stored once successful.

Step 3. Generate proof of tx

Alice generates a tx proof using the CLI of the transaction containing the event she is interested in.

Step 4. Call verifyAndExecute providing proof of tx in block

Alice uses the proof generated in step 3 and submits it to a function call that checks the proof against the stored block. Once all checks have passed, it executes the contract function.

Home Page

Ion

Design

Ethereum Integration

Ethereum

Implementations

Validation - Clique

Validation - IBFT Soma

Hyperledger Fabric

Hyperledger Fabric Integration

Ethereum

Implementations

Other

FAQ

Contributing

Clone this wiki locally