From 1aaee5d31e7d2afb4b98c51b53e4a330c7110e44 Mon Sep 17 00:00:00 2001 From: Web3 Philosopher Date: Tue, 27 Feb 2024 16:10:33 +0100 Subject: [PATCH] Tesseract v0.2.0 (#57) * some refactorings * Respect module filter * make delivery endpoints optional * use debian bookworm slim * clean up some interfaces * if filter is empty, allow all modules * fix log * accumulate fees for all chains * some more stuff * intialize latest heights for L2s * add execution layer state commitment * verified that accumulate & withdraw works --- .github/workflows/build-test-and-lint.yml | 2 +- .github/workflows/{release.yml => docker.yml} | 0 .gitignore | 4 +- Cargo.lock | 76 +- Cargo.toml | 16 +- {tesseract-any-client => client}/Cargo.toml | 8 +- {tesseract-any-client => client}/src/any.rs | 1 + {tesseract-any-client => client}/src/lib.rs | 2 +- ethereum/bsc-pos/Cargo.toml | 44 -- ethereum/bsc-pos/src/byzantine.rs | 71 -- ethereum/bsc-pos/src/host.rs | 191 ----- ethereum/bsc-pos/src/lib.rs | 134 ---- ethereum/bsc-pos/src/notification.rs | 36 - ethereum/evm/src/arbitrum/client.rs | 163 ----- ethereum/evm/src/arbitrum/host.rs | 74 -- ethereum/evm/src/arbitrum/mod.rs | 4 - ethereum/evm/src/arbitrum/tests.rs | 114 --- ethereum/evm/src/optimism/client.rs | 191 ----- ethereum/evm/src/optimism/host.rs | 74 -- ethereum/evm/src/optimism/mod.rs | 4 - ethereum/evm/src/optimism/tests.rs | 59 -- ethereum/polygon-pos/Cargo.toml | 45 -- ethereum/polygon-pos/src/byzantine.rs | 86 --- ethereum/polygon-pos/src/host.rs | 84 --- ethereum/polygon-pos/src/lib.rs | 122 ---- ethereum/polygon-pos/src/notification.rs | 101 --- ethereum/sync-committee/Cargo.toml | 47 -- ethereum/sync-committee/src/byzantine.rs | 72 -- ethereum/sync-committee/src/host.rs | 286 -------- ethereum/sync-committee/src/lib.rs | 197 ------ ethereum/sync-committee/src/notification.rs | 122 ---- ethereum/sync-committee/src/test.rs | 199 ------ {ethereum/evm => evm/common}/Cargo.toml | 0 .../evm => evm/common}/abis/ArbGasInfo.json | 0 {ethereum/evm => evm/common}/abis/ERC20.json | 0 .../evm => evm/common}/abis/IRollupCore.json | 0 .../common}/abis/L2OutputOracle.json | 0 .../common}/abis/OVM_gasPriceOracle.json | 0 {ethereum/evm => evm/common}/build.rs | 0 {ethereum/evm => evm/common}/src/abi.rs | 0 .../common}/src/abi/arb_gas_info.rs | 0 .../evm => evm/common}/src/abi/erc_20.rs | 0 .../evm => evm/common}/src/abi/i_rollup.rs | 0 .../common}/src/abi/l2_output_oracle.rs | 0 .../common}/src/abi/ovm_gas_price_oracle.rs | 0 {ethereum/evm => evm/common}/src/consts.rs | 0 .../evm => evm/common}/src/gas_oracle.rs | 7 - {ethereum/evm => evm/common}/src/host.rs | 0 {ethereum/evm => evm/common}/src/lib.rs | 0 {ethereum/evm => evm/common}/src/mock.rs | 2 +- {ethereum/evm => evm/common}/src/provider.rs | 0 {ethereum/evm => evm/common}/src/test.rs | 0 {ethereum/evm => evm/common}/src/tx.rs | 0 {payments => fees}/Cargo.toml | 5 +- {payments => fees}/prisma-cli/Cargo.toml | 0 {payments => fees}/prisma-cli/src/main.rs | 0 .../migrations/20240119151018_/migration.sql | 0 .../prisma/migrations/migration_lock.toml | 0 {payments => fees}/prisma/schema.prisma | 0 {payments => fees}/src/db.rs | 2 +- {payments => fees}/src/lib.rs | 59 +- {payments => fees}/src/mock.rs | 0 fees/src/tests.rs | 242 +++++++ messaging/Cargo.toml | 5 +- messaging/src/events.rs | 32 +- messaging/src/lib.rs | 4 +- payments/src/tests.rs | 82 --- primitives/src/config.rs | 2 +- primitives/src/lib.rs | 9 +- relayer/Cargo.toml | 12 +- relayer/src/cli.rs | 13 +- relayer/src/config.rs | 2 +- relayer/src/fees.rs | 210 ++++++ relayer/src/lib.rs | 2 +- relayer/src/main.rs | 2 +- relayer/src/tx_payment.rs | 232 ------ scripts/docker/Dockerfile | 7 - scripts/docker/slim.Dockerfile | 2 +- substrate/common/src/calls.rs | 23 +- substrate/common/src/lib.rs | 24 +- substrate/common/src/runtime.rs | 662 ++++++++++-------- telemetry/src/main.rs | 1 + test-config.toml | 45 +- 83 files changed, 977 insertions(+), 3340 deletions(-) rename .github/workflows/{release.yml => docker.yml} (100%) rename {tesseract-any-client => client}/Cargo.toml (85%) rename {tesseract-any-client => client}/src/any.rs (99%) rename {tesseract-any-client => client}/src/lib.rs (98%) delete mode 100644 ethereum/bsc-pos/Cargo.toml delete mode 100644 ethereum/bsc-pos/src/byzantine.rs delete mode 100644 ethereum/bsc-pos/src/host.rs delete mode 100644 ethereum/bsc-pos/src/lib.rs delete mode 100644 ethereum/bsc-pos/src/notification.rs delete mode 100644 ethereum/evm/src/arbitrum/client.rs delete mode 100644 ethereum/evm/src/arbitrum/host.rs delete mode 100644 ethereum/evm/src/arbitrum/mod.rs delete mode 100644 ethereum/evm/src/arbitrum/tests.rs delete mode 100644 ethereum/evm/src/optimism/client.rs delete mode 100644 ethereum/evm/src/optimism/host.rs delete mode 100644 ethereum/evm/src/optimism/mod.rs delete mode 100644 ethereum/evm/src/optimism/tests.rs delete mode 100644 ethereum/polygon-pos/Cargo.toml delete mode 100644 ethereum/polygon-pos/src/byzantine.rs delete mode 100644 ethereum/polygon-pos/src/host.rs delete mode 100644 ethereum/polygon-pos/src/lib.rs delete mode 100644 ethereum/polygon-pos/src/notification.rs delete mode 100644 ethereum/sync-committee/Cargo.toml delete mode 100644 ethereum/sync-committee/src/byzantine.rs delete mode 100644 ethereum/sync-committee/src/host.rs delete mode 100644 ethereum/sync-committee/src/lib.rs delete mode 100644 ethereum/sync-committee/src/notification.rs delete mode 100644 ethereum/sync-committee/src/test.rs rename {ethereum/evm => evm/common}/Cargo.toml (100%) rename {ethereum/evm => evm/common}/abis/ArbGasInfo.json (100%) rename {ethereum/evm => evm/common}/abis/ERC20.json (100%) rename {ethereum/evm => evm/common}/abis/IRollupCore.json (100%) rename {ethereum/evm => evm/common}/abis/L2OutputOracle.json (100%) rename {ethereum/evm => evm/common}/abis/OVM_gasPriceOracle.json (100%) rename {ethereum/evm => evm/common}/build.rs (100%) rename {ethereum/evm => evm/common}/src/abi.rs (100%) rename {ethereum/evm => evm/common}/src/abi/arb_gas_info.rs (100%) rename {ethereum/evm => evm/common}/src/abi/erc_20.rs (100%) rename {ethereum/evm => evm/common}/src/abi/i_rollup.rs (100%) rename {ethereum/evm => evm/common}/src/abi/l2_output_oracle.rs (100%) rename {ethereum/evm => evm/common}/src/abi/ovm_gas_price_oracle.rs (100%) rename {ethereum/evm => evm/common}/src/consts.rs (100%) rename {ethereum/evm => evm/common}/src/gas_oracle.rs (96%) rename {ethereum/evm => evm/common}/src/host.rs (100%) rename {ethereum/evm => evm/common}/src/lib.rs (100%) rename {ethereum/evm => evm/common}/src/mock.rs (99%) rename {ethereum/evm => evm/common}/src/provider.rs (100%) rename {ethereum/evm => evm/common}/src/test.rs (100%) rename {ethereum/evm => evm/common}/src/tx.rs (100%) rename {payments => fees}/Cargo.toml (92%) rename {payments => fees}/prisma-cli/Cargo.toml (100%) rename {payments => fees}/prisma-cli/src/main.rs (100%) rename {payments => fees}/prisma/migrations/20240119151018_/migration.sql (100%) rename {payments => fees}/prisma/migrations/migration_lock.toml (100%) rename {payments => fees}/prisma/schema.prisma (100%) rename {payments => fees}/src/db.rs (99%) rename {payments => fees}/src/lib.rs (86%) rename {payments => fees}/src/mock.rs (100%) create mode 100644 fees/src/tests.rs delete mode 100644 payments/src/tests.rs create mode 100644 relayer/src/fees.rs delete mode 100644 relayer/src/tx_payment.rs delete mode 100644 scripts/docker/Dockerfile diff --git a/.github/workflows/build-test-and-lint.yml b/.github/workflows/build-test-and-lint.yml index 11d84beed..662bcd254 100644 --- a/.github/workflows/build-test-and-lint.yml +++ b/.github/workflows/build-test-and-lint.yml @@ -63,7 +63,7 @@ jobs: run: cargo test -p tesseract-evm --verbose - name: Run Transaction Payment Tests - run: cargo test -p transaction-payment --verbose + run: cargo test -p transaction-fees --verbose lint: if: github.event.pull_request.draft == false diff --git a/.github/workflows/release.yml b/.github/workflows/docker.yml similarity index 100% rename from .github/workflows/release.yml rename to .github/workflows/docker.yml diff --git a/.gitignore b/.gitignore index e48c9f4b8..cde1a1c39 100644 --- a/.gitignore +++ b/.gitignore @@ -28,7 +28,7 @@ .uuid **/proofs/* -/payments/prisma/dev.db -/payments/prisma/dev.db-journal +/fees/prisma/dev.db +/fees/prisma/dev.db-journal / dev.db \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 3f60970aa..ba390e3ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13628,7 +13628,7 @@ dependencies = [ [[package]] name = "tesseract" -version = "0.1.4" +version = "0.2.0" dependencies = [ "anyhow", "async-trait", @@ -13647,9 +13647,9 @@ dependencies = [ "serde_json", "sp-core 28.0.0", "telemetry-server", - "tesseract-any-client", "tesseract-beefy", "tesseract-bsc-pos", + "tesseract-client", "tesseract-consensus", "tesseract-evm", "tesseract-fisherman", @@ -13661,37 +13661,7 @@ dependencies = [ "tokio", "toml 0.7.8", "tracing-subscriber 0.3.18", - "transaction-payment", -] - -[[package]] -name = "tesseract-any-client" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-trait", - "env_logger", - "ethers", - "futures", - "hex", - "ismp", - "ismp-sync-committee", - "log", - "pallet-ismp", - "parity-scale-codec", - "serde", - "serde_json", - "sp-core 28.0.0", - "tesseract-beefy", - "tesseract-bsc-pos", - "tesseract-evm", - "tesseract-parachain", - "tesseract-primitives", - "tesseract-substrate", - "tesseract-sync-committee", - "tokio", - "toml 0.7.8", - "tracing-subscriber 0.3.18", + "transaction-fees", ] [[package]] @@ -13759,6 +13729,36 @@ dependencies = [ "tokio-stream", ] +[[package]] +name = "tesseract-client" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "env_logger", + "ethers", + "futures", + "hex", + "ismp", + "ismp-sync-committee", + "log", + "pallet-ismp", + "parity-scale-codec", + "serde", + "serde_json", + "sp-core 28.0.0", + "tesseract-beefy", + "tesseract-bsc-pos", + "tesseract-evm", + "tesseract-parachain", + "tesseract-primitives", + "tesseract-substrate", + "tesseract-sync-committee", + "tokio", + "toml 0.7.8", + "tracing-subscriber 0.3.18", +] + [[package]] name = "tesseract-consensus" version = "0.1.0" @@ -13866,7 +13866,7 @@ dependencies = [ "tesseract-substrate", "tesseract-sync-committee", "tokio", - "transaction-payment", + "transaction-fees", ] [[package]] @@ -13875,16 +13875,17 @@ version = "0.1.0" dependencies = [ "anyhow", "futures", + "hex", "ismp", "ismp-solidity-abi", "log", "pallet-ismp", "percentage", "sp-core 21.0.0", - "tesseract-any-client", + "tesseract-client", "tesseract-primitives", "tokio", - "transaction-payment", + "transaction-fees", ] [[package]] @@ -14598,7 +14599,7 @@ dependencies = [ ] [[package]] -name = "transaction-payment" +name = "transaction-fees" version = "0.1.0" dependencies = [ "anyhow", @@ -14607,6 +14608,7 @@ dependencies = [ "futures", "hex", "ismp", + "itertools 0.12.1", "pallet-ismp", "pallet-relayer-fees", "parity-scale-codec", diff --git a/Cargo.toml b/Cargo.toml index 0f9d8e896..8e1d3905b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,22 +12,20 @@ members = [ "relayer", "integration-tests", - "ethereum/sync-committee", - "ethereum/evm", - "ethereum/bsc-pos", - "payments", - "payments/prisma-cli", + "evm/sync-committee", + "evm/common", + "evm/bsc-pos", + "fees", + "fees/prisma-cli", "fisherman", "telemetry", - "tesseract-any-client" + "client" ] [profile.release] panic = "unwind" opt-level = 3 - - [workspace.dependencies] # wasm frame-benchmarking = { version = "28.0.0" } @@ -89,7 +87,7 @@ pallet-mmr = { version = "27.0.0" } # client frame-benchmarking-cli = "32.0.0" -pallet-transaction-payment-rpc = "30.0.0" +pallet-transaction-fees-rpc = "30.0.0" sc-basic-authorship = "0.34.0" sc-chain-spec = "27.0.0" sc-cli = "0.36.0" diff --git a/tesseract-any-client/Cargo.toml b/client/Cargo.toml similarity index 85% rename from tesseract-any-client/Cargo.toml rename to client/Cargo.toml index e7bd8de2d..abe11390e 100644 --- a/tesseract-any-client/Cargo.toml +++ b/client/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "tesseract-any-client" +name = "tesseract-client" version = "0.1.0" edition = "2021" @@ -9,10 +9,10 @@ edition = "2021" parachain = { package = "tesseract-parachain", path = "../substrate/parachain" } tesseract-substrate = { path = "../substrate/common" } primitives = { package = "tesseract-primitives", path = "../primitives" } -tesseract-evm = { path = "../ethereum/evm" } +tesseract-evm = { path = "../evm/common" } #tesseract-polygon-pos = { path = "../ethereum/polygon-pos" } -tesseract-bsc-pos = { path = "../ethereum/bsc-pos" } -tesseract-sync-committee = { path = "../ethereum/sync-committee" } +tesseract-bsc-pos = { path = "../evm/bsc-pos" } +tesseract-sync-committee = { path = "../evm/sync-committee" } tesseract-beefy = { path = "../substrate/beefy/naive" } # polytope labs diff --git a/tesseract-any-client/src/any.rs b/client/src/any.rs similarity index 99% rename from tesseract-any-client/src/any.rs rename to client/src/any.rs index b2228c15d..647db3bc5 100644 --- a/tesseract-any-client/src/any.rs +++ b/client/src/any.rs @@ -12,6 +12,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + /// This will wrap the provided implementations into an [`AnyClient`] that implement the required /// traits [`IsmpHost`], [`IsmpProvider`] & [`ByzantineHandler`]. #[macro_export] diff --git a/tesseract-any-client/src/lib.rs b/client/src/lib.rs similarity index 98% rename from tesseract-any-client/src/lib.rs rename to client/src/lib.rs index 84c7949af..fd23fa1f2 100644 --- a/tesseract-any-client/src/lib.rs +++ b/client/src/lib.rs @@ -40,7 +40,7 @@ impl AnyConfig { pub async fn into_client(self) -> Result { let client = match self { AnyConfig::KeccakParachain(config) | AnyConfig::Parachain(config) => { - match config.hashing { + match config.hashing.clone().unwrap_or(HashAlgorithm::Keccak) { HashAlgorithm::Keccak => { let host = ParachainHost::default(); AnyClient::KeccakParachain(Parachain::new(Some(host), config).await?) diff --git a/ethereum/bsc-pos/Cargo.toml b/ethereum/bsc-pos/Cargo.toml deleted file mode 100644 index fe65b2b19..000000000 --- a/ethereum/bsc-pos/Cargo.toml +++ /dev/null @@ -1,44 +0,0 @@ -[package] -name = "tesseract-bsc-pos" -version = "0.1.0" -edition = "2021" -description = "Bsc consensus host for tesseract" -authors = ["Polytope Labs "] - -[dependencies] -bsc-pos-prover.workspace = true -bsc-pos-verifier.workspace = true -ismp-bsc-pos.workspace = true -geth-primitives.workspace = true -ismp.workspace = true - -serde = { version = "1.0.164", features = ["derive"] } -serde_json = "1.0.105" -hex = "0.4.3" -log = "0.4.19" -anyhow = "1.0.75" -codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } -futures = "0.3.28" -async-trait = "0.1.71" -tokio = { version = "1.32.0", features = ["full"] } -tokio-stream = "0.1.14" -hex-literal = "0.4.1" -base2 = "0.3.1" -primitive-types = { version = "0.12.1", features = ["impl-codec"] } -ethers = { workspace = true, features = ["rustls"] } -sp-core = "22.0.0" - - -tesseract-primitives = { path = "../../primitives" } -tesseract-evm = { path = "../evm"} -reconnecting-jsonrpsee-ws-client.workspace = true -jsonrpsee = { version = "0.21", features = ["ws-client"]} -debounced = { git = "https://github.com/polytope-labs/debounced", branch = "main"} - -[dev-dependencies] -tesseract-evm = { path = "../evm", features = ["testing"] } -sp-core = "22.0.0" -codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } -dotenv = "0.15.0" -anyhow = "1.0.75" - diff --git a/ethereum/bsc-pos/src/byzantine.rs b/ethereum/bsc-pos/src/byzantine.rs deleted file mode 100644 index ca3ad53af..000000000 --- a/ethereum/bsc-pos/src/byzantine.rs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use codec::{Decode, Encode}; -use geth_primitives::CodecHeader; -use ismp::{ - consensus::{StateMachineHeight, StateMachineId}, - events::StateMachineUpdated, - messaging::ConsensusMessage, -}; -use tesseract_primitives::{ByzantineHandler, IsmpHost, IsmpProvider}; - -use crate::BscPosHost; - -#[async_trait::async_trait] -impl ByzantineHandler for BscPosHost { - async fn query_consensus_message( - &self, - event: StateMachineUpdated, - ) -> Result { - let header = self.prover.fetch_header(event.latest_height).await?.ok_or_else(|| { - anyhow::anyhow!("Header not found: Could not query consensus message") - })?; - let message = ConsensusMessage { - consensus_proof: header.encode(), - consensus_state_id: self.consensus_state_id, - }; - - Ok(message) - } - - async fn check_for_byzantine_attack( - &self, - counterparty: &C, - consensus_message: ConsensusMessage, - ) -> Result<(), anyhow::Error> { - let source_header = CodecHeader::decode(&mut &*consensus_message.consensus_proof)?; - let finalized_state_root = source_header.state_root; - let height = StateMachineHeight { - id: StateMachineId { - state_id: self.state_machine, - consensus_state_id: self.consensus_state_id, - }, - height: source_header.number.low_u64(), - }; - let state_machine_commitment = counterparty.query_state_machine_commitment(height).await?; - if finalized_state_root != state_machine_commitment.state_root { - // Submit message - log::info!( - "Freezing {:?} on {:?}", - self.state_machine, - counterparty.state_machine_id().state_id - ); - counterparty.freeze_state_machine(height.id).await?; - } - - Ok(()) - } -} diff --git a/ethereum/bsc-pos/src/host.rs b/ethereum/bsc-pos/src/host.rs deleted file mode 100644 index 9ece40abc..000000000 --- a/ethereum/bsc-pos/src/host.rs +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use anyhow::{anyhow, Error}; -use bsc_pos_verifier::primitives::{compute_epoch, EPOCH_LENGTH}; -use codec::{Decode, Encode}; -use futures::stream; -use ismp::messaging::{ConsensusMessage, CreateConsensusState}; - -use ethers::providers::Middleware; -use ismp_bsc_pos::ConsensusState; -use std::time::Duration; - -use crate::{notification::consensus_notification, BscPosHost, KeccakHasher}; -use tesseract_primitives::{BoxStream, IsmpHost, IsmpProvider}; - -#[async_trait::async_trait] -impl IsmpHost for BscPosHost { - async fn consensus_notification( - &self, - counterparty: C, - ) -> Result, anyhow::Error> - where - C: IsmpHost + IsmpProvider + 'static, - { - let client = BscPosHost::clone(&self); - - let interval = tokio::time::interval(Duration::from_secs( - self.host.consensus_update_frequency.unwrap_or(300), - )); - - let stream = stream::unfold(interval, move |mut interval| { - let client = client.clone(); - let counterparty = counterparty.clone(); - async move { - let consensus_state = if let Ok(consensus_state) = - counterparty.query_consensus_state(None, client.consensus_state_id).await - { - consensus_state - } else { - return Some(( - Err(anyhow!( - "Not a fatal error: Error fetching consensus state for {:?} on {:?}", - client.state_machine, - counterparty.state_machine_id().state_id - )), - interval, - )) - }; - let consensus_state = ConsensusState::decode(&mut &*consensus_state) - .expect("Consensus state should always decode correctly"); - let current_epoch = compute_epoch(consensus_state.finalized_height); - let attested_header = if let Ok(header) = client.prover.latest_header().await { - header - } else { - return Some(( - Err(anyhow!( - "Not a fatal error: Error fetching latest header for {:?}", - client.state_machine - )), - interval, - )) - }; - - let attested_epoch = compute_epoch(attested_header.number.low_u64()); - // Try to sync the client first - if attested_epoch > current_epoch { - log::info!( - "Syncing {:?} on {:?}", - client.state_machine, - counterparty.state_machine_id().state_id - ); - let next_epoch = current_epoch + 1; - - let epoch_block_number = next_epoch * EPOCH_LENGTH; - // Find a block that finalizes the epoch change block, Validators are rotated 12 - // blocks after the epoch boundary - let mut block = epoch_block_number + 2; - while block <= epoch_block_number + 11 { - let header = if let Ok(header) = client.prover.fetch_header(block).await { - header - } else { - return Some(( - Err(anyhow!( - "Not a fatal error: Error fetching {:?} header for {block}", - client.state_machine - )), - interval, - )) - }; - let header = if let Some(header) = header { - header - } else { - // If header does not exist yet wait before continuing - tokio::time::sleep(Duration::from_secs(6)).await; - continue - }; - - match client.prover.fetch_bsc_update::(header).await { - Ok(Some(update)) => { - if update.source_header.number.low_u64() <= - consensus_state.finalized_height - { - block += 1; - continue - } - - if !update.epoch_header_ancestry.is_empty() { - return Some(( - Ok(ConsensusMessage { - consensus_proof: update.encode(), - consensus_state_id: client.consensus_state_id, - }), - interval, - )) - } - - block += 1; - }, - Ok(None) => { - block += 1; - continue - }, - Err(_) => - return Some(( - Err(anyhow!( - "Not a fatal error: Error fetching sync update for {:?}", - client.state_machine - )), - interval, - )), - } - } - } - - interval.tick().await; - - let lambda = || async { - let block_number = client.prover.client.get_block_number().await?; - let block = - client.prover.client.get_block(block_number).await?.ok_or_else(|| { - anyhow!( - "Block with number {block_number} not found for {:?}", - client.state_machine - ) - })?; - - let result = consensus_notification(&client, counterparty, block) - .await? - .map(|update| ConsensusMessage { - consensus_proof: update.encode(), - consensus_state_id: client.consensus_state_id, - }) - .ok_or_else(|| anyhow!("Failed to fetch consensus proof"))?; - - Ok(result) - }; - - let result = lambda().await; - - Some((result, interval)) - } - }); - - Ok(Box::pin(stream)) - } - - async fn query_initial_consensus_state(&self) -> Result, Error> { - let initial_consensus_state = - self.get_consensus_state::(self.evm.ismp_host).await?; - Ok(Some(CreateConsensusState { - consensus_state: initial_consensus_state.encode(), - consensus_client_id: ismp_bsc_pos::BSC_CONSENSUS_ID, - consensus_state_id: self.consensus_state_id, - unbonding_period: 60 * 60 * 60 * 27, - challenge_period: 5 * 60, - state_machine_commitments: vec![], - })) - } -} diff --git a/ethereum/bsc-pos/src/lib.rs b/ethereum/bsc-pos/src/lib.rs deleted file mode 100644 index 14796135b..000000000 --- a/ethereum/bsc-pos/src/lib.rs +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use bsc_pos_prover::BscPosProver; -use bsc_pos_verifier::primitives::compute_epoch; -pub use bsc_pos_verifier::verify_bsc_header; -use ethers::providers::{Http, Provider}; -pub use geth_primitives::Header; -use ismp::{consensus::ConsensusStateId, host::StateMachine, util::Keccak256}; -pub use ismp_bsc_pos::ConsensusState; -use primitive_types::H160; - -use serde::{Deserialize, Serialize}; -use std::time::Duration; -use tesseract_evm::{EvmClient, EvmConfig}; - -mod byzantine; -mod host; -mod notification; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct BscPosConfig { - /// Host configuration options - pub host: Option, - /// General ethereum config - #[serde[flatten]] - pub evm_config: EvmConfig, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct HostConfig { - pub consensus_update_frequency: Option, -} - -impl BscPosConfig { - /// Convert the config into a client. - pub async fn into_client(self) -> anyhow::Result> { - let host = if let Some(ref host) = self.host { - Some(BscPosHost::new(host, &self.evm_config).await?) - } else { - None - }; - let client = EvmClient::new(host, self.evm_config).await?; - - Ok(client) - } - - pub fn state_machine(&self) -> StateMachine { - self.evm_config.state_machine - } -} - -#[derive(Clone)] -pub struct BscPosHost { - /// Consensus state id on counterparty chain - pub consensus_state_id: ConsensusStateId, - /// State machine Identifier for this chain. - pub state_machine: StateMachine, - /// Consensus prover - pub prover: BscPosProver, - /// Host config options - pub host: HostConfig, - /// Evm config options - pub evm: EvmConfig, -} - -impl BscPosHost { - pub async fn new(host: &HostConfig, evm: &EvmConfig) -> Result { - let provider = Provider::::try_from(evm.rpc_url.clone())?; - let prover = BscPosProver::new(provider); - - Ok(Self { - consensus_state_id: { - let mut consensus_state_id: ConsensusStateId = Default::default(); - consensus_state_id.copy_from_slice(evm.consensus_state_id.as_bytes()); - consensus_state_id - }, - state_machine: evm.state_machine, - prover, - host: host.clone(), - evm: evm.clone(), - }) - } - - pub async fn get_consensus_state( - &self, - ismp_contract_address: H160, - ) -> Result { - let (header, current_validators) = - self.prover.fetch_finalized_state::().await?; - let latest_header = self.prover.latest_header().await?; - if latest_header.number.low_u64() - header.number.low_u64() < 12 { - // We want to ensure the current validators are signing before creating the consensus - // state - tokio::time::sleep(Duration::from_secs( - (latest_header.number.low_u64() - header.number.low_u64()) * 12, - )) - .await; - } - let consensus_state = ConsensusState { - current_validators, - next_validators: None, - finalized_hash: Header::from(&header).hash::(), - finalized_height: header.number.as_u64(), - ismp_contract_address, - current_epoch: compute_epoch(header.number.low_u64()), - }; - - Ok(consensus_state) - } -} - -pub struct KeccakHasher; - -impl Keccak256 for KeccakHasher { - fn keccak256(bytes: &[u8]) -> primitive_types::H256 - where - Self: Sized, - { - sp_core::keccak_256(bytes).into() - } -} diff --git a/ethereum/bsc-pos/src/notification.rs b/ethereum/bsc-pos/src/notification.rs deleted file mode 100644 index 023eacd24..000000000 --- a/ethereum/bsc-pos/src/notification.rs +++ /dev/null @@ -1,36 +0,0 @@ -use codec::Decode; -use ismp_bsc_pos::ConsensusState; - -use bsc_pos_verifier::primitives::{compute_epoch, BscClientUpdate}; -use ethers::types::Block; -use primitive_types::H256; - -use tesseract_primitives::{IsmpHost, IsmpProvider}; - -use crate::{BscPosHost, KeccakHasher}; - -pub async fn consensus_notification( - client: &BscPosHost, - counterparty: C, - _block: Block, -) -> Result, anyhow::Error> -where - C: IsmpHost + IsmpProvider + 'static, -{ - let consensus_state = - counterparty.query_consensus_state(None, client.consensus_state_id).await?; - let consensus_state = ConsensusState::decode(&mut &*consensus_state)?; - let current_epoch = compute_epoch(consensus_state.finalized_height); - let attested_header = client.prover.latest_header().await?; - - let attested_epoch = compute_epoch(attested_header.number.low_u64()); - - if attested_epoch < current_epoch || - consensus_state.finalized_height >= attested_header.number.low_u64() - { - return Ok(None); - } - - let bsc_client_update = client.prover.fetch_bsc_update::(attested_header).await?; - return Ok(bsc_client_update); -} diff --git a/ethereum/evm/src/arbitrum/client.rs b/ethereum/evm/src/arbitrum/client.rs deleted file mode 100644 index 64ed65bc6..000000000 --- a/ethereum/evm/src/arbitrum/client.rs +++ /dev/null @@ -1,163 +0,0 @@ -use crate::{abi::i_rollup::*, derive_map_key, EvmClient, EvmConfig}; -use anyhow::anyhow; -use ethabi::ethereum_types::U256; -use ethers::{ - prelude::Provider, - providers::{Http, Middleware}, - types::{H160, H256}, -}; -use geth_primitives::CodecHeader; -use ismp::{consensus::ConsensusStateId, host::StateMachine}; -use ismp_sync_committee::{ - arbitrum::{ArbitrumPayloadProof, GlobalState as RustGlobalState}, - presets::NODES_SLOT, -}; -use serde::{Deserialize, Serialize}; -use std::sync::Arc; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ArbConfig { - pub host: Option, - - /// General evm config - #[serde[flatten]] - pub evm_config: EvmConfig, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct HostConfig { - /// RPC url for beacon execution client - pub beacon_rpc_url: String, - /// RollupCore contract address on L1 - pub rollup_core: H160, -} - -impl ArbConfig { - /// Convert the config into a client. - pub async fn into_client(self) -> anyhow::Result> { - let host = if let Some(ref config) = self.host { - Some(ArbHost::new(config, &self.evm_config).await?) - } else { - None - }; - let client = EvmClient::new(host, self.evm_config).await?; - - Ok(client) - } - - pub fn state_machine(&self) -> StateMachine { - self.evm_config.state_machine - } -} - -#[derive(Clone)] -pub struct ArbHost { - /// Arbitrum execution client - pub(crate) arb_execution_client: Arc>, - /// Beacon execution client - pub(crate) beacon_execution_client: Arc>, - /// Rollup core contract address - pub(crate) rollup_core: H160, - /// Host config - pub host: HostConfig, - /// Evm Config - pub evm: EvmConfig, - /// Consensus State Id - pub consensus_state_id: ConsensusStateId, -} - -impl ArbHost { - pub async fn new(host: &HostConfig, evm: &EvmConfig) -> Result { - let provider = Provider::::try_from(&evm.rpc_url)?; - let beacon_client = Provider::::try_from(&host.beacon_rpc_url)?; - - Ok(Self { - arb_execution_client: Arc::new(provider), - beacon_execution_client: Arc::new(beacon_client), - rollup_core: host.rollup_core, - host: host.clone(), - evm: evm.clone(), - consensus_state_id: { - let mut consensus_state_id: ConsensusStateId = Default::default(); - consensus_state_id.copy_from_slice(evm.consensus_state_id.as_bytes()); - consensus_state_id - }, - }) - } - - async fn fetch_header(&self, block: H256) -> Result { - let block = self - .arb_execution_client - .get_block(block) - .await? - .ok_or_else(|| anyhow!("Header not found for {:?}", block))?; - let arb_header = block.into(); - - Ok(arb_header) - } - - pub async fn latest_event( - &self, - from: u64, - to: u64, - ) -> Result, anyhow::Error> { - let client = Arc::new(self.beacon_execution_client.clone()); - let contract = IRollup::new(self.rollup_core, client); - let mut events = contract - .event::() - .address(self.rollup_core.into()) - .from_block(from) - .to_block(to) - .query() - .await? - .into_iter() - .collect::>(); - - events.sort_unstable_by(|a, b| a.node_num.cmp(&b.node_num)); - - Ok(events.last().cloned()) - } - - pub async fn fetch_arbitrum_payload( - &self, - at: u64, - event: NodeCreatedFilter, - ) -> Result { - let mut node_num = [0u8; 32]; - U256::from(event.node_num).to_big_endian(&mut node_num); - let state_hash_key = derive_map_key(node_num.to_vec(), NODES_SLOT as u64); - let proof = self - .beacon_execution_client - .get_proof(self.rollup_core, vec![state_hash_key], Some(at.into())) - .await?; - let arb_block_hash = event.assertion.after_state.global_state.bytes_32_vals[0].into(); - let arbitrum_header = self.fetch_header(arb_block_hash).await?; - let payload = ArbitrumPayloadProof { - arbitrum_header, - global_state: RustGlobalState { - block_hash: arb_block_hash, - send_root: event.assertion.after_state.global_state.bytes_32_vals[1].into(), - inbox_position: event.assertion.after_state.global_state.u_64_vals[0], - position_in_message: event.assertion.after_state.global_state.u_64_vals[1], - }, - machine_status: { - let status = event.assertion.after_state.machine_status; - status.try_into().map_err(|e| anyhow!("{:?}", e))? - }, - inbox_max_count: event.inbox_max_count, - node_number: event.node_num, - storage_proof: proof - .storage_proof - .get(0) - .cloned() - .ok_or_else(|| anyhow!("Storage proof not found for arbitrum state_hash"))? - .proof - .into_iter() - .map(|node| node.0.into()) - .collect(), - contract_proof: proof.account_proof.into_iter().map(|node| node.0.into()).collect(), - }; - - Ok(payload) - } -} diff --git a/ethereum/evm/src/arbitrum/host.rs b/ethereum/evm/src/arbitrum/host.rs deleted file mode 100644 index 07f013caa..000000000 --- a/ethereum/evm/src/arbitrum/host.rs +++ /dev/null @@ -1,74 +0,0 @@ -use crate::arbitrum::client::ArbHost; -use anyhow::{anyhow, Error}; -use codec::{Decode, Encode}; -use ethers::prelude::Middleware; -use futures::stream; -use geth_primitives::CodecHeader; -use ismp::{ - consensus::{StateMachineHeight, StateMachineId}, - events::StateMachineUpdated, - messaging::{ConsensusMessage, CreateConsensusState}, -}; -use tesseract_primitives::{BoxStream, ByzantineHandler, IsmpHost, IsmpProvider}; - -#[async_trait::async_trait] -impl ByzantineHandler for ArbHost { - async fn query_consensus_message( - &self, - event: StateMachineUpdated, - ) -> Result { - let header: CodecHeader = self - .arb_execution_client - .get_block(event.latest_height) - .await? - .ok_or_else(|| anyhow!("Header should be available"))? - .into(); - Ok(ConsensusMessage { - consensus_proof: header.encode(), - consensus_state_id: self.consensus_state_id, - }) - } - - async fn check_for_byzantine_attack( - &self, - counterparty: &C, - consensus_message: ConsensusMessage, - ) -> Result<(), anyhow::Error> { - let header = CodecHeader::decode(&mut &*consensus_message.consensus_proof)?; - let height = StateMachineHeight { - id: StateMachineId { - state_id: self.evm.state_machine, - consensus_state_id: self.consensus_state_id, - }, - height: header.number.low_u64(), - }; - let state_machine_commitment = counterparty.query_state_machine_commitment(height).await?; - if state_machine_commitment.state_root != header.state_root { - // Submit Freeze message - log::info!( - "Freezing {:?} on {:?}", - self.evm.state_machine, - counterparty.state_machine_id().state_id - ); - counterparty.freeze_state_machine(height.id).await?; - } - Ok(()) - } -} - -#[async_trait::async_trait] -impl IsmpHost for ArbHost { - async fn consensus_notification( - &self, - _counterparty: I, - ) -> Result, anyhow::Error> - where - I: IsmpHost + IsmpProvider + Clone + 'static, - { - Ok(Box::pin(stream::pending())) - } - - async fn query_initial_consensus_state(&self) -> Result, Error> { - Ok(None) - } -} diff --git a/ethereum/evm/src/arbitrum/mod.rs b/ethereum/evm/src/arbitrum/mod.rs deleted file mode 100644 index 39e0f78f1..000000000 --- a/ethereum/evm/src/arbitrum/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod client; -pub mod host; -#[cfg(test)] -mod tests; diff --git a/ethereum/evm/src/arbitrum/tests.rs b/ethereum/evm/src/arbitrum/tests.rs deleted file mode 100644 index 7c47aa6d7..000000000 --- a/ethereum/evm/src/arbitrum/tests.rs +++ /dev/null @@ -1,114 +0,0 @@ -use crate::EvmConfig; -use alloy_primitives::{Address, Bytes, U256}; -use alloy_rlp::Decodable; -use geth_primitives::Header; -use ismp_sync_committee::arbitrum::verify_arbitrum_payload; - -use crate::{ - arbitrum::client::{ArbConfig, ArbHost, HostConfig}, - mock::Host, -}; -use alloy_rlp_derive::{RlpDecodable, RlpEncodable}; -use ethabi::ethereum_types::{H160, H256}; -use ethers::{providers::Middleware, utils::keccak256}; -use hex_literal::hex; - -const ROLLUP_CORE: [u8; 20] = hex!("45e5cAea8768F42B385A366D3551Ad1e0cbFAb17"); - -#[tokio::test] -#[ignore] -async fn test_payload_proof_verification() { - dotenv::dotenv().ok(); - let arb_url = std::env::var("ARB_URL").expect("ARB_URL must be set."); - let geth_url = std::env::var("GETH_URL").expect("GETH_URL must be set."); - let host = HostConfig { beacon_rpc_url: geth_url, rollup_core: H160::from_slice(&ROLLUP_CORE) }; - let config = ArbConfig { - host: Some(host.clone()), - evm_config: EvmConfig { rpc_url: arb_url, ..Default::default() }, - }; - - let arb_client = ArbHost::new(&host, &config.evm_config).await.expect("Host creation failed"); - - let event = arb_client - .latest_event(9779597, 9779597) - .await - .expect("Failed to fetch latest event") - .expect("There should be an event"); - - let payload_proof = arb_client - .fetch_arbitrum_payload(9779597, event) - .await - .expect("Error fetching payload proof"); - - let l1_header = arb_client - .beacon_execution_client - .get_block(9779597) - .await - .unwrap() - .expect("Block should exist"); - - let state_root = l1_header.state_root; - - let _ = verify_arbitrum_payload::( - payload_proof, - state_root.as_bytes(), - arb_client.rollup_core, - Default::default(), - ) - .expect("Payload proof verification should succeed"); -} - -#[derive(RlpDecodable, RlpEncodable, Debug, Clone)] -pub struct Block { - header: Header, - transactions: Vec, - uncles: Vec
, -} - -// // LegacyTx is the transaction data of regular Ethereum transactions. -// type LegacyTx struct { -// Nonce uint64 // nonce of sender account -// GasPrice *big.Int // wei per gas -// Gas uint64 // gas limit -// To *common.Address `rlp:"nil"` // nil means contract creation -// Value *big.Int // wei amount -// Data []byte // contract invocation input data -// V, R, S *big.Int // signature values -// } - -#[derive(RlpDecodable, RlpEncodable, Debug, Clone)] -pub struct Transaction { - pub nonce: u64, - pub gas_price: U256, - /// Gas amount - pub gas: u64, - /// Recipient (None when contract creation) - pub to: Address, - /// Transferred value - pub value: U256, - - /// Input data - pub data: Bytes, - /// ECDSA recovery id - pub v: U256, - - /// ECDSA signature r - pub r: U256, - - /// ECDSA signature s - pub s: U256, -} - -#[test] -fn test_block_decoding() { - let bytes = hex::decode("f90285f90219a0f052d217bd5275a5177a3c3b7debdfe2670f1c8394b2965ccd5c1883cc1a524da01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0bac6177a79e910c98d86ec31a09ae37ac2de15b754fd7bed1ba52362c49416bfa0498785da562aa0c5dd5937cf15f22139b0b1bcf3b4fc48986e1bb1dae9292796a0c7778a7376099ee2e5c455791c1885b5c361b95713fddcbe32d97fd01334d296b90100000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000400000000000000000000000000000000000000000000000000000008302000001832fefba82560b845754130ea00102030405060708091011121314151617181920212223242526272829303132a0a4db124f7da8798b0467e96875d72bb656b0efb293af8bfc1f879468162973ae88edb3a329535a66a0f866f864800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d8785012a05f200801ca0ee0b9ec878fbd4258a9473199d8ecc32996a20c323c004e79e0cda20e0418ce3a04e6bc63927d1510bab54f37e46fa036faf4b2c465d271920d9afea1fadf7bd21c0").unwrap(); - let block: Block = Decodable::decode(&mut &*bytes).unwrap(); - - let encoding = alloy_rlp::encode(block.clone()).to_vec(); - - let header_encoding = alloy_rlp::encode(block.header.clone()).to_vec(); - let hash: H256 = keccak256(&header_encoding).into(); - - assert_eq!(hash.0, hex!("590075f673be110bb0c0320ca89dea14877d5403dabbb54e45509449cbad0b14")); - assert_eq!(hex::encode(bytes), hex::encode(encoding)); -} diff --git a/ethereum/evm/src/optimism/client.rs b/ethereum/evm/src/optimism/client.rs deleted file mode 100644 index bb0252604..000000000 --- a/ethereum/evm/src/optimism/client.rs +++ /dev/null @@ -1,191 +0,0 @@ -use crate::{EvmClient, EvmConfig}; -use anyhow::anyhow; -use ethabi::ethereum_types::{H256, U256}; -use ethers::{ - prelude::Provider, - providers::{Http, Middleware}, - types::H160, -}; -use ismp::{consensus::ConsensusStateId, host::StateMachine}; -use ismp_sync_committee::{optimism::OptimismPayloadProof, presets::L2_OUTPUTS_SLOT}; -use serde::{Deserialize, Serialize}; -use sp_core::keccak_256; -use std::sync::Arc; - -use crate::abi::l2_output_oracle::*; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct OpConfig { - /// Host config - pub host: Option, - /// General Evm client config - #[serde[flatten]] - pub evm_config: EvmConfig, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct HostConfig { - /// WS url for the beacon execution client - pub beacon_rpc_url: String, - /// L2Oracle contract address on L1 - pub l2_oracle: H160, - /// Withdrawals Message Passer contract address on L2 - pub message_parser: H160, -} - -impl OpConfig { - /// Convert the config into a client. - pub async fn into_client(self) -> anyhow::Result> { - let host = if let Some(ref config) = self.host { - Some(OpHost::new(config, &self.evm_config).await?) - } else { - None - }; - let client = EvmClient::new(host, self.evm_config).await?; - - Ok(client) - } - - pub fn state_machine(&self) -> StateMachine { - self.evm_config.state_machine - } -} - -#[derive(Clone)] -pub struct OpHost { - /// Optimism stack execution client - pub(crate) op_execution_client: Arc>, - /// Beacon execution client - pub(crate) beacon_execution_client: Arc>, - /// L2Oracle contract address on L1 - pub(crate) l2_oracle: H160, - /// Withdrawals Message Passer contract address on L2 - pub(crate) message_parser: H160, - /// Host config - pub host: HostConfig, - /// Evm Config - pub evm: EvmConfig, - /// Consensus state id - pub consensus_state_id: ConsensusStateId, -} - -pub fn derive_array_item_key(index_in_array: u64, offset: u64) -> H256 { - let mut bytes = [0u8; 32]; - U256::from(L2_OUTPUTS_SLOT as u64).to_big_endian(&mut bytes); - - let hash_result = keccak_256(&bytes); - - let array_pos = U256::from_big_endian(&hash_result); - let item_pos = array_pos + U256::from(index_in_array * 2) + U256::from(offset); - - let mut pos = [0u8; 32]; - item_pos.to_big_endian(&mut pos); - - pos.into() -} - -impl OpHost { - pub async fn new(host: &HostConfig, evm: &EvmConfig) -> Result { - let provider = Provider::::try_from(&evm.rpc_url)?; - let beacon_client = Provider::::try_from(&host.beacon_rpc_url)?; - Ok(Self { - op_execution_client: Arc::new(provider), - beacon_execution_client: Arc::new(beacon_client), - l2_oracle: host.l2_oracle, - message_parser: host.message_parser, - evm: evm.clone(), - host: host.clone(), - consensus_state_id: { - let mut consensus_state_id: ConsensusStateId = Default::default(); - consensus_state_id.copy_from_slice(evm.consensus_state_id.as_bytes()); - consensus_state_id - }, - }) - } - - pub async fn latest_event( - &self, - from: u64, - to: u64, - ) -> Result, anyhow::Error> { - let client = Arc::new(self.beacon_execution_client.clone()); - let contract = L2OutputOracle::new(self.l2_oracle, client); - let mut events = contract - .event::() - .address(self.l2_oracle.into()) - .from_block(from) - .to_block(to) - .query() - .await? - .into_iter() - .collect::>(); - - events.sort_unstable_by(|a, b| a.l_2_output_index.cmp(&b.l_2_output_index)); - - Ok(events.last().cloned()) - } - - pub async fn fetch_op_payload( - &self, - at: u64, - event: OutputProposedFilter, - ) -> Result { - let output_roots_key = derive_array_item_key(event.l_2_output_index.low_u64(), 0); - let timestamp_and_block_proof = derive_array_item_key(event.l_2_output_index.low_u64(), 1); - - let proof = self - .beacon_execution_client - .get_proof( - self.l2_oracle, - vec![output_roots_key, timestamp_and_block_proof], - Some(at.into()), - ) - .await?; - let output_root_proof = proof - .storage_proof - .get(0) - .cloned() - .ok_or_else(|| anyhow!("Storage proof not found for optimism output root"))? - .proof - .into_iter() - .map(|node| node.0.into()) - .collect(); - - let multi_proof = proof - .storage_proof - .get(1) - .cloned() - .ok_or_else(|| { - anyhow!("Storage proof not found for optimism timestamp and block number") - })? - .proof - .into_iter() - .map(|node| node.0.into()) - .collect(); - let block = self - .op_execution_client - .get_block(event.l_2_block_number.as_u64()) - .await? - .ok_or_else(|| anyhow!("Header not found for {:?}", event.l_2_block_number))?; - let message_parser_proof = self - .op_execution_client - .get_proof(self.message_parser, vec![], Some(event.l_2_block_number.low_u64().into())) - .await?; - - let payload = OptimismPayloadProof { - state_root: block.state_root, - withdrawal_storage_root: message_parser_proof.storage_hash, - l2_block_hash: block.hash.ok_or_else(|| anyhow!("Missing optimism block hash"))?, - // Version bytes is still the default value - version: H256::zero(), - l2_oracle_proof: proof.account_proof.into_iter().map(|node| node.0.into()).collect(), - output_root_proof, - multi_proof, - output_root_index: event.l_2_output_index.low_u64(), - block_number: event.l_2_block_number.low_u64(), - timestamp: block.timestamp.low_u64(), - }; - - Ok(payload) - } -} diff --git a/ethereum/evm/src/optimism/host.rs b/ethereum/evm/src/optimism/host.rs deleted file mode 100644 index 700040ef9..000000000 --- a/ethereum/evm/src/optimism/host.rs +++ /dev/null @@ -1,74 +0,0 @@ -use crate::optimism::client::OpHost; -use anyhow::{anyhow, Error}; -use codec::{Decode, Encode}; -use ethers::prelude::Middleware; -use futures::stream; -use geth_primitives::CodecHeader; -use ismp::{ - consensus::{StateMachineHeight, StateMachineId}, - events::StateMachineUpdated, - messaging::{ConsensusMessage, CreateConsensusState}, -}; -use tesseract_primitives::{BoxStream, ByzantineHandler, IsmpHost, IsmpProvider}; - -#[async_trait::async_trait] -impl ByzantineHandler for OpHost { - async fn query_consensus_message( - &self, - event: StateMachineUpdated, - ) -> Result { - let header: CodecHeader = self - .op_execution_client - .get_block(event.latest_height) - .await? - .ok_or_else(|| anyhow!("Header should be available"))? - .into(); - Ok(ConsensusMessage { - consensus_proof: header.encode(), - consensus_state_id: self.consensus_state_id, - }) - } - - async fn check_for_byzantine_attack( - &self, - counterparty: &C, - consensus_message: ConsensusMessage, - ) -> Result<(), anyhow::Error> { - let header = CodecHeader::decode(&mut &*consensus_message.consensus_proof)?; - let height = StateMachineHeight { - id: StateMachineId { - state_id: self.evm.state_machine, - consensus_state_id: self.consensus_state_id, - }, - height: header.number.low_u64(), - }; - let state_machine_commitment = counterparty.query_state_machine_commitment(height).await?; - if state_machine_commitment.state_root != header.state_root { - // Submit Freeze message - log::info!( - "Freezing {:?} on {:?}", - self.evm.state_machine, - counterparty.state_machine_id().state_id - ); - counterparty.freeze_state_machine(height.id).await?; - } - Ok(()) - } -} - -#[async_trait::async_trait] -impl IsmpHost for OpHost { - async fn consensus_notification( - &self, - _counterparty: I, - ) -> Result, anyhow::Error> - where - I: IsmpHost + IsmpProvider + Clone + 'static, - { - Ok(Box::pin(stream::pending())) - } - - async fn query_initial_consensus_state(&self) -> Result, Error> { - Ok(None) - } -} diff --git a/ethereum/evm/src/optimism/mod.rs b/ethereum/evm/src/optimism/mod.rs deleted file mode 100644 index 39e0f78f1..000000000 --- a/ethereum/evm/src/optimism/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod client; -pub mod host; -#[cfg(test)] -mod tests; diff --git a/ethereum/evm/src/optimism/tests.rs b/ethereum/evm/src/optimism/tests.rs deleted file mode 100644 index 10d2e1cdd..000000000 --- a/ethereum/evm/src/optimism/tests.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::{ - mock::Host, - optimism::client::{HostConfig, OpConfig, OpHost}, - EvmConfig, -}; -use ethabi::ethereum_types::H160; -use ethers::providers::Middleware; -use hex_literal::hex; -use ismp_sync_committee::optimism::verify_optimism_payload; - -const L2_ORACLE: [u8; 20] = hex!("E6Dfba0953616Bacab0c9A8ecb3a9BBa77FC15c0"); -const MESSAGE_PARSER: [u8; 20] = hex!("4200000000000000000000000000000000000016"); - -#[tokio::test] -#[ignore] -async fn test_payload_proof_verification() { - dotenv::dotenv().ok(); - let op_orl = std::env::var("OP_URL").expect("OP_URL must be set."); - let geth_url = std::env::var("GETH_URL").expect("GETH_URL must be set."); - let host = HostConfig { - beacon_rpc_url: geth_url, - l2_oracle: H160::from(L2_ORACLE), - message_parser: H160::from(MESSAGE_PARSER), - }; - let config = OpConfig { - host: Some(host.clone()), - evm_config: EvmConfig { rpc_url: op_orl, ..Default::default() }, - }; - - let op_client = OpHost::new(&host, &config.evm_config).await.expect("Host creation failed"); - - let event = op_client - .latest_event(9779635, 9779635) - .await - .expect("Failed to fetch latest event") - .expect("There should be an event"); - - let payload_proof = op_client - .fetch_op_payload(9779635, event) - .await - .expect("Error fetching payload proof"); - - let l1_header = op_client - .beacon_execution_client - .get_block(9779635) - .await - .unwrap() - .expect("Block should exist"); - - let state_root = l1_header.state_root; - - let _ = verify_optimism_payload::( - payload_proof, - state_root.as_bytes(), - op_client.l2_oracle, - Default::default(), - ) - .expect("Payload proof verification should succeed"); -} diff --git a/ethereum/polygon-pos/Cargo.toml b/ethereum/polygon-pos/Cargo.toml deleted file mode 100644 index 672d646d5..000000000 --- a/ethereum/polygon-pos/Cargo.toml +++ /dev/null @@ -1,45 +0,0 @@ -[package] -name = "tesseract-polygon-pos" -version = "0.1.0" -edition = "2021" -description = "Polyon consensus host for tesseract" -authors = ["Polytope Labs "] - -[dependencies] - -serde = { version = "1.0.164", features = ["derive"] } -serde_json = "1.0.105" -hex = "0.4.3" -log = "0.4.19" -anyhow = "1.0.75" -codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } -futures = "0.3.28" -async-trait = "0.1.71" -tokio = { version = "1.32.0", features = ["full"] } -tokio-stream = "0.1.14" -hex-literal = "0.4.1" -base2 = "0.3.1" -primitive-types = { version = "0.12.1", features = ["impl-codec"] } -ethers = { workspace = true, features = ["rustls"] } -sp-core = "22.0.0" - -ismp.workspace = true -polygon-pos-prover.workspace = true -polygon-pos-verifier.workspace = true -ismp-polygon-pos.workspace = true -geth-primitives.workspace = true - -tesseract-primitives = { path = "../../primitives" } -tesseract-evm = { path = "../evm"} -reconnecting-jsonrpsee-ws-client.workspace = true -jsonrpsee = { version = "0.21", features = ["ws-client"]} -debounced = { git = "https://github.com/polytope-labs/debounced", branch = "main"} - -[dev-dependencies] -tesseract-evm = { path = "../evm", features = ["testing"] } -sp-core = "22.0.0" -ismp = { workspace = true } -codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } -dotenv = "0.15.0" -anyhow = "1.0.75" - diff --git a/ethereum/polygon-pos/src/byzantine.rs b/ethereum/polygon-pos/src/byzantine.rs deleted file mode 100644 index 28f79c012..000000000 --- a/ethereum/polygon-pos/src/byzantine.rs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use anyhow::anyhow; -use codec::{Decode, Encode}; -use ethers::prelude::Middleware; -use geth_primitives::CodecHeader; -use ismp::{ - consensus::{StateMachineHeight, StateMachineId}, - events::StateMachineUpdated, - messaging::ConsensusMessage, -}; -use tesseract_primitives::{ByzantineHandler, IsmpHost, IsmpProvider}; - -use crate::PolygonPosHost; - -#[async_trait::async_trait] -impl ByzantineHandler for PolygonPosHost { - async fn query_consensus_message( - &self, - event: StateMachineUpdated, - ) -> Result { - let header = self - .prover - .fetch_header(event.latest_height) - .await? - .ok_or_else(|| anyhow!("Consensus update header not found"))?; - - Ok(ConsensusMessage { - consensus_proof: header.encode(), - consensus_state_id: self.consensus_state_id, - }) - } - - async fn check_for_byzantine_attack( - &self, - counterparty: &C, - consensus_message: ConsensusMessage, - ) -> Result<(), anyhow::Error> { - let header = CodecHeader::decode(&mut &*consensus_message.consensus_proof)?; - let uncle_count = - self.prover.client.get_uncle_count(header.number.low_u64()).await?.low_u64(); - let mut headers: Vec = vec![]; - for i in 0..uncle_count { - let header = self.prover.client.get_uncle(header.number.low_u64(), i.into()).await?; - if let Some(header) = header { - headers.push(header.into()) - } - } - - headers.push(header); - headers.sort_by(|a, b| a.difficulty.cmp(&b.difficulty)); - let highest = headers[headers.len() - 1].clone(); - - let height = StateMachineHeight { - id: StateMachineId { - state_id: self.state_machine, - consensus_state_id: self.consensus_state_id, - }, - height: highest.number.low_u64(), - }; - let state_machine_commitment = counterparty.query_state_machine_commitment(height).await?; - if state_machine_commitment.state_root != highest.state_root { - // Submit Freeze message - log::info!( - "Freezing {:?} on {:?}", - self.state_machine, - counterparty.state_machine_id().state_id - ); - counterparty.freeze_state_machine(height.id).await?; - } - Ok(()) - } -} diff --git a/ethereum/polygon-pos/src/host.rs b/ethereum/polygon-pos/src/host.rs deleted file mode 100644 index a965f0ffc..000000000 --- a/ethereum/polygon-pos/src/host.rs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -use anyhow::Error; -use codec::Encode; -use debounced::Debounced; -use futures::StreamExt; -use ismp::messaging::{ConsensusMessage, CreateConsensusState}; -use jsonrpsee::rpc_params; -use std::time::Duration; - -use crate::{notification::consensus_notification, PolygonPosHost}; -use tesseract_primitives::{BoxStream, IsmpHost, IsmpProvider}; - -#[async_trait::async_trait] -impl IsmpHost for PolygonPosHost { - async fn consensus_notification( - &self, - counterparty: C, - ) -> Result, anyhow::Error> - where - C: IsmpHost + IsmpProvider + 'static, - { - let client = PolygonPosHost::clone(&self); - - let sub = self - .rpc_client - .subscribe( - "eth_subscribe".to_string(), - rpc_params!("newHeads"), - "eth_unsubscribe".to_string(), - ) - .await?; - let debounced_sub = Debounced::new(sub, Duration::from_secs(4)); - let stream = debounced_sub.filter_map(move |res| { - let client = client.clone(); - let counterparty = counterparty.clone(); - async move { - match res { - Ok(raw) => { - let header = serde_json::from_str(raw.get()).ok()?; - consensus_notification(&client, counterparty, header) - .await - .ok() - .flatten() - .map(|update| { - Ok(ConsensusMessage { - consensus_proof: update.encode(), - consensus_state_id: client.consensus_state_id, - }) - }) - }, - _ => None, - } - } - }); - - Ok(Box::pin(stream)) - } - - async fn query_initial_consensus_state(&self) -> Result, Error> { - let initial_consensus_state = - self.get_consensus_state(self.config.evm_config.ismp_host).await?; - Ok(Some(CreateConsensusState { - consensus_state: initial_consensus_state.encode(), - consensus_client_id: ismp_polygon_pos::POLYGON_CONSENSUS_ID, - consensus_state_id: self.consensus_state_id, - unbonding_period: 60 * 60 * 60 * 27, - challenge_period: 5 * 60, - state_machine_commitments: vec![], - })) - } -} diff --git a/ethereum/polygon-pos/src/lib.rs b/ethereum/polygon-pos/src/lib.rs deleted file mode 100644 index 8306f26b2..000000000 --- a/ethereum/polygon-pos/src/lib.rs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use ethers::providers::{Http, Provider, Ws}; -pub use geth_primitives::Header; -use ismp::{consensus::ConsensusStateId, host::StateMachine, util::Keccak256}; -pub use ismp_polygon_pos::{ConsensusState, PolygonClientUpdate}; -use polygon_pos_prover::PolygonPosProver; -pub use polygon_pos_verifier::verify_polygon_header; -use primitive_types::H160; -use reconnecting_jsonrpsee_ws_client::{Client, ExponentialBackoff, PingConfig}; -use serde::{Deserialize, Serialize}; -use std::{sync::Arc, time::Duration}; -use tesseract_evm::{EvmClient, EvmConfig}; - -mod byzantine; -mod host; -mod notification; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PolygonPosConfig { - /// General ethereum config - #[serde[flatten]] - pub evm_config: EvmConfig, -} - -impl PolygonPosConfig { - /// Convert the config into a client. - pub async fn into_client(self) -> anyhow::Result> { - let host = PolygonPosHost::new(&self).await?; - let client = EvmClient::new(Some(host), self.evm_config).await?; - - Ok(client) - } - - pub fn state_machine(&self) -> StateMachine { - self.evm_config.state_machine - } -} - -#[derive(Clone)] -pub struct PolygonPosHost { - /// Consensus state id on counterparty chain - pub consensus_state_id: ConsensusStateId, - /// State machine Identifier for this chain. - pub state_machine: StateMachine, - /// Consensus prover - pub prover: PolygonPosProver, - /// Config - pub config: PolygonPosConfig, - /// Jsonrpsee client for event susbscription, ethers does not expose a Send and Sync stream for - /// susbcribing to contract logs - pub rpc_client: Arc, -} - -impl PolygonPosHost { - pub async fn new(config: &PolygonPosConfig) -> Result { - let provider = - Provider::::try_from(config.evm_config.rpc_url.clone())?; - let prover = PolygonPosProver::new(provider); - let rpc_client = Client::builder() - .retry_policy(ExponentialBackoff::from_millis(100)) - .enable_ws_ping( - PingConfig::new() - .ping_interval(Duration::from_secs(6)) - .inactive_limit(Duration::from_secs(30)), - ) - .build(config.evm_config.rpc_url.clone()) - .await?; - Ok(Self { - consensus_state_id: { - let mut consensus_state_id: ConsensusStateId = Default::default(); - consensus_state_id.copy_from_slice(config.evm_config.consensus_state_id.as_bytes()); - consensus_state_id - }, - state_machine: config.evm_config.state_machine, - prover, - config: config.clone(), - rpc_client: Arc::new(rpc_client), - }) - } - - pub async fn get_consensus_state( - &self, - ismp_contract_address: H160, - ) -> Result { - let (header, validators) = self.prover.fetch_finalized_state().await?; - - let consensus_state = ConsensusState { - frozen_height: None, - finalized_hash: Header::from(&header).hash::(), - finalized_validators: validators, - forks: Default::default(), - ismp_contract_address, - }; - - Ok(consensus_state) - } -} - -pub struct KeccakHasher; - -impl Keccak256 for KeccakHasher { - fn keccak256(bytes: &[u8]) -> primitive_types::H256 - where - Self: Sized, - { - sp_core::keccak_256(bytes).into() - } -} diff --git a/ethereum/polygon-pos/src/notification.rs b/ethereum/polygon-pos/src/notification.rs deleted file mode 100644 index b8e246f7e..000000000 --- a/ethereum/polygon-pos/src/notification.rs +++ /dev/null @@ -1,101 +0,0 @@ -use anyhow::anyhow; -use codec::{Decode, Encode}; -use ismp_polygon_pos::{ConsensusState, PolygonClientUpdate}; - -use ethers::types::Block; -use geth_primitives::CodecHeader; -use ismp::messaging::{ConsensusMessage, Message}; -use polygon_pos_prover::PolygonPosProver; -use primitive_types::H256; - -use tesseract_primitives::{IsmpHost, IsmpProvider}; - -use crate::PolygonPosHost; - -pub async fn consensus_notification( - client: &PolygonPosHost, - counterparty: C, - block: Block, -) -> Result, anyhow::Error> -where - C: IsmpHost + IsmpProvider + 'static, -{ - let consensus_state = - counterparty.query_consensus_state(None, client.consensus_state_id).await?; - let consensus_state = ConsensusState::decode(&mut &*consensus_state)?; - - let mut chain_heads = consensus_state - .forks - .iter() - .map(|chain| chain.hashes[chain.hashes.len() - 1].1) - .collect::>(); - chain_heads.push(consensus_state.finalized_hash); - let mut headers = next_headers( - &client.prover, - chain_heads.clone(), - block.hash.ok_or_else(|| anyhow!("Hash should be present in block"))?, - consensus_state.finalized_hash, - ) - .await?; - - if headers.len() > 1000 { - for chunk in headers.chunks(1000) { - let chain_head = chunk[0].parent_hash; - let update = PolygonClientUpdate { - consensus_update: chunk.to_vec().try_into().expect("Infallible"), - chain_head, - }; - let message = ConsensusMessage { - consensus_proof: update.encode(), - consensus_state_id: client.consensus_state_id, - }; - let _ = counterparty.submit(vec![Message::Consensus(message)]).await; - } - headers = vec![]; - } - - if !headers.is_empty() { - let chain_head = headers[0].parent_hash; - Ok(Some(PolygonClientUpdate { - consensus_update: headers.try_into().expect("Infallible"), - chain_head, - })) - } else { - Ok(None) - } -} - -// Find the next headers to be submitted - -async fn next_headers( - prover: &PolygonPosProver, - chain_heads: Vec, - latest_block_hash: H256, - finalized_hash: H256, -) -> Result, anyhow::Error> { - let mut headers = vec![]; - let latest_header = prover - .fetch_header(latest_block_hash) - .await? - .ok_or_else(|| anyhow!("Header should exist"))?; - let finalized_header = prover - .fetch_header(finalized_hash) - .await? - .ok_or_else(|| anyhow!("Header should exist"))?; - let mut parent_hash = latest_header.parent_hash; - headers.push(latest_header); - while !chain_heads.contains(&parent_hash) { - let header = prover - .fetch_header(parent_hash) - .await? - .ok_or_else(|| anyhow!("Header should exist"))?; - if header.number <= finalized_header.number { - // Unknown chain - headers = vec![]; - break - } - parent_hash = header.parent_hash; - headers.insert(0, header) - } - Ok(headers) -} diff --git a/ethereum/sync-committee/Cargo.toml b/ethereum/sync-committee/Cargo.toml deleted file mode 100644 index e9ca0c1dc..000000000 --- a/ethereum/sync-committee/Cargo.toml +++ /dev/null @@ -1,47 +0,0 @@ -[package] -name = "tesseract-sync-committee" -version = "0.1.0" -edition = "2021" -description = "Sync-committee primitives for ISMP messaging relay" -authors = ["Polytope Labs "] - -[dependencies] -serde = { version = "1.0.164", features = ["derive"] } -serde_json = "1.0.105" -hex = "0.4.3" -log = "0.4.19" -anyhow = "1.0.75" -codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } -futures = "0.3.28" -async-trait = "0.1.71" -tokio = { version = "1.32.0", features = ["full"] } -tokio-stream = "0.1.14" -hex-literal = "0.4.1" -base2 = "0.3.1" -primitive-types = { version = "0.12.1", features = ["impl-codec"] } -eventsource-client = "0.11.0" - -ismp.workspace = true -sync-committee-prover.workspace = true -sync-committee-verifier.workspace = true -sync-committee-primitives.workspace = true -ismp-sync-committee.workspace = true -ethers = { workspace = true, features = ["rustls"] } -geth-primitives.workspace = true - -tesseract-primitives = { path = "../../primitives" } -tesseract-evm = { path = "../evm"} - -[dev-dependencies] -tesseract-evm = { path = "../evm", features = ["testing"] } -tesseract-primitives = { path = "../../primitives", features = ["testing"] } -sp-core = "22.0.0" -ismp = { workspace = true } -codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } -dotenv = "0.15.0" -anyhow = "1.0.75" - -[features] -finality-events = [] -testing = [] -sepolia = ["sync-committee-prover/sepolia-deneb", "sync-committee-primitives/sepolia-deneb"] diff --git a/ethereum/sync-committee/src/byzantine.rs b/ethereum/sync-committee/src/byzantine.rs deleted file mode 100644 index e0a059f63..000000000 --- a/ethereum/sync-committee/src/byzantine.rs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use crate::SyncCommitteeHost; -use anyhow::anyhow; -use codec::{Decode, Encode}; -use ethers::prelude::Middleware; -use geth_primitives::CodecHeader; -use ismp::{ - consensus::{StateMachineHeight, StateMachineId}, - events::StateMachineUpdated, - messaging::ConsensusMessage, -}; -use sync_committee_primitives::constants::Config; -use tesseract_primitives::{ByzantineHandler, IsmpHost, IsmpProvider}; - -#[async_trait::async_trait] -impl ByzantineHandler for SyncCommitteeHost { - async fn query_consensus_message( - &self, - event: StateMachineUpdated, - ) -> Result { - let header: CodecHeader = self - .el - .get_block(event.latest_height) - .await? - .ok_or_else(|| anyhow!("Header should be available"))? - .into(); - Ok(ConsensusMessage { - consensus_proof: header.encode(), - consensus_state_id: self.consensus_state_id, - }) - } - - async fn check_for_byzantine_attack( - &self, - counterparty: &C, - consensus_message: ConsensusMessage, - ) -> Result<(), anyhow::Error> { - let header = CodecHeader::decode(&mut &*consensus_message.consensus_proof)?; - let height = StateMachineHeight { - id: StateMachineId { - state_id: self.state_machine, - consensus_state_id: self.consensus_state_id, - }, - height: header.number.low_u64(), - }; - let state_machine_commitment = counterparty.query_state_machine_commitment(height).await?; - if state_machine_commitment.state_root != header.state_root { - // Submit Freeze message - log::info!( - "Freezing {:?} on {:?}", - self.state_machine, - counterparty.state_machine_id().state_id - ); - counterparty.freeze_state_machine(height.id).await?; - } - Ok(()) - } -} diff --git a/ethereum/sync-committee/src/host.rs b/ethereum/sync-committee/src/host.rs deleted file mode 100644 index 895b63dc1..000000000 --- a/ethereum/sync-committee/src/host.rs +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -use anyhow::Error; -use std::collections::BTreeMap; -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -use crate::SyncCommitteeHost; -use codec::{Decode, Encode}; -use ismp::{ - consensus::StateMachineId, - messaging::{ConsensusMessage, CreateConsensusState}, -}; -use ismp_sync_committee::types::ConsensusState; -use primitive_types::H160; -use sync_committee_primitives::{constants::Config, util::compute_sync_committee_period}; - -use crate::notification::{consensus_notification, get_beacon_update}; -use tesseract_primitives::{BoxStream, IsmpHost, IsmpProvider}; - -// todo: Figure out the issue with the stream -#[cfg(feature = "finality-events")] -#[async_trait::async_trait] -impl IsmpHost for SyncCommitteeHost { - async fn consensus_notification( - &self, - counterparty: C, - ) -> Result, anyhow::Error> - where - C: IsmpHost + IsmpProvider + 'static, - { - use eventsource_client::Client; - use sync_committee_primitives::consensus_types::Checkpoint; - let client = SyncCommitteeHost::clone(&self); - let challenge_period = counterparty.query_challenge_period(self.consensus_state_id).await?; - let node_url = - format!("{}/eth/v1/events?topics=finalized_checkpoint", client.beacon_node_rpc); - let ev_source = std::sync::Arc::new( - eventsource_client::ClientBuilder::for_url(&node_url) - .expect("Failed to create stream") - .build(), - ); - - let stream = ev_source.stream().filter_map(move |event| { - let counterparty = counterparty.clone(); - let client = client.clone(); - async move { - let last_consensus_update = counterparty - .query_state_machine_update_time(client.consensus_state_id) - .await - .ok() - .unwrap_or_else(|| { - log::error!( - "Failed to fetch last consensus update time: from {}", - counterparty.name() - ); - Default::default() - }); - match event { - Ok(eventsource_client::SSE::Event(ev)) => { - if let Ok(message) = serde_json::from_str::(&ev.data) { - tokio::time::sleep(std::time::Duration::from_secs(150)).await; - let checkpoint = Checkpoint { - epoch: message.epoch.parse().expect("Epoch is always available"), - root: message.block, - }; - let counterparty_timestamp = - counterparty.query_timestamp().await.ok().unwrap_or_else(|| { - log::error!( - "Failed to fetch consensus update time from: {}", - counterparty.name(), - ); - Default::default() - }); - - if counterparty_timestamp - last_consensus_update < challenge_period { - return None - } - - if let Ok(Some(beacon_message)) = - consensus_notification(&client, counterparty.clone(), checkpoint) - .await - { - Some(Ok(ConsensusMessage { - consensus_proof: beacon_message.encode(), - consensus_state_id: client.consensus_state_id, - })) - } else { - None - } - } else { - None - } - }, - Err(err) => { - log::error!("SyncCommittee: Consensus stream encountered error {err:?}"); - None - }, - _ => return None, - } - } - }); - - Ok(Box::pin(stream)) - } - - async fn query_initial_consensus_state(&self) -> Result, Error> { - let mut ismp_contract_addresses = BTreeMap::new(); - let mut l2_oracle = BTreeMap::new(); - let mut rollup_core = H160::default(); - if let Some(host) = &self.arbitrum_client { - ismp_contract_addresses.insert(host.evm.state_machine, host.evm.ismp_host); - rollup_core = host.host.rollup_core; - } - - if let Some(host) = &self.optimism_client { - ismp_contract_addresses.insert(host.evm.state_machine, host.evm.ismp_host); - l2_oracle.insert(host.evm.state_machine, host.host.l2_oracle); - } - - if let Some(host) = &self.base_client { - ismp_contract_addresses.insert(host.evm.state_machine, host.evm.ismp_host); - l2_oracle.insert(host.evm.state_machine, host.host.l2_oracle); - } - ismp_contract_addresses.insert(self.state_machine, self.evm.ismp_host); - let initial_consensus_state = self - .get_consensus_state(ismp_contract_addresses, l2_oracle, rollup_core, None) - .await?; - Ok(Some(CreateConsensusState { - consensus_state: initial_consensus_state.encode(), - consensus_client_id: consensus_client::BEACON_CONSENSUS_ID, - consensus_state_id: self.consensus_state_id, - unbonding_period: 60 * 60 * 60 * 27, - challenge_period: 5 * 60, - state_machine_commitments: vec![], - })) - } -} - -#[cfg(not(feature = "finality-events"))] -#[async_trait::async_trait] -impl IsmpHost for SyncCommitteeHost { - async fn consensus_notification( - &self, - counterparty: C, - ) -> Result, Error> - where - C: IsmpHost + IsmpProvider + 'static, - { - let client = SyncCommitteeHost::clone(&self); - - let interval = tokio::time::interval(self.consensus_update_frequency); - - let interval_stream = futures::stream::unfold(interval, move |mut interval| { - let client = client.clone(); - let counterparty = counterparty.clone(); - - async move { - let sync = || async { - let checkpoint = - client.prover.fetch_finalized_checkpoint(Some("head")).await?.finalized; - - let consensus_state = - counterparty.query_consensus_state(None, client.consensus_state_id).await?; - let consensus_state = ConsensusState::decode(&mut &*consensus_state)?; - let light_client_state = consensus_state.light_client_state; - // Do a sync check before returning any updates - let state_period = light_client_state.state_period; - - let checkpoint_period = compute_sync_committee_period::(checkpoint.epoch); - if !(state_period..=(state_period + 1)).contains(&checkpoint_period) { - let next_period = state_period + 1; - let update = client.prover.latest_update_for_period(next_period).await?; - let state_machine_id = StateMachineId { - state_id: client.state_machine, - consensus_state_id: client.consensus_state_id, - }; - let execution_layer_height = - counterparty.query_latest_height(state_machine_id).await? as u64; - let message = - get_beacon_update(&client, update, execution_layer_height).await?; - return Ok::<_, Error>(Some(message)); - } - Ok(None) - }; - loop { - match sync().await { - Ok(Some(beacon_message)) => { - let update = ConsensusMessage { - consensus_proof: beacon_message.encode(), - consensus_state_id: client.consensus_state_id, - }; - return Some((Ok::<_, Error>(update), interval)); - }, - Ok(None) => {}, - Err(err) => - return Some(( - Err::<_, Error>(err.context(format!( - "Error trying to fetch sync message for {:?}", - client.state_machine - ))), - interval, - )), - }; - - // tick the interval - interval.tick().await; - - let checkpoint = - match client.prover.fetch_finalized_checkpoint(Some("head")).await { - Ok(head) => head.finalized, - Err(err) => { - log::error!( - "Failed to fetch latest finalized header for {:?}: {err:?}", - client.state_machine - ); - continue; - }, - }; - - match consensus_notification(&client, counterparty.clone(), checkpoint).await { - Ok(Some(beacon_message)) => { - let update = ConsensusMessage { - consensus_proof: beacon_message.encode(), - consensus_state_id: client.consensus_state_id, - }; - return Some((Ok::<_, Error>(update), interval)) - }, - Ok(None) => continue, - Err(err) => - return Some(( - Err::<_, Error>(err.context(format!( - "Failed to fetch consensus proof for {:?}", - client.state_machine - ))), - interval, - )), - } - } - } - }); - - Ok(Box::pin(interval_stream)) - } - - async fn query_initial_consensus_state(&self) -> Result, Error> { - let mut ismp_contract_addresses = BTreeMap::new(); - let mut l2_oracle = BTreeMap::new(); - let mut rollup_core = H160::default(); - if let Some(host) = &self.arbitrum_client { - ismp_contract_addresses.insert(host.evm.state_machine, host.evm.ismp_host); - rollup_core = host.host.rollup_core; - } - - if let Some(host) = &self.optimism_client { - ismp_contract_addresses.insert(host.evm.state_machine, host.evm.ismp_host); - l2_oracle.insert(host.evm.state_machine, host.host.l2_oracle); - } - - if let Some(host) = &self.base_client { - ismp_contract_addresses.insert(host.evm.state_machine, host.evm.ismp_host); - l2_oracle.insert(host.evm.state_machine, host.host.l2_oracle); - } - ismp_contract_addresses.insert(self.state_machine, self.evm.ismp_host); - let initial_consensus_state = self - .get_consensus_state(ismp_contract_addresses, l2_oracle, rollup_core, None) - .await?; - Ok(Some(CreateConsensusState { - consensus_state: initial_consensus_state.encode(), - consensus_client_id: ismp_sync_committee::BEACON_CONSENSUS_ID, - consensus_state_id: self.consensus_state_id, - unbonding_period: 60 * 60 * 60 * 27, - challenge_period: 0, - state_machine_commitments: vec![], - })) - } -} diff --git a/ethereum/sync-committee/src/lib.rs b/ethereum/sync-committee/src/lib.rs deleted file mode 100644 index c3851313b..000000000 --- a/ethereum/sync-committee/src/lib.rs +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright (C) 2023 Polytope Labs. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use anyhow::Context; -use ethers::{prelude::Provider, providers::Http}; -use ismp::{consensus::ConsensusStateId, host::StateMachine}; -pub use ismp_sync_committee::types::{BeaconClientUpdate, ConsensusState}; -use primitive_types::H160; -use serde::{Deserialize, Serialize}; -use std::{collections::BTreeMap, sync::Arc, time::Duration}; -use sync_committee_primitives::{ - constants::Config, - types::VerifierState, - util::{compute_epoch_at_slot, compute_sync_committee_period_at_slot}, -}; -use sync_committee_prover::SyncCommitteeProver; -pub use sync_committee_verifier::verify_sync_committee_attestation; -use tesseract_evm::{arbitrum::client::ArbHost, optimism::client::OpHost, EvmClient, EvmConfig}; - -mod byzantine; -mod host; -mod notification; -#[cfg(test)] -mod test; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SyncCommitteeConfig { - /// Host config - pub host: Option, - /// General ethereum config - #[serde[flatten]] - pub evm_config: EvmConfig, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct HostConfig { - /// Http url for a beacon client - pub beacon_http_url: String, - - /// Interval in seconds at which consensus updates should happen - pub consensus_update_frequency: u64, -} - -impl SyncCommitteeConfig { - /// Convert the config into a client. - pub async fn into_client( - self, - ) -> anyhow::Result>> { - let host = if let Some(host) = self.host { - Some(SyncCommitteeHost::new(&host, &self.evm_config).await?) - } else { - None - }; - let client = EvmClient::new(host, self.evm_config).await?; - - Ok(client) - } - - pub fn state_machine(&self) -> StateMachine { - self.evm_config.state_machine - } -} - -pub struct SyncCommitteeHost { - /// Consensus state id on counterparty chain - pub consensus_state_id: ConsensusStateId, - /// State machine Identifier for this chain. - pub state_machine: StateMachine, - /// Arbitrum client - pub arbitrum_client: Option, - /// Optimism client - pub optimism_client: Option, - /// Base client - pub base_client: Option, - /// Consensus prover - pub prover: SyncCommitteeProver, - /// Http URl beacon chain, required for subscribing to events SSE - pub beacon_node_rpc: String, - /// Interval in seconds at which consensus updates should happen - pub consensus_update_frequency: Duration, - - pub evm: EvmConfig, - /// Eth L1 execution client - pub el: Arc>, -} - -impl SyncCommitteeHost { - pub async fn new(host: &HostConfig, evm: &EvmConfig) -> Result { - let prover = SyncCommitteeProver::new(host.beacon_http_url.clone()); - let el = Provider::::try_from(&evm.rpc_url) - .context("Failed to connect to beacon chain consensus client rpc")?; - Ok(Self { - consensus_state_id: { - let mut consensus_state_id: ConsensusStateId = Default::default(); - consensus_state_id.copy_from_slice(evm.consensus_state_id.as_bytes()); - consensus_state_id - }, - state_machine: evm.state_machine, - arbitrum_client: None, - optimism_client: None, - base_client: None, - prover, - evm: evm.clone(), - beacon_node_rpc: host.beacon_http_url.clone(), - consensus_update_frequency: Duration::from_secs(host.consensus_update_frequency), - el: Arc::new(el), - }) - } - - pub fn set_arb_host(&mut self, host: ArbHost) { - self.arbitrum_client = Some(host) - } - - pub fn set_op_host(&mut self, host: OpHost) { - self.optimism_client = Some(host) - } - - pub fn set_base_host(&mut self, host: OpHost) { - self.base_client = Some(host) - } - - pub fn set_l2_hosts(&mut self, hosts: Vec) { - for host in hosts { - match host { - L2Host::Arb(host) => self.set_arb_host(host), - L2Host::Op(host) => self.set_op_host(host), - L2Host::Base(host) => self.set_base_host(host), - } - } - } - - pub async fn get_consensus_state( - &self, - ismp_contract_addresses: BTreeMap, - l2_oracle: BTreeMap, - rollup_core: H160, - trusted_block_id: Option<&str>, - ) -> Result { - let block_id = trusted_block_id.unwrap_or("finalized"); - let block_header = self.prover.fetch_header(&block_id).await?; - let state = self.prover.fetch_beacon_state(&block_header.slot.to_string()).await?; - - let client_state = VerifierState { - finalized_header: block_header.clone(), - latest_finalized_epoch: compute_epoch_at_slot::(block_header.slot), - current_sync_committee: state.current_sync_committee, - next_sync_committee: state.next_sync_committee, - state_period: compute_sync_committee_period_at_slot::(block_header.slot), - }; - - let consensus_state = ConsensusState { - frozen_height: None, - light_client_state: client_state, - ismp_contract_addresses, - l2_oracle_address: l2_oracle, - rollup_core_address: rollup_core, - }; - - Ok(consensus_state) - } -} - -/// Hosts for the various l2s -pub enum L2Host { - Arb(ArbHost), - Op(OpHost), - Base(OpHost), -} - -impl Clone for SyncCommitteeHost { - fn clone(&self) -> Self { - Self { - consensus_state_id: self.consensus_state_id, - state_machine: self.state_machine, - arbitrum_client: self.arbitrum_client.clone(), - optimism_client: self.optimism_client.clone(), - base_client: self.base_client.clone(), - prover: self.prover.clone(), - beacon_node_rpc: self.beacon_node_rpc.clone(), - consensus_update_frequency: self.consensus_update_frequency, - evm: self.evm.clone(), - el: self.el.clone(), - } - } -} diff --git a/ethereum/sync-committee/src/notification.rs b/ethereum/sync-committee/src/notification.rs deleted file mode 100644 index d8e6bd637..000000000 --- a/ethereum/sync-committee/src/notification.rs +++ /dev/null @@ -1,122 +0,0 @@ -use crate::SyncCommitteeHost; -use codec::Decode; -use ismp::{ - consensus::StateMachineId, - host::{Ethereum, StateMachine}, -}; -use ismp_sync_committee::types::{BeaconClientUpdate, ConsensusState}; -use std::collections::BTreeMap; -use sync_committee_primitives::{ - consensus_types::Checkpoint, - constants::{Config, Root}, - types::VerifierStateUpdate, -}; -use tesseract_primitives::{IsmpHost, IsmpProvider}; - -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] -pub struct EventResponse { - pub block: Root, - pub state: Root, - pub epoch: String, - pub execution_optimistic: bool, -} - -pub async fn get_beacon_update( - client: &SyncCommitteeHost, - consensus_update: VerifierStateUpdate, - execution_layer_height: u64, -) -> Result { - let latest_height = { - if execution_layer_height == 0 { - // Check the past 10 blocks behind latest execution layer block number - consensus_update.execution_payload.block_number.saturating_sub(10) - } else { - execution_layer_height - } - }; - - let arbitrum_payload = if let Some(arb_client) = client.arbitrum_client.as_ref() { - let latest_event = arb_client - .latest_event(latest_height, consensus_update.execution_payload.block_number) - .await?; - if let Some(event) = latest_event { - let payload = arb_client - .fetch_arbitrum_payload(consensus_update.execution_payload.block_number, event) - .await?; - Some(payload) - } else { - None - } - } else { - None - }; - - let mut op_stack_payload = BTreeMap::new(); - if let Some(op_client) = client.optimism_client.as_ref() { - let latest_event = op_client - .latest_event(latest_height, consensus_update.execution_payload.block_number) - .await?; - if let Some(event) = latest_event { - let payload = op_client - .fetch_op_payload(consensus_update.execution_payload.block_number, event) - .await?; - op_stack_payload.insert(StateMachine::Ethereum(Ethereum::Optimism), payload); - } - } - - if let Some(base_client) = client.base_client.as_ref() { - let latest_event = base_client - .latest_event(latest_height, consensus_update.execution_payload.block_number) - .await?; - if let Some(event) = latest_event { - let payload = base_client - .fetch_op_payload(consensus_update.execution_payload.block_number, event) - .await?; - op_stack_payload.insert(StateMachine::Ethereum(Ethereum::Base), payload); - } - } - - let message = BeaconClientUpdate { consensus_update, op_stack_payload, arbitrum_payload }; - Ok(message) -} - -pub async fn consensus_notification( - client: &SyncCommitteeHost, - counterparty: C, - checkpoint: Checkpoint, -) -> Result, anyhow::Error> -where - C: IsmpHost + IsmpProvider + 'static, -{ - let consensus_state = - counterparty.query_consensus_state(None, client.consensus_state_id).await?; - let consensus_state = ConsensusState::decode(&mut &*consensus_state)?; - let light_client_state = consensus_state.light_client_state; - let state_machine_id = StateMachineId { - state_id: client.state_machine, - consensus_state_id: client.consensus_state_id, - }; - let update = client - .prover - .fetch_light_client_update( - light_client_state.clone(), - checkpoint.clone(), - None, - "tesseract", - ) - .await?; - - let execution_layer_height = counterparty.query_latest_height(state_machine_id).await? as u64; - - let consensus_update = if let Some(update) = update { update } else { return Ok(None) }; - - if consensus_update.execution_payload.block_number <= execution_layer_height && - consensus_update.sync_committee_update.is_none() || - consensus_update.attested_header.slot <= light_client_state.finalized_header.slot - { - return Ok(None) - } - - let message = get_beacon_update(client, consensus_update, execution_layer_height).await?; - Ok(Some(message)) -} diff --git a/ethereum/sync-committee/src/test.rs b/ethereum/sync-committee/src/test.rs deleted file mode 100644 index f7a4a6dad..000000000 --- a/ethereum/sync-committee/src/test.rs +++ /dev/null @@ -1,199 +0,0 @@ -use crate::{HostConfig, SyncCommitteeHost}; -use codec::Decode; -use futures::StreamExt; -use ismp::host::{Ethereum, StateMachine}; -use ismp_sync_committee::{ - arbitrum::verify_arbitrum_payload, optimism::verify_optimism_payload, types::BeaconClientUpdate, -}; -use sync_committee_primitives::constants::sepolia::Sepolia; -use tesseract_evm::{ - arbitrum::client::{ArbHost, HostConfig as ArbHostConfig}, - mock::Host, - optimism::client::{HostConfig as OpHostConfig, OpHost}, - EvmClient, EvmConfig, -}; -use tesseract_primitives::{mocks::MockHost, IsmpHost}; - -#[tokio::test] -async fn check_consensus_notification() -> anyhow::Result<()> { - dotenv::dotenv().ok(); - let op_orl = std::env::var("OP_URL").expect("OP_URL must be set."); - let arb_orl = std::env::var("ARB_URL").expect("OP_URL must be set."); - let base_orl = std::env::var("BASE_URL").expect("BASE_URL must be set."); - let geth_url = std::env::var("GETH_URL").expect("GETH_URL must be set."); - let beacon_url = std::env::var("BEACON_URL").expect("BEACON_URL must be set."); - let chain_a = MockHost::new( - ismp_sync_committee::types::ConsensusState { - frozen_height: Default::default(), - light_client_state: Default::default(), - ismp_contract_addresses: Default::default(), - l2_oracle_address: Default::default(), - rollup_core_address: Default::default(), - }, - 0, - StateMachine::Polygon, - ); - - let chain_b = { - let config = EvmConfig { - rpc_url: geth_url.clone(), - state_machine: StateMachine::Ethereum(Ethereum::ExecutionLayer), - consensus_state_id: "SYNC".to_string(), - ismp_host: Default::default(), - handler: Default::default(), - signer: "2e0834786285daccd064ca17f1654f67b4aef298acbb82cef9ec422fb4975622".to_string(), - etherscan_api_key: Default::default(), - tracing_batch_size: None, - }; - - let host = HostConfig { beacon_http_url: beacon_url, consensus_update_frequency: 180 }; - let arb_host = { - let config = EvmConfig { - rpc_url: arb_orl, - state_machine: StateMachine::Ethereum(Ethereum::Arbitrum), - consensus_state_id: "SYNC".to_string(), - ismp_host: Default::default(), - handler: Default::default(), - signer: "2e0834786285daccd064ca17f1654f67b4aef298acbb82cef9ec422fb4975622" - .to_string(), - etherscan_api_key: Default::default(), - tracing_batch_size: None, - }; - - ArbHost::new( - &ArbHostConfig { - beacon_rpc_url: geth_url.clone(), - rollup_core: sp_core::H160::from(hex_literal::hex!( - "45e5cAea8768F42B385A366D3551Ad1e0cbFAb17" - )), - }, - &config, - ) - .await? - }; - - let op_host = { - let config = EvmConfig { - rpc_url: op_orl, - state_machine: StateMachine::Ethereum(Ethereum::Optimism), - consensus_state_id: "SYNC".to_string(), - ismp_host: Default::default(), - handler: Default::default(), - signer: "2e0834786285daccd064ca17f1654f67b4aef298acbb82cef9ec422fb4975622" - .to_string(), - etherscan_api_key: Default::default(), - tracing_batch_size: None, - }; - - OpHost::new( - &OpHostConfig { - beacon_rpc_url: geth_url.clone(), - l2_oracle: sp_core::H160::from(hex_literal::hex!( - "E6Dfba0953616Bacab0c9A8ecb3a9BBa77FC15c0" - )), - message_parser: sp_core::H160::from(hex_literal::hex!( - "4200000000000000000000000000000000000016" - )), - }, - &config, - ) - .await? - }; - - let base_host = { - let config = EvmConfig { - rpc_url: base_orl, - state_machine: StateMachine::Ethereum(Ethereum::Optimism), - consensus_state_id: "SYNC".to_string(), - ismp_host: Default::default(), - handler: Default::default(), - signer: "2e0834786285daccd064ca17f1654f67b4aef298acbb82cef9ec422fb4975622" - .to_string(), - etherscan_api_key: Default::default(), - tracing_batch_size: None, - }; - - OpHost::new( - &OpHostConfig { - beacon_rpc_url: geth_url, - l2_oracle: sp_core::H160::from(hex_literal::hex!( - "2A35891ff30313CcFa6CE88dcf3858bb075A2298" - )), - message_parser: sp_core::H160::from(hex_literal::hex!( - "4200000000000000000000000000000000000016" - )), - }, - &config, - ) - .await? - }; - - let mut host = SyncCommitteeHost::::new(&host, &config).await?; - host.set_arb_host(arb_host); - host.set_op_host(op_host); - host.set_base_host(base_host); - EvmClient::new(Some(host), config).await? - }; - - let mut consensus_stream = chain_b.consensus_notification(chain_a.clone()).await.unwrap(); - - while let Some(res) = consensus_stream.next().await { - println!("Received new event"); - match res { - Ok(res) => { - let BeaconClientUpdate { mut op_stack_payload, consensus_update, arbitrum_payload } = - BeaconClientUpdate::decode(&mut &res.consensus_proof[..]).unwrap(); - (*chain_a.consensus_state.lock().unwrap()).light_client_state.finalized_header = - consensus_update.finalized_header; - (*chain_a.consensus_state.lock().unwrap()) - .light_client_state - .latest_finalized_epoch = consensus_update.finality_proof.epoch; - (*chain_a.latest_height.lock().unwrap()) = - consensus_update.execution_payload.block_number; - dbg!(consensus_update.execution_payload.block_number); - let state_root = consensus_update.execution_payload.state_root; - - let op_stack = [ - ( - StateMachine::Ethereum(Ethereum::Base), - hex_literal::hex!("2A35891ff30313CcFa6CE88dcf3858bb075A2298"), - ), - ( - StateMachine::Ethereum(Ethereum::Optimism), - hex_literal::hex!("E6Dfba0953616Bacab0c9A8ecb3a9BBa77FC15c0"), - ), - ]; - - for (state_machine_id, l2_oracle) in op_stack { - println!("Verifying {state_machine_id:?} payload proof"); - if let Some(payload) = op_stack_payload.remove(&state_machine_id) { - let _state = verify_optimism_payload::( - payload, - &state_root[..], - l2_oracle.into(), - Default::default(), - ) - .unwrap(); - } - } - - if let Some(arbitrum_payload) = arbitrum_payload { - println!("Verifying arbitrum payload proof"); - let _state = verify_arbitrum_payload::( - arbitrum_payload, - &state_root[..], - hex_literal::hex!("45e5cAea8768F42B385A366D3551Ad1e0cbFAb17").into(), - Default::default(), - ) - .unwrap(); - } - - println!("Finished payload proof verification"); - }, - Err(err) => { - println!("Failed to fetch light client update {err:?}") - }, - } - } - Ok(()) -} diff --git a/ethereum/evm/Cargo.toml b/evm/common/Cargo.toml similarity index 100% rename from ethereum/evm/Cargo.toml rename to evm/common/Cargo.toml diff --git a/ethereum/evm/abis/ArbGasInfo.json b/evm/common/abis/ArbGasInfo.json similarity index 100% rename from ethereum/evm/abis/ArbGasInfo.json rename to evm/common/abis/ArbGasInfo.json diff --git a/ethereum/evm/abis/ERC20.json b/evm/common/abis/ERC20.json similarity index 100% rename from ethereum/evm/abis/ERC20.json rename to evm/common/abis/ERC20.json diff --git a/ethereum/evm/abis/IRollupCore.json b/evm/common/abis/IRollupCore.json similarity index 100% rename from ethereum/evm/abis/IRollupCore.json rename to evm/common/abis/IRollupCore.json diff --git a/ethereum/evm/abis/L2OutputOracle.json b/evm/common/abis/L2OutputOracle.json similarity index 100% rename from ethereum/evm/abis/L2OutputOracle.json rename to evm/common/abis/L2OutputOracle.json diff --git a/ethereum/evm/abis/OVM_gasPriceOracle.json b/evm/common/abis/OVM_gasPriceOracle.json similarity index 100% rename from ethereum/evm/abis/OVM_gasPriceOracle.json rename to evm/common/abis/OVM_gasPriceOracle.json diff --git a/ethereum/evm/build.rs b/evm/common/build.rs similarity index 100% rename from ethereum/evm/build.rs rename to evm/common/build.rs diff --git a/ethereum/evm/src/abi.rs b/evm/common/src/abi.rs similarity index 100% rename from ethereum/evm/src/abi.rs rename to evm/common/src/abi.rs diff --git a/ethereum/evm/src/abi/arb_gas_info.rs b/evm/common/src/abi/arb_gas_info.rs similarity index 100% rename from ethereum/evm/src/abi/arb_gas_info.rs rename to evm/common/src/abi/arb_gas_info.rs diff --git a/ethereum/evm/src/abi/erc_20.rs b/evm/common/src/abi/erc_20.rs similarity index 100% rename from ethereum/evm/src/abi/erc_20.rs rename to evm/common/src/abi/erc_20.rs diff --git a/ethereum/evm/src/abi/i_rollup.rs b/evm/common/src/abi/i_rollup.rs similarity index 100% rename from ethereum/evm/src/abi/i_rollup.rs rename to evm/common/src/abi/i_rollup.rs diff --git a/ethereum/evm/src/abi/l2_output_oracle.rs b/evm/common/src/abi/l2_output_oracle.rs similarity index 100% rename from ethereum/evm/src/abi/l2_output_oracle.rs rename to evm/common/src/abi/l2_output_oracle.rs diff --git a/ethereum/evm/src/abi/ovm_gas_price_oracle.rs b/evm/common/src/abi/ovm_gas_price_oracle.rs similarity index 100% rename from ethereum/evm/src/abi/ovm_gas_price_oracle.rs rename to evm/common/src/abi/ovm_gas_price_oracle.rs diff --git a/ethereum/evm/src/consts.rs b/evm/common/src/consts.rs similarity index 100% rename from ethereum/evm/src/consts.rs rename to evm/common/src/consts.rs diff --git a/ethereum/evm/src/gas_oracle.rs b/evm/common/src/gas_oracle.rs similarity index 96% rename from ethereum/evm/src/gas_oracle.rs rename to evm/common/src/gas_oracle.rs index 25e6a6b1e..8eb535a4d 100644 --- a/ethereum/evm/src/gas_oracle.rs +++ b/evm/common/src/gas_oracle.rs @@ -89,7 +89,6 @@ pub async fn get_current_gas_cost_in_usd( let node_gas_price = client.get_gas_price().await?; let arb_gas_info_contract = ArbGasInfo::new(H160(ARB_GAS_INFO), client); let oracle_gas_price = arb_gas_info_contract.get_minimum_gas_price().await?; - log::trace!("Oracle gas price for {chain:?}: {oracle_gas_price}, node gas price {node_gas_price}"); gas_price = std::cmp::max(node_gas_price, oracle_gas_price); // minimum gas price is 0.1 Gwei let response_json = get_eth_to_usd_price(ð_price_uri).await?; let eth_usd = parse_to_27_decimals(&response_json.result.ethusd)?; @@ -100,7 +99,6 @@ pub async fn get_current_gas_cost_in_usd( let node_gas_price: U256 = client.get_gas_price().await?; let ovm_gas_price_oracle = OVM_gasPriceOracle::new(H160(OP_GAS_ORACLE), client); let ovm_gas_price = ovm_gas_price_oracle.gas_price().await?; - log::trace!("Oracle gas price for {chain:?}: {ovm_gas_price}, node gas price {node_gas_price}"); gas_price = std::cmp::max(ovm_gas_price, node_gas_price); // minimum gas price is 0.1 Gwei let response_json = get_eth_to_usd_price(ð_price_uri).await?; let eth_usd = parse_to_27_decimals(&response_json.result.ethusd)?; @@ -133,7 +131,6 @@ pub async fn get_current_gas_cost_in_usd( let price = data as f64 * 1.25f64; let node_gas_price: U256 = client.get_gas_price().await?; let oracle_gas_price = U256::from(price as u128); - log::trace!("Oracle gas price for {chain:?}: {oracle_gas_price}, node gas price {node_gas_price}"); gas_price = std::cmp::max(node_gas_price, oracle_gas_price); let response_json = get_eth_gas_and_price(&uri, ð_price_uri).await?; let eth_usd = parse_to_27_decimals(&response_json.usd_price)?; @@ -145,7 +142,6 @@ pub async fn get_current_gas_cost_in_usd( let response_json = get_eth_gas_and_price(&uri, ð_price_uri).await?; let oracle_gas_price = parse_units(response_json.safe_gas_price.to_string(), "gwei")?.into(); - log::trace!("Oracle gas price for {chain:?}: {oracle_gas_price}, node gas price {node_gas_price}"); gas_price = std::cmp::max(node_gas_price, oracle_gas_price); let eth_usd = parse_to_27_decimals(&response_json.usd_price)?; unit_wei = get_cost_of_one_wei(eth_usd); @@ -182,7 +178,6 @@ pub async fn get_current_gas_cost_in_usd( let node_gas_price: U256 = client.get_gas_price().await?; let oracle_gas_price = parse_units(response.standard.max_priority_fee.to_string(), "gwei")?.into(); - log::trace!("Oracle gas price for {chain:?}: {oracle_gas_price}, node gas price {node_gas_price}"); gas_price = std::cmp::max(node_gas_price, oracle_gas_price); let response = make_request(&uri, Default::default()).await?; let response_json: GasResponse = response.json().await?; @@ -199,7 +194,6 @@ pub async fn get_current_gas_cost_in_usd( let response_json: GasResponse = response.json().await?; let oracle_gas_price = parse_units(response_json.result.safe_gas_price.to_string(), "gwei")?.into(); - log::trace!("Oracle gas price for {chain:?}: {oracle_gas_price}, node gas price {node_gas_price}"); gas_price = std::cmp::max(node_gas_price, oracle_gas_price); let eth_usd = parse_to_27_decimals(&response_json.result.usd_price)?; unit_wei = get_cost_of_one_wei(eth_usd); @@ -225,7 +219,6 @@ pub async fn get_current_gas_cost_in_usd( let response_json: GasResponse = response.json().await?; let oracle_gas_price = parse_units(response_json.result.safe_gas_price.to_string(), "gwei")?.into(); - log::trace!("Oracle gas price for {chain:?}: {oracle_gas_price}, node gas price {node_gas_price}"); gas_price = std::cmp::max(node_gas_price, oracle_gas_price); let eth_usd = parse_to_27_decimals(&response_json.result.usd_price)?; unit_wei = get_cost_of_one_wei(eth_usd); diff --git a/ethereum/evm/src/host.rs b/evm/common/src/host.rs similarity index 100% rename from ethereum/evm/src/host.rs rename to evm/common/src/host.rs diff --git a/ethereum/evm/src/lib.rs b/evm/common/src/lib.rs similarity index 100% rename from ethereum/evm/src/lib.rs rename to evm/common/src/lib.rs diff --git a/ethereum/evm/src/mock.rs b/evm/common/src/mock.rs similarity index 99% rename from ethereum/evm/src/mock.rs rename to evm/common/src/mock.rs index b76d282cf..95bf20541 100644 --- a/ethereum/evm/src/mock.rs +++ b/evm/common/src/mock.rs @@ -312,7 +312,7 @@ mod tests { dest: chain.to_string().as_bytes().to_vec().into(), module: ping_addr.clone().into(), timeout: 10 * 60 * 60, - fee: U256::from(900_000_000_000_000_000u128), + fee: U256::from(9_000_000_000_000_000_000u128), count: U256::from(100), }); let gas = diff --git a/ethereum/evm/src/provider.rs b/evm/common/src/provider.rs similarity index 100% rename from ethereum/evm/src/provider.rs rename to evm/common/src/provider.rs diff --git a/ethereum/evm/src/test.rs b/evm/common/src/test.rs similarity index 100% rename from ethereum/evm/src/test.rs rename to evm/common/src/test.rs diff --git a/ethereum/evm/src/tx.rs b/evm/common/src/tx.rs similarity index 100% rename from ethereum/evm/src/tx.rs rename to evm/common/src/tx.rs diff --git a/payments/Cargo.toml b/fees/Cargo.toml similarity index 92% rename from payments/Cargo.toml rename to fees/Cargo.toml index 09f1792ef..b8a7c44aa 100644 --- a/payments/Cargo.toml +++ b/fees/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "transaction-payment" +name = "transaction-fees" version = "0.1.0" edition = "2021" description = "Local tracking of relayer payments on all chains" @@ -15,6 +15,7 @@ tokio = { version = "1.32.0", features = ["full"] } primitive-types = "0.12.2" prisma-client-rust = { git = "https://github.com/Brendonovich/prisma-client-rust.git", tag = "0.6.11", features = ["sqlite", "migrations"] } serde = { version = "1.0.195", features = ["derive"] } +itertools = "0.12.1" sp-core = { workspace = true } hex = "0.4.3" @@ -22,7 +23,7 @@ subxt = { version = "0.30.1", features = ["substrate-compat"] } chrono = "0.4.31" codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } -tesseract-evm = { path = "../ethereum/evm" } +tesseract-evm = { path = "../evm/common" } tesseract-substrate = { path = "../substrate/common" } tesseract-primitives = { path = "../primitives" } diff --git a/payments/prisma-cli/Cargo.toml b/fees/prisma-cli/Cargo.toml similarity index 100% rename from payments/prisma-cli/Cargo.toml rename to fees/prisma-cli/Cargo.toml diff --git a/payments/prisma-cli/src/main.rs b/fees/prisma-cli/src/main.rs similarity index 100% rename from payments/prisma-cli/src/main.rs rename to fees/prisma-cli/src/main.rs diff --git a/payments/prisma/migrations/20240119151018_/migration.sql b/fees/prisma/migrations/20240119151018_/migration.sql similarity index 100% rename from payments/prisma/migrations/20240119151018_/migration.sql rename to fees/prisma/migrations/20240119151018_/migration.sql diff --git a/payments/prisma/migrations/migration_lock.toml b/fees/prisma/migrations/migration_lock.toml similarity index 100% rename from payments/prisma/migrations/migration_lock.toml rename to fees/prisma/migrations/migration_lock.toml diff --git a/payments/prisma/schema.prisma b/fees/prisma/schema.prisma similarity index 100% rename from payments/prisma/schema.prisma rename to fees/prisma/schema.prisma diff --git a/payments/src/db.rs b/fees/src/db.rs similarity index 99% rename from payments/src/db.rs rename to fees/src/db.rs index 64c61c63f..f5037a281 100644 --- a/payments/src/db.rs +++ b/fees/src/db.rs @@ -4,7 +4,7 @@ pub static DATAMODEL_STR: &'static str = include_str!("../prisma/schema.prisma") static DATABASE_STR: &'static str = "sqlite"; use ::prisma_client_rust::migrations::include_dir; pub static MIGRATIONS_DIR: &::prisma_client_rust::migrations::include_dir::Dir = - &::prisma_client_rust::migrations::include_dir::include_dir!("./payments/prisma/migrations"); + &::prisma_client_rust::migrations::include_dir::include_dir!("./fees/prisma/migrations"); pub async fn new_client() -> Result { PrismaClient::_builder().build().await } diff --git a/payments/src/lib.rs b/fees/src/lib.rs similarity index 86% rename from payments/src/lib.rs rename to fees/src/lib.rs index bb37bcb32..8daa6e60c 100644 --- a/payments/src/lib.rs +++ b/fees/src/lib.rs @@ -17,6 +17,7 @@ use ismp::{ router::{Post, Request, RequestResponse}, util::{hash_request, hash_response, Keccak256}, }; +use itertools::Itertools; use pallet_relayer_fees::withdrawal::{Key, WithdrawalProof}; use primitive_types::H256; use prisma_client_rust::BatchItem; @@ -48,53 +49,19 @@ impl TransactionPayment { Ok(Self { db: Arc::new(client) }) } - pub async fn store_latest_height( - &self, - source: StateMachine, - dest_chain: StateMachine, - height: u64, - ) -> anyhow::Result<()> { - self.db - .latest_heights() - .delete_many(vec![ - db::latest_heights::WhereParam::SourceChain(StringFilter::Equals( - source.to_string(), - )), - db::latest_heights::WhereParam::DestChain(StringFilter::Equals( - dest_chain.to_string(), - )), - ]) - .exec() - .await?; - self.db - .latest_heights() - .create(source.to_string(), dest_chain.to_string(), height as i32, Default::default()) - .exec() - .await?; - Ok(()) - } - - pub async fn retreive_latest_height( - &self, - source: StateMachine, - dest_chain: StateMachine, - ) -> anyhow::Result> { - let res = self - .db - .latest_heights() - .find_first(vec![ - db::latest_heights::WhereParam::SourceChain(StringFilter::Equals( - source.to_string(), - )), - db::latest_heights::WhereParam::DestChain(StringFilter::Equals( - dest_chain.to_string(), - )), - ]) - .exec() - .await? - .map(|data| data.latest_height as u64); + /// Query all deliveries in the db and make them unique by the source & destination pair + pub async fn distinct_deliveries(&self) -> anyhow::Result> { + let deliveries = self.db.deliveries().find_many(vec![]).exec().await?; + let data = deliveries + .into_iter() + .unique_by(|data| { + let mut pair = vec![data.source_chain.clone(), data.dest_chain.clone()]; + pair.sort(); + pair.concat() + }) + .collect(); - Ok(res) + Ok(data) } /// Store entries for delivered post requests and responses diff --git a/payments/src/mock.rs b/fees/src/mock.rs similarity index 100% rename from payments/src/mock.rs rename to fees/src/mock.rs diff --git a/fees/src/tests.rs b/fees/src/tests.rs new file mode 100644 index 000000000..125363123 --- /dev/null +++ b/fees/src/tests.rs @@ -0,0 +1,242 @@ +use crate::TransactionPayment; +use ismp::{ + consensus::{StateMachineHeight, StateMachineId}, + host::{Ethereum, StateMachine}, + messaging::{Message, Proof, RequestMessage, ResponseMessage}, + router::{Post, PostResponse, Request, RequestResponse, Response}, + util::{hash_request, hash_response}, +}; +use tesseract_primitives::{mocks::MockHost, Hasher, Query, TxReceipt}; + +#[tokio::test] +async fn transaction_payments_flow() { + let tx_payment = TransactionPayment::initialize("./dev.db").await.unwrap(); + let receipts = (0..500).into_iter().map(|i| { + let post = Post { + source: StateMachine::Bsc, + dest: StateMachine::Polygon, + nonce: i, + from: vec![], + to: vec![], + timeout_timestamp: 0, + data: vec![], + gas_limit: i, + }; + let req = Request::Post(post); + let commitment = hash_request::(&req); + TxReceipt::Request(Query { + source_chain: req.source_chain(), + dest_chain: req.dest_chain(), + nonce: req.nonce(), + commitment, + }) + }); + + let response_receipts = (0..500).into_iter().map(|i| { + let resp = Response::Post(PostResponse { + post: Post { + source: StateMachine::Polygon, + dest: StateMachine::Bsc, + nonce: i, + from: vec![], + to: vec![], + timeout_timestamp: 0, + data: vec![], + gas_limit: i, + }, + response: vec![0u8; 64], + timeout_timestamp: i, + gas_limit: i, + }); + + let commitment = hash_response::(&resp); + let request_commitment = hash_request::(&resp.request()); + + TxReceipt::Response { + query: Query { + source_chain: resp.source_chain(), + dest_chain: resp.dest_chain(), + nonce: resp.nonce(), + commitment, + }, + request_commitment, + } + }); + + tx_payment + .store_messages(receipts.chain(response_receipts).collect()) + .await + .unwrap(); + + let claim_proof = tx_payment + .create_claim_proof( + 0, + 0, + &MockHost::new((), 0, StateMachine::Bsc), + &MockHost::new((), 0, StateMachine::Polygon), + ) + .await + .unwrap(); + + assert_eq!(claim_proof.commitments.len(), 1000); +} + +#[tokio::test] +async fn test_unique_deliveries() -> anyhow::Result<()> { + let tx_payment = TransactionPayment::initialize("./dev2.db").await.unwrap(); + let receipts = (0..5).into_iter().map(|i| { + let post = Post { + source: StateMachine::Bsc, + dest: StateMachine::Polygon, + nonce: i, + from: vec![], + to: vec![], + timeout_timestamp: 0, + data: vec![], + gas_limit: i, + }; + let req = Request::Post(post); + let commitment = hash_request::(&req); + TxReceipt::Request(Query { + source_chain: req.source_chain(), + dest_chain: req.dest_chain(), + nonce: req.nonce(), + commitment, + }) + }); + + let response_receipts = (0..5).into_iter().map(|i| { + let resp = Response::Post(PostResponse { + post: Post { + source: StateMachine::Polygon, + dest: StateMachine::Bsc, + nonce: i, + from: vec![], + to: vec![], + timeout_timestamp: 0, + data: vec![], + gas_limit: i, + }, + response: vec![0u8; 64], + timeout_timestamp: i, + gas_limit: i, + }); + + let commitment = hash_response::(&resp); + let request_commitment = hash_request::(&resp.request()); + + TxReceipt::Response { + query: Query { + source_chain: resp.source_chain(), + dest_chain: resp.dest_chain(), + nonce: resp.nonce(), + commitment, + }, + request_commitment, + } + }); + + let receipts2 = (0..5).into_iter().map(|i| { + let post = Post { + source: StateMachine::Ethereum(Ethereum::ExecutionLayer), + dest: StateMachine::Polygon, + nonce: i, + from: vec![], + to: vec![], + timeout_timestamp: 0, + data: vec![], + gas_limit: i, + }; + let req = Request::Post(post); + let commitment = hash_request::(&req); + TxReceipt::Request(Query { + source_chain: req.source_chain(), + dest_chain: req.dest_chain(), + nonce: req.nonce(), + commitment, + }) + }); + + let receipts3 = (0..5).into_iter().map(|i| { + let post = Post { + dest: StateMachine::Ethereum(Ethereum::ExecutionLayer), + source: StateMachine::Polygon, + nonce: i, + from: vec![], + to: vec![], + timeout_timestamp: 0, + data: vec![], + gas_limit: i, + }; + let req = Request::Post(post); + let commitment = hash_request::(&req); + TxReceipt::Request(Query { + source_chain: req.source_chain(), + dest_chain: req.dest_chain(), + nonce: req.nonce(), + commitment, + }) + }); + + let receipts4 = (0..5).into_iter().map(|i| { + let post = Post { + dest: StateMachine::Ethereum(Ethereum::Optimism), + source: StateMachine::Ethereum(Ethereum::Base), + nonce: i, + from: vec![], + to: vec![], + timeout_timestamp: 0, + data: vec![], + gas_limit: i, + }; + let req = Request::Post(post); + let commitment = hash_request::(&req); + TxReceipt::Request(Query { + source_chain: req.source_chain(), + dest_chain: req.dest_chain(), + nonce: req.nonce(), + commitment, + }) + }); + + let receipts5 = (0..5).into_iter().map(|i| { + let post = Post { + source: StateMachine::Ethereum(Ethereum::Optimism), + dest: StateMachine::Ethereum(Ethereum::Base), + nonce: i, + from: vec![], + to: vec![], + timeout_timestamp: 0, + data: vec![], + gas_limit: i, + }; + let req = Request::Post(post); + let commitment = hash_request::(&req); + TxReceipt::Request(Query { + source_chain: req.source_chain(), + dest_chain: req.dest_chain(), + nonce: req.nonce(), + commitment, + }) + }); + + tx_payment + .store_messages( + receipts + .chain(receipts2) + .chain(receipts3) + .chain(receipts4) + .chain(receipts5) + .chain(response_receipts) + .collect(), + ) + .await + .unwrap(); + + let unique = tx_payment.distinct_deliveries().await?; + + // there are only 3 unique chain pairs in the db + assert_eq!(unique.len(), 3); + + Ok(()) +} diff --git a/messaging/Cargo.toml b/messaging/Cargo.toml index b40eb0e12..ed820f65a 100644 --- a/messaging/Cargo.toml +++ b/messaging/Cargo.toml @@ -12,11 +12,12 @@ percentage = "0.1.0" futures = "0.3.28" tokio = { version = "1.32.0", features = ["full"] } sp-core = { version = "21.0.0", features = ["full_crypto"] } +hex = "0.4.3" ismp.workspace = true pallet-ismp.workspace = true -transaction-payment = { path = "../payments" } +transaction-fees = { path = "../fees" } tesseract-primitives = { path = "../primitives" } ismp-solidity-abi.workspace = true -tesseract-any-client = { path = "../tesseract-any-client" } +tesseract-client = { path = "../client" } diff --git a/messaging/src/events.rs b/messaging/src/events.rs index 89c4da4b7..b36e56a72 100644 --- a/messaging/src/events.rs +++ b/messaging/src/events.rs @@ -8,7 +8,7 @@ use ismp::{ }; use sp_core::U256; use std::collections::HashMap; -use tesseract_any_client::AnyClient; +use tesseract_client::AnyClient; use tesseract_primitives::{config::RelayerConfig, Cost, Hasher, IsmpHost, IsmpProvider, Query}; /// Short description of a request/response event @@ -79,6 +79,16 @@ where let mut response_messages = vec![]; let counterparty_timestamp = sink.query_timestamp().await?; + let is_allowed_module = |module: &Vec| match config.module_filter { + Some(ref filters) => + if !filters.is_empty() { + filters.iter().find(|filter| **filter == *module).is_some() + } else { + true + }, + // if no filter is provided, allow all modules + None => true, + }; for event in events.iter() { match event { @@ -94,6 +104,15 @@ where ); continue } + + if !is_allowed_module(&post.from) { + log::trace!( + "Request from module {}, filtered by module filter", + hex::encode(&post.from), + ); + continue + } + let req = Request::Post(post.clone()); let hash = hash_request::(&req); @@ -128,6 +147,15 @@ where ); continue } + + if !is_allowed_module(&post_response.source_module()) { + log::trace!( + "Request from module {}, filtered by module filter", + hex::encode(&post_response.source_module()), + ); + continue + } + let resp = Response::Post(post_response.clone()); let hash = hash_response::(&resp); @@ -305,7 +333,7 @@ where let og_source = if let Some(client) = client_map.get(&queries[index].source_chain) { client } else { - log::info!("Skipping tx because fee metadata cannot be queried, client for {:?} was not provided in the client map", queries[index].source_chain); + log::info!("Skipping tx because fee metadata cannot be queried, client for {:?} was not provided", queries[index].source_chain); queries_to_be_relayed.push(None); continue }; diff --git a/messaging/src/lib.rs b/messaging/src/lib.rs index 896588d92..ce6f5a176 100644 --- a/messaging/src/lib.rs +++ b/messaging/src/lib.rs @@ -23,12 +23,12 @@ use std::{collections::HashMap, sync::Arc}; use crate::events::{filter_events, translate_events_to_messages, Event}; use futures::StreamExt; use ismp::{consensus::StateMachineHeight, host::StateMachine}; -use tesseract_any_client::AnyClient; +use tesseract_client::AnyClient; use tesseract_primitives::{ config::RelayerConfig, wait_for_challenge_period, IsmpHost, IsmpProvider, StateMachineUpdated, }; -use transaction_payment::TransactionPayment; +use transaction_fees::TransactionPayment; pub async fn relay( chain_a: A, diff --git a/payments/src/tests.rs b/payments/src/tests.rs deleted file mode 100644 index 42f7fe3e7..000000000 --- a/payments/src/tests.rs +++ /dev/null @@ -1,82 +0,0 @@ -use crate::TransactionPayment; -use ismp::{ - consensus::{StateMachineHeight, StateMachineId}, - host::StateMachine, - messaging::{Message, Proof, RequestMessage, ResponseMessage}, - router::{Post, PostResponse, Request, RequestResponse, Response}, - util::{hash_request, hash_response}, -}; -use tesseract_primitives::{mocks::MockHost, Hasher, Query, TxReceipt}; - -#[tokio::test] -async fn transaction_payments_flow() { - let tx_payment = TransactionPayment::initialize("./dev.db").await.unwrap(); - let receipts = (0..500).into_iter().map(|i| { - let post = Post { - source: StateMachine::Bsc, - dest: StateMachine::Polygon, - nonce: i, - from: vec![], - to: vec![], - timeout_timestamp: 0, - data: vec![], - gas_limit: i, - }; - let req = Request::Post(post); - let commitment = hash_request::(&req); - TxReceipt::Request(Query { - source_chain: req.source_chain(), - dest_chain: req.dest_chain(), - nonce: req.nonce(), - commitment, - }) - }); - - let response_receipts = (0..500).into_iter().map(|i| { - let resp = Response::Post(PostResponse { - post: Post { - source: StateMachine::Polygon, - dest: StateMachine::Bsc, - nonce: i, - from: vec![], - to: vec![], - timeout_timestamp: 0, - data: vec![], - gas_limit: i, - }, - response: vec![0u8; 64], - timeout_timestamp: i, - gas_limit: i, - }); - - let commitment = hash_response::(&resp); - let request_commitment = hash_request::(&resp.request()); - - TxReceipt::Response { - query: Query { - source_chain: resp.source_chain(), - dest_chain: resp.dest_chain(), - nonce: resp.nonce(), - commitment, - }, - request_commitment, - } - }); - - tx_payment - .store_messages(receipts.chain(response_receipts).collect()) - .await - .unwrap(); - - let claim_proof = tx_payment - .create_claim_proof( - 0, - 0, - &MockHost::new((), 0, StateMachine::Bsc), - &MockHost::new((), 0, StateMachine::Polygon), - ) - .await - .unwrap(); - - assert_eq!(claim_proof.commitments.len(), 1000); -} diff --git a/primitives/src/config.rs b/primitives/src/config.rs index eeed36e3a..b1375c3ee 100644 --- a/primitives/src/config.rs +++ b/primitives/src/config.rs @@ -22,7 +22,7 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct RelayerConfig { /// Modules we're interested in relaying - pub module_filter: Vec>, + pub module_filter: Option>>, /// Relay consensus messages pub consensus: Option, /// Relay messages diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index a38af6956..f329059d1 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -324,10 +324,17 @@ pub trait IsmpHost: ByzantineHandler + Clone + Send + Sync { #[async_trait::async_trait] pub trait HyperbridgeClaim { + async fn available_amount( + &self, + _client: &C, + _chain: &StateMachine, + ) -> anyhow::Result { + Ok(U256::from(0)) + } async fn accumulate_fees(&self, proof: WithdrawalProof) -> anyhow::Result<()>; async fn withdraw_funds( &self, - counterparty: &C, + client: &C, chain: StateMachine, gas_limit: u64, ) -> anyhow::Result; diff --git a/relayer/Cargo.toml b/relayer/Cargo.toml index 5e48a3441..1942931ae 100644 --- a/relayer/Cargo.toml +++ b/relayer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tesseract" -version = "0.1.4" +version = "0.2.0" edition = "2021" description = "Chain agnostic relayer implementation for ISMP" authors = ["Polytope Labs "] @@ -12,14 +12,14 @@ consensus = { package = "tesseract-consensus", path = "../consensus" } primitives = { package = "tesseract-primitives", path = "../primitives" } messaging = { package = "tesseract-messaging", path = "../messaging" } fisherman = { package = "tesseract-fisherman", path = "../fisherman" } -tesseract-evm = { path = "../ethereum/evm" } +tesseract-evm = { path = "../evm/common" } #tesseract-polygon-pos = { path = "../ethereum/polygon-pos" } -tesseract-bsc-pos = { path = "../ethereum/bsc-pos" } -tesseract-sync-committee = { path = "../ethereum/sync-committee" } +tesseract-bsc-pos = { path = "../evm/bsc-pos" } +tesseract-sync-committee = { path = "../evm/sync-committee" } tesseract-beefy = { path = "../substrate/beefy/naive" } -transaction-payment = { path = "../payments" } +transaction-fees = { path = "../fees" } telemetry-server = { path = "../telemetry" } -tesseract-any-client = { path = "../tesseract-any-client" } +tesseract-client = { path = "../client" } # polytope labs ismp.workspace = true diff --git a/relayer/src/cli.rs b/relayer/src/cli.rs index 13997c7ed..85c8b7596 100644 --- a/relayer/src/cli.rs +++ b/relayer/src/cli.rs @@ -15,7 +15,7 @@ //! Tesseract CLI utilities -use crate::{config::HyperbridgeConfig, logging, tx_payment::Subcommand}; +use crate::{config::HyperbridgeConfig, fees::Subcommand, logging}; use anyhow::{anyhow, Context}; use clap::Parser; use codec::Encode; @@ -26,10 +26,10 @@ use rust_socketio::ClientBuilder; use sp_core::{ecdsa, ByteArray, Pair}; use std::{collections::HashMap, sync::Arc}; use telemetry_server::{Message, SECRET_KEY}; -use tesseract_any_client::AnyClient; +use tesseract_client::AnyClient; use tesseract_substrate::config::{Blake2SubstrateChain, KeccakSubstrateChain}; use tesseract_sync_committee::L2Host; -use transaction_payment::TransactionPayment; +use transaction_fees::TransactionPayment; /// CLI interface for tesseract relayer. #[derive(Parser, Debug)] @@ -133,14 +133,15 @@ impl Cli { ); let clients = create_client_map(config.clone()).await?; if config.relayer.delivery_endpoints.is_empty() { - log::warn!("Delivery endpoints not specified in relayer config"); + log::warn!("Delivery endpoints not specified in relayer config, will deliver to all chains."); } // messaging streams for (state_machine, mut client) in clients.clone() { // If the delivery endpoint is not empty then we only spawn tasks for chains // explicitly mentioned in the config - - if !config.relayer.delivery_endpoints.contains(&state_machine) { + if !config.relayer.delivery_endpoints.is_empty() && + !config.relayer.delivery_endpoints.contains(&state_machine) + { continue } diff --git a/relayer/src/config.rs b/relayer/src/config.rs index 102a4977a..9fff91180 100644 --- a/relayer/src/config.rs +++ b/relayer/src/config.rs @@ -20,8 +20,8 @@ use ismp::host::StateMachine; use primitives::config::RelayerConfig; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use tesseract_any_client::AnyConfig; use tesseract_beefy::BeefyConfig; +use tesseract_client::AnyConfig; use toml::Table; /// Defines the format of the tesseract config.toml file. diff --git a/relayer/src/fees.rs b/relayer/src/fees.rs new file mode 100644 index 000000000..6668c50b8 --- /dev/null +++ b/relayer/src/fees.rs @@ -0,0 +1,210 @@ +use crate::{config::HyperbridgeConfig, create_client_map, logging}; +use anyhow::anyhow; +use futures::StreamExt; +use ismp::{ + consensus::StateMachineHeight, + host::StateMachine, + messaging::{Message, Proof, RequestMessage}, + router::Request, + util::hash_request, +}; +use primitives::{ + wait_for_challenge_period, Cost, HyperbridgeClaim, IsmpProvider, Query, WithdrawFundsResult, +}; +use sp_core::U256; +use std::{collections::HashMap, str::FromStr}; +use tesseract_bsc_pos::KeccakHasher; +use tesseract_client::AnyClient; +use tesseract_substrate::config::{Blake2SubstrateChain, KeccakSubstrateChain}; +use transaction_fees::TransactionPayment; + +#[derive(Debug, clap::Subcommand)] +pub enum Subcommand { + /// Withdraw fees on hyperbridge + AccumulateFees(AccumulateFees), +} + +#[derive(Debug, clap::Parser)] +#[command( + propagate_version = true, + args_conflicts_with_subcommands = true, + subcommand_negates_reqs = true +)] +pub struct AccumulateFees { + /// Accumulate fees and withdraw the funds + #[arg(short, long)] + pub withdraw: bool, + /// Gas limit for executing withdrawal requests on both chains + #[arg(short, long)] + pub gas_limit: Option, +} + +impl AccumulateFees { + /// Accumulate fees accrued through deliveries from source to dest and dest to source + pub async fn accumulate_fees(&self, config_path: String, db: String) -> anyhow::Result<()> { + logging::setup()?; + let config = HyperbridgeConfig::parse_conf(&config_path).await?; + + let HyperbridgeConfig { hyperbridge: hyperbridge_config, .. } = config.clone(); + + let hyperbridge: tesseract_substrate::SubstrateClient< + tesseract_beefy::BeefyHost, + KeccakSubstrateChain, + > = hyperbridge_config + .clone() + .into_client::() + .await?; + + let clients = create_client_map(config).await?; + + // early return if withdrawing + if self.withdraw { + self.withdraw(&hyperbridge, clients).await?; + return Ok(()) + } + + let tx_payment = TransactionPayment::initialize(&db).await?; + log::info!("Initialized database"); + + for delivery in tx_payment.distinct_deliveries().await? { + let source_chain = StateMachine::from_str(&delivery.source_chain) + .expect("Invalid Source State Machine provided"); + let dest_chain = StateMachine::from_str(&delivery.dest_chain) + .expect("Invalid Dest State Machine provided"); + + let source = clients + .get(&source_chain) + .ok_or_else(|| anyhow!("Client not found for source state machine"))?; + let dest = clients + .get(&dest_chain) + .ok_or_else(|| anyhow!("Client not found for dest state machine"))?; + + let source_height = hyperbridge.query_latest_height(source.state_machine_id()).await?; + let dest_height = hyperbridge.query_latest_height(dest.state_machine_id()).await?; + + // Create claim proof for deliveries from source to dest + log::info!("Creating withdrawal proof from db for deliveries from {source_chain:?}->{dest_chain:?}"); + let claim_proof = tx_payment + .create_claim_proof(source_height.into(), dest_height.into(), source, dest) + .await?; + if !claim_proof.commitments.is_empty() { + log::info!("Submitting proof for {source_chain:?}->{dest_chain:?} to hyperbridge"); + hyperbridge.accumulate_fees(claim_proof.clone()).await?; + log::info!("Proof sucessfully submitted"); + tx_payment.delete_claimed_entries(claim_proof).await?; + } else { + log::info!("No deliveries found in db for {source_chain:?}->{dest_chain:?}"); + }; + // Create claim proof for deliveries from dest to source + log::info!("Creating withdrawal proof from db for deliveries from {dest_chain:?}->{source_chain:?}"); + let claim_proof = tx_payment + .create_claim_proof(dest_height.into(), source_height.into(), dest, source) + .await?; + if !claim_proof.commitments.is_empty() { + log::info!("Submitting proof for {dest_chain:?}->{source_chain:?} to hyperbridge"); + hyperbridge.accumulate_fees(claim_proof.clone()).await?; + log::info!("Proof sucessfully submitted"); + tx_payment.delete_claimed_entries(claim_proof).await?; + } else { + log::info!("No deliveries found in db for {dest_chain:?}->{source_chain:?}"); + }; + } + + Ok(()) + } + + pub async fn withdraw( + &self, + hyperbridge: &C, + clients: HashMap, + ) -> anyhow::Result<()> { + for chain in clients.keys().cloned() { + let client = clients + .get(&chain) + .ok_or_else(|| anyhow!("Client not found for state machine"))?; + let amount = hyperbridge.available_amount(client, &chain).await?; + + if amount == U256::zero() { + continue; + } + + log::info!("Submitting withdrawal request to {chain:?} for amount ${}", Cost(amount)); + let result = hyperbridge + .withdraw_funds(client, chain, self.gas_limit.unwrap_or_default()) + .await?; + log::info!("Request submitted to hyperbridge successfully"); + log::info!("Starting delivery of withdrawal message to {:?}", chain); + // Wait for state machine update + deliver_post_request(client, hyperbridge, result).await?; + } + + Ok(()) + } +} + +async fn deliver_post_request( + dest_chain: &C, + hyperbridge: &D, + result: WithdrawFundsResult, +) -> anyhow::Result<()> { + let mut stream = dest_chain + .state_machine_update_notification(hyperbridge.state_machine_id()) + .await?; + + while let Some(Ok(event)) = stream.next().await { + log::info!("Waiting for state machine update"); + if event.latest_height < result.block { + continue + } + log::info!("Found a valid state machine update"); + let challenge_period = dest_chain + .query_challenge_period(event.state_machine_id.consensus_state_id) + .await?; + let height = StateMachineHeight { id: event.state_machine_id, height: event.latest_height }; + let last_consensus_update = dest_chain.query_state_machine_update_time(height).await?; + log::info!("Waiting for challenge period to elapse"); + wait_for_challenge_period(dest_chain, last_consensus_update, challenge_period).await?; + let query = Query { + source_chain: result.post.source, + dest_chain: result.post.dest, + nonce: result.post.nonce, + commitment: hash_request::(&Request::Post(result.post.clone())), + }; + log::info!("Querying request proof from hyperbridge at {}", event.latest_height); + let proof = hyperbridge.query_requests_proof(event.latest_height, vec![query]).await?; + log::info!("Successfully queried request proof from hyperbridge"); + let msg = RequestMessage { + requests: vec![result.post.clone()], + proof: Proof { + height: StateMachineHeight { + id: hyperbridge.state_machine_id(), + height: event.latest_height, + }, + proof, + }, + signer: dest_chain.address(), + }; + + log::info!("Submitting post request to {:?}", dest_chain.state_machine_id().state_id); + + let mut count = 5; + while count != 0 { + if let Err(e) = dest_chain.submit(vec![Message::Request(msg.clone())]).await { + log::info!( + "Encountered error trying to submit withdrawal request to {:?}.\n{e:?}\nWill retry {count} more times.", + dest_chain.state_machine_id().state_id + ); + count -= 1; + } else { + log::info!( + "Withdrawal message submitted successfully to {:?}", + dest_chain.state_machine_id().state_id + ); + return Ok(()) + } + } + + break + } + Ok(()) +} diff --git a/relayer/src/lib.rs b/relayer/src/lib.rs index d56b1f957..02c56a26a 100644 --- a/relayer/src/lib.rs +++ b/relayer/src/lib.rs @@ -2,6 +2,6 @@ mod cli; mod config; mod logging; -pub mod tx_payment; +pub mod fees; pub use cli::*; diff --git a/relayer/src/main.rs b/relayer/src/main.rs index 86ba38dd7..afa992f05 100644 --- a/relayer/src/main.rs +++ b/relayer/src/main.rs @@ -14,7 +14,7 @@ // limitations under the License. use clap::Parser; -use tesseract::{tx_payment::Subcommand, Cli}; +use tesseract::{fees::Subcommand, Cli}; #[tokio::main] async fn main() -> Result<(), anyhow::Error> { diff --git a/relayer/src/tx_payment.rs b/relayer/src/tx_payment.rs deleted file mode 100644 index 44cf07116..000000000 --- a/relayer/src/tx_payment.rs +++ /dev/null @@ -1,232 +0,0 @@ -use crate::{config::HyperbridgeConfig, create_client_map, logging}; -use anyhow::anyhow; -use futures::StreamExt; -use ismp::{ - consensus::StateMachineHeight, - host::StateMachine, - messaging::{Message, Proof, RequestMessage}, - router::Request, - util::hash_request, -}; -use primitives::{ - wait_for_challenge_period, HyperbridgeClaim, IsmpProvider, Query, WithdrawFundsResult, -}; -use std::{collections::HashMap, str::FromStr, time::Duration}; -use tesseract_any_client::AnyClient; -use tesseract_bsc_pos::KeccakHasher; -use tesseract_substrate::config::{Blake2SubstrateChain, KeccakSubstrateChain}; -use transaction_payment::TransactionPayment; - -#[derive(Debug, clap::Subcommand)] -pub enum Subcommand { - /// Withdraw fees on hyperbridge - AccumulateFees(AccumulateFees), -} - -#[derive(Debug, clap::Parser)] -#[command( - propagate_version = true, - args_conflicts_with_subcommands = true, - subcommand_negates_reqs = true -)] -pub struct AccumulateFees { - /// Source chain - #[arg(short, long)] - pub source: String, - /// Destination chain - #[arg(short, long)] - pub dest: String, - /// Accumulate fees and withdraw the funds - #[arg(short, long)] - pub withdraw: bool, - /// Gas limit for executing withdrawal requests on both chains - #[arg(short, long)] - pub gas_limit: Option, -} - -impl AccumulateFees { - /// Accumulate fees accrued through deliveries from source to dest and dest to source - pub async fn accumulate_fees(&self, config_path: String, db: String) -> anyhow::Result<()> { - logging::setup()?; - let config = HyperbridgeConfig::parse_conf(&config_path).await?; - - let HyperbridgeConfig { hyperbridge: hyperbridge_config, .. } = config.clone(); - - let hyperbridge: tesseract_substrate::SubstrateClient< - tesseract_beefy::BeefyHost, - KeccakSubstrateChain, - > = hyperbridge_config - .clone() - .into_client::() - .await?; - - let clients = create_client_map(config).await?; - - let tx_payment = TransactionPayment::initialize(&db).await?; - log::info!("Initialized database"); - let source_chain = - StateMachine::from_str(&self.source).expect("Invalid Source State Machine provided"); - let dest_chain = - StateMachine::from_str(&self.dest).expect("Invalid Dest State Machine provided"); - let source = clients - .get(&source_chain) - .ok_or_else(|| anyhow!("Client not found for source state machine"))?; - let dest = clients - .get(&dest_chain) - .ok_or_else(|| anyhow!("Client not found for dest state machine"))?; - let source_height = hyperbridge.query_latest_height(source.state_machine_id()).await?; - let dest_height = hyperbridge.query_latest_height(dest.state_machine_id()).await?; - // Create claim proof for deliveries from source to dest - log::info!("Creating withdrawal proof from db for deliveries from {source_chain:?}->{dest_chain:?}"); - let claim_proof = tx_payment - .create_claim_proof(source_height.into(), dest_height.into(), source, dest) - .await?; - let withdraw_from_a = if !claim_proof.commitments.is_empty() { - log::info!("Submitting proof for {source_chain:?}->{dest_chain:?} to hyperbridge"); - hyperbridge.accumulate_fees(claim_proof.clone()).await?; - log::info!("Proof sucessfully submitted"); - tx_payment.delete_claimed_entries(claim_proof).await?; - true - } else { - log::info!("No deliveries found in db"); - false - }; - // Create claim proof for deliveries from dest to source - log::info!("Creating withdrawal proof from db for deliveries from {dest_chain:?}->{source_chain:?}"); - let claim_proof = tx_payment - .create_claim_proof(dest_height.into(), source_height.into(), dest, source) - .await?; - let withdraw_from_b = if !claim_proof.commitments.is_empty() { - log::info!("Submitting proof for {dest_chain:?}->{source_chain:?} to hyperbridge"); - hyperbridge.accumulate_fees(claim_proof.clone()).await?; - log::info!("Proof sucessfully submitted"); - tx_payment.delete_claimed_entries(claim_proof).await?; - true - } else { - log::info!("No deliveries found in db"); - false - }; - if self.withdraw { - self.withdraw(&hyperbridge, clients, withdraw_from_a, withdraw_from_b).await?; - } - Ok(()) - } - - pub async fn withdraw( - &self, - hyperbridge: &C, - mut clients: HashMap, - withdraw_from_a: bool, - withdraw_from_b: bool, - ) -> anyhow::Result<()> { - let source_chain = - StateMachine::from_str(&self.source).expect("Invalid Source State Machine provided"); - let dest_chain = - StateMachine::from_str(&self.dest).expect("Invalid Dest State Machine provided"); - if withdraw_from_a { - let chain_a = clients - .get_mut(&source_chain) - .ok_or_else(|| anyhow!("Client not found for state machine"))?; - log::info!("Submitting withdrawal request to hyperbridge"); - let result = hyperbridge - .withdraw_funds(chain_a, source_chain, self.gas_limit.unwrap_or_default()) - .await?; - log::info!("Request submitted to hyperbridge successfully"); - log::info!("Starting delivery of withdrawal message to {}", source_chain); - // Wait for state machine update - deliver_post_request(chain_a, hyperbridge, result).await?; - } - - if withdraw_from_b { - let chain_b = clients - .get_mut(&dest_chain) - .ok_or_else(|| anyhow!("Client not found for state machine"))?; - let result = hyperbridge - .withdraw_funds(chain_b, dest_chain, self.gas_limit.unwrap_or_default()) - .await?; - log::info!("Starting delivery of withdrawal message to {}", dest_chain); - // Wait for state machine update - deliver_post_request(chain_b, hyperbridge, result).await?; - } - - Ok(()) - } -} - -async fn deliver_post_request( - dest_chain: &mut C, - hyperbridge: &D, - result: WithdrawFundsResult, -) -> anyhow::Result<()> { - let mut stream = dest_chain - .state_machine_update_notification(hyperbridge.state_machine_id()) - .await?; - let mut delivered = false; - let mut retries = 0; - loop { - while let Some(Ok(event)) = stream.next().await { - log::info!("Waiting for state machine update"); - if event.latest_height >= result.block { - log::info!("Found a valid state machine update"); - let challenge_period = dest_chain - .query_challenge_period(event.state_machine_id.consensus_state_id) - .await?; - let height = - StateMachineHeight { id: event.state_machine_id, height: event.latest_height }; - let last_consensus_update = - dest_chain.query_state_machine_update_time(height).await?; - log::info!("Waiting for challenge period to elapse"); - wait_for_challenge_period(dest_chain, last_consensus_update, challenge_period) - .await?; - let query = Query { - source_chain: result.post.source, - dest_chain: result.post.dest, - nonce: result.post.nonce, - commitment: hash_request::(&Request::Post(result.post.clone())), - }; - log::info!("Queryng request proof from hyperbridge"); - let proof = - hyperbridge.query_requests_proof(event.latest_height, vec![query]).await?; - log::info!("Successfully queried request proof from hyperbridge"); - let msg = RequestMessage { - requests: vec![result.post.clone()], - proof: Proof { - height: StateMachineHeight { - id: hyperbridge.state_machine_id(), - height: event.latest_height, - }, - proof, - }, - signer: dest_chain.address(), - }; - - log::info!( - "Submitting post request to message to {:?}", - dest_chain.state_machine_id().state_id - ); - - if let Err(e) = dest_chain.submit(vec![Message::Request(msg)]).await { - log::info!( - "Encountered error trying to submit withdrawal request to {:?}\n {e:?}", - dest_chain.state_machine_id().state_id - ); - } else { - log::info!( - "Withdrawal message submitted successfully to {:?}", - dest_chain.state_machine_id().state_id - ); - delivered = true; - break - } - } - } - if !delivered && retries < 5 { - // Try again - tokio::time::sleep(Duration::from_secs(30)).await; - retries += 1; - } else { - break - } - } - Ok(()) -} diff --git a/scripts/docker/Dockerfile b/scripts/docker/Dockerfile deleted file mode 100644 index 676748fad..000000000 --- a/scripts/docker/Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -FROM paritytech/ci-linux:production - -COPY ../.. ./ - -RUN RUSTFLAGS="-C link-args=-Wl,--allow-multiple-definition" cargo build --release -p tesseract - -ENTRYPOINT ["./target/release/tesseract"] diff --git a/scripts/docker/slim.Dockerfile b/scripts/docker/slim.Dockerfile index 32acea3b2..b2ab69ae6 100644 --- a/scripts/docker/slim.Dockerfile +++ b/scripts/docker/slim.Dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/library/debian:bullseye-slim +FROM docker.io/library/debian:bookworm-slim RUN apt-get update && apt-get install -y ca-certificates && update-ca-certificates diff --git a/substrate/common/src/calls.rs b/substrate/common/src/calls.rs index 66257b90a..9ccca2d31 100644 --- a/substrate/common/src/calls.rs +++ b/substrate/common/src/calls.rs @@ -94,9 +94,17 @@ where + Sync, C::Signature: From + Send + Sync, { + async fn available_amount( + &self, + client: &P, + chain: &StateMachine, + ) -> anyhow::Result { + Ok(relayer_account_balance(&self.client, chain.clone(), client.address()).await?) + } + /// Accumulate accrued fees on hyperbridge by submitting a claim proof async fn accumulate_fees(&self, proof: WithdrawalProof) -> anyhow::Result<()> { - let tx = Extrinsic::new("RelayerFees", "accumulate_fees", proof.encode()); + let tx = Extrinsic::new("Relayer", "accumulate_fees", proof.encode()); send_unsigned_extrinsic(&self.client, tx).await?; Ok(()) @@ -109,7 +117,7 @@ where chain: StateMachine, gas_limit: u64, ) -> anyhow::Result { - let addr = runtime::api::storage().relayer_fees().nonce(counterparty.address().as_slice()); + let addr = runtime::api::storage().relayer().nonce(counterparty.address().as_slice()); let nonce = self.client.storage().at_latest().await?.fetch(&addr).await?.unwrap_or_default(); @@ -121,7 +129,7 @@ where let input_data = WithdrawalInputData { signature, dest_chain: chain, amount, gas_limit }; - let tx = Extrinsic::new("RelayerFees", "withdraw_fees", input_data.encode()); + let tx = Extrinsic::new("Relayer", "withdraw_fees", input_data.encode()); let hash = send_unsigned_extrinsic(&self.client, tx) .await? .ok_or_else(|| anyhow!("Transaction submission failed"))?; @@ -179,16 +187,15 @@ async fn relayer_account_balance( chain: StateMachine, address: Vec, ) -> anyhow::Result { - let addr = runtime::api::storage() - .relayer_fees() - .relayer_fees(&chain.into(), address.as_slice()); + let addr = runtime::api::storage().relayer().fees(&chain.into(), address.as_slice()); let balance = client .storage() .at_latest() .await? .fetch(&addr) .await? - .ok_or_else(|| anyhow!("Failed to fetch Relayer Balance"))?; + .map(|val| U256(val.0)) + .unwrap_or(U256::zero()); - Ok(U256(balance.0)) + Ok(balance) } diff --git a/substrate/common/src/lib.rs b/substrate/common/src/lib.rs index dd911ee4e..89488245d 100644 --- a/substrate/common/src/lib.rs +++ b/substrate/common/src/lib.rs @@ -46,13 +46,13 @@ pub struct SubstrateConfig { /// Hyperbridge network pub chain: Chain, /// The hashing algorithm that substrate chain uses. - pub hashing: HashAlgorithm, + pub hashing: Option, /// Consensus state id - pub consensus_state_id: String, - /// RPC url for the chain - pub chain_rpc_ws: String, + pub consensus_state_id: Option, + /// Websocket RPC url for the chain + pub rpc_ws: String, /// Relayer account seed - pub signer: String, + pub signer: Option, /// Latest state machine height pub latest_height: Option, } @@ -105,9 +105,9 @@ where .ping_interval(Duration::from_secs(6)) .inactive_limit(Duration::from_secs(30)), ) - .build(config.chain_rpc_ws.clone()) + .build(config.rpc_ws.clone()) .await - .context(format!("Failed to connect to substrate rpc {}", config.chain_rpc_ws))?; + .context(format!("Failed to connect to substrate rpc {}", config.rpc_ws))?; let client = OnlineClient::::from_rpc_client(Arc::new(ClientWrapper(raw_client))) .await .context("Failed to query from substrate rpc")?; @@ -124,17 +124,21 @@ where .number() .into() }; - let bytes = from_hex(&config.signer)?; + let bytes = config + .signer + .and_then(|seed| from_hex(&seed).ok()) + .unwrap_or(H256::random().0.to_vec()); let signer = sr25519::Pair::from_seed_slice(&bytes)?; let mut consensus_state_id: ConsensusStateId = Default::default(); - consensus_state_id.copy_from_slice(config.consensus_state_id.as_bytes()); + consensus_state_id + .copy_from_slice(config.consensus_state_id.unwrap_or("PARA".into()).as_bytes()); let address = signer.public().0.to_vec(); Ok(Self { host, client, consensus_state_id, state_machine: config.chain.state_machine(), - hashing: config.hashing, + hashing: config.hashing.clone().unwrap_or(HashAlgorithm::Keccak), signer, address, initial_height: latest_height, diff --git a/substrate/common/src/runtime.rs b/substrate/common/src/runtime.rs index 1da96c0fb..bc8091a1f 100644 --- a/substrate/common/src/runtime.rs +++ b/substrate/common/src/runtime.rs @@ -6,7 +6,7 @@ pub mod api { mod root_mod { pub use super::*; } - pub static PALLETS: [&str; 22usize] = [ + pub static PALLETS: [&str; 21usize] = [ "System", "Timestamp", "ParachainSystem", @@ -26,8 +26,7 @@ pub mod api { "Ismp", "IsmpSyncCommittee", "IsmpDemo", - "RelayerFees", - "IsmpPolygonPos", + "Relayer", "StateMachineManager", ]; pub static RUNTIME_APIS: [&str; 14usize] = [ @@ -970,9 +969,10 @@ pub mod api { "query_call_info", types::QueryCallInfo { call, len }, [ - 65u8, 10u8, 231u8, 148u8, 45u8, 78u8, 59u8, 8u8, 196u8, 71u8, 69u8, - 232u8, 213u8, 165u8, 239u8, 11u8, 94u8, 185u8, 1u8, 179u8, 118u8, - 124u8, 185u8, 110u8, 132u8, 54u8, 227u8, 65u8, 224u8, 24u8, 73u8, 66u8, + 178u8, 110u8, 234u8, 69u8, 237u8, 93u8, 242u8, 209u8, 159u8, 80u8, + 248u8, 9u8, 42u8, 91u8, 67u8, 73u8, 254u8, 91u8, 225u8, 124u8, 198u8, + 38u8, 74u8, 28u8, 252u8, 203u8, 69u8, 214u8, 127u8, 138u8, 250u8, + 120u8, ], ) } @@ -992,9 +992,10 @@ pub mod api { "query_call_fee_details", types::QueryCallFeeDetails { call, len }, [ - 56u8, 136u8, 76u8, 163u8, 103u8, 19u8, 233u8, 60u8, 14u8, 46u8, 23u8, - 75u8, 219u8, 161u8, 200u8, 86u8, 4u8, 203u8, 120u8, 57u8, 102u8, 171u8, - 100u8, 101u8, 124u8, 13u8, 130u8, 105u8, 26u8, 63u8, 210u8, 244u8, + 221u8, 41u8, 14u8, 211u8, 141u8, 147u8, 219u8, 216u8, 49u8, 53u8, + 101u8, 31u8, 71u8, 235u8, 103u8, 116u8, 94u8, 170u8, 202u8, 239u8, + 228u8, 115u8, 127u8, 129u8, 138u8, 65u8, 196u8, 172u8, 251u8, 41u8, + 156u8, 59u8, ], ) } @@ -1628,11 +1629,8 @@ pub mod api { pub fn ismp(&self) -> ismp::storage::StorageApi { ismp::storage::StorageApi } - pub fn relayer_fees(&self) -> relayer_fees::storage::StorageApi { - relayer_fees::storage::StorageApi - } - pub fn ismp_polygon_pos(&self) -> ismp_polygon_pos::storage::StorageApi { - ismp_polygon_pos::storage::StorageApi + pub fn relayer(&self) -> relayer::storage::StorageApi { + relayer::storage::StorageApi } pub fn state_machine_manager(&self) -> state_machine_manager::storage::StorageApi { state_machine_manager::storage::StorageApi @@ -1682,8 +1680,8 @@ pub mod api { pub fn ismp_demo(&self) -> ismp_demo::calls::TransactionApi { ismp_demo::calls::TransactionApi } - pub fn relayer_fees(&self) -> relayer_fees::calls::TransactionApi { - relayer_fees::calls::TransactionApi + pub fn relayer(&self) -> relayer::calls::TransactionApi { + relayer::calls::TransactionApi } pub fn state_machine_manager(&self) -> state_machine_manager::calls::TransactionApi { state_machine_manager::calls::TransactionApi @@ -1698,9 +1696,9 @@ pub mod api { .hash(); runtime_metadata_hash == [ - 194u8, 226u8, 216u8, 7u8, 207u8, 11u8, 142u8, 220u8, 67u8, 171u8, 101u8, 127u8, - 231u8, 164u8, 6u8, 178u8, 15u8, 149u8, 239u8, 117u8, 240u8, 147u8, 25u8, 114u8, - 180u8, 93u8, 202u8, 28u8, 216u8, 139u8, 112u8, 204u8, + 223u8, 7u8, 142u8, 19u8, 216u8, 62u8, 232u8, 18u8, 136u8, 255u8, 3u8, 54u8, 210u8, + 159u8, 189u8, 113u8, 33u8, 86u8, 224u8, 130u8, 206u8, 24u8, 58u8, 42u8, 69u8, + 117u8, 193u8, 203u8, 239u8, 30u8, 75u8, 59u8, ] } pub mod system { @@ -2528,10 +2526,10 @@ pub mod api { "Events", vec![], [ - 116u8, 85u8, 0u8, 167u8, 8u8, 195u8, 110u8, 209u8, 57u8, 133u8, 216u8, - 60u8, 194u8, 49u8, 124u8, 198u8, 142u8, 125u8, 104u8, 230u8, 143u8, - 15u8, 218u8, 142u8, 20u8, 143u8, 248u8, 239u8, 229u8, 163u8, 84u8, - 20u8, + 82u8, 190u8, 2u8, 206u8, 254u8, 87u8, 149u8, 220u8, 201u8, 12u8, 158u8, + 86u8, 197u8, 213u8, 165u8, 139u8, 108u8, 157u8, 204u8, 189u8, 180u8, + 21u8, 94u8, 102u8, 240u8, 111u8, 125u8, 124u8, 155u8, 180u8, 101u8, + 5u8, ], ) } @@ -6279,10 +6277,10 @@ pub mod api { "sudo", types::Sudo { call: ::std::boxed::Box::new(call) }, [ - 80u8, 108u8, 112u8, 105u8, 186u8, 33u8, 58u8, 253u8, 114u8, 227u8, - 138u8, 35u8, 103u8, 240u8, 160u8, 19u8, 247u8, 126u8, 234u8, 231u8, - 244u8, 249u8, 192u8, 129u8, 83u8, 134u8, 179u8, 187u8, 180u8, 79u8, - 30u8, 32u8, + 45u8, 153u8, 121u8, 219u8, 52u8, 22u8, 168u8, 242u8, 197u8, 122u8, + 177u8, 72u8, 233u8, 213u8, 11u8, 82u8, 225u8, 169u8, 192u8, 91u8, + 135u8, 199u8, 118u8, 158u8, 151u8, 243u8, 131u8, 92u8, 120u8, 189u8, + 144u8, 150u8, ], ) } @@ -6297,10 +6295,9 @@ pub mod api { "sudo_unchecked_weight", types::SudoUncheckedWeight { call: ::std::boxed::Box::new(call), weight }, [ - 57u8, 92u8, 112u8, 215u8, 136u8, 18u8, 172u8, 244u8, 197u8, 25u8, - 221u8, 103u8, 153u8, 113u8, 92u8, 31u8, 116u8, 230u8, 207u8, 161u8, - 2u8, 99u8, 152u8, 75u8, 1u8, 175u8, 142u8, 5u8, 123u8, 75u8, 69u8, - 179u8, + 218u8, 202u8, 109u8, 103u8, 202u8, 40u8, 249u8, 151u8, 196u8, 167u8, + 97u8, 156u8, 215u8, 55u8, 155u8, 191u8, 29u8, 77u8, 72u8, 212u8, 116u8, + 81u8, 87u8, 234u8, 57u8, 110u8, 205u8, 89u8, 158u8, 216u8, 196u8, 69u8, ], ) } @@ -6331,10 +6328,10 @@ pub mod api { "sudo_as", types::SudoAs { who, call: ::std::boxed::Box::new(call) }, [ - 178u8, 148u8, 60u8, 101u8, 27u8, 124u8, 205u8, 71u8, 169u8, 14u8, - 171u8, 188u8, 198u8, 251u8, 75u8, 231u8, 242u8, 232u8, 100u8, 95u8, - 173u8, 85u8, 150u8, 142u8, 166u8, 68u8, 39u8, 164u8, 3u8, 61u8, 154u8, - 171u8, + 181u8, 104u8, 0u8, 75u8, 184u8, 223u8, 186u8, 149u8, 143u8, 127u8, + 131u8, 248u8, 5u8, 159u8, 114u8, 207u8, 11u8, 141u8, 154u8, 13u8, + 149u8, 81u8, 47u8, 71u8, 100u8, 129u8, 10u8, 243u8, 152u8, 170u8, 1u8, + 80u8, ], ) } @@ -8887,6 +8884,23 @@ pub mod api { const PALLET: &'static str = "Ismp"; const CALL: &'static str = "update_consensus_state"; } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + :: subxt :: ext :: scale_decode :: DecodeAsType, + :: subxt :: ext :: scale_encode :: EncodeAsType, + Debug, + )] + # [codec (crate = :: subxt :: ext :: codec)] + #[decode_as_type(crate_path = ":: subxt :: ext :: scale_decode")] + #[encode_as_type(crate_path = ":: subxt :: ext :: scale_encode")] + pub struct ValidateMessages { + pub messages: ::std::vec::Vec, + } + impl ::subxt::blocks::StaticExtrinsic for ValidateMessages { + const PALLET: &'static str = "Ismp"; + const CALL: &'static str = "validate_messages"; + } } pub struct TransactionApi; impl TransactionApi { @@ -8940,6 +8954,23 @@ pub mod api { ], ) } + #[doc = "See [`Pallet::validate_messages`]."] + pub fn validate_messages( + &self, + messages: ::std::vec::Vec, + ) -> ::subxt::tx::Payload { + ::subxt::tx::Payload::new_static( + "Ismp", + "validate_messages", + types::ValidateMessages { messages }, + [ + 237u8, 199u8, 118u8, 107u8, 240u8, 108u8, 176u8, 194u8, 116u8, 19u8, + 246u8, 24u8, 178u8, 209u8, 45u8, 172u8, 225u8, 145u8, 134u8, 194u8, + 222u8, 158u8, 86u8, 175u8, 186u8, 223u8, 216u8, 80u8, 87u8, 122u8, + 251u8, 231u8, + ], + ) + } } } #[doc = "The `Event` enum of this pallet"] @@ -10305,12 +10336,12 @@ pub mod api { } } } - pub mod relayer_fees { + pub mod relayer { use super::{root_mod, runtime_types}; #[doc = "The `Error` enum of this pallet."] - pub type Error = runtime_types::pallet_relayer_fees::pallet::Error; + pub type Error = runtime_types::pallet_ismp_relayer::pallet::Error; #[doc = "Contains a variant per dispatchable extrinsic that this pallet has."] - pub type Call = runtime_types::pallet_relayer_fees::pallet::Call; + pub type Call = runtime_types::pallet_ismp_relayer::pallet::Call; pub mod calls { use super::{root_mod, runtime_types}; type DispatchError = runtime_types::sp_runtime::DispatchError; @@ -10328,10 +10359,10 @@ pub mod api { #[encode_as_type(crate_path = ":: subxt :: ext :: scale_encode")] pub struct AccumulateFees { pub withdrawal_proof: - runtime_types::pallet_relayer_fees::withdrawal::WithdrawalProof, + runtime_types::pallet_ismp_relayer::withdrawal::WithdrawalProof, } impl ::subxt::blocks::StaticExtrinsic for AccumulateFees { - const PALLET: &'static str = "RelayerFees"; + const PALLET: &'static str = "Relayer"; const CALL: &'static str = "accumulate_fees"; } #[derive( @@ -10346,10 +10377,10 @@ pub mod api { #[encode_as_type(crate_path = ":: subxt :: ext :: scale_encode")] pub struct WithdrawFees { pub withdrawal_data: - runtime_types::pallet_relayer_fees::withdrawal::WithdrawalInputData, + runtime_types::pallet_ismp_relayer::withdrawal::WithdrawalInputData, } impl ::subxt::blocks::StaticExtrinsic for WithdrawFees { - const PALLET: &'static str = "RelayerFees"; + const PALLET: &'static str = "Relayer"; const CALL: &'static str = "withdraw_fees"; } } @@ -10358,10 +10389,10 @@ pub mod api { #[doc = "See [`Pallet::accumulate_fees`]."] pub fn accumulate_fees( &self, - withdrawal_proof : runtime_types :: pallet_relayer_fees :: withdrawal :: WithdrawalProof, + withdrawal_proof : runtime_types :: pallet_ismp_relayer :: withdrawal :: WithdrawalProof, ) -> ::subxt::tx::Payload { ::subxt::tx::Payload::new_static( - "RelayerFees", + "Relayer", "accumulate_fees", types::AccumulateFees { withdrawal_proof }, [ @@ -10374,10 +10405,10 @@ pub mod api { #[doc = "See [`Pallet::withdraw_fees`]."] pub fn withdraw_fees( &self, - withdrawal_data : runtime_types :: pallet_relayer_fees :: withdrawal :: WithdrawalInputData, + withdrawal_data : runtime_types :: pallet_ismp_relayer :: withdrawal :: WithdrawalInputData, ) -> ::subxt::tx::Payload { ::subxt::tx::Payload::new_static( - "RelayerFees", + "Relayer", "withdraw_fees", types::WithdrawFees { withdrawal_data }, [ @@ -10390,12 +10421,61 @@ pub mod api { } } } + #[doc = "Events emiited by the relayer pallet"] + pub type Event = runtime_types::pallet_ismp_relayer::pallet::Event; + pub mod events { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + :: subxt :: ext :: scale_decode :: DecodeAsType, + :: subxt :: ext :: scale_encode :: EncodeAsType, + Debug, + )] + # [codec (crate = :: subxt :: ext :: codec)] + #[decode_as_type(crate_path = ":: subxt :: ext :: scale_decode")] + #[encode_as_type(crate_path = ":: subxt :: ext :: scale_encode")] + #[doc = "A relayer with the [`address`] has accumulated some fees on the [`state_machine`]"] + pub struct AccumulateFees { + pub address: runtime_types::bounded_collections::bounded_vec::BoundedVec< + ::core::primitive::u8, + >, + pub state_machine: runtime_types::ismp::host::StateMachine, + pub amount: runtime_types::primitive_types::U256, + } + impl ::subxt::events::StaticEvent for AccumulateFees { + const PALLET: &'static str = "Relayer"; + const EVENT: &'static str = "AccumulateFees"; + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + :: subxt :: ext :: scale_decode :: DecodeAsType, + :: subxt :: ext :: scale_encode :: EncodeAsType, + Debug, + )] + # [codec (crate = :: subxt :: ext :: codec)] + #[decode_as_type(crate_path = ":: subxt :: ext :: scale_decode")] + #[encode_as_type(crate_path = ":: subxt :: ext :: scale_encode")] + #[doc = "A relayer with the the [`address`] has initiated a withdrawal on the [`state_machine`]"] + pub struct Withdraw { + pub address: runtime_types::bounded_collections::bounded_vec::BoundedVec< + ::core::primitive::u8, + >, + pub state_machine: runtime_types::ismp::host::StateMachine, + pub amount: runtime_types::primitive_types::U256, + } + impl ::subxt::events::StaticEvent for Withdraw { + const PALLET: &'static str = "Relayer"; + const EVENT: &'static str = "Withdraw"; + } + } pub mod storage { use super::runtime_types; pub struct StorageApi; impl StorageApi { #[doc = " double map of address to source chain, which holds the amount of the relayer address"] - pub fn relayer_fees( + pub fn fees( &self, _0: impl ::std::borrow::Borrow, _1: impl ::std::borrow::Borrow<[::core::primitive::u8]>, @@ -10407,22 +10487,22 @@ pub mod api { ::subxt::storage::address::Yes, > { ::subxt::storage::address::Address::new_static( - "RelayerFees", - "RelayerFees", + "Relayer", + "Fees", vec![ ::subxt::storage::address::make_static_storage_map_key(_0.borrow()), ::subxt::storage::address::make_static_storage_map_key(_1.borrow()), ], [ - 240u8, 48u8, 101u8, 194u8, 97u8, 234u8, 84u8, 66u8, 164u8, 124u8, - 119u8, 221u8, 69u8, 175u8, 127u8, 179u8, 253u8, 124u8, 215u8, 27u8, - 154u8, 215u8, 96u8, 78u8, 53u8, 190u8, 176u8, 226u8, 56u8, 49u8, 233u8, - 254u8, + 101u8, 173u8, 207u8, 100u8, 23u8, 157u8, 168u8, 60u8, 218u8, 251u8, + 154u8, 121u8, 118u8, 108u8, 126u8, 251u8, 128u8, 77u8, 161u8, 227u8, + 201u8, 112u8, 76u8, 108u8, 14u8, 159u8, 67u8, 54u8, 59u8, 84u8, 47u8, + 9u8, ], ) } #[doc = " double map of address to source chain, which holds the amount of the relayer address"] - pub fn relayer_fees_root( + pub fn fees_root( &self, ) -> ::subxt::storage::address::Address< ::subxt::storage::address::StaticStorageMapKey, @@ -10432,14 +10512,14 @@ pub mod api { ::subxt::storage::address::Yes, > { ::subxt::storage::address::Address::new_static( - "RelayerFees", - "RelayerFees", + "Relayer", + "Fees", Vec::new(), [ - 240u8, 48u8, 101u8, 194u8, 97u8, 234u8, 84u8, 66u8, 164u8, 124u8, - 119u8, 221u8, 69u8, 175u8, 127u8, 179u8, 253u8, 124u8, 215u8, 27u8, - 154u8, 215u8, 96u8, 78u8, 53u8, 190u8, 176u8, 226u8, 56u8, 49u8, 233u8, - 254u8, + 101u8, 173u8, 207u8, 100u8, 23u8, 157u8, 168u8, 60u8, 218u8, 251u8, + 154u8, 121u8, 118u8, 108u8, 126u8, 251u8, 128u8, 77u8, 161u8, 227u8, + 201u8, 112u8, 76u8, 108u8, 14u8, 159u8, 67u8, 54u8, 59u8, 84u8, 47u8, + 9u8, ], ) } @@ -10455,7 +10535,7 @@ pub mod api { ::subxt::storage::address::Yes, > { ::subxt::storage::address::Address::new_static( - "RelayerFees", + "Relayer", "Nonce", vec![::subxt::storage::address::make_static_storage_map_key(_0.borrow())], [ @@ -10477,7 +10557,7 @@ pub mod api { ::subxt::storage::address::Yes, > { ::subxt::storage::address::Address::new_static( - "RelayerFees", + "Relayer", "Nonce", Vec::new(), [ @@ -10488,57 +10568,48 @@ pub mod api { ], ) } - } - } - } - pub mod ismp_polygon_pos { - use super::{root_mod, runtime_types}; - pub mod storage { - use super::runtime_types; - pub struct StorageApi; - impl StorageApi { - #[doc = " Polygon block headers"] - pub fn headers( + #[doc = " Request and response commitments that have been claimed"] + pub fn claimed( &self, _0: impl ::std::borrow::Borrow<::subxt::utils::H256>, ) -> ::subxt::storage::address::Address< ::subxt::storage::address::StaticStorageMapKey, - runtime_types::geth_primitives::CodecHeader, + ::core::primitive::bool, + ::subxt::storage::address::Yes, ::subxt::storage::address::Yes, - (), ::subxt::storage::address::Yes, > { ::subxt::storage::address::Address::new_static( - "IsmpPolygonPos", - "Headers", + "Relayer", + "Claimed", vec![::subxt::storage::address::make_static_storage_map_key(_0.borrow())], [ - 156u8, 180u8, 34u8, 103u8, 55u8, 77u8, 87u8, 126u8, 200u8, 29u8, 66u8, - 156u8, 183u8, 193u8, 197u8, 218u8, 89u8, 134u8, 60u8, 177u8, 112u8, - 243u8, 144u8, 226u8, 203u8, 252u8, 86u8, 207u8, 51u8, 158u8, 38u8, - 149u8, + 172u8, 193u8, 164u8, 35u8, 40u8, 35u8, 217u8, 209u8, 141u8, 218u8, + 195u8, 160u8, 124u8, 100u8, 81u8, 223u8, 247u8, 129u8, 55u8, 117u8, + 143u8, 196u8, 243u8, 5u8, 41u8, 139u8, 201u8, 226u8, 75u8, 133u8, 74u8, + 233u8, ], ) } - #[doc = " Polygon block headers"] - pub fn headers_root( + #[doc = " Request and response commitments that have been claimed"] + pub fn claimed_root( &self, ) -> ::subxt::storage::address::Address< ::subxt::storage::address::StaticStorageMapKey, - runtime_types::geth_primitives::CodecHeader, - (), + ::core::primitive::bool, (), ::subxt::storage::address::Yes, + ::subxt::storage::address::Yes, > { ::subxt::storage::address::Address::new_static( - "IsmpPolygonPos", - "Headers", + "Relayer", + "Claimed", Vec::new(), [ - 156u8, 180u8, 34u8, 103u8, 55u8, 77u8, 87u8, 126u8, 200u8, 29u8, 66u8, - 156u8, 183u8, 193u8, 197u8, 218u8, 89u8, 134u8, 60u8, 177u8, 112u8, - 243u8, 144u8, 226u8, 203u8, 252u8, 86u8, 207u8, 51u8, 158u8, 38u8, - 149u8, + 172u8, 193u8, 164u8, 35u8, 40u8, 35u8, 217u8, 209u8, 141u8, 218u8, + 195u8, 160u8, 124u8, 100u8, 81u8, 223u8, 247u8, 129u8, 55u8, 117u8, + 143u8, 196u8, 243u8, 5u8, 41u8, 139u8, 201u8, 226u8, 75u8, 133u8, 74u8, + 233u8, ], ) } @@ -11369,37 +11440,6 @@ pub mod api { >, } } - pub mod ethbloom { - use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - :: subxt :: ext :: scale_decode :: DecodeAsType, - :: subxt :: ext :: scale_encode :: EncodeAsType, - Debug, - )] - # [codec (crate = :: subxt :: ext :: codec)] - #[decode_as_type(crate_path = ":: subxt :: ext :: scale_decode")] - #[encode_as_type(crate_path = ":: subxt :: ext :: scale_encode")] - pub struct Bloom(pub [::core::primitive::u8; 256usize]); - } - pub mod ethereum_types { - use super::runtime_types; - pub mod hash { - use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - :: subxt :: ext :: scale_decode :: DecodeAsType, - :: subxt :: ext :: scale_encode :: EncodeAsType, - Debug, - )] - # [codec (crate = :: subxt :: ext :: codec)] - #[decode_as_type(crate_path = ":: subxt :: ext :: scale_decode")] - #[encode_as_type(crate_path = ":: subxt :: ext :: scale_encode")] - pub struct H64(pub [::core::primitive::u8; 8usize]); - } - } pub mod frame_support { use super::runtime_types; pub mod dispatch { @@ -11950,7 +11990,7 @@ pub mod api { #[codec(index = 42)] IsmpDemo(runtime_types::ismp_demo::pallet::Call), #[codec(index = 43)] - RelayerFees(runtime_types::pallet_relayer_fees::pallet::Call), + Relayer(runtime_types::pallet_ismp_relayer::pallet::Call), #[codec(index = 45)] StateMachineManager(runtime_types::state_machine_manager::pallet::Call), } @@ -11990,7 +12030,7 @@ pub mod api { #[codec(index = 42)] IsmpDemo(runtime_types::ismp_demo::pallet::Error), #[codec(index = 43)] - RelayerFees(runtime_types::pallet_relayer_fees::pallet::Error), + Relayer(runtime_types::pallet_ismp_relayer::pallet::Error), #[codec(index = 45)] StateMachineManager(runtime_types::state_machine_manager::pallet::Error), } @@ -12031,6 +12071,8 @@ pub mod api { Ismp(runtime_types::pallet_ismp::pallet::Event), #[codec(index = 42)] IsmpDemo(runtime_types::ismp_demo::pallet::Event), + #[codec(index = 43)] + Relayer(runtime_types::pallet_ismp_relayer::pallet::Event), #[codec(index = 45)] StateMachineManager(runtime_types::state_machine_manager::pallet::Event), } @@ -12059,38 +12101,6 @@ pub mod api { pub aura: runtime_types::sp_consensus_aura::sr25519::app_sr25519::Public, } } - pub mod geth_primitives { - use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - :: subxt :: ext :: scale_decode :: DecodeAsType, - :: subxt :: ext :: scale_encode :: EncodeAsType, - Debug, - )] - # [codec (crate = :: subxt :: ext :: codec)] - #[decode_as_type(crate_path = ":: subxt :: ext :: scale_decode")] - #[encode_as_type(crate_path = ":: subxt :: ext :: scale_encode")] - pub struct CodecHeader { - pub parent_hash: ::subxt::utils::H256, - pub uncle_hash: ::subxt::utils::H256, - pub coinbase: ::subxt::utils::H160, - pub state_root: ::subxt::utils::H256, - pub transactions_root: ::subxt::utils::H256, - pub receipts_root: ::subxt::utils::H256, - pub logs_bloom: runtime_types::ethbloom::Bloom, - pub difficulty: runtime_types::primitive_types::U256, - pub number: runtime_types::primitive_types::U256, - pub gas_limit: ::core::primitive::u64, - pub gas_used: ::core::primitive::u64, - pub timestamp: ::core::primitive::u64, - pub extra_data: ::std::vec::Vec<::core::primitive::u8>, - pub mix_hash: ::subxt::utils::H256, - pub nonce: runtime_types::ethereum_types::hash::H64, - pub base_fee_per_gas: ::core::option::Option, - pub withdrawals_hash: ::core::option::Option<::subxt::utils::H256>, - } - } pub mod ismp { use super::runtime_types; pub mod consensus { @@ -13370,6 +13380,11 @@ pub mod api { update_consensus_state { message: runtime_types::pallet_ismp::pallet::UpdateConsensusState, }, + #[codec(index = 3)] + #[doc = "See [`Pallet::validate_messages`]."] + validate_messages { + messages: ::std::vec::Vec, + }, } #[derive( :: subxt :: ext :: codec :: Decode, @@ -13552,6 +13567,189 @@ pub mod api { pub relayer: ::std::vec::Vec<::core::primitive::u8>, } } + pub mod pallet_ismp_relayer { + use super::runtime_types; + pub mod pallet { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + :: subxt :: ext :: scale_decode :: DecodeAsType, + :: subxt :: ext :: scale_encode :: EncodeAsType, + Debug, + )] + # [codec (crate = :: subxt :: ext :: codec)] + #[decode_as_type(crate_path = ":: subxt :: ext :: scale_decode")] + #[encode_as_type(crate_path = ":: subxt :: ext :: scale_encode")] + #[doc = "Contains a variant per dispatchable extrinsic that this pallet has."] + pub enum Call { + #[codec(index = 0)] + #[doc = "See [`Pallet::accumulate_fees`]."] + accumulate_fees { + withdrawal_proof: + runtime_types::pallet_ismp_relayer::withdrawal::WithdrawalProof, + }, + #[codec(index = 1)] + #[doc = "See [`Pallet::withdraw_fees`]."] + withdraw_fees { + withdrawal_data: + runtime_types::pallet_ismp_relayer::withdrawal::WithdrawalInputData, + }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + :: subxt :: ext :: scale_decode :: DecodeAsType, + :: subxt :: ext :: scale_encode :: EncodeAsType, + Debug, + )] + # [codec (crate = :: subxt :: ext :: codec)] + #[decode_as_type(crate_path = ":: subxt :: ext :: scale_decode")] + #[encode_as_type(crate_path = ":: subxt :: ext :: scale_encode")] + #[doc = "The `Error` enum of this pallet."] + pub enum Error { + #[codec(index = 0)] + #[doc = "Withdrawal Proof Validation Error"] + ProofValidationError, + #[codec(index = 1)] + #[doc = "Invalid Public Key"] + InvalidPublicKey, + #[codec(index = 2)] + #[doc = "Invalid Withdrawal signature"] + InvalidSignature, + #[codec(index = 3)] + #[doc = "Empty balance"] + EmptyBalance, + #[codec(index = 4)] + #[doc = "Invalid Amount"] + InvalidAmount, + #[codec(index = 5)] + #[doc = "Relayer Manager Address on Dest chain not set"] + MissingMangerAddress, + #[codec(index = 6)] + #[doc = "Failed to dispatch request"] + DispatchFailed, + #[codec(index = 7)] + #[doc = "Error"] + ErrorCompletingCall, + #[codec(index = 8)] + #[doc = "Missing commitments"] + MissingCommitments, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + :: subxt :: ext :: scale_decode :: DecodeAsType, + :: subxt :: ext :: scale_encode :: EncodeAsType, + Debug, + )] + # [codec (crate = :: subxt :: ext :: codec)] + #[decode_as_type(crate_path = ":: subxt :: ext :: scale_decode")] + #[encode_as_type(crate_path = ":: subxt :: ext :: scale_encode")] + #[doc = "Events emiited by the relayer pallet"] + pub enum Event { + #[codec(index = 0)] + #[doc = "A relayer with the [`address`] has accumulated some fees on the [`state_machine`]"] + AccumulateFees { + address: runtime_types::bounded_collections::bounded_vec::BoundedVec< + ::core::primitive::u8, + >, + state_machine: runtime_types::ismp::host::StateMachine, + amount: runtime_types::primitive_types::U256, + }, + #[codec(index = 1)] + #[doc = "A relayer with the the [`address`] has initiated a withdrawal on the [`state_machine`]"] + Withdraw { + address: runtime_types::bounded_collections::bounded_vec::BoundedVec< + ::core::primitive::u8, + >, + state_machine: runtime_types::ismp::host::StateMachine, + amount: runtime_types::primitive_types::U256, + }, + } + } + pub mod withdrawal { + use super::runtime_types; + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + :: subxt :: ext :: scale_decode :: DecodeAsType, + :: subxt :: ext :: scale_encode :: EncodeAsType, + Debug, + )] + # [codec (crate = :: subxt :: ext :: codec)] + #[decode_as_type(crate_path = ":: subxt :: ext :: scale_decode")] + #[encode_as_type(crate_path = ":: subxt :: ext :: scale_encode")] + pub enum Key { + #[codec(index = 0)] + Request(::subxt::utils::H256), + #[codec(index = 1)] + Response { + request_commitment: ::subxt::utils::H256, + response_commitment: ::subxt::utils::H256, + }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + :: subxt :: ext :: scale_decode :: DecodeAsType, + :: subxt :: ext :: scale_encode :: EncodeAsType, + Debug, + )] + # [codec (crate = :: subxt :: ext :: codec)] + #[decode_as_type(crate_path = ":: subxt :: ext :: scale_decode")] + #[encode_as_type(crate_path = ":: subxt :: ext :: scale_encode")] + pub enum Signature { + #[codec(index = 0)] + Ethereum { + address: ::std::vec::Vec<::core::primitive::u8>, + signature: ::std::vec::Vec<::core::primitive::u8>, + }, + #[codec(index = 1)] + Sr25519 { + public_key: ::std::vec::Vec<::core::primitive::u8>, + signature: ::std::vec::Vec<::core::primitive::u8>, + }, + #[codec(index = 2)] + Ed25519 { + public_key: ::std::vec::Vec<::core::primitive::u8>, + signature: ::std::vec::Vec<::core::primitive::u8>, + }, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + :: subxt :: ext :: scale_decode :: DecodeAsType, + :: subxt :: ext :: scale_encode :: EncodeAsType, + Debug, + )] + # [codec (crate = :: subxt :: ext :: codec)] + #[decode_as_type(crate_path = ":: subxt :: ext :: scale_decode")] + #[encode_as_type(crate_path = ":: subxt :: ext :: scale_encode")] + pub struct WithdrawalInputData { + pub signature: runtime_types::pallet_ismp_relayer::withdrawal::Signature, + pub dest_chain: runtime_types::ismp::host::StateMachine, + pub amount: runtime_types::primitive_types::U256, + pub gas_limit: ::core::primitive::u64, + } + #[derive( + :: subxt :: ext :: codec :: Decode, + :: subxt :: ext :: codec :: Encode, + :: subxt :: ext :: scale_decode :: DecodeAsType, + :: subxt :: ext :: scale_encode :: EncodeAsType, + Debug, + )] + # [codec (crate = :: subxt :: ext :: codec)] + #[decode_as_type(crate_path = ":: subxt :: ext :: scale_decode")] + #[encode_as_type(crate_path = ":: subxt :: ext :: scale_encode")] + pub struct WithdrawalProof { + pub commitments: + ::std::vec::Vec, + pub source_proof: runtime_types::ismp::messaging::Proof, + pub dest_proof: runtime_types::ismp::messaging::Proof, + } + } + } pub mod pallet_message_queue { use super::runtime_types; pub mod pallet { @@ -13728,158 +13926,6 @@ pub mod api { >, } } - pub mod pallet_relayer_fees { - use super::runtime_types; - pub mod pallet { - use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - :: subxt :: ext :: scale_decode :: DecodeAsType, - :: subxt :: ext :: scale_encode :: EncodeAsType, - Debug, - )] - # [codec (crate = :: subxt :: ext :: codec)] - #[decode_as_type(crate_path = ":: subxt :: ext :: scale_decode")] - #[encode_as_type(crate_path = ":: subxt :: ext :: scale_encode")] - #[doc = "Contains a variant per dispatchable extrinsic that this pallet has."] - pub enum Call { - #[codec(index = 0)] - #[doc = "See [`Pallet::accumulate_fees`]."] - accumulate_fees { - withdrawal_proof: - runtime_types::pallet_relayer_fees::withdrawal::WithdrawalProof, - }, - #[codec(index = 1)] - #[doc = "See [`Pallet::withdraw_fees`]."] - withdraw_fees { - withdrawal_data: - runtime_types::pallet_relayer_fees::withdrawal::WithdrawalInputData, - }, - } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - :: subxt :: ext :: scale_decode :: DecodeAsType, - :: subxt :: ext :: scale_encode :: EncodeAsType, - Debug, - )] - # [codec (crate = :: subxt :: ext :: codec)] - #[decode_as_type(crate_path = ":: subxt :: ext :: scale_decode")] - #[encode_as_type(crate_path = ":: subxt :: ext :: scale_encode")] - #[doc = "The `Error` enum of this pallet."] - pub enum Error { - #[codec(index = 0)] - #[doc = "Withdrawal Proof Validation Error"] - ProofValidationError, - #[codec(index = 1)] - #[doc = "Invalid Public Key"] - InvalidPublicKey, - #[codec(index = 2)] - #[doc = "Invalid Withdrawal signature"] - InvalidSignature, - #[codec(index = 3)] - #[doc = "Empty balance"] - EmptyBalance, - #[codec(index = 4)] - #[doc = "Invalid Amount"] - InvalidAmount, - #[codec(index = 5)] - #[doc = "Relayer Manager Address on Dest chain not set"] - MissingMangerAddress, - #[codec(index = 6)] - #[doc = "Failed to dispatch request"] - DispatchFailed, - #[codec(index = 7)] - #[doc = "Error"] - ErrorCompletingCall, - #[codec(index = 8)] - #[doc = "Missing commitments"] - MissingCommitments, - } - } - pub mod withdrawal { - use super::runtime_types; - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - :: subxt :: ext :: scale_decode :: DecodeAsType, - :: subxt :: ext :: scale_encode :: EncodeAsType, - Debug, - )] - # [codec (crate = :: subxt :: ext :: codec)] - #[decode_as_type(crate_path = ":: subxt :: ext :: scale_decode")] - #[encode_as_type(crate_path = ":: subxt :: ext :: scale_encode")] - pub enum Key { - #[codec(index = 0)] - Request(::subxt::utils::H256), - #[codec(index = 1)] - Response { - request_commitment: ::subxt::utils::H256, - response_commitment: ::subxt::utils::H256, - }, - } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - :: subxt :: ext :: scale_decode :: DecodeAsType, - :: subxt :: ext :: scale_encode :: EncodeAsType, - Debug, - )] - # [codec (crate = :: subxt :: ext :: codec)] - #[decode_as_type(crate_path = ":: subxt :: ext :: scale_decode")] - #[encode_as_type(crate_path = ":: subxt :: ext :: scale_encode")] - pub enum Signature { - #[codec(index = 0)] - Ethereum { - address: ::std::vec::Vec<::core::primitive::u8>, - signature: ::std::vec::Vec<::core::primitive::u8>, - }, - #[codec(index = 1)] - Sr25519 { - public_key: ::std::vec::Vec<::core::primitive::u8>, - signature: ::std::vec::Vec<::core::primitive::u8>, - }, - #[codec(index = 2)] - Ed25519 { - public_key: ::std::vec::Vec<::core::primitive::u8>, - signature: ::std::vec::Vec<::core::primitive::u8>, - }, - } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - :: subxt :: ext :: scale_decode :: DecodeAsType, - :: subxt :: ext :: scale_encode :: EncodeAsType, - Debug, - )] - # [codec (crate = :: subxt :: ext :: codec)] - #[decode_as_type(crate_path = ":: subxt :: ext :: scale_decode")] - #[encode_as_type(crate_path = ":: subxt :: ext :: scale_encode")] - pub struct WithdrawalInputData { - pub signature: runtime_types::pallet_relayer_fees::withdrawal::Signature, - pub dest_chain: runtime_types::ismp::host::StateMachine, - pub amount: runtime_types::primitive_types::U256, - pub gas_limit: ::core::primitive::u64, - } - #[derive( - :: subxt :: ext :: codec :: Decode, - :: subxt :: ext :: codec :: Encode, - :: subxt :: ext :: scale_decode :: DecodeAsType, - :: subxt :: ext :: scale_encode :: EncodeAsType, - Debug, - )] - # [codec (crate = :: subxt :: ext :: codec)] - #[decode_as_type(crate_path = ":: subxt :: ext :: scale_decode")] - #[encode_as_type(crate_path = ":: subxt :: ext :: scale_encode")] - pub struct WithdrawalProof { - pub commitments: - ::std::vec::Vec, - pub source_proof: runtime_types::ismp::messaging::Proof, - pub dest_proof: runtime_types::ismp::messaging::Proof, - } - } - } pub mod pallet_session { use super::runtime_types; pub mod pallet { diff --git a/telemetry/src/main.rs b/telemetry/src/main.rs index c387ffaae..cf1dfbbbc 100644 --- a/telemetry/src/main.rs +++ b/telemetry/src/main.rs @@ -157,6 +157,7 @@ mod tests { use std::{thread, time::Duration}; #[test] + #[ignore] fn test_socket_io_client() -> Result<(), anyhow::Error> { let pair = ecdsa::Pair::from_seed(&SECRET_KEY); let mut message = diff --git a/test-config.toml b/test-config.toml index 5f1df5947..bfad8f4f7 100644 --- a/test-config.toml +++ b/test-config.toml @@ -1,48 +1,17 @@ # Required [hyperbridge] -state_machine = { Kusama = 2000 } +chain = "Dev" hashing = "Keccak" -consensus_state_id = "PARA" # empty on other chains -relay_rpc_ws = "ws://127.0.0.1:9944" -chain_rpc_ws = "ws://127.0.0.1:9933" -consensus_update_frequency = 390 # seconds, -para_ids = [4296] -epoch_length = 600 -signer = "e5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a" - -[ethereum] -type = "ethereum_sepolia" -state_machine = { Ethereum = "ExecutionLayer" } -beacon_http_url = "https://127.0.0.1:443" -execution_ws = "wss://127.0.0.1:7000" -consensus_state_id = "ET14" -ismp_host = "0x5b5f63c8f3985cafe1ce53e6374f42ab60de5a6b" -handler = "0x577efa5c6184e10d54fda5eb195f7eca30644082" -signer = "2e0834786285daccd064ca17f1654f67b4aef298acbb82cef9ec422fb4975622" -latest_height = "LastMessaging" -consensus_update_frequency = 30 # seconds, -gas_limit = 20000000 - - -[base] -type = "base" -state_machine = { Ethereum = "Base" } -beacon_rpc_url = "wss://127.0.0.1:8565" -execution_ws = "wss://127.0.0.1:8000" -l2_oracle = "0x84457ca9D0163FbC4bbfe4Dfbb20ba46e48DF254" -message_parser = "0x4200000000000000000000000000000000000016" -consensus_state_id = "ET14" -ismp_host = "0x87825f839d95c6021c0e821917F93aDB299eD6F8" -handler = "0x3cfb5eE8D00c2620e0A63FD25deAA2d7a671F449" -signer = "2e0834786285daccd064ca17f1654f67b4aef298acbb82cef9ec422fb4975622" -latest_height = "LastMessaging" -gas_limit = 10000000 - +rpc_ws = "ws://127.0.0.1:9933" +signer = "" # Required [relayer] +chain = "Dev" consensus = false messaging = true fisherman = false router = { Kusama = 4296 } -module_filter = [] \ No newline at end of file +module_filter = [] +minimum_profit_percentage = 0 +delivery_endpoints = [] \ No newline at end of file