Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions packages/relayer/lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ anyhow = { workspace = true, features = ["std"] }
futures = { workspace = true, default-features = true }
futures-timer = { workspace = true }
tracing = { workspace = true, default-features = true }
tonic = { workspace = true }

tendermint = { workspace = true, features = ["std"] }
tendermint-rpc = { workspace = true, features = ["http-client"] }
tendermint-light-client-verifier = { workspace = true }

ibc-proto-eureka = { workspace = true }
ibc-core-commitment-types = { workspace = true }
ics23 = { workspace = true }

alloy = { workspace = true, features = ["full", "node-bindings"] }

Expand Down
2 changes: 2 additions & 0 deletions packages/relayer/lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@ use ibc_core_commitment_types as _;
pub mod chain;
pub mod events;
pub mod listener;
pub mod service_utils;
pub mod tendermint_client;
pub mod tx_builder;
pub mod utils;
46 changes: 46 additions & 0 deletions packages/relayer/lib/src/service_utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//! Common service patterns and utilities for relayer modules
//! This module provides shared functionality for `RelayerService` implementations
use tendermint::Hash;
use tonic::Status;

/// Convert `anyhow::Error` to `tonic::Status`
#[inline]
#[must_use]
pub fn to_tonic_status(err: anyhow::Error) -> Status {
Status::from_error(err.into())
}

/// Parse Cosmos transaction hashes from request
///
/// # Errors
///
/// Returns a `Status` error if any transaction ID cannot be parsed as a valid hash
#[inline]
#[allow(clippy::result_large_err)]
pub fn parse_cosmos_tx_hashes(tx_ids: Vec<Vec<u8>>) -> Result<Vec<Hash>, Status> {
tx_ids
.into_iter()
.map(Hash::try_from)
.collect::<Result<Vec<_>, _>>()
.map_err(|e| Status::from_error(e.into()))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably should use to_tonic_status from above.

}

/// Parse Ethereum transaction hashes from request
///
/// # Errors
///
/// Returns a `Status` error if any transaction ID is not exactly 32 bytes
#[inline]
#[allow(clippy::result_large_err)]
pub fn parse_eth_tx_hashes(tx_ids: Vec<Vec<u8>>) -> Result<Vec<[u8; 32]>, Status> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it better to return TxHash?

tx_ids
.into_iter()
.map(|tx_id| {
tx_id
.try_into()
.map_err(|tx| format!("invalid tx hash: {tx:?}"))
})
.collect::<Result<Vec<[u8; 32]>, _>>()
.map_err(|e| Status::from_error(e.into()))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto: probably should use to_tonic_status

}
59 changes: 59 additions & 0 deletions packages/relayer/lib/src/tendermint_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//! Utilities for Tendermint light client configuration
//! This module provides common functionality for creating and configuring Tendermint client states
use ibc_proto_eureka::{
google::protobuf::Duration,
ibc::{
core::client::v1::Height,
lightclients::tendermint::v1::{ClientState, Fraction},
},
};

/// Default trust level for Tendermint light clients (1/3)
#[must_use]
pub const fn default_trust_level() -> Fraction {
Fraction {
numerator: 1,
denominator: 3,
}
}

/// Default max clock drift for Tendermint light clients (15 seconds)
#[must_use]
pub const fn default_max_clock_drift() -> Duration {
Duration {
seconds: 15,
nanos: 0,
}
}

/// Build a Tendermint client state with common defaults
///
/// # Arguments
/// * `chain_id` - The chain ID
/// * `height` - The latest height
/// * `trusting_period` - The trusting period
/// * `unbonding_period` - The unbonding period
/// * `proof_specs` - The proof specifications for ICS23
///
/// Returns a `ClientState` with default trust level, max clock drift, and the provided proof specs
#[must_use]
pub fn build_tendermint_client_state(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this function should also take trust_level: Option<Fraction> and max_clock_drift: Option<Duration>.

And if these fields are None, then we use the default values?

chain_id: String,
height: Height,
trusting_period: Duration,
unbonding_period: Duration,
proof_specs: Vec<ics23::ProofSpec>,
) -> ClientState {
ClientState {
chain_id,
trust_level: Some(default_trust_level()),
trusting_period: Some(trusting_period),
unbonding_period: Some(unbonding_period),
max_clock_drift: Some(default_max_clock_drift()),
latest_height: Some(height),
proof_specs,
upgrade_path: vec!["upgrade".to_string(), "upgradedIBCState".to_string()],
..Default::default()
}
}
23 changes: 8 additions & 15 deletions packages/relayer/modules/cosmos-to-cosmos/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,19 @@
unused_crate_dependencies
)]

use ics23 as _;
use tendermint as _;

Comment on lines +11 to +13
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are these needed?

pub mod tx_builder;

use std::collections::HashMap;

