Awesome
IBC Eureka in Solidity
This is a work-in-progress IBC Eureka implementation in Solidity. IBC Eureka is a simplified version of the IBC protocol that is encoding agnostic. This project also includes an SP1 based tendermint light client for the Ethereum chain, and a POC relayer implementation.
Overview
solidity-ibc-eureka
is an implementation of IBC in Solidity.
Project Structure
This project is structered as a foundry project with the following directories:
contracts/
: Contains the Solidity contracts.test/
: Contains the Solidity tests.scripts/
: Contains the Solidity scripts.abi/
: Contains the ABIs of the contracts needed for end-to-end tests.abigen/
: Contains the abi generated go files for the Solidity contracts.e2e/
: Contains the end-to-end tests, powered by interchaintest.programs/
: Contains the Rust programs for the project.relayer/
: Contains the relayer implementation.operator/
: Contains the operator for the SP1 light client.sp1-programs/
: Contains the SP1 programs for the light client.cw-ics08-wasm-eth/
: Contains the (WIP) CosmWasm 08-wasm light client for Ethereum
packages/
: Contains the Rust packages for the project.
Contracts
Contracts | Description | Status |
---|---|---|
ICS26Router.sol | IBC Eureka router handles sequencing, replay protection, and timeout checks. Passes proofs to ICS02Client.sol for verification, and resolves portId for app callbacks. Provable IBC storage is stored in this contract. | ✅ |
ICS02Client.sol | IBC Eureka light client router resolves clientId for proof verification. It also stores the counterparty information for each client. | ✅ |
ICS20Transfer.sol | IBC Eureka transfer application to send and receive tokens to/from another Eureka transfer implementation. | ✅ |
SP1ICS07Tendermint.sol | The light client contract, and the entry point for SP1 proofs. | ✅ |
ICS27Controller.sol | IBC Eureka interchain accounts controller. | ❌ |
ICS27Host.sol | IBC Eureka interchain accounts host. | ❌ |
SP1 Programs for the Light Client
Programs | Description | Status |
---|---|---|
update-client | Once the initial client state and consensus state are submitted, future consensus states can be added to the client by submitting IBC Headers. These headers contain all necessary information to run the Comet BFT Light Client protocol. Also supports partial misbehavior check. | ✅ |
membership | As consensus states are added to the client, they can be used for proof verification by relayers wishing to prove packet flow messages against a particular height on the counterparty. This uses the verify_membership and verify_non_membership methods on the tendermint client. | ✅ |
uc-and-membership | This is a program that combines update-client and membership to update the client, and prove membership of packet flow messages against the new consensus state. | ✅ |
misbehaviour | In case, the malicious subset of the validators exceeds the trust level of the client; then the client can be deceived into accepting invalid blocks and the connection is no longer secure. The tendermint client has some mitigations in place to prevent this. | ✅ |
Requirements
Foundry typically uses git submodules to manage contract dependencies, but this repository uses Node.js packages (via Bun) because submodules don't scale. You can install the contracts dependencies by running the following command:
bun install
You also need to have the operator and relayer binaries installed on your machine to run some of the end-to-end tests. You can install them by running the following commands:
just install-operator
just install-relayer
[!TIP] Nix users can enter a development shell with all the necessary dependencies by running:
nix develop
Unit Testing
There are multiple unit tests for the solidity contracts located in the test/
directory. The tests are written in Solidity using foundry/forge.
To run all the tests, run the following command:
just test-foundry
The recipe also accepts a testname
argument that will only run the test with the given name. For example:
just test-foundry test_success_sendTransfer
End to End Testing
There are several end-to-end tests in the e2e/interchaintestv8
directory. These tests are written in Go and use the interchaintest
library.
It spins up a local Ethereum and a Tendermint network and runs the tests found in e2e/interchaintestv8/ibc_eureka_test.go
.
Some of the tests use the prover network to generate the proofs, so you need to provide your SP1 network private key to .env
for these tests to pass.
To prepare for running the e2e tests, you need to make sure you have done the following:
- Installed the
sp1-ics07-tendermint
operator binary (see instructions above) - Set up an .env file (see the instructions in the
.env.example
file) - If you have made changes to the contract interfaces or types, you need to update the ABIs by running
just generate-abi
[!NOTE] If you are running on a Mac with an M chip, you will need to do the following:
Set up Rosetta
Enable Rosetta for Docker (in Docker Desktop: Settings -> General -> enable "Use Rosetta for x86_64/amd64 emulation on Apple Silicon")
Pull the foundry image with the following command:
docker pull --platform=linux/amd64 ghcr.io/foundry-rs/foundry:latest
Running the tests
There are three test suites in the e2e/interchaintestv8
directory:
TestWithIbcEurekaTestSuite
: This test suite tests the IBC Eureka contracts via manual relaying (requires the operator to be installed).- To run any of the tests, run the following command:
just test-e2e $TEST_NAME
- To run any of the tests, run the following command:
TestWithRelayerTestSuite
: This test suite tests the IBC Eureka contracts via the relayer (requires the relayer and operator to be installed).- To run any of the tests, run the following command:
just test-e2e-relayer $TEST_NAME
- To run any of the tests, run the following command:
TestWithSP1ICS07TendermintTestSuite
: This test suite tests the SP1 ICS07 Tendermint light client (requires the operator to be installed).- To run any of the tests, run the following command:
just test-e2e-sp1-ics07 $TEST_NAME
- To run any of the tests, run the following command:
Where $TEST_NAME
is the name of the test you want to run, for example:
just test-e2e TestDeploy_Groth16
Linting
Before committing, you should lint your code to ensure it follows the style guide. You can do this by running the following command:
just lint
End to End Benchmarks
The contracts in this repository are benchmarked end-to-end using foundry. The following benchmarks were ran with the underlying sp1-ics07-tendermint. About ~230,000 gas is used for each light client verification (groth16), and this is included in the gas costs below for recvPacket
, timeoutPacket
and ackPacket
. At the time of writing, proof generation takes around 1 minute. More granular and in-depth benchmarks are planned for the future.
Single Packet Benchmarks
The following benchmarks are for a single packet transfer without aggregation.
Contract | Method | Description | Gas (groth16) | Gas (plonk) |
---|---|---|---|---|
ICS26Router.sol | sendPacket | Initiating an IBC transfer with an ERC20 . | ~198,798 | ~198,798 |
ICS26Router.sol | recvPacket | Receiving back an ERC20 token. | ~518,409 | ~602,503 |
ICS26Router.sol | recvPacket | Receiving a new Cosmos token for the first time. (Deploying an ERC20 contract) | ~1,414,262 | ~1,497,617 |
ICS26Router.sol | ackPacket | Acknowledging an ICS20 packet. | ~397,928 | ~428,009 |
ICS26Router.sol | timeoutPacket | Timing out an ICS20 packet | ~449,137 | ~532,397 |
Aggregated Packet Benchmarks
The gas costs are substantially lower when aggregating multiple packets into a single proof, as long as the packets are submitted in the same tx. Since there is no meaningful difference in gas costs between plonk and groth16 in the aggregated case, they are not separated in the table below.
ICS26Router Method | Description | Avg Gas (25 packets) | Avg Gas (50 packets) |
---|---|---|---|
multicall/recvPacket | Receiving back an ERC20 token. | ~194,375 | ~187,866 |
multicall/ackPacket | Acknowledging an ICS20 packet. | ~101,630 | ~95,815 |
Note: These gas benchmarks are with Groth16.
Run ICS-07 Tendermint Light Client End to End
-
Set the environment variables by filling in the
.env
file with the following:cp .env.example .env
You need to fill in the
PRIVATE_KEY
,SP1_PROVER
,TENDERMINT_RPC_URL
, andRPC_URL
. You also need theSP1_PRIVATE_KEY
field if you are using the SP1 prover network. -
Deploy the
SP1ICS07Tendermint
contract:just deploy-sp1-ics07
This will generate the
contracts/script/genesis.json
file which contains the initialization parameters for the contract. And then deploy the contract usingcontracts/script/SP1ICS07Tendermint.s.sol
. If you see the following error, add--legacy
to the command in thejustfile
:Error: Failed to get EIP-1559 fees
-
Your deployed contract address will be printed to the terminal.
== Return == 0: address <CONTRACT_ADDRESS>
This will be used when you run the operator in step 5. So add this to your
.env
file.CONTRACT_ADDRESS=<CONTRACT_ADDRESS>
-
Run the Tendermint operator.
To run the operator, you need to select the prover type for SP1. This is set in the
.env
file with theSP1_PROVER
value (network|local|mock
). If you run the operator with thenetwork
prover, you need to provide your SP1 network private key withSP1_PRIVATE_KEY=0xyourprivatekey
in.env
.RUST_LOG=info cargo run --bin operator --release -- start
Etheruem Light Client
[!CAUTION] ⚠ The Ethereum Light Client is currently under heavy development, and is not ready for use.
This repository contains an Ethereum light client which is implemented as two separate layers:
- A CosmWasm contract that supports the 08-wasm light client interface in
programs/cw-ics08-wasm-eth
- A stateless light client verification package in
packages/ethereum-light-client
License
This project is licensed under MIT.
Acknowledgements
This project was bootstrapped with this template. Implementations of IBC specifications in solidity, CosmWasm, golang, and rust were used as references. We are also grateful to unionlabs for their 08-wasm
ethereum light client implementation for ibc-go which our own implementation is based on.