diff --git a/Cargo.toml b/Cargo.toml index d1241440..158068c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,11 @@ +[workspace.package] +version = "0.2.0-dev" +authors = ["Webb Developers "] +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +homepage = "https://webb.tools" +repository = "https://github.com/webb-tools/pallet-eth2-light-client" +edition = "2021" + [workspace] members = [ "pallets/*", @@ -48,7 +56,7 @@ thread = "*" dotenvy = "0.15.7" rand = { version = "0.7.3", default-features = false } rand_chacha = { version = "0.3.1", default-features = false } - +backoff = { version = "0.4.0", features = ["tokio"] } codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } diff --git a/crates/bls/Cargo.toml b/crates/bls/Cargo.toml index ca7f4a44..1d48a277 100644 --- a/crates/bls/Cargo.toml +++ b/crates/bls/Cargo.toml @@ -1,8 +1,10 @@ [package] name = "webb-bls" -version = "0.2.0" +version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } authors = ["Paul Hauner , Webb Developers "] -edition = "2021" + [dependencies] ssz = { package = "webb-eth2-ssz", path = "../ssz", default-features = false } diff --git a/crates/consensus-types/Cargo.toml b/crates/consensus-types/Cargo.toml index 8d27d01d..03243e73 100644 --- a/crates/consensus-types/Cargo.toml +++ b/crates/consensus-types/Cargo.toml @@ -1,8 +1,9 @@ [package] name = "webb-consensus-types" -version = "0.1.0" -authors = ["Webb Developers"] -edition = "2021" +version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +authors = { workspace = true } [dependencies] diff --git a/crates/eth-rpc-client/Cargo.toml b/crates/eth-rpc-client/Cargo.toml index d9ec4c95..cde53da1 100644 --- a/crates/eth-rpc-client/Cargo.toml +++ b/crates/eth-rpc-client/Cargo.toml @@ -1,7 +1,9 @@ [package] name = "webb-eth-rpc-client" -version = "0.1.0" -edition = "2021" +version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +authors = { workspace = true } [dependencies] eth-types = { path = "../eth-types", default-features = false, features = ["eth2", "arbitrary"] } diff --git a/crates/eth-types/Cargo.toml b/crates/eth-types/Cargo.toml index e2187636..f81a9c97 100644 --- a/crates/eth-types/Cargo.toml +++ b/crates/eth-types/Cargo.toml @@ -1,8 +1,10 @@ [package] name = "eth-types" -version = "0.1.0" +version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } authors = ["Near Inc ", "Webb Developers "] -edition = "2021" + [dependencies] ssz = { package = "webb-eth2-ssz", path = "../ssz", default-features = false } diff --git a/crates/eth2-hashing/Cargo.toml b/crates/eth2-hashing/Cargo.toml index 2fe869d3..ed14384c 100644 --- a/crates/eth2-hashing/Cargo.toml +++ b/crates/eth2-hashing/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "webb-eth2-hashing" -version = "0.3.0" +version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } authors = ["Paul Hauner , Webb Developers "] -edition = "2021" -license = "Apache-2.0" description = "Hashing primitives used in Ethereum 2.0" [dependencies] diff --git a/crates/eth2-pallet-init/Cargo.toml b/crates/eth2-pallet-init/Cargo.toml index afb46177..a5eb6c88 100644 --- a/crates/eth2-pallet-init/Cargo.toml +++ b/crates/eth2-pallet-init/Cargo.toml @@ -1,7 +1,9 @@ [package] name = "webb-eth2-pallet-init" -version = "0.1.0" -edition = "2021" +version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +authors = { workspace = true } [dependencies] types = { workspace = true } diff --git a/crates/eth2-pallet-init/src/init_pallet.rs b/crates/eth2-pallet-init/src/init_pallet.rs index f87009bb..cc59fffd 100644 --- a/crates/eth2-pallet-init/src/init_pallet.rs +++ b/crates/eth2-pallet-init/src/init_pallet.rs @@ -194,7 +194,7 @@ mod tests { Config::load_from_toml("config_for_tests.toml".try_into().unwrap()); let api = setup_api().await.unwrap(); - let mut eth_client_pallet = EthClientPallet::new(api, TypedChainId::Evm(1)); + let mut eth_client_pallet = EthClientPallet::new(api.into(), TypedChainId::Evm(1)); config_for_test.validate_updates = Some(false); init_pallet(&config_for_test, &mut eth_client_pallet).await.unwrap(); @@ -209,7 +209,7 @@ mod tests { Config::load_from_toml("config_for_tests.toml".try_into().unwrap()); let api = setup_api().await.unwrap(); - let mut eth_client_pallet = EthClientPallet::new(api, TypedChainId::Evm(1)); + let mut eth_client_pallet = EthClientPallet::new(api.into(), TypedChainId::Evm(1)); config_for_test.trusted_signer_account_id = None; init_pallet(&config_for_test, &mut eth_client_pallet).await.unwrap(); @@ -220,7 +220,7 @@ mod tests { let config_for_test = Config::load_from_toml("config_for_tests.toml".try_into().unwrap()); let api = setup_api().await.unwrap(); - let mut eth_client_pallet = EthClientPallet::new(api, TypedChainId::Evm(1)); + let mut eth_client_pallet = EthClientPallet::new(api.into(), TypedChainId::Evm(1)); init_pallet(&config_for_test, &mut eth_client_pallet).await.unwrap(); diff --git a/crates/eth2-pallet-init/src/main.rs b/crates/eth2-pallet-init/src/main.rs index 5ec8cd39..3a8cd64a 100644 --- a/crates/eth2-pallet-init/src/main.rs +++ b/crates/eth2-pallet-init/src/main.rs @@ -28,7 +28,7 @@ async fn main() -> anyhow::Result<()> { .await .expect("failed to connect to substrate node"); - let mut eth_client_contract = EthClientPallet::new(api, TypedChainId::None); + let mut eth_client_contract = EthClientPallet::new(api.into(), TypedChainId::None); init_pallet(&config, &mut eth_client_contract) .await .expect("Error on pallet initialization"); diff --git a/crates/eth2-pallet-init/src/substrate_pallet_client.rs b/crates/eth2-pallet-init/src/substrate_pallet_client.rs index ffa395ff..062545c4 100644 --- a/crates/eth2-pallet-init/src/substrate_pallet_client.rs +++ b/crates/eth2-pallet-init/src/substrate_pallet_client.rs @@ -8,6 +8,7 @@ use eth_types::{ BlockHeader, H256, }; +use std::sync::Arc; use subxt::{error::DispatchError, utils::AccountId32}; use webb::substrate::{ scale::{Decode, Encode}, @@ -64,18 +65,18 @@ pub async fn setup_api() -> anyhow::Result> { #[derive(Clone)] pub struct EthClientPallet { - api: OnlineClient, + api: Arc>, pair: Pair, chain: TypedChainId, } impl EthClientPallet { - pub fn new(api: OnlineClient, typed_chain_id: TypedChainId) -> Self { + pub fn new(api: Arc>, typed_chain_id: TypedChainId) -> Self { Self::new_with_pair(api, sp_keyring::AccountKeyring::Alice.pair(), typed_chain_id) } pub fn new_with_pair( - api: OnlineClient, + api: Arc>, pair: Pair, typed_chain_id: TypedChainId, ) -> Self { @@ -83,7 +84,7 @@ impl EthClientPallet { } pub fn new_with_suri_key>( - api: OnlineClient, + api: Arc>, suri_key: T, typed_chain_id: TypedChainId, ) -> anyhow::Result { diff --git a/crates/finality-update-verify/Cargo.toml b/crates/finality-update-verify/Cargo.toml index f7e735ea..27885a50 100644 --- a/crates/finality-update-verify/Cargo.toml +++ b/crates/finality-update-verify/Cargo.toml @@ -1,7 +1,9 @@ [package] name = "webb-finality-update-verify" -version = "0.1.0" -edition = "2021" +version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +authors = { workspace = true } [dependencies] eth-types = { path = "../eth-types", default-features = false, features = ["eth2"] } diff --git a/crates/lc-relay-config/Cargo.toml b/crates/lc-relay-config/Cargo.toml new file mode 100644 index 00000000..0a8806e4 --- /dev/null +++ b/crates/lc-relay-config/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "webb-lc-relay-config" +version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +authors = { workspace = true } + +[dependencies] +serde = { workspace = true } +eth-rpc-client = { package = "webb-eth-rpc-client", path = "../eth-rpc-client" } +eth2-pallet-init = { package = "webb-eth2-pallet-init", path = "../eth2-pallet-init" } +reqwest = { workspace = true, features = ["blocking", "json"] } +dotenvy = { workspace = true } +toml = { workspace = true } diff --git a/eth2substrate-block-relay-rs/src/config.rs b/crates/lc-relay-config/src/lib.rs similarity index 94% rename from eth2substrate-block-relay-rs/src/config.rs rename to crates/lc-relay-config/src/lib.rs index 670a43cf..380488c3 100644 --- a/eth2substrate-block-relay-rs/src/config.rs +++ b/crates/lc-relay-config/src/lib.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use std::{io::Read, path::PathBuf}; #[derive(Serialize, Deserialize, Debug, Clone)] -pub struct Config { +pub struct RelayConfig { // endpoint to a full node of Eth2 Beacon chain with Light Client API pub beacon_endpoint: String, @@ -70,12 +70,13 @@ pub struct Config { pub get_light_client_update_by_epoch: Option, } -impl Config { +impl RelayConfig { pub fn load_from_toml(path: PathBuf) -> Self { let mut config = std::fs::File::open(path).expect("Error on parsing path to config"); let mut content = String::new(); config.read_to_string(&mut content).expect("Error on reading config"); - let mut config: Config = toml::from_str(content.as_str()).expect("Error on config parsing"); + let mut config: RelayConfig = + toml::from_str(content.as_str()).expect("Error on config parsing"); dotenv().ok(); let api_key_string = std::env::var("ETH1_INFURA_API_KEY").unwrap(); config.eth1_endpoint = config.eth1_endpoint.replace("ETH1_INFURA_API_KEY", &api_key_string); @@ -96,8 +97,8 @@ impl Config { } } -impl From for eth2_pallet_init::config::Config { - fn from(val: Config) -> Self { +impl From for eth2_pallet_init::config::Config { + fn from(val: RelayConfig) -> Self { eth2_pallet_init::config::Config { beacon_endpoint: val.beacon_endpoint, eth1_endpoint: val.eth1_endpoint, diff --git a/crates/lc-relayer-context/Cargo.toml b/crates/lc-relayer-context/Cargo.toml new file mode 100644 index 00000000..805d516d --- /dev/null +++ b/crates/lc-relayer-context/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "webb-lc-relayer-context" +version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +authors = { workspace = true } + + +[dependencies] +# Eth2 Light Client +eth2-pallet-init = { package = "webb-eth2-pallet-init", path = "../eth2-pallet-init" } +lc-relay-config = { package = "webb-lc-relay-config", path = "../lc-relay-config" } +anyhow = { workspace = true } +tracing = { workspace = true } +subxt = { workspace = true } +tokio = { workspace = true, features = ["macros", "rt", "time"] } \ No newline at end of file diff --git a/crates/lc-relayer-context/src/lib.rs b/crates/lc-relayer-context/src/lib.rs new file mode 100644 index 00000000..7824c273 --- /dev/null +++ b/crates/lc-relayer-context/src/lib.rs @@ -0,0 +1,86 @@ +use eth2_pallet_init::config::Config as InitConfig; +use lc_relay_config::RelayConfig; +use subxt::OnlineClient; +use tokio::sync::broadcast; + +/// LightClientRelayerContext contains Relayer's configuration and shutdown signal. +#[derive(Clone)] +pub struct LightClientRelayerContext { + pub lc_relay_config: RelayConfig, + pub lc_init_config: InitConfig, + /// Broadcasts a shutdown signal to all active connections. + /// + /// The initial `shutdown` trigger is provided by the `run` caller. The + /// server is responsible for gracefully shutting down active connections. + /// When a connection task is spawned, it is passed a broadcast receiver + /// handle. When a graceful shutdown is initiated, a `()` value is sent via + /// the broadcast::Sender. Each active connection receives it, reaches a + /// safe terminal state, and completes the task. + notify_shutdown: broadcast::Sender<()>, +} + +impl LightClientRelayerContext { + pub fn new(lc_relay_config: RelayConfig, lc_init_config: InitConfig) -> Self { + let (notify_shutdown, _) = broadcast::channel(2); + Self { lc_relay_config, lc_init_config, notify_shutdown } + } + pub async fn substrate_provider(self) -> anyhow::Result> { + let maybe_client = OnlineClient::::from_url( + self.lc_relay_config.substrate_endpoint.clone(), + ) + .await; + let client = match maybe_client { + Ok(client) => client, + Err(err) => return Err(err.into()), + }; + Ok(client) + } + /// Returns a broadcast receiver handle for the shutdown signal. + pub fn shutdown_signal(&self) -> Shutdown { + Shutdown::new(self.notify_shutdown.subscribe()) + } + /// Sends a shutdown signal to all subscribed tasks/connections. + pub fn shutdown(&self) { + let _ = self.notify_shutdown.send(()); + } +} + +/// Listens for the server shutdown signal. +/// +/// Shutdown is signalled using a `broadcast::Receiver`. Only a single value is +/// ever sent. Once a value has been sent via the broadcast channel, the server +/// should shutdown. +/// +/// The `Shutdown` struct listens for the signal and tracks that the signal has +/// been received. Callers may query for whether the shutdown signal has been +/// received or not. +#[derive(Debug)] +pub struct Shutdown { + /// `true` if the shutdown signal has been received + shutdown: bool, + + /// The receive half of the channel used to listen for shutdown. + notify: broadcast::Receiver<()>, +} + +impl Shutdown { + /// Create a new `Shutdown` backed by the given `broadcast::Receiver`. + pub fn new(notify: broadcast::Receiver<()>) -> Shutdown { + Shutdown { shutdown: false, notify } + } + + /// Receive the shutdown notice, waiting if necessary. + pub async fn recv(&mut self) { + // If the shutdown signal has already been received, then return + // immediately. + if self.shutdown { + return + } + + // Cannot receive a "lag error" as only one value is ever sent. + let _ = self.notify.recv().await; + + // Remember that the signal has been received. + self.shutdown = true; + } +} diff --git a/crates/merkle-proof/Cargo.toml b/crates/merkle-proof/Cargo.toml index 9b6a8d33..a799c7ab 100644 --- a/crates/merkle-proof/Cargo.toml +++ b/crates/merkle-proof/Cargo.toml @@ -1,8 +1,9 @@ [package] name = "webb-merkle-proof" -version = "0.2.0" +version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } authors = ["Michael Sproul ", "Webb Developers "] -edition = "2021" [dependencies] eth2-hashing = { package = "webb-eth2-hashing", path = "../eth2-hashing", default-features = false, features = ["zero_hash_cache"] } diff --git a/crates/safe-arith/Cargo.toml b/crates/safe-arith/Cargo.toml index ea3d2df2..23183f3d 100644 --- a/crates/safe-arith/Cargo.toml +++ b/crates/safe-arith/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "webb-safe-arith" -version = "0.1.0" authors = ["Michael Sproul "] -edition = "2021" +version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } [dependencies] diff --git a/crates/serde-utils/Cargo.toml b/crates/serde-utils/Cargo.toml index fd2e10a1..daa7ac80 100644 --- a/crates/serde-utils/Cargo.toml +++ b/crates/serde-utils/Cargo.toml @@ -1,10 +1,12 @@ [package] name = "webb-eth2-serde-utils" -version = "0.1.1" -authors = ["Paul Hauner "] -edition = "2021" +version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +authors = ["Paul Hauner ", "Webb Developers "] description = "Serialization and deserialization utilities useful for JSON representations of Ethereum 2.0 types." -license = "Apache-2.0" + + [dependencies] serde = { workspace = true, features = ["derive"], optional = true } diff --git a/crates/ssz-derive/Cargo.toml b/crates/ssz-derive/Cargo.toml index c6e29871..78c9f093 100644 --- a/crates/ssz-derive/Cargo.toml +++ b/crates/ssz-derive/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "webb-eth2-ssz-derive" -version = "0.3.0" -authors = ["Paul Hauner "] -edition = "2021" +version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +authors = ["Paul Hauner ", "Webb Developers "] description = "Procedural derive macros to accompany the eth2_ssz crate." -license = "Apache-2.0" [lib] name = "ssz_derive" diff --git a/crates/ssz/Cargo.toml b/crates/ssz/Cargo.toml index dd81d3eb..a4fdc242 100644 --- a/crates/ssz/Cargo.toml +++ b/crates/ssz/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "webb-eth2-ssz" -version = "0.4.1" -authors = ["Paul Hauner "] -edition = "2021" +version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +authors = ["Paul Hauner ", "Webb Developers "] description = "SimpleSerialize (SSZ) as used in Ethereum 2.0" -license = "Apache-2.0" [lib] name = "ssz" diff --git a/crates/tree-hash-derive/Cargo.toml b/crates/tree-hash-derive/Cargo.toml index 4bb181f8..76abbb7a 100644 --- a/crates/tree-hash-derive/Cargo.toml +++ b/crates/tree-hash-derive/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "webb-tree-hash-derive" -version = "0.4.0" -authors = ["Paul Hauner "] -edition = "2021" +version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +authors = ["Paul Hauner ", "Webb Developers "] description = "Procedural derive macros to accompany the tree_hash crate." -license = "Apache-2.0" [lib] proc-macro = true diff --git a/crates/tree-hash/Cargo.toml b/crates/tree-hash/Cargo.toml index ec7dc97c..239d2784 100644 --- a/crates/tree-hash/Cargo.toml +++ b/crates/tree-hash/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "webb-tree-hash" -version = "0.4.1" -authors = ["Paul Hauner "] -edition = "2021" -license = "Apache-2.0" +version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +authors = ["Paul Hauner ", "Webb Developers "] description = "Efficient Merkle-hashing as used in Ethereum 2.0" [lib] diff --git a/eth2substrate-block-relay-rs/Cargo.toml b/eth2substrate-block-relay-rs/Cargo.toml index cf987611..72726dc7 100644 --- a/eth2substrate-block-relay-rs/Cargo.toml +++ b/eth2substrate-block-relay-rs/Cargo.toml @@ -1,7 +1,9 @@ [package] name = "eth2-to-substrate-relay" -version = "0.1.0" -edition = "2021" +version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +authors = { workspace = true } [dependencies] eth-types = { path = "../crates/eth-types", features = ["eth2"] } @@ -11,6 +13,8 @@ consensus-types = { package = "webb-consensus-types", path = "../crates/consensu eth-rpc-client = { package = "webb-eth-rpc-client", path = "../crates/eth-rpc-client" } eth2-pallet-init = { package = "webb-eth2-pallet-init", path = "../crates/eth2-pallet-init" } finality-update-verify = { package = "webb-finality-update-verify", path = "../crates/finality-update-verify" } +lc-relay-config = { package = "webb-lc-relay-config", path = "../crates/lc-relay-config" } +lc-relayer-context = { package = "webb-lc-relayer-context", path = "../crates/lc-relayer-context" } types = { workspace = true } eth2_hashing = { package = "ethereum_hashing", version = "1.0.0-beta.2" } @@ -40,3 +44,5 @@ lazy_static = { workspace = true } warp = { workspace = true } dotenvy = { workspace = true } min-max = { workspace = true } +backoff = { workspace = true } +tracing = { workspace = true } diff --git a/eth2substrate-block-relay-rs/src/eth2substrate_relay.rs b/eth2substrate-block-relay-rs/src/eth2substrate_relay.rs index 792f0103..ec1cbd40 100644 --- a/eth2substrate-block-relay-rs/src/eth2substrate_relay.rs +++ b/eth2substrate-block-relay-rs/src/eth2substrate_relay.rs @@ -1,5 +1,4 @@ use crate::{ - config::Config, prometheus_metrics, prometheus_metrics::{ CHAIN_FINALIZED_EXECUTION_BLOCK_HEIGHT_ON_ETH, @@ -24,6 +23,7 @@ use eth_types::{ primitives::FinalExecutionStatus, BlockHeader, }; +use lc_relay_config::RelayConfig; use log::{debug, info, trace, warn}; use min_max::*; use std::{cmp, str::FromStr, thread, time::Duration, vec::Vec}; @@ -101,7 +101,7 @@ pub struct Eth2SubstrateRelay { } impl Eth2SubstrateRelay { - pub async fn init(config: &Config, eth_pallet: Box) -> Self { + pub async fn init(config: &RelayConfig, eth_pallet: Box) -> Self { info!(target: "relay", "=== Relay initialization === "); let beacon_rpc_client = BeaconRPCClient::new( @@ -193,27 +193,17 @@ impl Eth2SubstrateRelay { Ok(last_finalized_slot_on_eth) } - pub async fn run(&mut self, max_iterations: Option) { + pub async fn run(&mut self, max_iterations: Option) -> anyhow::Result<()> { info!(target: "relay", "=== Relay running ==="); + let mut iter_id = 0; while !self.terminate { iter_id += 1; self.set_terminate(iter_id, max_iterations); - skip_fail!( - self.wait_for_synchronization().await, - "Fail to get sync status", - self.sleep_time_on_sync_secs - ); - + self.wait_for_synchronization().await?; info!(target: "relay", "== New relay loop =="); tokio::time::sleep(Duration::from_secs(12)).await; - - let client_mode: ClientMode = skip_fail!( - self.eth_client_pallet.get_client_mode().await, - "Fail to get client mode", - self.sleep_time_on_sync_secs - ); - + let client_mode = self.eth_client_pallet.get_client_mode().await?; let submitted_in_this_iteration = match client_mode { ClientMode::SubmitLightClientUpdate => self.submit_light_client_update().await, ClientMode::SubmitHeader => self.submit_headers().await, @@ -224,6 +214,8 @@ impl Eth2SubstrateRelay { sleep(Duration::from_secs(self.sleep_time_on_sync_secs)).await; } } + + Ok(()) } async fn submit_light_client_update(&mut self) -> bool { @@ -298,7 +290,7 @@ impl Eth2SubstrateRelay { } async fn get_light_client_update_from_file( - config: &Config, + config: &RelayConfig, beacon_rpc_client: &BeaconRPCClient, ) -> anyhow::Result> { let mut next_light_client_update: Option = None; diff --git a/eth2substrate-block-relay-rs/src/lib.rs b/eth2substrate-block-relay-rs/src/lib.rs index 8742ddda..f9293826 100644 --- a/eth2substrate-block-relay-rs/src/lib.rs +++ b/eth2substrate-block-relay-rs/src/lib.rs @@ -1,4 +1,3 @@ -pub mod config; pub mod eth2substrate_relay; pub mod logger; pub mod prometheus_metrics; diff --git a/eth2substrate-block-relay-rs/src/test_utils.rs b/eth2substrate-block-relay-rs/src/test_utils.rs index 54b51b67..75efa738 100644 --- a/eth2substrate-block-relay-rs/src/test_utils.rs +++ b/eth2substrate-block-relay-rs/src/test_utils.rs @@ -1,6 +1,5 @@ use crate::{ - config::Config, config_for_tests::ConfigForTests, eth2substrate_relay::Eth2SubstrateRelay, - test_utils, + config_for_tests::ConfigForTests, eth2substrate_relay::Eth2SubstrateRelay, test_utils, }; use eth2_pallet_init::{ eth_client_pallet_trait::EthClientPalletTrait, @@ -16,6 +15,7 @@ use eth_types::{ eth2::{ExtendedBeaconBlockHeader, LightClientUpdate, SyncCommittee}, BlockHeader, }; +use lc_relay_config::RelayConfig; use std::time; use tree_hash::TreeHash; use webb_proposals::TypedChainId; @@ -188,8 +188,8 @@ pub async fn init_pallet_from_specific_slot( tokio::time::sleep(time::Duration::from_secs(30)).await; } -pub fn get_config(config_for_test: &ConfigForTests) -> Config { - Config { +pub fn get_config(config_for_test: &ConfigForTests) -> RelayConfig { + RelayConfig { beacon_endpoint: config_for_test.beacon_endpoint.to_string(), eth1_endpoint: config_for_test.eth1_endpoint.to_string(), headers_batch_size: 8, @@ -240,7 +240,7 @@ pub async fn get_client_pallet( ) -> Box { let api = setup_api().await.unwrap(); let typed_chain_id = get_typed_chain_id(config_for_test); - let mut eth_client_pallet = EthClientPallet::new(api, typed_chain_id); + let mut eth_client_pallet = EthClientPallet::new(api.into(), typed_chain_id); let config = get_init_config(config_for_test, ð_client_pallet); @@ -280,7 +280,7 @@ pub async fn get_relay_from_slot( let config = get_config(config_for_test); let api = setup_api().await.unwrap(); let typed_chain_id = get_typed_chain_id(config_for_test); - let mut eth_client_pallet = EthClientPallet::new(api, typed_chain_id); + let mut eth_client_pallet = EthClientPallet::new(api.into(), typed_chain_id); init_pallet_from_specific_slot(&mut eth_client_pallet, slot, config_for_test).await; diff --git a/gadget/Cargo.toml b/gadget/Cargo.toml index 06734fb3..9c84327b 100644 --- a/gadget/Cargo.toml +++ b/gadget/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "pallet-eth2-light-client-relayer-gadget" -version = "0.0.1" -authors = ["Webb Developers "] -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -edition = "2021" +version = { workspace = true } +edition = { workspace = true } +license = { workspace = true } +authors = { workspace = true} [dependencies] anyhow = { workspace = true } @@ -11,10 +11,13 @@ tracing = { workspace = true } ethereum-types = { workspace = true } subxt = { workspace = true } tokio = { workspace = true, features = ["macros", "rt", "time"] } +backoff = { workspace = true } +async-trait = { workspace = true } # Eth2 Light Client eth2-to-substrate-relay = { path = "../eth2substrate-block-relay-rs" } eth2-pallet-init = { package = "webb-eth2-pallet-init", path = "../crates/eth2-pallet-init" } - +lc-relayer-context = { package = "webb-lc-relayer-context", path = "../crates/lc-relayer-context" } +lc-relay-config = { package = "webb-lc-relay-config", path = "../crates/lc-relay-config" } # Webb webb-proposals = { workspace = true, features = ["scale", "evm"] } \ No newline at end of file diff --git a/gadget/src/lib.rs b/gadget/src/lib.rs index e8b498a3..0c8e049e 100644 --- a/gadget/src/lib.rs +++ b/gadget/src/lib.rs @@ -3,8 +3,10 @@ //! Integrates the Webb Relayer into the Substrate Node. use eth2_pallet_init::{init_pallet, substrate_pallet_client::EthClientPallet}; use eth2_to_substrate_relay::eth2substrate_relay::Eth2SubstrateRelay; -use std::path::PathBuf; -use subxt::OnlineClient; +use lc_relay_config::RelayConfig; +use lc_relayer_context::LightClientRelayerContext; +use std::{path::PathBuf, sync::Arc}; +use tokio::signal::unix; use webb_proposals::TypedChainId; pub mod errors; @@ -18,6 +20,49 @@ pub struct Eth2LightClientParams { pub eth2_chain_id: TypedChainId, } +pub async fn ignite_lc_relayer(ctx: LightClientRelayerContext) -> anyhow::Result<()> { + let backoff = backoff::ExponentialBackoff { max_elapsed_time: None, ..Default::default() }; + + let task = || async { + let maybe_client = ctx.clone().substrate_provider().await; + let api_client = match maybe_client { + Ok(client) => client, + Err(err) => { + tracing::error!("Failed to connect with substrate client, retrying...!"); + return Err(backoff::Error::transient(err)) + }, + }; + let api_client = Arc::new(api_client); + let mut eth_pallet = EthClientPallet::new( + api_client, + ctx.lc_relay_config.ethereum_network.as_typed_chain_id(), + ); + let mut relay = + Eth2SubstrateRelay::init(&ctx.lc_relay_config, Box::new(eth_pallet.clone())).await; + tracing::info!(target: "relay", "=== Initializing relay ==="); + + if let Ok(is_initialized) = eth_pallet + .is_initialized(init_pallet::get_typed_chain_id(&ctx.lc_init_config)) + .await + { + if !is_initialized { + match init_pallet::init_pallet(&ctx.lc_init_config, &mut eth_pallet).await { + Ok(_) => tracing::info!(target: "relay", "=== Pallet initialized ==="), + Err(e) => { + tracing::error!(target: "relay", "=== Failed to initialize pallet: {:?} ===", e); + return Err(backoff::Error::permanent(e)) + }, + }; + } + } + tracing::info!(target: "relay", "=== Relay initialized ==="); + relay.run(None).await.map_err(backoff::Error::transient)?; + Ok(()) + }; + backoff::future::retry(backoff, task).await?; + Ok(()) +} + pub async fn start_gadget(relayer_params: Eth2LightClientParams) { // Light Client Relayer let lc_relay_config = match relayer_params.lc_relay_config_path.as_ref() { @@ -43,41 +88,47 @@ pub async fn start_gadget(relayer_params: Eth2LightClientParams) { return }, }; - - let api = - OnlineClient::::from_url(lc_relay_config.substrate_endpoint.clone()) - .await - .expect("failed to connect to substrate node"); - - let mut eth_pallet = - EthClientPallet::new(api, lc_relay_config.ethereum_network.as_typed_chain_id()); - - let mut relay = Eth2SubstrateRelay::init(&lc_relay_config, Box::new(eth_pallet.clone())).await; - - tracing::info!(target: "relay", "=== Initializing relay ==="); - - if let Ok(is_initialized) = eth_pallet - .is_initialized(init_pallet::get_typed_chain_id(&lc_init_config)) - .await - { - if !is_initialized { - match init_pallet::init_pallet(&lc_init_config, &mut eth_pallet).await { - Ok(_) => tracing::info!(target: "relay", "=== Pallet initialized ==="), - Err(e) => - tracing::error!(target: "relay", "=== Failed to initialize pallet: {:?} ===", e), - }; - } + let ctx = LightClientRelayerContext::new(lc_relay_config, lc_init_config); + let lc_relayer_task = ignite_lc_relayer(ctx.clone()); + + // watch for signals + let mut ctrlc_signal = + unix::signal(unix::SignalKind::interrupt()).expect("failed to register ctrlc handler"); + let mut termination_signal = unix::signal(unix::SignalKind::terminate()) + .expect("failed to register termination handler"); + let mut quit_signal = + unix::signal(unix::SignalKind::quit()).expect("failed to register quit handler"); + let shutdown = || { + tracing::warn!("Shutting down..."); + // shut down storage fetching + // send shutdown signal to all of the application. + ctx.shutdown(); + std::thread::sleep(std::time::Duration::from_millis(300)); + tracing::info!("Clean Exit .."); + }; + tokio::select! { + _ = lc_relayer_task => { + tracing::warn!( + "Light client relayer stopped ..."); + }, + _ = ctrlc_signal.recv() => { + tracing::warn!("Interrupted (Ctrl+C) ..."); + shutdown(); + }, + _ = termination_signal.recv() => { + tracing::warn!("Got Terminate signal ..."); + shutdown(); + }, + _ = quit_signal.recv() => { + tracing::warn!("Quitting ..."); + shutdown(); + }, } - - tracing::info!(target: "relay", "=== Relay initialized ==="); - relay.run(None).await; } /// Loads the configuration for the light client -fn loads_light_client_relayer_config( - config_path: &PathBuf, -) -> anyhow::Result { - Ok(eth2_to_substrate_relay::config::Config::load_from_toml(config_path.clone())) +fn loads_light_client_relayer_config(config_path: &PathBuf) -> anyhow::Result { + Ok(RelayConfig::load_from_toml(config_path.clone())) } /// Loads the configuration for the light client