use ibc_eureka_relayer_lib::{
listener::{cosmos_sdk, ChainListenerService},
service_utils::{parse_cosmos_tx_hashes, to_tonic_status},
tx_builder::TxBuilderService,
};
use ibc_eureka_utils::rpc::TendermintRpcExt;
use tendermint::Hash;
use tendermint_rpc::HttpClient;
use tonic::{Request, Response};

Expand Down Expand Up @@ -85,7 +88,7 @@ impl RelayerService for CosmosToCosmosRelayerModuleService {
.target_listener
.chain_id()
.await
.map_err(|e| tonic::Status::from_error(e.into()))?,
.map_err(to_tonic_status)?,
ibc_version: "2".to_string(),
ibc_contract: String::new(),
}),
Expand All @@ -94,7 +97,7 @@ impl RelayerService for CosmosToCosmosRelayerModuleService {
.src_listener
.chain_id()
.await
.map_err(|e| tonic::Status::from_error(e.into()))?,
.map_err(to_tonic_status)?,
ibc_version: "2".to_string(),
ibc_contract: String::new(),
}),
Expand All @@ -112,19 +115,9 @@ impl RelayerService for CosmosToCosmosRelayerModuleService {
let inner_req = request.into_inner();
tracing::info!("Got {} source tx IDs", inner_req.source_tx_ids.len());
tracing::info!("Got {} timeout tx IDs", inner_req.timeout_tx_ids.len());
let src_txs = inner_req
.source_tx_ids
.into_iter()
.map(Hash::try_from)
.collect::<Result<Vec<_>, _>>()
.map_err(|e| tonic::Status::from_error(e.into()))?;
let src_txs = parse_cosmos_tx_hashes(inner_req.source_tx_ids)?;

let target_txs = inner_req
.timeout_tx_ids
.into_iter()
.map(Hash::try_from)
.collect::<Result<Vec<_>, _>>()
.map_err(|e| tonic::Status::from_error(e.into()))?;
let target_txs = parse_cosmos_tx_hashes(inner_req.timeout_tx_ids)?;

let src_events = self
.src_listener
Expand Down
34 changes: 13 additions & 21 deletions packages/relayer/modules/cosmos-to-cosmos/src/tx_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ use ibc_proto_eureka::{
google::protobuf::{Any, Duration},
ibc::{
core::client::v1::{Height, MsgCreateClient, MsgUpdateClient},
lightclients::tendermint::v1::{ClientState, Fraction},
lightclients::tendermint::v1::ClientState,
},
};
use prost::Message;
use tendermint_rpc::HttpClient;

use ibc_eureka_relayer_lib::{
chain::CosmosSdk, events::EurekaEventWithHeight, tx_builder::TxBuilderService, utils::cosmos,
chain::CosmosSdk, events::EurekaEventWithHeight,
tendermint_client::build_tendermint_client_state, tx_builder::TxBuilderService, utils::cosmos,
};

/// The `TxBuilder` produces txs to [`CosmosSdk`] based on events from [`CosmosSdk`].
Expand Down Expand Up @@ -167,37 +168,28 @@ impl TxBuilderService<CosmosSdk, CosmosSdk> for TxBuilder {
revision_number: chain_id.revision_number(),
revision_height: latest_light_block.height().value(),
};
let default_trust_level = Fraction {
numerator: 1,
denominator: 3,
};
let default_max_clock_drift = Duration {
seconds: 15,
nanos: 0,
};
let unbonding_period = self
.source_tm_client
.sdk_staking_params()
.await?
.unbonding_time
.ok_or_else(|| anyhow::anyhow!("No unbonding time found"))?;

// Defaults to the recommended 2/3 of the UnbondingPeriod
let trusting_period = Duration {
seconds: 2 * (unbonding_period.seconds / 3),
nanos: 0,
};

let client_state = ClientState {
chain_id: chain_id.to_string(),
trust_level: Some(default_trust_level),
trusting_period: Some(trusting_period),
unbonding_period: Some(unbonding_period),
max_clock_drift: Some(default_max_clock_drift),
latest_height: Some(height),
proof_specs: vec![ics23::iavl_spec(), ics23::tendermint_spec()],
upgrade_path: vec!["upgrade".to_string(), "upgradedIBCState".to_string()],
..Default::default()
};
let proof_specs = vec![ics23::iavl_spec(), ics23::tendermint_spec()];

let client_state = build_tendermint_client_state(
chain_id.to_string(),
height,
trusting_period,
unbonding_period,
proof_specs,
);

let consensus_state = latest_light_block.to_consensus_state();

Expand Down
19 changes: 6 additions & 13 deletions packages/relayer/modules/cosmos-to-eth/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
unused_crate_dependencies
)]

use tendermint as _;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does this do?


pub mod tx_builder;

use alloy::{
Expand All @@ -16,6 +18,7 @@ use alloy::{
};
use ibc_eureka_relayer_lib::{
listener::{cosmos_sdk, eth_eureka, ChainListenerService},
service_utils::{parse_cosmos_tx_hashes, to_tonic_status},
tx_builder::TxBuilderService,
};
use ibc_eureka_utils::rpc::TendermintRpcExt;
Expand All @@ -25,7 +28,6 @@ use sp1_ics07_tendermint_prover::programs::{
};
use sp1_prover::components::CpuProverComponents;
use sp1_sdk::{Prover, ProverClient};
use tendermint::Hash;
use tendermint_rpc::HttpClient;
use tonic::{Request, Response};
use tx_builder::TxBuilder;
Expand Down Expand Up @@ -249,16 +251,12 @@ impl RelayerService for CosmosToEthRelayerModuleService {
.eth_listener
.chain_id()
.await
.map_err(|e| tonic::Status::from_error(e.into()))?,
.map_err(to_tonic_status)?,
ibc_version: "2".to_string(),
ibc_contract: self.tx_builder.ics26_router.address().to_string(),
}),
source_chain: Some(api::Chain {
chain_id: self
.tm_listener
.chain_id()
.await
.map_err(|e| tonic::Status::from_error(e.into()))?,
chain_id: self.tm_listener.chain_id().await.map_err(to_tonic_status)?,
ibc_version: "2".to_string(),
ibc_contract: String::new(),
}),
Expand All @@ -276,12 +274,7 @@ impl RelayerService for CosmosToEthRelayerModuleService {
let inner_req = request.into_inner();
tracing::info!("Got {} source tx IDs", inner_req.source_tx_ids.len());
tracing::info!("Got {} timeout tx IDs", inner_req.timeout_tx_ids.len());
let cosmos_txs = inner_req
.source_tx_ids
.into_iter()
.map(Hash::try_from)
.collect::<Result<Vec<_>, _>>()
.map_err(|e| tonic::Status::from_error(e.into()))?;
let cosmos_txs = parse_cosmos_tx_hashes(inner_req.source_tx_ids)?;

let eth_txs = inner_req
.timeout_tx_ids
Expand Down
30 changes: 9 additions & 21 deletions packages/relayer/modules/eth-to-cosmos/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
unused_crate_dependencies
)]

use tendermint as _;

pub mod tx_builder;

use std::collections::HashMap;
Expand All @@ -19,10 +21,10 @@ use alloy::{
use ibc_eureka_relayer_lib::{
events::EurekaEventWithHeight,
listener::{cosmos_sdk, eth_eureka, ChainListenerService},
service_utils::{parse_cosmos_tx_hashes, parse_eth_tx_hashes, to_tonic_status},
tx_builder::TxBuilderService,
};
use ibc_eureka_utils::rpc::TendermintRpcExt;
use tendermint::Hash;
use tendermint_rpc::HttpClient;
use tonic::{Request, Response};

Expand Down Expand Up @@ -114,11 +116,7 @@ impl RelayerService for EthToCosmosRelayerModuleService {
tracing::info!("Handling info request for Eth to Cosmos...");
Ok(Response::new(api::InfoResponse {
target_chain: Some(api::Chain {
chain_id: self
.tm_listener
.chain_id()
.await
.map_err(|e| tonic::Status::from_error(e.into()))?,
chain_id: self.tm_listener.chain_id().await.map_err(to_tonic_status)?,
ibc_version: "2".to_string(),
ibc_contract: String::new(),
}),
Expand All @@ -127,7 +125,7 @@ impl RelayerService for EthToCosmosRelayerModuleService {
.eth_listener
.chain_id()
.await
.map_err(|e| tonic::Status::from_error(e.into()))?,
.map_err(to_tonic_status)?,
ibc_version: "2".to_string(),
ibc_contract: self.tx_builder.ics26_router_address().to_string(),
}),
Expand All @@ -145,20 +143,10 @@ impl RelayerService for EthToCosmosRelayerModuleService {
let inner_req = request.into_inner();
tracing::info!("Got {} source tx IDs", inner_req.source_tx_ids.len());
tracing::info!("Got {} timeout tx IDs", inner_req.timeout_tx_ids.len());
let eth_txs = inner_req
.source_tx_ids
.into_iter()
.map(TryInto::<[u8; 32]>::try_into)
.map(|tx_hash| tx_hash.map(TxHash::from))
.collect::<Result<Vec<_>, _>>()
.map_err(|tx| tonic::Status::from_error(format!("invalid tx hash: {tx:?}").into()))?;

let cosmos_txs = inner_req
.timeout_tx_ids
.into_iter()
.map(Hash::try_from)
.collect::<Result<Vec<_>, _>>()
.map_err(|e| tonic::Status::from_error(e.into()))?;
let eth_tx_hashes = parse_eth_tx_hashes(inner_req.source_tx_ids)?;
let eth_txs = eth_tx_hashes.into_iter().map(TxHash::from).collect();

let cosmos_txs = parse_cosmos_tx_hashes(inner_req.timeout_tx_ids)?;

let eth_events = self
.eth_listener
Expand Down
Loading