From 8480742e1d13abfd3ad582364a0508b837f75464 Mon Sep 17 00:00:00 2001 From: segfault_magnet Date: Sat, 4 May 2024 16:41:42 +0200 Subject: [PATCH 01/23] move to workspaces --- Cargo.lock | 108 +++++++++- Cargo.toml | 82 ++++---- committer/Cargo.toml | 48 +++++ {src => committer/src}/api.rs | 4 +- {src => committer/src}/config.rs | 6 +- committer/src/errors.rs | 77 +++++++ {src => committer/src}/main.rs | 11 +- {src => committer/src}/services.rs | 5 + .../src}/services/block_committer.rs | 24 +-- .../src}/services/block_watcher.rs | 24 +-- .../src}/services/commit_listener.rs | 74 +++---- .../src}/services/health_reporter.rs | 3 +- .../src}/services/status_reporter.rs | 10 +- .../src}/services/wallet_balance_tracker.rs | 39 ++-- .../helpers.rs => committer/src/setup.rs | 41 ++-- committer/src/setup/helpers.rs | 2 + committer/tests/eth_test_adapter.rs | 2 + committer/tests/harness.rs | 2 + e2e/Cargo.toml | 13 ++ e2e/src/lib.rs | 40 ++++ packages/eth_rpc/Cargo.toml | 27 +++ packages/eth_rpc/src/lib.rs | 96 +++++++++ packages/eth_rpc/src/metrics.rs | 27 +++ packages/eth_rpc/src/websocket.rs | 84 ++++++++ packages/eth_rpc/src/websocket/connection.rs | 156 +++++++++++++++ .../eth_rpc/src/websocket/event_streamer.rs | 53 +++++ .../websocket/health_tracking_middleware.rs | 92 ++++----- packages/fuel_rpc/Cargo.toml | 22 ++ packages/fuel_rpc/src/client.rs | 55 +++++ .../fuel_rpc/src/lib.rs | 98 +++------ packages/fuel_rpc/src/metrics.rs | 54 +++++ packages/metrics/Cargo.toml | 10 + .../metrics/src/connection_health_tracker.rs | 43 ++++ .../metrics.rs => packages/metrics/src/lib.rs | 10 +- packages/ports/Cargo.toml | 22 ++ packages/ports/src/eth_rpc.rs | 55 +++++ packages/ports/src/fuel_rpc.rs | 14 ++ packages/ports/src/lib.rs | 13 ++ packages/ports/src/storage.rs | 26 +++ packages/storage/Cargo.toml | 30 +++ .../storage/migrations}/0001_initial.down.sql | 0 .../storage/migrations}/0001_initial.up.sql | 0 packages/storage/src/lib.rs | 137 +++++++++++++ packages/storage/src/postgres.rs | 107 ++++++++++ .../storage/src}/tables.rs | 7 +- .../storage/src}/test_instance.rs | 5 +- packages/types/Cargo.toml | 14 ++ packages/types/src/block_submission.rs | 21 ++ packages/types/src/eth_height.rs | 69 +++++++ .../types/src/fuel_block.rs | 18 +- packages/types/src/lib.rs | 7 + src/adapters.rs | 4 - src/adapters/ethereum_adapter.rs | 112 ----------- src/adapters/ethereum_adapter/websocket.rs | 4 - .../ethereum_adapter/websocket/adapter.rs | 129 ------------ .../websocket/event_streamer.rs | 54 ----- src/adapters/fuel_adapter/fuel_metrics.rs | 26 --- src/adapters/fuel_adapter/health_tracker.rs | 47 ----- src/adapters/fuel_adapter/metrics.rs | 26 --- src/adapters/runner.rs | 6 - src/adapters/storage.rs | 59 ------ src/adapters/storage/postgresql.rs | 189 ------------------ src/errors.rs | 60 ------ src/setup.rs | 1 - src/telemetry.rs | 7 - src/telemetry/health_check.rs | 4 - src/telemetry/health_tracker.rs | 47 ----- tests/eth_test_adapter.rs | 56 ------ tests/harness.rs | 33 --- 69 files changed, 1649 insertions(+), 1202 deletions(-) create mode 100644 committer/Cargo.toml rename {src => committer/src}/api.rs (97%) rename {src => committer/src}/config.rs (96%) create mode 100644 committer/src/errors.rs rename {src => committer/src}/main.rs (91%) rename {src => committer/src}/services.rs (76%) rename {src => committer/src}/services/block_committer.rs (89%) rename {src => committer/src}/services/block_watcher.rs (95%) rename {src => committer/src}/services/commit_listener.rs (81%) rename {src => committer/src}/services/health_reporter.rs (95%) rename {src => committer/src}/services/status_reporter.rs (93%) rename {src => committer/src}/services/wallet_balance_tracker.rs (67%) rename src/setup/helpers.rs => committer/src/setup.rs (83%) create mode 100644 committer/src/setup/helpers.rs create mode 100644 committer/tests/eth_test_adapter.rs create mode 100644 committer/tests/harness.rs create mode 100644 e2e/Cargo.toml create mode 100644 e2e/src/lib.rs create mode 100644 packages/eth_rpc/Cargo.toml create mode 100644 packages/eth_rpc/src/lib.rs create mode 100644 packages/eth_rpc/src/metrics.rs create mode 100644 packages/eth_rpc/src/websocket.rs create mode 100644 packages/eth_rpc/src/websocket/connection.rs create mode 100644 packages/eth_rpc/src/websocket/event_streamer.rs rename src/adapters/ethereum_adapter/monitored_adapter.rs => packages/eth_rpc/src/websocket/health_tracking_middleware.rs (69%) create mode 100644 packages/fuel_rpc/Cargo.toml create mode 100644 packages/fuel_rpc/src/client.rs rename src/adapters/fuel_adapter/fuel_client.rs => packages/fuel_rpc/src/lib.rs (59%) create mode 100644 packages/fuel_rpc/src/metrics.rs create mode 100644 packages/metrics/Cargo.toml create mode 100644 packages/metrics/src/connection_health_tracker.rs rename src/telemetry/metrics.rs => packages/metrics/src/lib.rs (58%) create mode 100644 packages/ports/Cargo.toml create mode 100644 packages/ports/src/eth_rpc.rs create mode 100644 packages/ports/src/fuel_rpc.rs create mode 100644 packages/ports/src/lib.rs create mode 100644 packages/ports/src/storage.rs create mode 100644 packages/storage/Cargo.toml rename {migrations => packages/storage/migrations}/0001_initial.down.sql (100%) rename {migrations => packages/storage/migrations}/0001_initial.up.sql (100%) create mode 100644 packages/storage/src/lib.rs create mode 100644 packages/storage/src/postgres.rs rename {src/adapters/storage/postgresql => packages/storage/src}/tables.rs (94%) rename {src/adapters/storage/postgresql => packages/storage/src}/test_instance.rs (97%) create mode 100644 packages/types/Cargo.toml create mode 100644 packages/types/src/block_submission.rs create mode 100644 packages/types/src/eth_height.rs rename src/adapters/fuel_adapter.rs => packages/types/src/fuel_block.rs (61%) create mode 100644 packages/types/src/lib.rs delete mode 100644 src/adapters.rs delete mode 100644 src/adapters/ethereum_adapter.rs delete mode 100644 src/adapters/ethereum_adapter/websocket.rs delete mode 100644 src/adapters/ethereum_adapter/websocket/adapter.rs delete mode 100644 src/adapters/ethereum_adapter/websocket/event_streamer.rs delete mode 100644 src/adapters/fuel_adapter/fuel_metrics.rs delete mode 100644 src/adapters/fuel_adapter/health_tracker.rs delete mode 100644 src/adapters/fuel_adapter/metrics.rs delete mode 100644 src/adapters/runner.rs delete mode 100644 src/adapters/storage.rs delete mode 100644 src/adapters/storage/postgresql.rs delete mode 100644 src/errors.rs delete mode 100644 src/setup.rs delete mode 100644 src/telemetry.rs delete mode 100644 src/telemetry/health_check.rs delete mode 100644 src/telemetry/health_tracker.rs delete mode 100644 tests/eth_test_adapter.rs delete mode 100644 tests/harness.rs diff --git a/Cargo.lock b/Cargo.lock index 2b0e3702..39aa9fe0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -375,9 +375,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" @@ -1183,6 +1183,17 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" +[[package]] +name = "e2e" +version = "0.1.0" +dependencies = [ + "anyhow", + "eth_rpc", + "fuel_rpc", + "ports", + "tokio", +] + [[package]] name = "ecdsa" version = "0.16.9" @@ -1328,6 +1339,24 @@ dependencies = [ "uuid", ] +[[package]] +name = "eth_rpc" +version = "0.1.0" +dependencies = [ + "async-trait", + "ethers", + "futures", + "metrics", + "mockall", + "ports", + "prometheus", + "serde_json", + "thiserror", + "tokio", + "tracing", + "url", +] + [[package]] name = "ethabi" version = "18.0.0" @@ -1687,18 +1716,19 @@ dependencies = [ "async-trait", "clap", "config", - "ethers", - "fuel-core-client", + "eth_rpc", + "fuel_rpc", "futures", "hex", "impl-tools", + "metrics", "mockall", + "ports", "prometheus", "rand", "serde", "serde_json", - "sqlx", - "testcontainers", + "storage", "thiserror", "tokio", "tokio-util", @@ -1860,6 +1890,20 @@ dependencies = [ "tai64", ] +[[package]] +name = "fuel_rpc" +version = "0.1.0" +dependencies = [ + "async-trait", + "fuel-core-client", + "metrics", + "ports", + "prometheus", + "thiserror", + "tokio", + "url", +] + [[package]] name = "funty" version = "2.0.0" @@ -2732,6 +2776,13 @@ version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +[[package]] +name = "metrics" +version = "0.1.0" +dependencies = [ + "prometheus", +] + [[package]] name = "mime" version = "0.3.17" @@ -2857,9 +2908,9 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -2868,9 +2919,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", @@ -3181,6 +3232,19 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" +[[package]] +name = "ports" +version = "0.1.0" +dependencies = [ + "async-trait", + "ethers-core", + "futures", + "impl-tools", + "mockall", + "thiserror", + "types", +] + [[package]] name = "postcard" version = "1.0.8" @@ -4347,6 +4411,22 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "storage" +version = "0.1.0" +dependencies = [ + "async-trait", + "hex", + "ports", + "rand", + "serde", + "sqlx", + "storage", + "testcontainers", + "thiserror", + "tokio", +] + [[package]] name = "stringprep" version = "0.1.4" @@ -4939,6 +5019,14 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "types" +version = "0.1.0" +dependencies = [ + "rand", + "serde", +] + [[package]] name = "uint" version = "0.9.5" diff --git a/Cargo.toml b/Cargo.toml index 0cc9a967..4e741b1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,55 +1,41 @@ -[package] -authors = ["Fuel Labs "] -edition = "2021" -homepage = "https://fuel.network/" -license = "Apache-2.0" -repository = "https://github.com/FuelLabs/fuel-block-committer" -rust-version = "1.77.0" -version = "0.4.0" -name = "fuel-block-committer" +[workspace] +resolver = "2" +members = [ + "committer", + "e2e", + "packages/eth_rpc", + "packages/fuel_rpc", + "packages/metrics", + "packages/ports", + "packages/storage", + "packages/types", +] -[[test]] -harness = true -name = "e2e" -path = "tests/harness.rs" +[workspace.dependencies] +types = { version = "0.1.0", path = "./packages/types/", default-features = false } +storage = { version = "0.1.0", path = "./packages/storage", default-features = false } +ports = { version = "0.1.0", path = "./packages/ports", default-features = false } +metrics = { version = "0.1.0", path = "./packages/metrics", default-features = false } +fuel_rpc = { version = "0.1.0", path = "./packages/fuel_rpc", default-features = false } +eth_rpc = { version = "0.1.0", path = "./packages/eth_rpc", default-features = false } +anyhow = { version = "1.0", default-features = false } + +mockall = { version = "0.12", default-features = false } +ethers-core = { version = "2.0", default-features = false } +futures = { version = "0.3", default-features = false } +ethers = { version = "2.0", default-features = false } -[dependencies] -actix-web = { version = "4", default-features = false, features = ["macros"] } +sqlx = { version = "0.7.4", default-features = false } +rand = { version = "0.8", default-features = false } async-trait = { version = "0.1", default-features = false } -clap = { version = "4.5", features = ["derive"] } -config = { version = "0.14", default-features = false, features = [ - "toml", - "async", -] } -ethers = { version = "2.0", features = ["ws"], default-features = false } +serde = { version = "1.0", default-features = false } +tracing = { version = "0.1", default-features = false } +url = { version = "2.3", default-features = false } fuel-core-client = { version = "0.26", default-features = false } -futures = { version = "0.3", default-features = false } -hex = "0.4.3" -impl-tools = "0.10.0" prometheus = { version = "0.13", default-features = false } -rand = { version = "0.8", default-features = false, features = ["std"] } -serde = { version = "1.0", features = ["derive"], default-features = false } serde_json = { version = "1.0", default-features = false } -sqlx = { version = "0.7.4", features = [ - "postgres", - "runtime-tokio", - "migrate", - "macros", -], default-features = false } thiserror = { version = "1.0", default-features = false } -tokio = { version = "1.28", features = [ - "rt-multi-thread", - "rt", - "macros", -], default-features = false } -tokio-util = { version = "0.7", default-features = false } -tracing = { version = "0.1", default-features = false } -tracing-subscriber = { version = "0.3", features = ["json"] } -url = { version = "2.3", default-features = false } - -[dev-dependencies] -anyhow = { version = "1.0", default-features = false } -mockall = { version = "0.12", default-features = false } -testcontainers = { version = "0.16.6", default-features = false } -#TODO: Once fuels-rs catches up tests will be reenabled -#fuels-test-helpers = "0.57" +impl-tools = { version = "0.10.0", default-features = false } +hex = { version = "0.4", default-features = false } +tokio = { version = "1.37", default-features = false } +testcontainers = { version = "0.16", default-features = false } diff --git a/committer/Cargo.toml b/committer/Cargo.toml new file mode 100644 index 00000000..b0bd2013 --- /dev/null +++ b/committer/Cargo.toml @@ -0,0 +1,48 @@ +[package] +authors = ["Fuel Labs "] +edition = "2021" +homepage = "https://fuel.network/" +license = "Apache-2.0" +repository = "https://github.com/FuelLabs/fuel-block-committer" +rust-version = "1.77.0" +version = "0.4.0" +name = "fuel-block-committer" + +[dependencies] +fuel_rpc = { workspace = true } +eth_rpc = { workspace = true } +ports = { workspace = true } +storage = { workspace = true } +metrics = { workspace = true } + +serde = { workspace = true } +url = { workspace = true } +actix-web = { version = "4", default-features = false, features = ["macros"] } +clap = { version = "4.5", features = ["derive"] } +async-trait = { workspace = true } +config = { version = "0.14", default-features = false, features = [ + "toml", + "async", +] } +hex = "0.4.3" +futures = { workspace = true } +impl-tools = { workspace = true } +prometheus = { version = "0.13", default-features = false } +rand = { workspace = true, features = ["std"] } +serde_json = { version = "1.0", default-features = false } +thiserror = { workspace = true } +tokio = { version = "1.28", features = [ + "rt-multi-thread", + "rt", + "macros", +], default-features = false } +tokio-util = { version = "0.7", default-features = false } +tracing = { version = "0.1", default-features = false } +tracing-subscriber = { version = "0.3", features = ["json"] } + +[dev-dependencies] +anyhow = { version = "1.0", default-features = false } +mockall = { workspace = true } +ports = { workspace = true, features = ["test-helpers"] } +#TODO: Once fuels-rs catches up tests will be reenabled +#fuels-test-helpers = "0.57" diff --git a/src/api.rs b/committer/src/api.rs similarity index 97% rename from src/api.rs rename to committer/src/api.rs index ee2ad0e1..d2167903 100644 --- a/src/api.rs +++ b/committer/src/api.rs @@ -1,16 +1,16 @@ use std::sync::Arc; +use ::metrics::HealthChecker; use actix_web::{ error::InternalError, get, http::StatusCode, web, App, HttpResponse, HttpServer, Responder, }; +use ports::storage::Storage; use prometheus::{Encoder, Registry, TextEncoder}; use crate::{ - adapters::storage::Storage, config::Config, errors::{Error, Result}, services::{HealthReporter, StatusReporter}, - telemetry::HealthChecker, }; pub async fn launch_api_server( diff --git a/src/config.rs b/committer/src/config.rs similarity index 96% rename from src/config.rs rename to committer/src/config.rs index d37a11a6..78b8e8f0 100644 --- a/src/config.rs +++ b/committer/src/config.rs @@ -1,12 +1,11 @@ use std::{net::Ipv4Addr, num::NonZeroU32, path::PathBuf, str::FromStr, time::Duration}; use clap::{command, Parser}; -use ethers::types::{Address, Chain}; +use eth_rpc::{Address, Chain}; use serde::Deserialize; +use storage::DbConfig; use url::Url; -use crate::adapters::storage::postgresql::DbConfig; - #[derive(Debug, Clone, Deserialize)] pub struct Config { pub eth: EthConfig, @@ -66,6 +65,7 @@ pub struct AppConfig { /// IPv4 address on which the server will listen for connections pub host: Ipv4Addr, /// Postgres database configuration + /// TODO: don't rely on this type it is not robust pub db: DbConfig, } diff --git a/committer/src/errors.rs b/committer/src/errors.rs new file mode 100644 index 00000000..7109f60a --- /dev/null +++ b/committer/src/errors.rs @@ -0,0 +1,77 @@ +use actix_web::ResponseError; +use tokio::task::JoinError; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("{0}")] + Other(String), + #[error("Network Error: {0}")] + Network(String), + #[error("Storage error: {0}")] + Storage(String), +} + +impl From for Error { + fn from(value: serde_json::Error) -> Self { + Self::Other(value.to_string()) + } +} + +impl From for Error { + fn from(error: std::io::Error) -> Self { + Self::Other(error.to_string()) + } +} + +impl From for Error { + fn from(error: JoinError) -> Self { + Self::Other(error.to_string()) + } +} + +impl From for Error { + fn from(error: ports::storage::Error) -> Self { + Self::Storage(error.to_string()) + } +} +impl From for Error { + fn from(error: storage::Error) -> Self { + Self::Storage(error.to_string()) + } +} + +impl From for Error { + fn from(value: ports::eth_rpc::Error) -> Self { + match value { + ports::eth_rpc::Error::Network(e) => Self::Network(e.to_string()), + _ => Self::Other(value.to_string()), + } + } +} + +impl From for Error { + fn from(value: eth_rpc::Error) -> Self { + match value { + eth_rpc::Error::Network(e) => Self::Network(e.to_string()), + _ => Self::Other(value.to_string()), + } + } +} + +impl From for Error { + fn from(value: ports::fuel_rpc::Error) -> Self { + match value { + ports::fuel_rpc::Error::Network(e) => Self::Network(e.to_string()), + } + } +} + +impl From for Error { + fn from(error: config::ConfigError) -> Self { + Self::Other(error.to_string()) + } +} + +impl ResponseError for Error {} + +pub type Result = std::result::Result; diff --git a/src/main.rs b/committer/src/main.rs similarity index 91% rename from src/main.rs rename to committer/src/main.rs index a7fbe6ce..86a5cb60 100644 --- a/src/main.rs +++ b/committer/src/main.rs @@ -1,22 +1,23 @@ -#![deny(unused_crate_dependencies)] -mod adapters; +// TODO: return this +// #![deny(unused_crate_dependencies)] mod api; mod config; mod errors; mod services; mod setup; -mod telemetry; use api::launch_api_server; use config::InternalConfig; use errors::Result; use prometheus::Registry; -use setup::helpers::{ - create_eth_adapter, setup_logger, setup_storage, shut_down, spawn_block_watcher, +use setup::{ + create_eth_adapter, setup_logger, setup_storage, spawn_block_watcher, spawn_eth_committer_and_listener, spawn_wallet_balance_tracker, }; use tokio_util::sync::CancellationToken; +use crate::setup::shut_down; + #[tokio::main] async fn main() -> Result<()> { setup_logger(); diff --git a/src/services.rs b/committer/src/services.rs similarity index 76% rename from src/services.rs rename to committer/src/services.rs index dcc3f967..37de3995 100644 --- a/src/services.rs +++ b/committer/src/services.rs @@ -11,3 +11,8 @@ pub use commit_listener::CommitListener; pub use health_reporter::HealthReporter; pub use status_reporter::StatusReporter; pub use wallet_balance_tracker::WalletBalanceTracker; + +#[async_trait::async_trait] +pub trait Runner: Send + Sync { + async fn run(&mut self) -> crate::errors::Result<()>; +} diff --git a/src/services/block_committer.rs b/committer/src/services/block_committer.rs similarity index 89% rename from src/services/block_committer.rs rename to committer/src/services/block_committer.rs index 1bbcbf83..d9e3b579 100644 --- a/src/services/block_committer.rs +++ b/committer/src/services/block_committer.rs @@ -1,18 +1,15 @@ use async_trait::async_trait; +use eth_rpc::WsAdapter; +use ports::{eth_rpc::EthereumAdapter, storage::Storage, BlockSubmission, FuelBlock}; +use storage::Postgres; use tokio::sync::mpsc::Receiver; use tracing::{error, info}; -use crate::{ - adapters::{ - ethereum_adapter::{EthereumAdapter, EthereumWs}, - fuel_adapter::FuelBlock, - runner::Runner, - storage::{postgresql::Postgres, BlockSubmission, Storage}, - }, - errors::Result, -}; - -pub struct BlockCommitter { +use crate::errors::Result; + +use super::Runner; + +pub struct BlockCommitter { rx_block: Receiver, ethereum_rpc: A, storage: Db, @@ -81,12 +78,11 @@ mod tests { use std::time::Duration; use mockall::predicate; + use ports::eth_rpc::MockEthereumAdapter; use rand::Rng; + use storage::PostgresProcess; use super::*; - use crate::adapters::{ - ethereum_adapter::MockEthereumAdapter, storage::postgresql::PostgresProcess, - }; #[tokio::test] async fn block_committer_will_submit_and_write_block() { diff --git a/src/services/block_watcher.rs b/committer/src/services/block_watcher.rs similarity index 95% rename from src/services/block_watcher.rs rename to committer/src/services/block_watcher.rs index 7a392841..580e96e3 100644 --- a/src/services/block_watcher.rs +++ b/committer/src/services/block_watcher.rs @@ -1,18 +1,16 @@ use std::{num::NonZeroU32, vec}; use async_trait::async_trait; +use fuel_rpc::client::Client; +use metrics::RegistersMetrics; +use ports::{fuel_rpc::FuelAdapter, storage::Storage, FuelBlock}; use prometheus::{core::Collector, IntGauge, Opts}; +use storage::Postgres; use tokio::sync::mpsc::Sender; -use crate::{ - adapters::{ - fuel_adapter::{FuelAdapter, FuelBlock, FuelClient}, - runner::Runner, - storage::{postgresql::Postgres, Storage}, - }, - errors::{Error, Result}, - telemetry::RegistersMetrics, -}; +use crate::errors::{Error, Result}; + +use super::Runner; struct Metrics { latest_fuel_block: IntGauge, @@ -36,7 +34,7 @@ impl Default for Metrics { } } -pub struct BlockWatcher { +pub struct BlockWatcher { fuel_adapter: A, tx_fuel_block: Sender, storage: Db, @@ -141,14 +139,12 @@ mod tests { use std::{sync::Arc, vec}; use mockall::predicate::eq; + use ports::{fuel_rpc::MockFuelAdapter, BlockSubmission}; use prometheus::{proto::Metric, Registry}; use rand::Rng; + use storage::PostgresProcess; use super::*; - use crate::adapters::{ - fuel_adapter::MockFuelAdapter, - storage::{postgresql::PostgresProcess, BlockSubmission}, - }; #[tokio::test] async fn will_fetch_and_propagate_missed_block() { diff --git a/src/services/commit_listener.rs b/committer/src/services/commit_listener.rs similarity index 81% rename from src/services/commit_listener.rs rename to committer/src/services/commit_listener.rs index 7abfbd57..579eec2a 100644 --- a/src/services/commit_listener.rs +++ b/committer/src/services/commit_listener.rs @@ -1,20 +1,20 @@ use async_trait::async_trait; +use eth_rpc::WsAdapter; use futures::{StreamExt, TryStreamExt}; +use metrics::RegistersMetrics; +use ports::{ + eth_rpc::{EthereumAdapter, FuelBlockCommittedOnEth}, + storage::Storage, + EthHeight, +}; use prometheus::{IntGauge, Opts}; +use storage::Postgres; use tokio_util::sync::CancellationToken; use tracing::{error, info}; -use crate::{ - adapters::{ - ethereum_adapter::{EthHeight, EthereumAdapter, EthereumWs, FuelBlockCommittedOnEth}, - runner::Runner, - storage::{postgresql::Postgres, Storage}, - }, - errors::Result, - telemetry::RegistersMetrics, -}; +use super::Runner; -pub struct CommitListener { +pub struct CommitListener { ethereum_rpc: E, storage: Db, metrics: Metrics, @@ -37,7 +37,7 @@ where E: EthereumAdapter, Db: Storage, { - async fn determine_starting_eth_block(&mut self) -> Result { + async fn determine_starting_eth_block(&mut self) -> crate::errors::Result { Ok(self .storage .submission_w_latest_block() @@ -48,7 +48,7 @@ where async fn handle_block_committed( &self, committed_on_eth: FuelBlockCommittedOnEth, - ) -> Result<()> { + ) -> crate::errors::Result<()> { info!("block comitted on eth {committed_on_eth:?}"); let submission = self @@ -63,7 +63,7 @@ where Ok(()) } - fn log_if_error(result: Result<()>) { + fn log_if_error(result: crate::errors::Result<()>) { if let Err(error) = result { error!("Received an error from block commit event stream: {error}"); } @@ -76,13 +76,14 @@ where E: EthereumAdapter, Db: Storage, { - async fn run(&mut self) -> Result<()> { + async fn run(&mut self) -> crate::errors::Result<()> { let eth_block = self.determine_starting_eth_block().await?; self.ethereum_rpc .event_streamer(eth_block.into()) .establish_stream() .await? + .map_err(Into::into) .and_then(|event| self.handle_block_committed(event)) .take_until(self.cancel_token.cancelled()) .for_each(|response| async { Self::log_if_error(response) }) @@ -119,32 +120,24 @@ impl Default for Metrics { #[cfg(test)] mod tests { - - use ethers::types::U256; use futures::stream; + use metrics::RegistersMetrics; use mockall::predicate; + use ports::{ + eth_rpc::{FuelBlockCommittedOnEth, MockEthereumAdapter, MockEventStreamer}, + storage::Storage, + BlockSubmission, EthHeight, U256, + }; use prometheus::{proto::Metric, Registry}; use rand::Rng; + use storage::{Postgres, PostgresProcess}; use tokio_util::sync::CancellationToken; - use crate::{ - adapters::{ - ethereum_adapter::{ - EthHeight, FuelBlockCommittedOnEth, MockEthereumAdapter, MockEventStreamer, - }, - runner::Runner, - storage::{ - postgresql::{Postgres, PostgresProcess}, - BlockSubmission, Storage, - }, - }, - errors::Result, - services::CommitListener, - telemetry::RegistersMetrics, - }; + use crate::services::{CommitListener, Runner}; #[tokio::test] async fn listener_will_update_storage_if_event_is_emitted() { + use ports::storage::Storage; // given let mut rng = rand::thread_rng(); let submission = BlockSubmission { @@ -154,7 +147,7 @@ mod tests { let block_hash = submission.block.hash; let eth_rpc_mock = - given_eth_rpc_that_will_stream(vec![Ok(block_hash)], submission.submittal_height); + given_eth_rpc_that_will_stream(vec![block_hash], submission.submittal_height); let process = PostgresProcess::shared().await.unwrap(); let db = db_with_submission(&process, submission).await; @@ -183,7 +176,7 @@ mod tests { let fuel_block_height = submission.block.height; let eth_rpc_mock = - given_eth_rpc_that_will_stream(vec![Ok(block_hash)], submission.submittal_height); + given_eth_rpc_that_will_stream(vec![block_hash], submission.submittal_height); let process = PostgresProcess::shared().await.unwrap(); let db = db_with_submission(&process, submission).await; @@ -223,7 +216,7 @@ mod tests { let incoming_hash = incoming_block.block.hash; let eth_rpc_mock = given_eth_rpc_that_will_stream( - vec![Ok(missing_hash), Ok(incoming_hash)], + vec![missing_hash, incoming_hash], incoming_block.submittal_height, ); @@ -259,7 +252,7 @@ mod tests { } fn given_eth_rpc_that_will_stream( - events: Vec>, + events: Vec<[u8; 32]>, starting_from_height: EthHeight, ) -> MockEthereumAdapter { let mut eth_rpc = MockEthereumAdapter::new(); @@ -273,16 +266,15 @@ mod tests { eth_rpc } - fn given_event_streamer_w_events(events: Vec>) -> MockEventStreamer { + fn given_event_streamer_w_events(events: Vec<[u8; 32]>) -> MockEventStreamer { let mut streamer = MockEventStreamer::new(); let events = events .into_iter() - .map(|e| { - e.map(|fuel_block_hash| FuelBlockCommittedOnEth { - fuel_block_hash, - commit_height: U256::default(), - }) + .map(|block_hash| FuelBlockCommittedOnEth { + fuel_block_hash: block_hash, + commit_height: U256::default(), }) + .map(Ok) .collect::>(); streamer diff --git a/src/services/health_reporter.rs b/committer/src/services/health_reporter.rs similarity index 95% rename from src/services/health_reporter.rs rename to committer/src/services/health_reporter.rs index aac9d213..0f724792 100644 --- a/src/services/health_reporter.rs +++ b/committer/src/services/health_reporter.rs @@ -1,7 +1,6 @@ +use metrics::HealthChecker; use serde::Serialize; -use crate::telemetry::HealthChecker; - #[derive(Debug, Serialize)] pub struct HealthReport { fuel_connection_up: bool, diff --git a/src/services/status_reporter.rs b/committer/src/services/status_reporter.rs similarity index 93% rename from src/services/status_reporter.rs rename to committer/src/services/status_reporter.rs index 75db1418..a59092e8 100644 --- a/src/services/status_reporter.rs +++ b/committer/src/services/status_reporter.rs @@ -1,9 +1,8 @@ +use ports::storage::Storage; use serde::Serialize; +use storage::Postgres; -use crate::{ - adapters::storage::{postgresql::Postgres, Storage}, - errors::Result, -}; +use crate::errors::Result; #[derive(Debug, Serialize, Default, PartialEq, Eq)] pub struct StatusReport { @@ -51,10 +50,11 @@ where mod tests { use std::sync::Arc; + use ports::BlockSubmission; use rand::Rng; + use storage::PostgresProcess; use super::*; - use crate::adapters::storage::{postgresql::PostgresProcess, BlockSubmission}; #[tokio::test] async fn status_depends_on_last_submission() { diff --git a/src/services/wallet_balance_tracker.rs b/committer/src/services/wallet_balance_tracker.rs similarity index 67% rename from src/services/wallet_balance_tracker.rs rename to committer/src/services/wallet_balance_tracker.rs index ea65068d..50ab6efb 100644 --- a/src/services/wallet_balance_tracker.rs +++ b/committer/src/services/wallet_balance_tracker.rs @@ -1,37 +1,28 @@ use std::str::FromStr; -use ethers::{ - signers::{LocalWallet, Signer}, - types::{H160, U256}, -}; +use metrics::RegistersMetrics; +use ports::{eth_rpc::EthereumAdapter, H160, U256}; use prometheus::{IntGauge, Opts}; -use crate::{ - adapters::{ethereum_adapter::EthereumAdapter, runner::Runner}, - errors::Result, - telemetry::RegistersMetrics, -}; +use crate::errors::Result; + +use super::Runner; pub struct WalletBalanceTracker { eth_adapter: Box, metrics: Metrics, - address: H160, } impl WalletBalanceTracker { - pub fn new(adapter: impl EthereumAdapter + 'static, ethereum_wallet_key: &str) -> Self { - let address = LocalWallet::from_str(ethereum_wallet_key) - .expect("Valid eth key") - .address(); + pub fn new(adapter: impl EthereumAdapter + 'static) -> Self { Self { eth_adapter: Box::new(adapter), metrics: Metrics::default(), - address, } } pub async fn update_balance(&self) -> Result<()> { - let balance = self.eth_adapter.balance(self.address).await?; + let balance = self.eth_adapter.balance().await?; let balance_gwei = balance / U256::from(1_000_000_000); self.metrics @@ -81,22 +72,18 @@ impl Runner for WalletBalanceTracker { #[cfg(test)] mod tests { use mockall::predicate::eq; + use ports::eth_rpc::MockEthereumAdapter; use prometheus::{proto::Metric, Registry}; use super::*; - use crate::adapters::ethereum_adapter::MockEthereumAdapter; #[tokio::test] async fn updates_metrics() { // given - let eth_private_key = "0000000000000000000000000000000000000000000000000000000000000001"; - let eth_adapter = given_eth_adapter( - "500000000000000000000", - "7E5F4552091A69125d5DfCb7b8C2659029395Bdf", - ); + let eth_adapter = given_eth_adapter("500000000000000000000"); let registry = Registry::new(); - let sut = WalletBalanceTracker::new(eth_adapter, eth_private_key); + let sut = WalletBalanceTracker::new(eth_adapter); sut.register_metrics(®istry); // when @@ -114,15 +101,13 @@ mod tests { assert_eq!(eth_balance_metric.get_value(), 500_000_000_000_f64); } - fn given_eth_adapter(wei_balance: &str, expected_addr: &str) -> MockEthereumAdapter { - let addr = H160::from_str(expected_addr).unwrap(); + fn given_eth_adapter(wei_balance: &str) -> MockEthereumAdapter { let balance = U256::from_dec_str(wei_balance).unwrap(); let mut eth_adapter = MockEthereumAdapter::new(); eth_adapter .expect_balance() - .with(eq(addr)) - .return_once(move |_| Ok(balance)); + .return_once(move || Ok(balance)); eth_adapter } diff --git a/src/setup/helpers.rs b/committer/src/setup.rs similarity index 83% rename from src/setup/helpers.rs rename to committer/src/setup.rs index 38aeae74..54e6f2d5 100644 --- a/src/setup/helpers.rs +++ b/committer/src/setup.rs @@ -1,21 +1,19 @@ use std::time::Duration; +use eth_rpc::WsAdapter; +use fuel_rpc::client::Client; +use metrics::{HealthChecker, RegistersMetrics}; +use ports::{storage::Storage, FuelBlock}; use prometheus::Registry; +use storage::Postgres; use tokio::{sync::mpsc::Receiver, task::JoinHandle}; use tokio_util::sync::CancellationToken; use tracing::{error, info}; use crate::{ - adapters::{ - ethereum_adapter::{EthereumWs, MonitoredEthAdapter}, - fuel_adapter::{FuelBlock, FuelClient}, - runner::Runner, - storage::{postgresql::Postgres, Storage}, - }, config::{Config, InternalConfig}, errors::Result, - services::{BlockCommitter, BlockWatcher, CommitListener, WalletBalanceTracker}, - telemetry::{HealthChecker, RegistersMetrics}, + services::{BlockCommitter, BlockWatcher, CommitListener, Runner, WalletBalanceTracker}, }; pub fn spawn_block_watcher( @@ -48,10 +46,10 @@ pub fn spawn_wallet_balance_tracker( config: &Config, internal_config: &InternalConfig, registry: &Registry, - ethereum_rpc: MonitoredEthAdapter, + ethereum_rpc: WsAdapter, cancel_token: CancellationToken, ) -> tokio::task::JoinHandle<()> { - let wallet_balance_tracker = WalletBalanceTracker::new(ethereum_rpc, &config.eth.wallet_key); + let wallet_balance_tracker = WalletBalanceTracker::new(ethereum_rpc); wallet_balance_tracker.register_metrics(registry); @@ -66,7 +64,7 @@ pub fn spawn_wallet_balance_tracker( pub fn spawn_eth_committer_and_listener( internal_config: &InternalConfig, rx_fuel_block: Receiver, - ethereum_rpc: MonitoredEthAdapter, + ethereum_rpc: WsAdapter, storage: Postgres, registry: &Registry, cancel_token: CancellationToken, @@ -89,7 +87,7 @@ pub fn spawn_eth_committer_and_listener( fn create_block_committer( rx_fuel_block: Receiver, - ethereum_rpc: MonitoredEthAdapter, + ethereum_rpc: WsAdapter, storage: impl Storage + 'static, ) -> tokio::task::JoinHandle<()> { let mut block_committer = BlockCommitter::new(rx_fuel_block, ethereum_rpc, storage); @@ -105,23 +103,22 @@ pub async fn create_eth_adapter( config: &Config, internal_config: &InternalConfig, registry: &Registry, -) -> Result<(MonitoredEthAdapter, HealthChecker)> { - let ethereum_rpc = EthereumWs::connect( +) -> Result<(WsAdapter, HealthChecker)> { + let ws_adapter = WsAdapter::connect( &config.eth.rpc, config.eth.chain_id, config.eth.state_contract_address, &config.eth.wallet_key, config.eth.commit_interval, + internal_config.eth_errors_before_unhealthy, ) .await?; - let monitored = - MonitoredEthAdapter::new(ethereum_rpc, internal_config.eth_errors_before_unhealthy); - monitored.register_metrics(registry); + ws_adapter.register_metrics(registry); - let health_check = monitored.connection_health_checker(); + let health_check = ws_adapter.connection_health_checker(); - Ok((monitored, health_check)) + Ok((ws_adapter, health_check)) } fn schedule_polling( @@ -151,8 +148,8 @@ fn create_fuel_adapter( config: &Config, internal_config: &InternalConfig, registry: &Registry, -) -> (FuelClient, HealthChecker) { - let fuel_adapter = FuelClient::new( +) -> (Client, HealthChecker) { + let fuel_adapter = Client::new( &config.fuel.graphql_endpoint, internal_config.fuel_errors_before_unhealthy, ); @@ -166,7 +163,7 @@ fn create_fuel_adapter( fn create_block_watcher( config: &Config, registry: &Registry, - fuel_adapter: FuelClient, + fuel_adapter: Client, storage: Postgres, ) -> (BlockWatcher, Receiver) { let (tx_fuel_block, rx_fuel_block) = tokio::sync::mpsc::channel(100); diff --git a/committer/src/setup/helpers.rs b/committer/src/setup/helpers.rs new file mode 100644 index 00000000..139597f9 --- /dev/null +++ b/committer/src/setup/helpers.rs @@ -0,0 +1,2 @@ + + diff --git a/committer/tests/eth_test_adapter.rs b/committer/tests/eth_test_adapter.rs new file mode 100644 index 00000000..139597f9 --- /dev/null +++ b/committer/tests/eth_test_adapter.rs @@ -0,0 +1,2 @@ + + diff --git a/committer/tests/harness.rs b/committer/tests/harness.rs new file mode 100644 index 00000000..139597f9 --- /dev/null +++ b/committer/tests/harness.rs @@ -0,0 +1,2 @@ + + diff --git a/e2e/Cargo.toml b/e2e/Cargo.toml new file mode 100644 index 00000000..cc5a1d46 --- /dev/null +++ b/e2e/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "e2e" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dev-dependencies] +anyhow = { workspace = true } +tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } +fuel_rpc = { workspace = true, features = ["test-helpers"] } +eth_rpc = { workspace = true, features = ["test-helpers"] } +ports = { workspace = true, features = ["fuel", "eth"] } diff --git a/e2e/src/lib.rs b/e2e/src/lib.rs new file mode 100644 index 00000000..8fb5ca99 --- /dev/null +++ b/e2e/src/lib.rs @@ -0,0 +1,40 @@ +#[cfg(test)] +mod tests { + use super::*; + use std::time::Duration; + + use anyhow::Result; + use eth_rpc::{Chain, WsAdapter}; + use fuel_rpc::client::Client; + use ports::fuel_rpc::FuelAdapter; + + const FUEL_NODE_PORT: u16 = 4000; + const ETH_NODE_PORT: u16 = 8089; + + #[tokio::test(flavor = "multi_thread")] + async fn submitted_correct_block_and_was_finalized() -> Result<()> { + let fuel_node_address = format!("http://localhost:{FUEL_NODE_PORT}"); + let provider = Client::new(&fuel_node_address.parse()?, 10); + + let fuel_contract = WsAdapter::connect( + &"ws://eth_node:8099".parse()?, + Chain::AnvilHardhat, + "0xdAad669b06d79Cb48C8cfef789972436dBe6F24d".parse()?, + "0x9e56ccf010fa4073274b8177ccaad46fbaf286645310d03ac9bb6afa922a7c36", + 3.try_into()?, + 10, + ) + .await?; + + provider.produce_blocks(3).await?; + + // time enough to fwd the block to ethereum and for the TIME_TO_FINALIZE (1s) to elapse + tokio::time::sleep(Duration::from_secs(5)).await; + + let latest_block = provider.latest_block().await?; + + assert!(fuel_contract.finalized(latest_block).await?); + + Ok(()) + } +} diff --git a/packages/eth_rpc/Cargo.toml b/packages/eth_rpc/Cargo.toml new file mode 100644 index 00000000..d52d6433 --- /dev/null +++ b/packages/eth_rpc/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "eth_rpc" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +async-trait = { workspace = true } +ethers = { workspace = true, features = ["ws"] } +serde_json = { workspace = true } +tracing = { workspace = true } +url = { workspace = true } +thiserror = { workspace = true } +ports = { workspace = true, features = ["eth"] } +futures = { workspace = true } +metrics = { workspace = true } +prometheus = { workspace = true } + +[dev-dependencies] +ports = { workspace = true, features = ["eth"] } +tokio = { workspace = true, features = ["macros"] } +mockall = { workspace = true } + +[features] +test-helpers = [] diff --git a/packages/eth_rpc/src/lib.rs b/packages/eth_rpc/src/lib.rs new file mode 100644 index 00000000..2aad6e89 --- /dev/null +++ b/packages/eth_rpc/src/lib.rs @@ -0,0 +1,96 @@ +use futures::{stream::TryStreamExt, Stream}; +use std::pin::Pin; +use websocket::EthEventStreamer; + +use async_trait::async_trait; +use ethers::{ + prelude::{ContractError, SignerMiddleware}, + providers::{Provider, Ws}, + signers::LocalWallet, + types::U256, +}; +use ports::{eth_rpc::FuelBlockCommittedOnEth, EthHeight}; + +mod metrics; +mod websocket; + +pub use ethers::types::Address; +pub use ethers::types::Chain; +pub use websocket::WsAdapter; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("wallet error: {0}")] + Wallet(#[from] ethers::signers::WalletError), + #[error("network error: {0}")] + Network(String), + #[error("other error: {0}")] + Other(String), +} + +impl From for Error { + fn from(err: ethers::providers::ProviderError) -> Self { + Error::Network(err.to_string()) + } +} + +type ContractErrorType = + ethers::contract::ContractError, LocalWallet>>; +impl From for Error { + fn from(value: ContractErrorType) -> Self { + match value { + ContractError::MiddlewareError { e } => Error::Other(e.to_string()), + ContractError::ProviderError { e } => Error::Network(e.to_string()), + _ => Error::Other(value.to_string()), + } + } +} + +pub type Result = std::result::Result; + +impl From for ports::eth_rpc::Error { + fn from(err: Error) -> Self { + match err { + Error::Network(err) => ports::eth_rpc::Error::Network(err), + Error::Other(err) => ports::eth_rpc::Error::Other(err), + Error::Wallet(err) => ports::eth_rpc::Error::Other(err.to_string()), + } + } +} + +#[async_trait] +impl ports::eth_rpc::EthereumAdapter for WsAdapter { + async fn submit(&self, block: ports::FuelBlock) -> ports::eth_rpc::Result<()> { + Ok(self.submit(block).await?) + } + + async fn get_block_number(&self) -> ports::eth_rpc::Result { + let block_num = self.get_block_number().await?; + let height = EthHeight::try_from(block_num)?; + Ok(height) + } + + fn event_streamer( + &self, + eth_block_height: u64, + ) -> Box { + let stream = self.event_streamer(eth_block_height); + Box::new(stream) + } + + async fn balance(&self) -> ports::eth_rpc::Result { + Ok(self.balance().await?) + } +} + +#[async_trait::async_trait] +impl ports::eth_rpc::EventStreamer for EthEventStreamer { + async fn establish_stream( + &self, + ) -> ports::eth_rpc::Result< + Pin> + '_ + Send>>, + > { + let stream = self.establish_stream().await?.map_err(Into::into); + Ok(Box::pin(stream)) + } +} diff --git a/packages/eth_rpc/src/metrics.rs b/packages/eth_rpc/src/metrics.rs new file mode 100644 index 00000000..435679bb --- /dev/null +++ b/packages/eth_rpc/src/metrics.rs @@ -0,0 +1,27 @@ +use metrics::{HealthChecker, RegistersMetrics}; +use prometheus::{IntCounter, Opts}; + +use crate::websocket::WsAdapter; + +#[derive(Clone)] +pub(crate) struct Metrics { + pub(crate) eth_network_errors: IntCounter, +} + +impl RegistersMetrics for Metrics { + fn metrics(&self) -> Vec> { + vec![Box::new(self.eth_network_errors.clone())] + } +} + +impl Default for Metrics { + fn default() -> Self { + let eth_network_errors = IntCounter::with_opts(Opts::new( + "eth_network_errors", + "Number of network errors encountered while running Ethereum RPCs.", + )) + .expect("eth_network_errors metric to be correctly configured"); + + Self { eth_network_errors } + } +} diff --git a/packages/eth_rpc/src/websocket.rs b/packages/eth_rpc/src/websocket.rs new file mode 100644 index 00000000..1782e244 --- /dev/null +++ b/packages/eth_rpc/src/websocket.rs @@ -0,0 +1,84 @@ +use std::num::NonZeroU32; + +use ethers::types::{Address, Chain}; +use metrics::{HealthChecker, RegistersMetrics}; +use ports::{FuelBlock, H160, U256}; +use url::Url; + +use crate::Result; + +pub(crate) use self::event_streamer::EthEventStreamer; +use self::{ + connection::WsConnection, + health_tracking_middleware::{HealthTrackingMiddleware, MyAdapter}, +}; + +mod connection; +mod event_streamer; +mod health_tracking_middleware; + +#[derive(Clone)] +pub struct WsAdapter { + inner: HealthTrackingMiddleware, +} + +impl WsAdapter { + pub async fn connect( + ethereum_rpc: &Url, + chain_id: Chain, + contract_address: Address, + ethereum_wallet_key: &str, + commit_interval: NonZeroU32, + unhealthy_after_n_errors: usize, + ) -> Result { + let provider = WsConnection::connect( + ethereum_rpc, + chain_id, + contract_address, + ethereum_wallet_key, + commit_interval, + ) + .await?; + + Ok(Self { + inner: HealthTrackingMiddleware::new(provider, unhealthy_after_n_errors), + }) + } + + pub fn connection_health_checker(&self) -> HealthChecker { + self.inner.connection_health_checker() + } + + pub(crate) fn event_streamer(&self, eth_block_height: u64) -> EthEventStreamer { + self.inner.event_streamer(eth_block_height) + } + + pub(crate) async fn submit(&self, block: FuelBlock) -> Result<()> { + self.inner.submit(block).await + } + + pub(crate) async fn get_block_number(&self) -> Result { + self.inner.get_block_number().await + } + + pub(crate) async fn balance(&self) -> Result { + self.inner.balance().await + } + + #[cfg(feature = "test-helpers")] + pub async fn finalized(&self, block: FuelBlock) -> Result { + self.inner.finalized(block).await + } + + #[cfg(feature = "test-helpers")] + pub async fn block_hash_at_commit_height(&self, commit_height: u32) -> Result<[u8; 32]> { + self.inner.block_hash_at_commit_height(commit_height).await + } +} + +// User responsible for registering any metrics T might have +impl RegistersMetrics for WsAdapter { + fn metrics(&self) -> Vec> { + self.inner.metrics.metrics() + } +} diff --git a/packages/eth_rpc/src/websocket/connection.rs b/packages/eth_rpc/src/websocket/connection.rs new file mode 100644 index 00000000..1c46df81 --- /dev/null +++ b/packages/eth_rpc/src/websocket/connection.rs @@ -0,0 +1,156 @@ +use ethers::providers::Middleware; +use ethers::providers::Ws; +use ethers::signers::Signer; +use ethers::types::H160; +use ports::FuelBlock; + +use crate::Result; + +use super::event_streamer::EthEventStreamer; +use super::health_tracking_middleware::MyAdapter; + +use ethers::types::U64; + +use serde_json::Value; + +use ethers::types::U256; + +use std::str::FromStr; +use std::sync::Arc; + +use ethers::types::Address; + +use ethers::types::Chain; + +use url::Url; + +use std::num::NonZeroU32; + +use ethers::signers::LocalWallet; + +use ethers::prelude::SignerMiddleware; + +use ethers::providers::Provider; + +use ethers::prelude::abigen; + +abigen!( + FUEL_STATE_CONTRACT, + r#"[ + function commit(bytes32 blockHash, uint256 commitHeight) external whenNotPaused + event CommitSubmitted(uint256 indexed commitHeight, bytes32 blockHash) + function finalized(bytes32 blockHash, uint256 blockHeight) external view whenNotPaused returns (bool) + function blockHashAtCommit(uint256 commitHeight) external view returns (bytes32) + ]"#, +); + +#[derive(Clone)] +pub struct WsConnection { + pub(crate) provider: Provider, + pub(crate) contract: FUEL_STATE_CONTRACT, LocalWallet>>, + pub(crate) commit_interval: NonZeroU32, + address: H160, +} + +#[async_trait::async_trait] +impl MyAdapter for WsConnection { + async fn submit(&self, block: FuelBlock) -> Result<()> { + let commit_height = Self::calculate_commit_height(block.height, self.commit_interval); + let contract_call = self.contract.commit(block.hash, commit_height); + let tx = contract_call.send().await?; + + tracing::info!("tx: {} submitted", tx.tx_hash()); + + Ok(()) + } + + async fn get_block_number(&self) -> Result { + // if provider.get_block_number is used the outgoing JSON RPC request would have the + // 'params' field set as `params: null`. This is accepted by Anvil but rejected by hardhat. + // By passing a preconstructed serde_json Value::Array it will cause params to be defined + // as `params: []` which is acceptable by both Anvil and Hardhat. + let response = self + .provider + .request::("eth_blockNumber", Value::Array(vec![])) + .await?; + Ok(response.as_u64()) + } + + async fn balance(&self) -> Result { + let address = self.address; + Ok(self.provider.get_balance(address, None).await?) + } + + fn event_streamer(&self, eth_block_height: u64) -> EthEventStreamer { + let events = self + .contract + .event::() + .from_block(eth_block_height); + + EthEventStreamer::new(events) + } + + async fn finalized(&self, block: FuelBlock) -> Result { + Ok(self + .contract + .finalized(block.hash, block.height.into()) + .call() + .await?) + } + + async fn block_hash_at_commit_height(&self, commit_height: u32) -> Result<[u8; 32]> { + Ok(self + .contract + .block_hash_at_commit(commit_height.into()) + .call() + .await?) + } +} + +impl WsConnection { + pub async fn connect( + ethereum_rpc: &Url, + chain_id: Chain, + contract_address: Address, + ethereum_wallet_key: &str, + commit_interval: NonZeroU32, + ) -> Result { + let provider = Provider::::connect(ethereum_rpc.to_string()).await?; + + let wallet = LocalWallet::from_str(ethereum_wallet_key)?.with_chain_id(chain_id); + let address = wallet.address(); + + let signer = SignerMiddleware::new(provider.clone(), wallet); + + let contract_address = Address::from_slice(contract_address.as_ref()); + let contract = FUEL_STATE_CONTRACT::new(contract_address, Arc::new(signer)); + + Ok(Self { + provider, + contract, + commit_interval, + address, + }) + } + + pub(crate) fn calculate_commit_height(block_height: u32, commit_interval: NonZeroU32) -> U256 { + (block_height / commit_interval).into() + } + + pub(crate) async fn _balance(&self, address: H160) -> Result { + Ok(self.provider.get_balance(address, None).await?) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn calculates_correctly_the_commit_height() { + assert_eq!( + WsConnection::calculate_commit_height(10, 3.try_into().unwrap()), + 3.into() + ); + } +} diff --git a/packages/eth_rpc/src/websocket/event_streamer.rs b/packages/eth_rpc/src/websocket/event_streamer.rs new file mode 100644 index 00000000..de1b9795 --- /dev/null +++ b/packages/eth_rpc/src/websocket/event_streamer.rs @@ -0,0 +1,53 @@ +use futures::TryStreamExt; +use ports::eth_rpc::FuelBlockCommittedOnEth; + +use futures::Stream; + +use ethers::prelude::k256::ecdsa::SigningKey; + +use ethers::signers::Wallet; + +use ethers::providers::{Provider, Ws}; + +use ethers::prelude::SignerMiddleware; + +use std::sync::Arc; + +use ethers::prelude::Event; + +use crate::Result; + +use super::connection::CommitSubmittedFilter; + +pub(crate) type EthStreamInitializer = Event< + Arc, Wallet>>, + SignerMiddleware, Wallet>, + CommitSubmittedFilter, +>; + +pub struct EthEventStreamer { + pub(crate) events: EthStreamInitializer, +} + +impl EthEventStreamer { + pub fn new(events: EthStreamInitializer) -> Self { + Self { events } + } + + pub(crate) async fn establish_stream( + &self, + ) -> Result> + Send + '_> { + let events = self.events.subscribe().await?; + let stream = events + .map_ok(|event| { + let fuel_block_hash = event.block_hash; + let commit_height = event.commit_height; + FuelBlockCommittedOnEth { + fuel_block_hash, + commit_height, + } + }) + .map_err(Into::into); + Ok(stream) + } +} diff --git a/src/adapters/ethereum_adapter/monitored_adapter.rs b/packages/eth_rpc/src/websocket/health_tracking_middleware.rs similarity index 69% rename from src/adapters/ethereum_adapter/monitored_adapter.rs rename to packages/eth_rpc/src/websocket/health_tracking_middleware.rs index 7ece8825..3d9ad7f0 100644 --- a/src/adapters/ethereum_adapter/monitored_adapter.rs +++ b/packages/eth_rpc/src/websocket/health_tracking_middleware.rs @@ -1,24 +1,27 @@ -use ethers::types::{H160, U256}; -use prometheus::{IntCounter, Opts}; - -use super::EthHeight; -use crate::{ - adapters::{ - ethereum_adapter::{EthereumAdapter, EventStreamer}, - fuel_adapter::FuelBlock, - }, - errors::{Error, Result}, - telemetry::{ConnectionHealthTracker, HealthChecker, RegistersMetrics}, -}; +use metrics::{ConnectionHealthTracker, HealthChecker, RegistersMetrics}; +use ports::{FuelBlock, U256}; + +use crate::{metrics::Metrics, websocket::event_streamer::EthEventStreamer, Error, Result}; + +#[cfg_attr(test, mockall::automock)] +#[async_trait::async_trait] +pub(crate) trait MyAdapter { + async fn submit(&self, block: FuelBlock) -> Result<()>; + async fn get_block_number(&self) -> Result; + async fn balance(&self) -> Result; + fn event_streamer(&self, eth_block_height: u64) -> EthEventStreamer; + async fn finalized(&self, block: FuelBlock) -> Result; + async fn block_hash_at_commit_height(&self, commit_height: u32) -> Result<[u8; 32]>; +} #[derive(Clone)] -pub struct MonitoredEthAdapter { +pub(crate) struct HealthTrackingMiddleware { adapter: T, - metrics: Metrics, + pub(crate) metrics: Metrics, health_tracker: ConnectionHealthTracker, } -impl MonitoredEthAdapter { +impl HealthTrackingMiddleware { pub fn new(adapter: T, unhealthy_after_n_errors: usize) -> Self { Self { adapter, @@ -32,7 +35,7 @@ impl MonitoredEthAdapter { } fn note_network_status(&self, response: &Result) { - match &response { + match response { Ok(_val) => { self.health_tracker.note_success(); } @@ -46,57 +49,47 @@ impl MonitoredEthAdapter { } // User responsible for registering any metrics T might have -impl RegistersMetrics for MonitoredEthAdapter { +impl RegistersMetrics for HealthTrackingMiddleware { fn metrics(&self) -> Vec> { self.metrics.metrics() } } #[async_trait::async_trait] -impl EthereumAdapter for MonitoredEthAdapter { +impl MyAdapter for HealthTrackingMiddleware +where + T: MyAdapter + Send + Sync, +{ async fn submit(&self, block: FuelBlock) -> Result<()> { let response = self.adapter.submit(block).await; self.note_network_status(&response); response } - async fn get_block_number(&self) -> Result { + async fn get_block_number(&self) -> Result { let response = self.adapter.get_block_number().await; self.note_network_status(&response); response } - fn event_streamer(&self, eth_block_height: u64) -> Box { + fn event_streamer(&self, eth_block_height: u64) -> EthEventStreamer { self.adapter.event_streamer(eth_block_height) } - async fn balance(&self, address: H160) -> Result { - let response = self.adapter.balance(address).await; + async fn balance(&self) -> Result { + let response = self.adapter.balance().await; self.note_network_status(&response); response } -} -#[derive(Clone)] -struct Metrics { - eth_network_errors: IntCounter, -} - -impl RegistersMetrics for Metrics { - fn metrics(&self) -> Vec> { - vec![Box::new(self.eth_network_errors.clone())] + async fn finalized(&self, block: FuelBlock) -> Result { + self.adapter.finalized(block).await } -} - -impl Default for Metrics { - fn default() -> Self { - let eth_network_errors = IntCounter::with_opts(Opts::new( - "eth_network_errors", - "Number of network errors encountered while running Ethereum RPCs.", - )) - .expect("eth_network_errors metric to be correctly configured"); - Self { eth_network_errors } + async fn block_hash_at_commit_height(&self, commit_height: u32) -> Result<[u8; 32]> { + self.adapter + .block_hash_at_commit_height(commit_height) + .await } } @@ -105,12 +98,11 @@ mod tests { use prometheus::{proto::Metric, Registry}; use super::*; - use crate::adapters::ethereum_adapter::MockEthereumAdapter; #[tokio::test] async fn recovers_after_successful_network_request() { // given - let mut eth_adapter = MockEthereumAdapter::new(); + let mut eth_adapter = MockMyAdapter::new(); eth_adapter .expect_submit() .returning(|_| Err(Error::Network("An error".into()))); @@ -119,7 +111,7 @@ mod tests { .expect_get_block_number() .returning(|| Ok(10u32.into())); - let adapter = MonitoredEthAdapter::new(eth_adapter, 1); + let adapter = HealthTrackingMiddleware::new(eth_adapter, 1); let health_check = adapter.connection_health_checker(); let _ = adapter.submit(given_a_block(42)).await; @@ -134,7 +126,7 @@ mod tests { #[tokio::test] async fn other_errors_dont_impact_health_status() { // given - let mut eth_adapter = MockEthereumAdapter::new(); + let mut eth_adapter = MockMyAdapter::new(); eth_adapter .expect_submit() .returning(|_| Err(Error::Other("An error".into()))); @@ -143,7 +135,7 @@ mod tests { .expect_get_block_number() .returning(|| Err(Error::Other("An error".into()))); - let adapter = MonitoredEthAdapter::new(eth_adapter, 2); + let adapter = HealthTrackingMiddleware::new(eth_adapter, 2); let health_check = adapter.connection_health_checker(); let _ = adapter.submit(given_a_block(42)).await; @@ -157,7 +149,7 @@ mod tests { #[tokio::test] async fn network_errors_impact_health_status() { - let mut eth_adapter = MockEthereumAdapter::new(); + let mut eth_adapter = MockMyAdapter::new(); eth_adapter .expect_submit() .returning(|_| Err(Error::Network("An error".into()))); @@ -166,7 +158,7 @@ mod tests { .expect_get_block_number() .returning(|| Err(Error::Network("An error".into()))); - let adapter = MonitoredEthAdapter::new(eth_adapter, 3); + let adapter = HealthTrackingMiddleware::new(eth_adapter, 3); let health_check = adapter.connection_health_checker(); assert!(health_check.healthy()); @@ -182,7 +174,7 @@ mod tests { #[tokio::test] async fn network_errors_seen_in_metrics() { - let mut eth_adapter = MockEthereumAdapter::new(); + let mut eth_adapter = MockMyAdapter::new(); eth_adapter .expect_submit() .returning(|_| Err(Error::Network("An error".into()))); @@ -192,7 +184,7 @@ mod tests { .returning(|| Err(Error::Network("An error".into()))); let registry = Registry::new(); - let adapter = MonitoredEthAdapter::new(eth_adapter, 3); + let adapter = HealthTrackingMiddleware::new(eth_adapter, 3); adapter.register_metrics(®istry); let _ = adapter.submit(given_a_block(42)).await; diff --git a/packages/fuel_rpc/Cargo.toml b/packages/fuel_rpc/Cargo.toml new file mode 100644 index 00000000..956f089c --- /dev/null +++ b/packages/fuel_rpc/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "fuel_rpc" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +fuel-core-client = { workspace = true } +url = { workspace = true } +ports = { workspace = true, features = ["fuel"] } +thiserror = { workspace = true } +async-trait = { workspace = true } +metrics = { workspace = true } +prometheus = { workspace = true } + +[dev-dependencies] +tokio = { workspace = true, features = ["macros"] } + +[features] +test-helpers = [] diff --git a/packages/fuel_rpc/src/client.rs b/packages/fuel_rpc/src/client.rs new file mode 100644 index 00000000..87569918 --- /dev/null +++ b/packages/fuel_rpc/src/client.rs @@ -0,0 +1,55 @@ +use fuel_core_client::client::{types::Block, FuelClient as GqlClient}; +use metrics::ConnectionHealthTracker; +use url::Url; + +use crate::{metrics::Metrics, Error, Result}; + +pub struct Client { + pub(crate) client: GqlClient, + pub(crate) metrics: Metrics, + pub(crate) health_tracker: ConnectionHealthTracker, +} + +impl Client { + pub fn new(url: &Url, unhealthy_after_n_errors: usize) -> Self { + let client = GqlClient::new(url).expect("Url to be well formed"); + Self { + client, + metrics: Metrics::default(), + health_tracker: ConnectionHealthTracker::new(unhealthy_after_n_errors), + } + } + + #[cfg(feature = "test-helpers")] + pub async fn produce_blocks(&self, num: u32) -> Result<()> { + self.client + .produce_blocks(num, None) + .await + .map_err(|e| Error::Network(e.to_string()))?; + + Ok(()) + } + + pub(crate) async fn _block_at_height(&self, height: u32) -> Result> { + let maybe_block = self + .client + .block_by_height(height.into()) + .await + .map_err(|e| Error::Network(e.to_string()))?; + + Ok(maybe_block.map(Into::into)) + } + + pub(crate) async fn _latest_block(&self) -> Result { + match self.client.chain_info().await { + Ok(chain_info) => { + self.handle_network_success(); + Ok(chain_info.latest_block) + } + Err(err) => { + self.handle_network_error(); + Err(Error::Network(err.to_string())) + } + } + } +} diff --git a/src/adapters/fuel_adapter/fuel_client.rs b/packages/fuel_rpc/src/lib.rs similarity index 59% rename from src/adapters/fuel_adapter/fuel_client.rs rename to packages/fuel_rpc/src/lib.rs index 885022fd..c1e359ba 100644 --- a/src/adapters/fuel_adapter/fuel_client.rs +++ b/packages/fuel_rpc/src/lib.rs @@ -1,86 +1,38 @@ -use fuel_core_client::client::{types::Block as FuelGqlBlock, FuelClient as FuelGqlClient}; -use url::Url; - -use crate::{ - adapters::fuel_adapter::{fuel_metrics::FuelMetrics, FuelAdapter, FuelBlock}, - errors::{Error, Result}, - telemetry::{ConnectionHealthTracker, HealthChecker, RegistersMetrics}, -}; - -impl RegistersMetrics for FuelClient { - fn metrics(&self) -> Vec> { - self.metrics.metrics() - } -} - -pub struct FuelClient { - client: FuelGqlClient, - metrics: FuelMetrics, - health_tracker: ConnectionHealthTracker, -} - -impl FuelClient { - pub fn new(url: &Url, unhealthy_after_n_errors: usize) -> Self { - let client = FuelGqlClient::new(url).expect("Url to be well formed"); - Self { - client, - metrics: FuelMetrics::default(), - health_tracker: ConnectionHealthTracker::new(unhealthy_after_n_errors), - } - } - - pub fn connection_health_checker(&self) -> HealthChecker { - self.health_tracker.tracker() - } - - fn handle_network_error(&self) { - self.health_tracker.note_failure(); - self.metrics.fuel_network_errors.inc(); - } - - fn handle_network_success(&self) { - self.health_tracker.note_success(); - } -} - -impl From for FuelBlock { - fn from(value: FuelGqlBlock) -> Self { - Self { - hash: *value.id, - height: value.header.height, - } +use fuel_core_client::client::types::Block; +use ports::FuelBlock; +pub mod client; +pub mod metrics; + +pub type Error = ports::fuel_rpc::Error; +pub type Result = ports::fuel_rpc::Result; + +fn convert_block(block: Block) -> FuelBlock { + FuelBlock { + hash: *block.id, + height: block.header.height, } } #[async_trait::async_trait] -impl FuelAdapter for FuelClient { - async fn block_at_height(&self, height: u32) -> Result> { - let maybe_block = self - .client - .block_by_height(height.into()) - .await - .map_err(|e| Error::Network(e.to_string()))?; - - Ok(maybe_block.map(Into::into)) +impl ports::fuel_rpc::FuelAdapter for client::Client { + async fn block_at_height(&self, height: u32) -> ports::fuel_rpc::Result> { + Ok(self._block_at_height(height).await?.map(convert_block)) } - async fn latest_block(&self) -> Result { - match self.client.chain_info().await { - Ok(chain_info) => { - self.handle_network_success(); - Ok(chain_info.latest_block.into()) - } - Err(err) => { - self.handle_network_error(); - Err(Error::Network(err.to_string())) - } - } + async fn latest_block(&self) -> ports::fuel_rpc::Result { + let block = self._latest_block().await?; + Ok(convert_block(block)) } } #[cfg(test)] mod tests { + use ::metrics::RegistersMetrics; + use ports::fuel_rpc::FuelAdapter; use prometheus::{proto::Metric, Registry}; + use url::Url; + + use crate::client::Client; use super::*; @@ -142,7 +94,7 @@ mod tests { // killing the node once the SDK supports it. let url = Url::parse("localhost:12344").unwrap(); - let fuel_adapter = FuelClient::new(&url, 1); + let fuel_adapter = Client::new(&url, 1); let registry = Registry::default(); fuel_adapter.register_metrics(®istry); @@ -169,7 +121,7 @@ mod tests { // killing the node once the SDK supports it. let url = Url::parse("http://localhost:12344").unwrap(); - let fuel_adapter = FuelClient::new(&url, 3); + let fuel_adapter = client::Client::new(&url, 3); let health_check = fuel_adapter.connection_health_checker(); assert!(health_check.healthy()); diff --git a/packages/fuel_rpc/src/metrics.rs b/packages/fuel_rpc/src/metrics.rs new file mode 100644 index 00000000..a70cd6fb --- /dev/null +++ b/packages/fuel_rpc/src/metrics.rs @@ -0,0 +1,54 @@ +use metrics::HealthChecker; +use prometheus::Opts; + +use metrics::Collector; + +use metrics::RegistersMetrics; + +use prometheus::IntCounter; + +use crate::client::Client; + +pub struct Metrics { + pub fuel_network_errors: IntCounter, +} + +impl RegistersMetrics for Metrics { + fn metrics(&self) -> Vec> { + vec![Box::new(self.fuel_network_errors.clone())] + } +} + +impl Default for Metrics { + fn default() -> Self { + let fuel_network_errors = IntCounter::with_opts(Opts::new( + "fuel_network_errors", + "Number of network errors encountered while polling for a new Fuel block.", + )) + .expect("fuel_network_errors metric to be correctly configured"); + Self { + fuel_network_errors, + } + } +} + +impl RegistersMetrics for Client { + fn metrics(&self) -> Vec> { + self.metrics.metrics() + } +} + +impl Client { + pub fn connection_health_checker(&self) -> HealthChecker { + self.health_tracker.tracker() + } + + pub(crate) fn handle_network_error(&self) { + self.health_tracker.note_failure(); + self.metrics.fuel_network_errors.inc(); + } + + pub(crate) fn handle_network_success(&self) { + self.health_tracker.note_success(); + } +} diff --git a/packages/metrics/Cargo.toml b/packages/metrics/Cargo.toml new file mode 100644 index 00000000..6e99b845 --- /dev/null +++ b/packages/metrics/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "metrics" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +prometheus = { workspace = true } diff --git a/packages/metrics/src/connection_health_tracker.rs b/packages/metrics/src/connection_health_tracker.rs new file mode 100644 index 00000000..9601eee7 --- /dev/null +++ b/packages/metrics/src/connection_health_tracker.rs @@ -0,0 +1,43 @@ +use super::HealthCheck; + +use super::HealthChecker; + +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering; + +use std::sync::Arc; + +#[derive(Debug, Clone)] +pub struct ConnectionHealthTracker { + // how many failures are needed before the connection is deemed unhealhty + pub(crate) max_consecutive_failures: usize, + // how many consecutive failures there currently are + pub(crate) consecutive_failures: Arc, +} + +impl ConnectionHealthTracker { + pub fn new(max_consecutive_failures: usize) -> Self { + Self { + max_consecutive_failures, + consecutive_failures: Arc::new(AtomicUsize::new(0)), + } + } + + pub fn note_failure(&self) { + self.consecutive_failures.fetch_add(1, Ordering::SeqCst); + } + + pub fn note_success(&self) { + self.consecutive_failures.store(0, Ordering::SeqCst); + } + + pub fn tracker(&self) -> HealthChecker { + Box::new(self.clone()) + } +} + +impl HealthCheck for ConnectionHealthTracker { + fn healthy(&self) -> bool { + self.consecutive_failures.load(Ordering::Relaxed) < self.max_consecutive_failures + } +} diff --git a/src/telemetry/metrics.rs b/packages/metrics/src/lib.rs similarity index 58% rename from src/telemetry/metrics.rs rename to packages/metrics/src/lib.rs index 4567cd85..3215ac60 100644 --- a/src/telemetry/metrics.rs +++ b/packages/metrics/src/lib.rs @@ -1,4 +1,12 @@ -use prometheus::{core::Collector, Registry}; +mod connection_health_tracker; +pub use connection_health_tracker::*; + +pub type HealthChecker = Box; +pub trait HealthCheck: Send + Sync { + fn healthy(&self) -> bool; +} + +pub use prometheus::{core::Collector, Registry}; pub trait RegistersMetrics { fn register_metrics(&self, registry: &Registry) { diff --git a/packages/ports/Cargo.toml b/packages/ports/Cargo.toml new file mode 100644 index 00000000..87d9b3e8 --- /dev/null +++ b/packages/ports/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "ports" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +types = { workspace = true } +thiserror = { workspace = true } +async-trait = { workspace = true } +impl-tools = { workspace = true } +mockall = { workspace = true, optional = true } +ethers-core = { workspace = true, optional = true } +futures = { workspace = true } + +[features] +test-helpers = ["dep:mockall"] +rand = ["types/rand"] +eth = ["dep:ethers-core"] +fuel = [] +storage = [] diff --git a/packages/ports/src/eth_rpc.rs b/packages/ports/src/eth_rpc.rs new file mode 100644 index 00000000..bba4cf8a --- /dev/null +++ b/packages/ports/src/eth_rpc.rs @@ -0,0 +1,55 @@ +use std::pin::Pin; + +use types::InvalidEthHeight; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("network error: {0}")] + Network(String), + #[error("{0}")] + Other(String), +} + +pub type Result = std::result::Result; + +impl From for Error { + fn from(err: InvalidEthHeight) -> Self { + Self::Other(err.to_string()) + } +} + +#[cfg_attr(feature = "test-helpers", mockall::automock)] +#[async_trait::async_trait] +pub trait EthereumAdapter: Send + Sync { + async fn submit(&self, block: crate::FuelBlock) -> Result<()>; + async fn get_block_number(&self) -> Result; + async fn balance(&self) -> Result; + fn event_streamer(&self, eth_block_height: u64) -> Box; +} + +#[cfg_attr(feature = "test-helpers", mockall::automock)] +#[async_trait::async_trait] +pub trait EventStreamer { + async fn establish_stream<'a>( + &'a self, + ) -> Result> + 'a + Send>>>; +} + +#[derive(Clone, Copy)] +pub struct FuelBlockCommittedOnEth { + pub fuel_block_hash: [u8; 32], + pub commit_height: crate::U256, +} + +impl std::fmt::Debug for FuelBlockCommittedOnEth { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let hash = self + .fuel_block_hash + .map(|byte| format!("{byte:02x?}")) + .join(""); + f.debug_struct("FuelBlockCommittedOnEth") + .field("hash", &hash) + .field("commit_height", &self.commit_height) + .finish() + } +} diff --git a/packages/ports/src/fuel_rpc.rs b/packages/ports/src/fuel_rpc.rs new file mode 100644 index 00000000..bb031015 --- /dev/null +++ b/packages/ports/src/fuel_rpc.rs @@ -0,0 +1,14 @@ +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("{0}")] + Network(String), +} + +pub type Result = std::result::Result; + +#[cfg_attr(feature = "test-helpers", mockall::automock)] +#[async_trait::async_trait] +pub trait FuelAdapter: Send + Sync { + async fn block_at_height(&self, height: u32) -> Result>; + async fn latest_block(&self) -> Result; +} diff --git a/packages/ports/src/lib.rs b/packages/ports/src/lib.rs new file mode 100644 index 00000000..bb71e5cc --- /dev/null +++ b/packages/ports/src/lib.rs @@ -0,0 +1,13 @@ +#[cfg(feature = "eth")] +pub mod eth_rpc; +#[cfg(feature = "fuel")] +pub mod fuel_rpc; +#[cfg(feature = "storage")] +pub mod storage; + +#[cfg(feature = "eth")] +pub use ethers_core::types::{H160, U256}; +#[cfg(feature = "eth")] +pub use futures::Stream; + +pub use types::{BlockSubmission, EthHeight, FuelBlock, InvalidEthHeight}; diff --git a/packages/ports/src/storage.rs b/packages/ports/src/storage.rs new file mode 100644 index 00000000..b554e06c --- /dev/null +++ b/packages/ports/src/storage.rs @@ -0,0 +1,26 @@ +use std::sync::Arc; + +#[derive(Debug, thiserror::Error)] +#[error("{msg}")] +pub struct Error { + msg: String, +} + +impl Error { + pub fn new(msg: String) -> Self { + Self { msg } + } +} + +pub type Result = std::result::Result; + +#[async_trait::async_trait] +#[impl_tools::autoimpl(for &T, &mut T, Arc, Box)] +pub trait Storage: Send + Sync { + async fn insert(&self, submission: crate::BlockSubmission) -> Result<()>; + async fn submission_w_latest_block(&self) -> Result>; + async fn set_submission_completed( + &self, + fuel_block_hash: [u8; 32], + ) -> Result; +} diff --git a/packages/storage/Cargo.toml b/packages/storage/Cargo.toml new file mode 100644 index 00000000..d304db98 --- /dev/null +++ b/packages/storage/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "storage" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +sqlx = { workspace = true, features = [ + "postgres", + "runtime-tokio", + "migrate", + "macros", +] } +async-trait = { workspace = true } +ports = { workspace = true, features = ["storage"] } +thiserror = { workspace = true } +serde = { workspace = true } +hex = { workspace = true } +testcontainers = { workspace = true, optional = true } +tokio = { workspace = true, optional = true } +rand = { workspace = true, optional = true } + +[dev-dependencies] +storage = { path = ".", features = ["runnable-instance"] } +ports = { workspace = true, features = ["storage", "rand"] } +rand = { workspace = true } +tokio = { workspace = true } + +[features] +runnable-instance = ["dep:testcontainers", "tokio/sync", "dep:rand"] diff --git a/migrations/0001_initial.down.sql b/packages/storage/migrations/0001_initial.down.sql similarity index 100% rename from migrations/0001_initial.down.sql rename to packages/storage/migrations/0001_initial.down.sql diff --git a/migrations/0001_initial.up.sql b/packages/storage/migrations/0001_initial.up.sql similarity index 100% rename from migrations/0001_initial.up.sql rename to packages/storage/migrations/0001_initial.up.sql diff --git a/packages/storage/src/lib.rs b/packages/storage/src/lib.rs new file mode 100644 index 00000000..d18cd2f7 --- /dev/null +++ b/packages/storage/src/lib.rs @@ -0,0 +1,137 @@ +mod tables; +#[cfg(feature = "runnable-instance")] +mod test_instance; +#[cfg(feature = "runnable-instance")] +pub use test_instance::*; + +mod postgres; +pub use postgres::*; + +pub type Result = std::result::Result; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Database Error {0}")] + Database(String), + #[error("Could not convert to/from domain/db type {0}")] + Conversion(String), + #[error("{0}")] + Other(String), +} + +impl From for ports::storage::Error { + fn from(value: Error) -> Self { + Self::new(value.to_string()) + } +} + +impl From for Error { + fn from(e: sqlx::Error) -> Self { + Self::Database(e.to_string()) + } +} + +impl From for Error { + fn from(e: sqlx::migrate::MigrateError) -> Self { + Self::Database(e.to_string()) + } +} + +use ports::BlockSubmission; + +#[async_trait::async_trait] +impl ports::storage::Storage for postgres::Postgres { + async fn insert(&self, submission: BlockSubmission) -> ports::storage::Result<()> { + Ok(self._insert(submission).await?) + } + + async fn submission_w_latest_block(&self) -> ports::storage::Result> { + Ok(self._submission_w_latest_block().await?) + } + + async fn set_submission_completed( + &self, + fuel_block_hash: [u8; 32], + ) -> ports::storage::Result { + Ok(self._set_submission_completed(fuel_block_hash).await?) + } +} + +#[cfg(test)] +mod tests { + use rand::{thread_rng, Rng}; + + use super::*; + + fn random_non_zero_height() -> u32 { + let mut rng = thread_rng(); + rng.gen_range(1..u32::MAX) + } + + #[tokio::test] + async fn can_insert_and_find_latest_block() { + // given + let process = PostgresProcess::shared().await.unwrap(); + let db = process.create_random_db().await.unwrap(); + let latest_height = random_non_zero_height(); + + let latest_submission = given_incomplete_submission(latest_height); + db._insert(latest_submission.clone()).await.unwrap(); + + let older_submission = given_incomplete_submission(latest_height - 1); + db._insert(older_submission).await.unwrap(); + + // when + let actual = db._submission_w_latest_block().await.unwrap().unwrap(); + + // then + assert_eq!(actual, latest_submission); + } + + #[tokio::test] + async fn can_update_completion_status() { + // given + let process = PostgresProcess::shared().await.unwrap(); + let db = process.create_random_db().await.unwrap(); + + let height = random_non_zero_height(); + let submission = given_incomplete_submission(height); + let block_hash = submission.block.hash; + db._insert(submission).await.unwrap(); + + // when + let submission = db._set_submission_completed(block_hash).await.unwrap(); + + // then + assert!(submission.completed); + } + + #[tokio::test] + async fn updating_a_missing_submission_causes_an_error() { + // given + let process = PostgresProcess::shared().await.unwrap(); + let db = process.create_random_db().await.unwrap(); + + let height = random_non_zero_height(); + let submission = given_incomplete_submission(height); + let block_hash = submission.block.hash; + + // when + let result = db._set_submission_completed(block_hash).await; + + // then + let Err(Error::Database(msg)) = result else { + panic!("should be storage error"); + }; + + let block_hash = hex::encode(block_hash); + assert_eq!(msg, format!("Cannot set submission to completed! Submission of block: `{block_hash}` not found in DB.")); + } + + fn given_incomplete_submission(fuel_block_height: u32) -> BlockSubmission { + let mut submission = rand::thread_rng().gen::(); + submission.block.height = fuel_block_height; + + submission + } +} diff --git a/packages/storage/src/postgres.rs b/packages/storage/src/postgres.rs new file mode 100644 index 00000000..11decc76 --- /dev/null +++ b/packages/storage/src/postgres.rs @@ -0,0 +1,107 @@ +use crate::tables; + +use super::Error; + +use ports::BlockSubmission; + +use sqlx::postgres::PgPoolOptions; + +use sqlx::postgres::PgConnectOptions; + +use super::Result; + +#[derive(Clone)] +pub struct Postgres { + pub(crate) connection_pool: sqlx::Pool, +} + +#[derive(Debug, Clone, serde::Deserialize)] +pub struct DbConfig { + /// The hostname or IP address of the PostgreSQL server. + pub host: String, + /// The port number on which the PostgreSQL server is listening. + pub port: u16, + /// The username used to authenticate with the PostgreSQL server. + pub username: String, + /// The password used to authenticate with the PostgreSQL server. + pub password: String, + /// The name of the database to connect to on the PostgreSQL server. + pub database: String, + /// The maximum number of connections allowed in the connection pool. + pub max_connections: u32, +} + +impl Postgres { + pub async fn connect(opt: &DbConfig) -> Result { + let options = PgConnectOptions::new() + .username(&opt.username) + .password(&opt.password) + .database(&opt.database) + .host(&opt.host) + .port(opt.port); + + let connection_pool = PgPoolOptions::new() + .max_connections(opt.max_connections) + .connect_with(options) + .await?; + + Ok(Self { connection_pool }) + } + + /// Close only when shutting down the application. Will close the connection pool even if it is + /// shared. + pub async fn close(self) { + self.connection_pool.close().await; + } + + pub async fn migrate(&self) -> Result<()> { + sqlx::migrate!().run(&self.connection_pool).await?; + Ok(()) + } + + pub(crate) async fn execute(&self, query: &str) -> Result<()> { + sqlx::query(query).execute(&self.connection_pool).await?; + Ok(()) + } + + pub(crate) async fn _insert(&self, submission: BlockSubmission) -> Result<()> { + let row = tables::EthFuelBlockSubmission::from(submission); + sqlx::query!( + "INSERT INTO eth_fuel_block_submission (fuel_block_hash, fuel_block_height, completed, submittal_height) VALUES ($1, $2, $3, $4)", + row.fuel_block_hash, + row.fuel_block_height, + row.completed, + row.submittal_height + ).execute(&self.connection_pool).await?; + Ok(()) + } + + pub(crate) async fn _submission_w_latest_block(&self) -> Result> { + sqlx::query_as!( + tables::EthFuelBlockSubmission, + "SELECT * FROM eth_fuel_block_submission ORDER BY fuel_block_height DESC LIMIT 1" + ) + .fetch_optional(&self.connection_pool) + .await? + .map(BlockSubmission::try_from) + .transpose() + } + + pub(crate) async fn _set_submission_completed( + &self, + fuel_block_hash: [u8; 32], + ) -> Result { + let updated_row = sqlx::query_as!( + tables::EthFuelBlockSubmission, + "UPDATE eth_fuel_block_submission SET completed = true WHERE fuel_block_hash = $1 RETURNING *", + fuel_block_hash.as_slice(), + ).fetch_optional(&self.connection_pool).await?; + + if let Some(row) = updated_row { + Ok(row.try_into()?) + } else { + let hash = hex::encode(fuel_block_hash); + Err(Error::Database(format!("Cannot set submission to completed! Submission of block: `{hash}` not found in DB."))) + } + } +} diff --git a/src/adapters/storage/postgresql/tables.rs b/packages/storage/src/tables.rs similarity index 94% rename from src/adapters/storage/postgresql/tables.rs rename to packages/storage/src/tables.rs index ac8dc2a1..b47bb660 100644 --- a/src/adapters/storage/postgresql/tables.rs +++ b/packages/storage/src/tables.rs @@ -1,7 +1,6 @@ -use crate::adapters::{ - fuel_adapter::FuelBlock, - storage::{BlockSubmission, Error}, -}; +use ports::{BlockSubmission, FuelBlock}; + +use crate::Error; #[derive(sqlx::FromRow)] pub struct EthFuelBlockSubmission { diff --git a/src/adapters/storage/postgresql/test_instance.rs b/packages/storage/src/test_instance.rs similarity index 97% rename from src/adapters/storage/postgresql/test_instance.rs rename to packages/storage/src/test_instance.rs index 26b44f45..f0dac359 100644 --- a/src/adapters/storage/postgresql/test_instance.rs +++ b/packages/storage/src/test_instance.rs @@ -2,8 +2,9 @@ use std::sync::{Arc, Weak}; use testcontainers::{core::WaitFor, runners::AsyncRunner, Image, RunnableImage}; -use super::{DbConfig, Postgres}; -use crate::adapters::storage::Result; +use crate::Result; + +use super::{postgres::DbConfig, postgres::Postgres}; struct PostgresImage; diff --git a/packages/types/Cargo.toml b/packages/types/Cargo.toml new file mode 100644 index 00000000..275b6f8e --- /dev/null +++ b/packages/types/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "types" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rand = { workspace = true, optional = true } +serde = { workspace = true, features = ["derive"] } + +[features] +rand = ["dep:rand"] diff --git a/packages/types/src/block_submission.rs b/packages/types/src/block_submission.rs new file mode 100644 index 00000000..f8a1bd42 --- /dev/null +++ b/packages/types/src/block_submission.rs @@ -0,0 +1,21 @@ +use crate::{EthHeight, FuelBlock}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BlockSubmission { + pub block: FuelBlock, + pub completed: bool, + // Eth block height moments before submitting the fuel block. Used to filter stale events in + // the commit listener. + pub submittal_height: EthHeight, +} + +#[cfg(feature = "rand")] +impl rand::distributions::Distribution for rand::distributions::Standard { + fn sample(&self, rng: &mut R) -> BlockSubmission { + BlockSubmission { + block: rng.gen(), + completed: rng.gen(), + submittal_height: rng.gen(), + } + } +} diff --git a/packages/types/src/eth_height.rs b/packages/types/src/eth_height.rs new file mode 100644 index 00000000..07faee2f --- /dev/null +++ b/packages/types/src/eth_height.rs @@ -0,0 +1,69 @@ +#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)] +pub struct EthHeight { + pub(crate) height: i64, +} + +#[derive(Debug, Clone)] +pub struct InvalidEthHeight(String); +impl std::fmt::Display for InvalidEthHeight { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "Invalid eth height: {}", self.0) + } +} +impl std::error::Error for InvalidEthHeight {} + +#[cfg(feature = "rand")] +impl rand::distributions::Distribution for rand::distributions::Standard { + fn sample(&self, rng: &mut R) -> EthHeight { + let height: i64 = rng.gen_range(0..=i64::MAX); + height.try_into().expect("Must be valid EthHeight") + } +} + +impl TryFrom for EthHeight { + type Error = InvalidEthHeight; + + fn try_from(height: i64) -> Result { + if height < 0 { + return Err(InvalidEthHeight(format!( + "must be non-negative, got {height}", + ))); + } + Ok(EthHeight { height }) + } +} + +impl TryFrom for EthHeight { + type Error = InvalidEthHeight; + fn try_from(height: u64) -> Result { + if height >= i64::MAX as u64 { + return Err(InvalidEthHeight(format!( + "{height} too large. DB can handle at most {}", + i64::MAX + ))); + } + Ok(Self { + height: height as i64, + }) + } +} + +impl From for EthHeight { + fn from(height: u32) -> Self { + Self { + height: i64::from(height), + } + } +} + +impl From for i64 { + fn from(height: EthHeight) -> Self { + height.height + } +} + +impl From for u64 { + fn from(height: EthHeight) -> Self { + height.height as u64 + } +} diff --git a/src/adapters/fuel_adapter.rs b/packages/types/src/fuel_block.rs similarity index 61% rename from src/adapters/fuel_adapter.rs rename to packages/types/src/fuel_block.rs index 2da49e1d..b45fb0b9 100644 --- a/src/adapters/fuel_adapter.rs +++ b/packages/types/src/fuel_block.rs @@ -1,19 +1,13 @@ -mod fuel_client; -mod fuel_metrics; - -pub use fuel_client::FuelClient; -use rand::distributions::{Distribution, Standard}; use serde::{Deserialize, Serialize}; -use crate::errors::Result; - #[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] pub struct FuelBlock { pub hash: [u8; 32], pub height: u32, } -impl Distribution for Standard { +#[cfg(feature = "rand")] +impl rand::distributions::Distribution for rand::distributions::Standard { fn sample(&self, rng: &mut R) -> FuelBlock { FuelBlock { hash: rng.gen(), @@ -21,6 +15,7 @@ impl Distribution for Standard { } } } + impl std::fmt::Debug for FuelBlock { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let hash = self.hash.map(|byte| format!("{byte:02x?}")).join(""); @@ -30,10 +25,3 @@ impl std::fmt::Debug for FuelBlock { .finish() } } - -#[cfg_attr(test, mockall::automock)] -#[async_trait::async_trait] -pub trait FuelAdapter: Send + Sync { - async fn block_at_height(&self, height: u32) -> Result>; - async fn latest_block(&self) -> Result; -} diff --git a/packages/types/src/lib.rs b/packages/types/src/lib.rs new file mode 100644 index 00000000..2438368f --- /dev/null +++ b/packages/types/src/lib.rs @@ -0,0 +1,7 @@ +mod block_submission; +mod eth_height; +mod fuel_block; + +pub use block_submission::*; +pub use eth_height::*; +pub use fuel_block::*; diff --git a/src/adapters.rs b/src/adapters.rs deleted file mode 100644 index 90983a67..00000000 --- a/src/adapters.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod ethereum_adapter; -pub mod fuel_adapter; -pub mod runner; -pub mod storage; diff --git a/src/adapters/ethereum_adapter.rs b/src/adapters/ethereum_adapter.rs deleted file mode 100644 index e39f946e..00000000 --- a/src/adapters/ethereum_adapter.rs +++ /dev/null @@ -1,112 +0,0 @@ -mod monitored_adapter; -mod websocket; - -use std::pin::Pin; - -use async_trait::async_trait; -use ethers::types::{H160, U256}; -use futures::Stream; -pub use monitored_adapter::MonitoredEthAdapter; -use rand::distributions::{Distribution, Standard}; -pub use websocket::EthereumWs; - -use crate::{ - adapters::fuel_adapter::FuelBlock, - errors::{Error, Result}, -}; - -#[derive(Clone, Copy)] -pub struct FuelBlockCommittedOnEth { - pub fuel_block_hash: [u8; 32], - pub commit_height: U256, -} - -impl std::fmt::Debug for FuelBlockCommittedOnEth { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let hash = self - .fuel_block_hash - .map(|byte| format!("{byte:02x?}")) - .join(""); - f.debug_struct("FuelBlockCommittedOnEth") - .field("hash", &hash) - .field("commit_height", &self.commit_height) - .finish() - } -} - -#[cfg_attr(test, mockall::automock)] -#[async_trait] -pub trait EventStreamer { - async fn establish_stream<'a>( - &'a self, - ) -> Result> + 'a + Send>>>; -} - -#[cfg_attr(test, mockall::automock)] -#[async_trait] -pub trait EthereumAdapter: Send + Sync { - async fn submit(&self, block: FuelBlock) -> Result<()>; - async fn get_block_number(&self) -> Result; - async fn balance(&self, address: H160) -> Result; - fn event_streamer(&self, eth_block_height: u64) -> Box; -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)] -pub struct EthHeight { - height: i64, -} - -impl Distribution for Standard { - fn sample(&self, rng: &mut R) -> EthHeight { - let height: i64 = rng.gen_range(0..=i64::MAX); - height.try_into().expect("Must be valid EthHeight") - } -} - -impl TryFrom for EthHeight { - type Error = Error; - - fn try_from(height: i64) -> Result { - if height < 0 { - return Err(Error::Other(format!( - "Height({height}) must be non-negative" - ))); - } - Ok(EthHeight { height }) - } -} - -impl TryFrom for EthHeight { - type Error = Error; - fn try_from(height: u64) -> Result { - if height >= i64::MAX as u64 { - return Err(Error::Other(format!( - "Height({height}) too large. DB can handle at most {}", - i64::MAX - ))); - } - Ok(Self { - height: height as i64, - }) - } -} - -impl From for EthHeight { - fn from(height: u32) -> Self { - Self { - height: i64::from(height), - } - } -} - -impl From for i64 { - fn from(height: EthHeight) -> Self { - height.height - } -} - -impl From for u64 { - fn from(height: EthHeight) -> Self { - height.height as u64 - } -} diff --git a/src/adapters/ethereum_adapter/websocket.rs b/src/adapters/ethereum_adapter/websocket.rs deleted file mode 100644 index 7e83b0d2..00000000 --- a/src/adapters/ethereum_adapter/websocket.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod adapter; -mod event_streamer; - -pub use adapter::EthereumWs; diff --git a/src/adapters/ethereum_adapter/websocket/adapter.rs b/src/adapters/ethereum_adapter/websocket/adapter.rs deleted file mode 100644 index c8e02681..00000000 --- a/src/adapters/ethereum_adapter/websocket/adapter.rs +++ /dev/null @@ -1,129 +0,0 @@ -use std::{num::NonZeroU32, str::FromStr, sync::Arc}; - -use async_trait::async_trait; -use ethers::{ - prelude::{abigen, ContractError, SignerMiddleware}, - providers::{Middleware, Provider, Ws}, - signers::{LocalWallet, Signer}, - types::{Address, Chain, H160, U256, U64}, -}; -use serde_json::Value; -use tracing::info; -use url::Url; - -use crate::{ - adapters::{ - ethereum_adapter::{ - websocket::event_streamer::EthEventStreamer, EthHeight, EthereumAdapter, EventStreamer, - }, - fuel_adapter::FuelBlock, - }, - errors::{Error, Result}, -}; - -abigen!( - FUEL_STATE_CONTRACT, - r#"[ - function commit(bytes32 blockHash, uint256 commitHeight) external whenNotPaused - event CommitSubmitted(uint256 indexed commitHeight, bytes32 blockHash) - ]"#, -); - -#[derive(Clone)] -pub struct EthereumWs { - provider: Provider, - contract: FUEL_STATE_CONTRACT, LocalWallet>>, - commit_interval: NonZeroU32, -} - -impl EthereumWs { - pub async fn connect( - ethereum_rpc: &Url, - chain_id: Chain, - contract_address: Address, - ethereum_wallet_key: &str, - commit_interval: NonZeroU32, - ) -> Result { - let provider = Provider::::connect(ethereum_rpc.to_string()) - .await - .map_err(|e| Error::Network(e.to_string()))?; - - let wallet = LocalWallet::from_str(ethereum_wallet_key)?.with_chain_id(chain_id); - - let signer = SignerMiddleware::new(provider.clone(), wallet); - - let contract_address = Address::from_slice(contract_address.as_ref()); - let contract = FUEL_STATE_CONTRACT::new(contract_address, Arc::new(signer)); - - Ok(Self { - provider, - contract, - commit_interval, - }) - } - - fn calculate_commit_height(block_height: u32, commit_interval: NonZeroU32) -> U256 { - (block_height / commit_interval).into() - } -} - -#[async_trait] -impl EthereumAdapter for EthereumWs { - async fn submit(&self, block: FuelBlock) -> Result<()> { - let commit_height = Self::calculate_commit_height(block.height, self.commit_interval); - let contract_call = self.contract.commit(block.hash, commit_height); - let tx = contract_call - .send() - .await - .map_err(|contract_err| match contract_err { - ContractError::ProviderError { e } => Error::Network(e.to_string()), - ContractError::MiddlewareError { e } => Error::Network(e.to_string()), - _ => Error::Other(contract_err.to_string()), - })?; - - info!("tx: {} submitted", tx.tx_hash()); - - Ok(()) - } - - async fn get_block_number(&self) -> Result { - // if provider.get_block_number is used the outgoing JSON RPC request would have the - // 'params' field set as `params: null`. This is accepted by Anvil but rejected by hardhat. - // By passing a preconstructed serde_json Value::Array it will cause params to be defined - // as `params: []` which is acceptable by both Anvil and Hardhat. - self.provider - .request("eth_blockNumber", Value::Array(vec![])) - .await - .map_err(|err| Error::Network(err.to_string())) - .and_then(|height: U64| height.as_u64().try_into()) - } - - fn event_streamer(&self, eth_block_height: u64) -> Box { - let events = self - .contract - .event::() - .from_block(eth_block_height); - - Box::new(EthEventStreamer::new(events)) - } - - async fn balance(&self, address: H160) -> Result { - self.provider - .get_balance(address, None) - .await - .map_err(|err| Error::Network(err.to_string())) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn calculates_correctly_the_commit_height() { - assert_eq!( - EthereumWs::calculate_commit_height(10, 3.try_into().unwrap()), - 3.into() - ); - } -} diff --git a/src/adapters/ethereum_adapter/websocket/event_streamer.rs b/src/adapters/ethereum_adapter/websocket/event_streamer.rs deleted file mode 100644 index dd259155..00000000 --- a/src/adapters/ethereum_adapter/websocket/event_streamer.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::{pin::Pin, sync::Arc}; - -use ethers::{ - prelude::{k256::ecdsa::SigningKey, Event, SignerMiddleware}, - providers::{Provider, Ws}, - signers::Wallet, -}; -use futures::{stream::TryStreamExt, Stream}; - -use crate::{ - adapters::ethereum_adapter::{ - websocket::adapter::CommitSubmittedFilter, EventStreamer, FuelBlockCommittedOnEth, - }, - errors::{Error, Result}, -}; - -type EthStreamInitializer = Event< - Arc, Wallet>>, - SignerMiddleware, Wallet>, - CommitSubmittedFilter, ->; - -pub struct EthEventStreamer { - events: EthStreamInitializer, -} - -#[async_trait::async_trait] -impl EventStreamer for EthEventStreamer { - async fn establish_stream( - &self, - ) -> Result> + '_ + Send>>> { - Ok(Box::pin( - self.events - .subscribe() - .await - .map_err(|e| Error::Network(e.to_string()))? - .map_ok(|event| { - let fuel_block_hash = event.block_hash; - let commit_height = event.commit_height; - FuelBlockCommittedOnEth { - fuel_block_hash, - commit_height, - } - }) - .map_err(|e| Error::Other(e.to_string())), - )) - } -} - -impl EthEventStreamer { - pub fn new(events: EthStreamInitializer) -> Self { - Self { events } - } -} diff --git a/src/adapters/fuel_adapter/fuel_metrics.rs b/src/adapters/fuel_adapter/fuel_metrics.rs deleted file mode 100644 index adc556ab..00000000 --- a/src/adapters/fuel_adapter/fuel_metrics.rs +++ /dev/null @@ -1,26 +0,0 @@ -use prometheus::{IntCounter, Opts}; - -use crate::telemetry::RegistersMetrics; - -pub struct FuelMetrics { - pub fuel_network_errors: IntCounter, -} - -impl RegistersMetrics for FuelMetrics { - fn metrics(&self) -> Vec> { - vec![Box::new(self.fuel_network_errors.clone())] - } -} - -impl Default for FuelMetrics { - fn default() -> Self { - let fuel_network_errors = IntCounter::with_opts(Opts::new( - "fuel_network_errors", - "Number of network errors encountered while polling for a new Fuel block.", - )) - .expect("fuel_network_errors metric to be correctly configured"); - Self { - fuel_network_errors, - } - } -} diff --git a/src/adapters/fuel_adapter/health_tracker.rs b/src/adapters/fuel_adapter/health_tracker.rs deleted file mode 100644 index d3cf3d0b..00000000 --- a/src/adapters/fuel_adapter/health_tracker.rs +++ /dev/null @@ -1,47 +0,0 @@ -use std::{ - borrow::BorrowMut, - sync::{Arc, Mutex}, -}; - -use crate::telemetry::{HealthCheck, HealthChecker}; - -#[derive(Debug, Clone)] -pub struct FuelHealthTracker { - // how many failures are needed before the connection is deemed unhealhty - max_consecutive_failures: usize, - // how many consecutive failures there currently are - consecutive_failures: Arc>, -} - -impl FuelHealthTracker { - pub fn new(max_consecutive_failures: usize) -> Self { - Self { - max_consecutive_failures, - consecutive_failures: Arc::new(Mutex::new(0)), - } - } - - pub fn note_failure(&self) { - **self.acquire_consecutive_failures().borrow_mut() += 1; - } - - pub fn note_success(&self) { - **self.acquire_consecutive_failures().borrow_mut() = 0; - } - - fn acquire_consecutive_failures(&self) -> std::sync::MutexGuard { - self.consecutive_failures - .lock() - .expect("no need to handle poisoning since lock duration is short and no panics occurr") - } - - pub fn tracker(&self) -> HealthChecker { - Box::new(self.clone()) - } -} - -impl HealthCheck for FuelHealthTracker { - fn healthy(&self) -> bool { - *self.acquire_consecutive_failures() < self.max_consecutive_failures - } -} diff --git a/src/adapters/fuel_adapter/metrics.rs b/src/adapters/fuel_adapter/metrics.rs deleted file mode 100644 index b48b99fa..00000000 --- a/src/adapters/fuel_adapter/metrics.rs +++ /dev/null @@ -1,26 +0,0 @@ -use prometheus::{IntCounter, Opts}; - -use crate::telemetry::RegistersMetrics; - -pub struct Metrics { - pub fuel_network_errors: IntCounter, -} - -impl RegistersMetrics for Metrics { - fn metrics(&self) -> Vec> { - vec![Box::new(self.fuel_network_errors.clone())] - } -} - -impl Default for Metrics { - fn default() -> Self { - let fuel_network_errors = IntCounter::with_opts(Opts::new( - "fuel_network_errors", - "Number of network errors encountered while polling for a new Fuel block.", - )) - .expect("fuel_network_errors metric to be correctly configured"); - Self { - fuel_network_errors, - } - } -} diff --git a/src/adapters/runner.rs b/src/adapters/runner.rs deleted file mode 100644 index 7876de14..00000000 --- a/src/adapters/runner.rs +++ /dev/null @@ -1,6 +0,0 @@ -use crate::errors::Result; - -#[async_trait::async_trait] -pub trait Runner: Send + Sync { - async fn run(&mut self) -> Result<()>; -} diff --git a/src/adapters/storage.rs b/src/adapters/storage.rs deleted file mode 100644 index 2c75ce75..00000000 --- a/src/adapters/storage.rs +++ /dev/null @@ -1,59 +0,0 @@ -pub mod postgresql; - -use std::sync::Arc; - -use async_trait::async_trait; -use rand::distributions::{Distribution, Standard}; - -use crate::adapters::{ethereum_adapter::EthHeight, fuel_adapter::FuelBlock}; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct BlockSubmission { - pub block: FuelBlock, - pub completed: bool, - // Eth block height moments before submitting the fuel block. Used to filter stale events in - // the commit listener. - pub submittal_height: EthHeight, -} - -impl Distribution for Standard { - fn sample(&self, rng: &mut R) -> BlockSubmission { - BlockSubmission { - block: rng.gen(), - completed: rng.gen(), - submittal_height: rng.gen(), - } - } -} - -#[async_trait] -#[impl_tools::autoimpl(for &T, &mut T, Arc, Box)] -pub trait Storage: Send + Sync { - async fn insert(&self, submission: BlockSubmission) -> Result<()>; - async fn submission_w_latest_block(&self) -> Result>; - async fn set_submission_completed(&self, fuel_block_hash: [u8; 32]) -> Result; -} - -pub type Result = std::result::Result; - -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("Database Error {0}")] - Database(String), - #[error("Could not convert to/from domain/db type {0}")] - Conversion(String), - #[error("{0}")] - Other(String), -} - -impl From for Error { - fn from(e: sqlx::Error) -> Self { - Self::Database(e.to_string()) - } -} - -impl From for Error { - fn from(e: sqlx::migrate::MigrateError) -> Self { - Self::Database(e.to_string()) - } -} diff --git a/src/adapters/storage/postgresql.rs b/src/adapters/storage/postgresql.rs deleted file mode 100644 index fe0e2ed5..00000000 --- a/src/adapters/storage/postgresql.rs +++ /dev/null @@ -1,189 +0,0 @@ -use super::{BlockSubmission, Error, Storage}; -use crate::adapters::storage::Result; - -mod tables; -#[cfg(test)] -mod test_instance; - -use serde::Deserialize; -#[cfg(test)] -pub use test_instance::*; - -#[derive(Clone)] -pub struct Postgres { - connection_pool: sqlx::Pool, -} - -#[derive(Debug, Clone, Deserialize)] -pub struct DbConfig { - /// The hostname or IP address of the PostgreSQL server. - pub host: String, - /// The port number on which the PostgreSQL server is listening. - pub port: u16, - /// The username used to authenticate with the PostgreSQL server. - pub username: String, - /// The password used to authenticate with the PostgreSQL server. - pub password: String, - /// The name of the database to connect to on the PostgreSQL server. - pub database: String, - /// The maximum number of connections allowed in the connection pool. - pub max_connections: u32, -} - -impl Postgres { - pub async fn connect(opt: &DbConfig) -> Result { - let options = PgConnectOptions::new() - .username(&opt.username) - .password(&opt.password) - .database(&opt.database) - .host(&opt.host) - .port(opt.port); - - let connection_pool = PgPoolOptions::new() - .max_connections(opt.max_connections) - .connect_with(options) - .await?; - - Ok(Self { connection_pool }) - } - - /// Close only when shutting down the application. Will close the connection pool even if it is - /// shared. - pub async fn close(self) { - self.connection_pool.close().await; - } - - pub async fn migrate(&self) -> Result<()> { - sqlx::migrate!().run(&self.connection_pool).await?; - Ok(()) - } - - #[cfg(test)] - pub async fn execute(&self, query: &str) -> Result<()> { - sqlx::query(query).execute(&self.connection_pool).await?; - Ok(()) - } -} - -use sqlx::postgres::{PgConnectOptions, PgPoolOptions}; - -#[async_trait::async_trait] -impl Storage for Postgres { - async fn insert(&self, submission: BlockSubmission) -> Result<()> { - let row = tables::EthFuelBlockSubmission::from(submission); - sqlx::query!( - "INSERT INTO eth_fuel_block_submission (fuel_block_hash, fuel_block_height, completed, submittal_height) VALUES ($1, $2, $3, $4)", - row.fuel_block_hash, - row.fuel_block_height, - row.completed, - row.submittal_height - ).execute(&self.connection_pool).await?; - Ok(()) - } - - async fn submission_w_latest_block(&self) -> Result> { - sqlx::query_as!( - tables::EthFuelBlockSubmission, - "SELECT * FROM eth_fuel_block_submission ORDER BY fuel_block_height DESC LIMIT 1" - ) - .fetch_optional(&self.connection_pool) - .await? - .map(BlockSubmission::try_from) - .transpose() - } - - async fn set_submission_completed(&self, fuel_block_hash: [u8; 32]) -> Result { - let updated_row = sqlx::query_as!( - tables::EthFuelBlockSubmission, - "UPDATE eth_fuel_block_submission SET completed = true WHERE fuel_block_hash = $1 RETURNING *", - fuel_block_hash.as_slice(), - ).fetch_optional(&self.connection_pool).await?; - - if let Some(row) = updated_row { - Ok(row.try_into()?) - } else { - let hash = hex::encode(fuel_block_hash); - Err(Error::Database(format!("Cannot set submission to completed! Submission of block: `{hash}` not found in DB."))) - } - } -} - -#[cfg(test)] -mod tests { - use rand::{thread_rng, Rng}; - - use super::*; - use crate::adapters::storage::{BlockSubmission, Error}; - - fn random_non_zero_height() -> u32 { - let mut rng = thread_rng(); - rng.gen_range(1..u32::MAX) - } - - #[tokio::test] - async fn can_insert_and_find_latest_block() { - // given - let process = PostgresProcess::shared().await.unwrap(); - let db = process.create_random_db().await.unwrap(); - let latest_height = random_non_zero_height(); - - let latest_submission = given_incomplete_submission(latest_height); - db.insert(latest_submission.clone()).await.unwrap(); - - let older_submission = given_incomplete_submission(latest_height - 1); - db.insert(older_submission).await.unwrap(); - - // when - let actual = db.submission_w_latest_block().await.unwrap().unwrap(); - - // then - assert_eq!(actual, latest_submission); - } - - #[tokio::test] - async fn can_update_completion_status() { - // given - let process = PostgresProcess::shared().await.unwrap(); - let db = process.create_random_db().await.unwrap(); - - let height = random_non_zero_height(); - let submission = given_incomplete_submission(height); - let block_hash = submission.block.hash; - db.insert(submission).await.unwrap(); - - // when - let submission = db.set_submission_completed(block_hash).await.unwrap(); - - // then - assert!(submission.completed); - } - - #[tokio::test] - async fn updating_a_missing_submission_causes_an_error() { - // given - let process = PostgresProcess::shared().await.unwrap(); - let db = process.create_random_db().await.unwrap(); - - let height = random_non_zero_height(); - let submission = given_incomplete_submission(height); - let block_hash = submission.block.hash; - - // when - let result = db.set_submission_completed(block_hash).await; - - // then - let Err(Error::Database(msg)) = result else { - panic!("should be storage error"); - }; - - let block_hash = hex::encode(block_hash); - assert_eq!(msg, format!("Cannot set submission to completed! Submission of block: `{block_hash}` not found in DB.")); - } - - fn given_incomplete_submission(fuel_block_height: u32) -> BlockSubmission { - let mut submission = rand::thread_rng().gen::(); - submission.block.height = fuel_block_height; - - submission - } -} diff --git a/src/errors.rs b/src/errors.rs deleted file mode 100644 index 78c226eb..00000000 --- a/src/errors.rs +++ /dev/null @@ -1,60 +0,0 @@ -use actix_web::ResponseError; -use ethers::signers::WalletError; -use tokio::task::JoinError; -use url::ParseError; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("{0}")] - Other(String), - #[error("Network Error: {0}")] - Network(String), - #[error("Storage Error: {0}")] - Storage(String), -} - -impl From for Error { - fn from(value: serde_json::Error) -> Self { - Self::Storage(value.to_string()) - } -} - -impl From for Error { - fn from(error: WalletError) -> Self { - Self::Other(error.to_string()) - } -} - -impl From for Error { - fn from(error: ParseError) -> Self { - Self::Other(error.to_string()) - } -} - -impl From for Error { - fn from(error: std::io::Error) -> Self { - Self::Other(error.to_string()) - } -} - -impl From for Error { - fn from(error: JoinError) -> Self { - Self::Other(error.to_string()) - } -} - -impl From for Error { - fn from(error: crate::adapters::storage::Error) -> Self { - Self::Storage(error.to_string()) - } -} - -impl From for Error { - fn from(error: config::ConfigError) -> Self { - Self::Other(error.to_string()) - } -} - -impl ResponseError for Error {} - -pub type Result = std::result::Result; diff --git a/src/setup.rs b/src/setup.rs deleted file mode 100644 index 1630fabc..00000000 --- a/src/setup.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod helpers; diff --git a/src/telemetry.rs b/src/telemetry.rs deleted file mode 100644 index ab771a7a..00000000 --- a/src/telemetry.rs +++ /dev/null @@ -1,7 +0,0 @@ -mod health_check; -mod health_tracker; -mod metrics; - -pub use health_check::*; -pub use health_tracker::*; -pub use metrics::*; diff --git a/src/telemetry/health_check.rs b/src/telemetry/health_check.rs deleted file mode 100644 index fb11c625..00000000 --- a/src/telemetry/health_check.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub type HealthChecker = Box; -pub trait HealthCheck: Send + Sync { - fn healthy(&self) -> bool; -} diff --git a/src/telemetry/health_tracker.rs b/src/telemetry/health_tracker.rs deleted file mode 100644 index 16a760a6..00000000 --- a/src/telemetry/health_tracker.rs +++ /dev/null @@ -1,47 +0,0 @@ -use std::{ - borrow::BorrowMut, - sync::{Arc, Mutex}, -}; - -use crate::telemetry::{HealthCheck, HealthChecker}; - -#[derive(Debug, Clone)] -pub struct ConnectionHealthTracker { - // how many failures are needed before the connection is deemed unhealhty - max_consecutive_failures: usize, - // how many consecutive failures there currently are - consecutive_failures: Arc>, -} - -impl ConnectionHealthTracker { - pub fn new(max_consecutive_failures: usize) -> Self { - Self { - max_consecutive_failures, - consecutive_failures: Arc::new(Mutex::new(0)), - } - } - - pub fn note_failure(&self) { - **self.acquire_consecutive_failures().borrow_mut() += 1; - } - - pub fn note_success(&self) { - **self.acquire_consecutive_failures().borrow_mut() = 0; - } - - fn acquire_consecutive_failures(&self) -> std::sync::MutexGuard { - self.consecutive_failures - .lock() - .expect("no need to handle poisoning since lock duration is short and no panics occur") - } - - pub fn tracker(&self) -> HealthChecker { - Box::new(self.clone()) - } -} - -impl HealthCheck for ConnectionHealthTracker { - fn healthy(&self) -> bool { - *self.acquire_consecutive_failures() < self.max_consecutive_failures - } -} diff --git a/tests/eth_test_adapter.rs b/tests/eth_test_adapter.rs deleted file mode 100644 index ca0143f9..00000000 --- a/tests/eth_test_adapter.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::{str::FromStr, sync::Arc}; - -use anyhow::Result; -use ethers::{ - prelude::{abigen, SignerMiddleware}, - providers::{Provider, Ws}, - signers::{LocalWallet, Signer}, - types::{Chain, H160}, -}; - -pub struct FuelStateContract { - contract: FUEL_STATE_CONTRACT, LocalWallet>>, -} - -abigen!( - FUEL_STATE_CONTRACT, - r#"[ - function finalized(bytes32 blockHash, uint256 blockHeight) external view whenNotPaused returns (bool) - function blockHashAtCommit(uint256 commitHeight) external view returns (bytes32) - ]"#, -); - -impl FuelStateContract { - pub async fn connect(eth_node_port: u16) -> Result { - let contract_address = "0xdAad669b06d79Cb48C8cfef789972436dBe6F24d"; - let provider = Provider::::connect(format!("ws://127.0.0.1:{eth_node_port}")).await?; - - let wallet = LocalWallet::from_str( - "0x9e56ccf010fa4073274b8177ccaad46fbaf286645310d03ac9bb6afa922a7c36", - )? - .with_chain_id(Chain::AnvilHardhat); - - let signer = SignerMiddleware::new(provider, wallet); - - let contract_address: H160 = contract_address.parse()?; - let contract = FUEL_STATE_CONTRACT::new(contract_address, Arc::new(signer)); - - Ok(Self { contract }) - } - - pub async fn finalized(&self, block_hash: [u8; 32], block_height: u32) -> Result { - Ok(self - .contract - .finalized(block_hash, block_height.into()) - .call() - .await?) - } - - pub async fn _block_hash_at_commit_height(&self, commit_height: u32) -> Result<[u8; 32]> { - Ok(self - .contract - .block_hash_at_commit(commit_height.into()) - .call() - .await?) - } -} diff --git a/tests/harness.rs b/tests/harness.rs deleted file mode 100644 index 4f88aa2f..00000000 --- a/tests/harness.rs +++ /dev/null @@ -1,33 +0,0 @@ -mod eth_test_adapter; - -use std::time::Duration; - -use anyhow::Result; -use fuel_core_client::client::FuelClient; - -use crate::eth_test_adapter::FuelStateContract; - -const FUEL_NODE_PORT: u16 = 4000; -const ETH_NODE_PORT: u16 = 8089; - -#[tokio::test(flavor = "multi_thread")] -async fn submitted_correct_block_and_was_finalized() -> Result<()> { - let fuel_node_address = format!("http://localhost:{FUEL_NODE_PORT}"); - let provider = FuelClient::new(&fuel_node_address).unwrap(); - - let fuel_contract = FuelStateContract::connect(ETH_NODE_PORT).await?; - - provider.produce_blocks(3, None).await?; - - // time enough to fwd the block to ethereum and for the TIME_TO_FINALIZE (1s) to elapse - tokio::time::sleep(Duration::from_secs(5)).await; - - let latest_block = provider.chain_info().await?.latest_block; - let height = latest_block.header.height; - let hash = *latest_block.id; - - assert_eq!(height, 3); - assert!(fuel_contract.finalized(hash, height).await?); - - Ok(()) -} From 5b40498f504e1c0405f05e24ccab890fec2b17d1 Mon Sep 17 00:00:00 2001 From: segfault_magnet Date: Sat, 4 May 2024 17:04:06 +0200 Subject: [PATCH 02/23] features --- committer/Cargo.toml | 1 + packages/ports/Cargo.toml | 3 +-- packages/storage/Cargo.toml | 11 ++++++++--- packages/storage/src/lib.rs | 4 ++-- packages/types/Cargo.toml | 2 +- packages/types/src/block_submission.rs | 2 +- packages/types/src/eth_height.rs | 2 +- packages/types/src/fuel_block.rs | 2 +- 8 files changed, 16 insertions(+), 11 deletions(-) diff --git a/committer/Cargo.toml b/committer/Cargo.toml index b0bd2013..9966f651 100644 --- a/committer/Cargo.toml +++ b/committer/Cargo.toml @@ -41,6 +41,7 @@ tracing = { version = "0.1", default-features = false } tracing-subscriber = { version = "0.3", features = ["json"] } [dev-dependencies] +storage = { workspace = true, features = ["test-helpers"] } anyhow = { version = "1.0", default-features = false } mockall = { workspace = true } ports = { workspace = true, features = ["test-helpers"] } diff --git a/packages/ports/Cargo.toml b/packages/ports/Cargo.toml index 87d9b3e8..1a26a9f4 100644 --- a/packages/ports/Cargo.toml +++ b/packages/ports/Cargo.toml @@ -15,8 +15,7 @@ ethers-core = { workspace = true, optional = true } futures = { workspace = true } [features] -test-helpers = ["dep:mockall"] -rand = ["types/rand"] +test-helpers = ["dep:mockall", "types/test-helpers"] eth = ["dep:ethers-core"] fuel = [] storage = [] diff --git a/packages/storage/Cargo.toml b/packages/storage/Cargo.toml index d304db98..6ca24b9a 100644 --- a/packages/storage/Cargo.toml +++ b/packages/storage/Cargo.toml @@ -21,10 +21,15 @@ tokio = { workspace = true, optional = true } rand = { workspace = true, optional = true } [dev-dependencies] -storage = { path = ".", features = ["runnable-instance"] } -ports = { workspace = true, features = ["storage", "rand"] } +storage = { path = ".", features = ["test-helpers"] } +ports = { workspace = true, features = ["storage"] } rand = { workspace = true } tokio = { workspace = true } [features] -runnable-instance = ["dep:testcontainers", "tokio/sync", "dep:rand"] +test-helpers = [ + "dep:testcontainers", + "tokio/sync", + "dep:rand", + "ports/test-helpers", +] diff --git a/packages/storage/src/lib.rs b/packages/storage/src/lib.rs index d18cd2f7..0bd1a581 100644 --- a/packages/storage/src/lib.rs +++ b/packages/storage/src/lib.rs @@ -1,7 +1,7 @@ mod tables; -#[cfg(feature = "runnable-instance")] +#[cfg(feature = "test-helpers")] mod test_instance; -#[cfg(feature = "runnable-instance")] +#[cfg(feature = "test-helpers")] pub use test_instance::*; mod postgres; diff --git a/packages/types/Cargo.toml b/packages/types/Cargo.toml index 275b6f8e..d6beb458 100644 --- a/packages/types/Cargo.toml +++ b/packages/types/Cargo.toml @@ -11,4 +11,4 @@ rand = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"] } [features] -rand = ["dep:rand"] +test-helpers = ["dep:rand"] diff --git a/packages/types/src/block_submission.rs b/packages/types/src/block_submission.rs index f8a1bd42..aed5557a 100644 --- a/packages/types/src/block_submission.rs +++ b/packages/types/src/block_submission.rs @@ -9,7 +9,7 @@ pub struct BlockSubmission { pub submittal_height: EthHeight, } -#[cfg(feature = "rand")] +#[cfg(feature = "test-helpers")] impl rand::distributions::Distribution for rand::distributions::Standard { fn sample(&self, rng: &mut R) -> BlockSubmission { BlockSubmission { diff --git a/packages/types/src/eth_height.rs b/packages/types/src/eth_height.rs index 07faee2f..cda719ac 100644 --- a/packages/types/src/eth_height.rs +++ b/packages/types/src/eth_height.rs @@ -12,7 +12,7 @@ impl std::fmt::Display for InvalidEthHeight { } impl std::error::Error for InvalidEthHeight {} -#[cfg(feature = "rand")] +#[cfg(feature = "test-helpers")] impl rand::distributions::Distribution for rand::distributions::Standard { fn sample(&self, rng: &mut R) -> EthHeight { let height: i64 = rng.gen_range(0..=i64::MAX); diff --git a/packages/types/src/fuel_block.rs b/packages/types/src/fuel_block.rs index b45fb0b9..619d4646 100644 --- a/packages/types/src/fuel_block.rs +++ b/packages/types/src/fuel_block.rs @@ -6,7 +6,7 @@ pub struct FuelBlock { pub height: u32, } -#[cfg(feature = "rand")] +#[cfg(feature = "test-helpers")] impl rand::distributions::Distribution for rand::distributions::Standard { fn sample(&self, rng: &mut R) -> FuelBlock { FuelBlock { From 1b22c2d5ecdebda427eb001ac846c086ca255f42 Mon Sep 17 00:00:00 2001 From: segfault_magnet Date: Sat, 4 May 2024 17:06:41 +0200 Subject: [PATCH 03/23] move all versions to workspace level --- Cargo.toml | 40 ++++++++++++++++++---------------- committer/Cargo.toml | 42 +++++++++++++++++++----------------- e2e/Cargo.toml | 5 ++--- packages/eth_rpc/Cargo.toml | 13 ++++++----- packages/fuel_rpc/Cargo.toml | 9 ++++---- packages/metrics/Cargo.toml | 1 - packages/ports/Cargo.toml | 9 ++++---- packages/storage/Cargo.toml | 14 ++++++------ packages/types/Cargo.toml | 1 - 9 files changed, 67 insertions(+), 67 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4e741b1c..b87ae07a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,30 +12,34 @@ members = [ ] [workspace.dependencies] -types = { version = "0.1.0", path = "./packages/types/", default-features = false } -storage = { version = "0.1.0", path = "./packages/storage", default-features = false } -ports = { version = "0.1.0", path = "./packages/ports", default-features = false } -metrics = { version = "0.1.0", path = "./packages/metrics", default-features = false } -fuel_rpc = { version = "0.1.0", path = "./packages/fuel_rpc", default-features = false } eth_rpc = { version = "0.1.0", path = "./packages/eth_rpc", default-features = false } -anyhow = { version = "1.0", default-features = false } +fuel_rpc = { version = "0.1.0", path = "./packages/fuel_rpc", default-features = false } +metrics = { version = "0.1.0", path = "./packages/metrics", default-features = false } +ports = { version = "0.1.0", path = "./packages/ports", default-features = false } +storage = { version = "0.1.0", path = "./packages/storage", default-features = false } +types = { version = "0.1.0", path = "./packages/types/", default-features = false } -mockall = { version = "0.12", default-features = false } +actix-web = { version = "4", default-features = false } +anyhow = { version = "1.0", default-features = false } +async-trait = { version = "0.1", default-features = false } +clap = { version = "4.5" } +config = { version = "0.14", default-features = false } +ethers = { version = "2.0", default-features = false } ethers-core = { version = "2.0", default-features = false } +fuel-core-client = { version = "0.26", default-features = false } futures = { version = "0.3", default-features = false } -ethers = { version = "2.0", default-features = false } - -sqlx = { version = "0.7.4", default-features = false } +hex = { version = "0.4", default-features = false } +impl-tools = { version = "0.10.0", default-features = false } +mockall = { version = "0.12", default-features = false } +prometheus = { version = "0.13", default-features = false } rand = { version = "0.8", default-features = false } -async-trait = { version = "0.1", default-features = false } serde = { version = "1.0", default-features = false } -tracing = { version = "0.1", default-features = false } -url = { version = "2.3", default-features = false } -fuel-core-client = { version = "0.26", default-features = false } -prometheus = { version = "0.13", default-features = false } serde_json = { version = "1.0", default-features = false } +sqlx = { version = "0.7.4", default-features = false } +testcontainers = { version = "0.16", default-features = false } thiserror = { version = "1.0", default-features = false } -impl-tools = { version = "0.10.0", default-features = false } -hex = { version = "0.4", default-features = false } tokio = { version = "1.37", default-features = false } -testcontainers = { version = "0.16", default-features = false } +tokio-util = { version = "0.7", default-features = false } +tracing = { version = "0.1", default-features = false } +tracing-subscriber = { version = "0.3" } +url = { version = "2.3", default-features = false } diff --git a/committer/Cargo.toml b/committer/Cargo.toml index 9966f651..892abfda 100644 --- a/committer/Cargo.toml +++ b/committer/Cargo.toml @@ -9,41 +9,43 @@ version = "0.4.0" name = "fuel-block-committer" [dependencies] -fuel_rpc = { workspace = true } -eth_rpc = { workspace = true } -ports = { workspace = true } -storage = { workspace = true } -metrics = { workspace = true } - -serde = { workspace = true } -url = { workspace = true } -actix-web = { version = "4", default-features = false, features = ["macros"] } -clap = { version = "4.5", features = ["derive"] } +actix-web = { workspace = true, default-features = false, features = [ + "macros", +] } async-trait = { workspace = true } -config = { version = "0.14", default-features = false, features = [ +clap = { workspace = true, features = ["derive"] } +config = { workspace = true, default-features = false, features = [ "toml", "async", ] } -hex = "0.4.3" +eth_rpc = { workspace = true } +fuel_rpc = { workspace = true } futures = { workspace = true } +hex = "0.4.3" impl-tools = { workspace = true } -prometheus = { version = "0.13", default-features = false } +metrics = { workspace = true } +ports = { workspace = true } +prometheus = { workspace = true, default-features = false } rand = { workspace = true, features = ["std"] } -serde_json = { version = "1.0", default-features = false } + +serde = { workspace = true } +serde_json = { workspace = true, default-features = false } +storage = { workspace = true } thiserror = { workspace = true } -tokio = { version = "1.28", features = [ +tokio = { workspace = true, features = [ "rt-multi-thread", "rt", "macros", ], default-features = false } -tokio-util = { version = "0.7", default-features = false } -tracing = { version = "0.1", default-features = false } -tracing-subscriber = { version = "0.3", features = ["json"] } +tokio-util = { workspace = true, default-features = false } +tracing = { workspace = true, default-features = false } +tracing-subscriber = { workspace = true, features = ["json"] } +url = { workspace = true } [dev-dependencies] -storage = { workspace = true, features = ["test-helpers"] } -anyhow = { version = "1.0", default-features = false } +anyhow = { workspace = true, default-features = false } mockall = { workspace = true } ports = { workspace = true, features = ["test-helpers"] } +storage = { workspace = true, features = ["test-helpers"] } #TODO: Once fuels-rs catches up tests will be reenabled #fuels-test-helpers = "0.57" diff --git a/e2e/Cargo.toml b/e2e/Cargo.toml index cc5a1d46..442a7e93 100644 --- a/e2e/Cargo.toml +++ b/e2e/Cargo.toml @@ -2,12 +2,11 @@ name = "e2e" version = "0.1.0" edition = "2021" - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dev-dependencies] anyhow = { workspace = true } -tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } -fuel_rpc = { workspace = true, features = ["test-helpers"] } eth_rpc = { workspace = true, features = ["test-helpers"] } +fuel_rpc = { workspace = true, features = ["test-helpers"] } ports = { workspace = true, features = ["fuel", "eth"] } +tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } diff --git a/packages/eth_rpc/Cargo.toml b/packages/eth_rpc/Cargo.toml index d52d6433..0d36d5f4 100644 --- a/packages/eth_rpc/Cargo.toml +++ b/packages/eth_rpc/Cargo.toml @@ -3,25 +3,24 @@ name = "eth_rpc" version = "0.1.0" edition = "2021" publish = false - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] async-trait = { workspace = true } ethers = { workspace = true, features = ["ws"] } -serde_json = { workspace = true } -tracing = { workspace = true } -url = { workspace = true } -thiserror = { workspace = true } -ports = { workspace = true, features = ["eth"] } futures = { workspace = true } metrics = { workspace = true } +ports = { workspace = true, features = ["eth"] } prometheus = { workspace = true } +serde_json = { workspace = true } +thiserror = { workspace = true } +tracing = { workspace = true } +url = { workspace = true } [dev-dependencies] +mockall = { workspace = true } ports = { workspace = true, features = ["eth"] } tokio = { workspace = true, features = ["macros"] } -mockall = { workspace = true } [features] test-helpers = [] diff --git a/packages/fuel_rpc/Cargo.toml b/packages/fuel_rpc/Cargo.toml index 956f089c..bdafdf19 100644 --- a/packages/fuel_rpc/Cargo.toml +++ b/packages/fuel_rpc/Cargo.toml @@ -3,17 +3,16 @@ name = "fuel_rpc" version = "0.1.0" edition = "2021" publish = false - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fuel-core-client = { workspace = true } -url = { workspace = true } -ports = { workspace = true, features = ["fuel"] } -thiserror = { workspace = true } async-trait = { workspace = true } +fuel-core-client = { workspace = true } metrics = { workspace = true } +ports = { workspace = true, features = ["fuel"] } prometheus = { workspace = true } +thiserror = { workspace = true } +url = { workspace = true } [dev-dependencies] tokio = { workspace = true, features = ["macros"] } diff --git a/packages/metrics/Cargo.toml b/packages/metrics/Cargo.toml index 6e99b845..bcc68a5a 100644 --- a/packages/metrics/Cargo.toml +++ b/packages/metrics/Cargo.toml @@ -3,7 +3,6 @@ name = "metrics" version = "0.1.0" edition = "2021" publish = false - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/packages/ports/Cargo.toml b/packages/ports/Cargo.toml index 1a26a9f4..6538fb43 100644 --- a/packages/ports/Cargo.toml +++ b/packages/ports/Cargo.toml @@ -2,17 +2,16 @@ name = "ports" version = "0.1.0" edition = "2021" - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -types = { workspace = true } -thiserror = { workspace = true } async-trait = { workspace = true } -impl-tools = { workspace = true } -mockall = { workspace = true, optional = true } ethers-core = { workspace = true, optional = true } futures = { workspace = true } +impl-tools = { workspace = true } +mockall = { workspace = true, optional = true } +thiserror = { workspace = true } +types = { workspace = true } [features] test-helpers = ["dep:mockall", "types/test-helpers"] diff --git a/packages/storage/Cargo.toml b/packages/storage/Cargo.toml index 6ca24b9a..9f2b6b96 100644 --- a/packages/storage/Cargo.toml +++ b/packages/storage/Cargo.toml @@ -5,25 +5,25 @@ edition = "2021" publish = false [dependencies] +async-trait = { workspace = true } +hex = { workspace = true } +ports = { workspace = true, features = ["storage"] } +rand = { workspace = true, optional = true } +serde = { workspace = true } sqlx = { workspace = true, features = [ "postgres", "runtime-tokio", "migrate", "macros", ] } -async-trait = { workspace = true } -ports = { workspace = true, features = ["storage"] } -thiserror = { workspace = true } -serde = { workspace = true } -hex = { workspace = true } testcontainers = { workspace = true, optional = true } +thiserror = { workspace = true } tokio = { workspace = true, optional = true } -rand = { workspace = true, optional = true } [dev-dependencies] -storage = { path = ".", features = ["test-helpers"] } ports = { workspace = true, features = ["storage"] } rand = { workspace = true } +storage = { path = ".", features = ["test-helpers"] } tokio = { workspace = true } [features] diff --git a/packages/types/Cargo.toml b/packages/types/Cargo.toml index d6beb458..ceeb6571 100644 --- a/packages/types/Cargo.toml +++ b/packages/types/Cargo.toml @@ -3,7 +3,6 @@ name = "types" version = "0.1.0" edition = "2021" publish = false - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] From ead46c562e84f7ec69429a1098adc71ce4723f5d Mon Sep 17 00:00:00 2001 From: segfault_magnet Date: Sat, 4 May 2024 17:12:01 +0200 Subject: [PATCH 04/23] reformat --- committer/src/services/block_committer.rs | 3 +- committer/src/services/block_watcher.rs | 3 +- .../src/services/wallet_balance_tracker.rs | 3 +- committer/tests/eth_test_adapter.rs | 1 - committer/tests/harness.rs | 1 - e2e/src/lib.rs | 3 +- packages/eth_rpc/src/lib.rs | 7 ++-- packages/eth_rpc/src/websocket.rs | 3 +- packages/eth_rpc/src/websocket/connection.rs | 41 +++++-------------- .../eth_rpc/src/websocket/event_streamer.rs | 24 ++++------- packages/fuel_rpc/src/lib.rs | 3 +- packages/fuel_rpc/src/metrics.rs | 10 +---- .../metrics/src/connection_health_tracker.rs | 12 +++--- packages/ports/src/lib.rs | 1 - packages/storage/src/postgres.rs | 12 ++---- packages/storage/src/test_instance.rs | 3 +- 16 files changed, 39 insertions(+), 91 deletions(-) diff --git a/committer/src/services/block_committer.rs b/committer/src/services/block_committer.rs index d9e3b579..66f2ab4f 100644 --- a/committer/src/services/block_committer.rs +++ b/committer/src/services/block_committer.rs @@ -5,9 +5,8 @@ use storage::Postgres; use tokio::sync::mpsc::Receiver; use tracing::{error, info}; -use crate::errors::Result; - use super::Runner; +use crate::errors::Result; pub struct BlockCommitter { rx_block: Receiver, diff --git a/committer/src/services/block_watcher.rs b/committer/src/services/block_watcher.rs index 580e96e3..019b05fb 100644 --- a/committer/src/services/block_watcher.rs +++ b/committer/src/services/block_watcher.rs @@ -8,9 +8,8 @@ use prometheus::{core::Collector, IntGauge, Opts}; use storage::Postgres; use tokio::sync::mpsc::Sender; -use crate::errors::{Error, Result}; - use super::Runner; +use crate::errors::{Error, Result}; struct Metrics { latest_fuel_block: IntGauge, diff --git a/committer/src/services/wallet_balance_tracker.rs b/committer/src/services/wallet_balance_tracker.rs index 50ab6efb..6095fb00 100644 --- a/committer/src/services/wallet_balance_tracker.rs +++ b/committer/src/services/wallet_balance_tracker.rs @@ -4,9 +4,8 @@ use metrics::RegistersMetrics; use ports::{eth_rpc::EthereumAdapter, H160, U256}; use prometheus::{IntGauge, Opts}; -use crate::errors::Result; - use super::Runner; +use crate::errors::Result; pub struct WalletBalanceTracker { eth_adapter: Box, diff --git a/committer/tests/eth_test_adapter.rs b/committer/tests/eth_test_adapter.rs index 139597f9..8b137891 100644 --- a/committer/tests/eth_test_adapter.rs +++ b/committer/tests/eth_test_adapter.rs @@ -1,2 +1 @@ - diff --git a/committer/tests/harness.rs b/committer/tests/harness.rs index 139597f9..8b137891 100644 --- a/committer/tests/harness.rs +++ b/committer/tests/harness.rs @@ -1,2 +1 @@ - diff --git a/e2e/src/lib.rs b/e2e/src/lib.rs index 8fb5ca99..b027d5d2 100644 --- a/e2e/src/lib.rs +++ b/e2e/src/lib.rs @@ -1,6 +1,5 @@ #[cfg(test)] mod tests { - use super::*; use std::time::Duration; use anyhow::Result; @@ -8,6 +7,8 @@ mod tests { use fuel_rpc::client::Client; use ports::fuel_rpc::FuelAdapter; + use super::*; + const FUEL_NODE_PORT: u16 = 4000; const ETH_NODE_PORT: u16 = 8089; diff --git a/packages/eth_rpc/src/lib.rs b/packages/eth_rpc/src/lib.rs index 2aad6e89..00337253 100644 --- a/packages/eth_rpc/src/lib.rs +++ b/packages/eth_rpc/src/lib.rs @@ -1,6 +1,4 @@ -use futures::{stream::TryStreamExt, Stream}; use std::pin::Pin; -use websocket::EthEventStreamer; use async_trait::async_trait; use ethers::{ @@ -9,13 +7,14 @@ use ethers::{ signers::LocalWallet, types::U256, }; +use futures::{stream::TryStreamExt, Stream}; use ports::{eth_rpc::FuelBlockCommittedOnEth, EthHeight}; +use websocket::EthEventStreamer; mod metrics; mod websocket; -pub use ethers::types::Address; -pub use ethers::types::Chain; +pub use ethers::types::{Address, Chain}; pub use websocket::WsAdapter; #[derive(Debug, thiserror::Error)] diff --git a/packages/eth_rpc/src/websocket.rs b/packages/eth_rpc/src/websocket.rs index 1782e244..30fd65d3 100644 --- a/packages/eth_rpc/src/websocket.rs +++ b/packages/eth_rpc/src/websocket.rs @@ -5,13 +5,12 @@ use metrics::{HealthChecker, RegistersMetrics}; use ports::{FuelBlock, H160, U256}; use url::Url; -use crate::Result; - pub(crate) use self::event_streamer::EthEventStreamer; use self::{ connection::WsConnection, health_tracking_middleware::{HealthTrackingMiddleware, MyAdapter}, }; +use crate::Result; mod connection; mod event_streamer; diff --git a/packages/eth_rpc/src/websocket/connection.rs b/packages/eth_rpc/src/websocket/connection.rs index 1c46df81..c149b626 100644 --- a/packages/eth_rpc/src/websocket/connection.rs +++ b/packages/eth_rpc/src/websocket/connection.rs @@ -1,38 +1,17 @@ -use ethers::providers::Middleware; -use ethers::providers::Ws; -use ethers::signers::Signer; -use ethers::types::H160; +use std::{num::NonZeroU32, str::FromStr, sync::Arc}; + +use ethers::{ + prelude::{abigen, SignerMiddleware}, + providers::{Middleware, Provider, Ws}, + signers::{LocalWallet, Signer}, + types::{Address, Chain, H160, U256, U64}, +}; use ports::FuelBlock; - -use crate::Result; - -use super::event_streamer::EthEventStreamer; -use super::health_tracking_middleware::MyAdapter; - -use ethers::types::U64; - use serde_json::Value; - -use ethers::types::U256; - -use std::str::FromStr; -use std::sync::Arc; - -use ethers::types::Address; - -use ethers::types::Chain; - use url::Url; -use std::num::NonZeroU32; - -use ethers::signers::LocalWallet; - -use ethers::prelude::SignerMiddleware; - -use ethers::providers::Provider; - -use ethers::prelude::abigen; +use super::{event_streamer::EthEventStreamer, health_tracking_middleware::MyAdapter}; +use crate::Result; abigen!( FUEL_STATE_CONTRACT, diff --git a/packages/eth_rpc/src/websocket/event_streamer.rs b/packages/eth_rpc/src/websocket/event_streamer.rs index de1b9795..1c92d4b8 100644 --- a/packages/eth_rpc/src/websocket/event_streamer.rs +++ b/packages/eth_rpc/src/websocket/event_streamer.rs @@ -1,23 +1,15 @@ -use futures::TryStreamExt; -use ports::eth_rpc::FuelBlockCommittedOnEth; - -use futures::Stream; - -use ethers::prelude::k256::ecdsa::SigningKey; - -use ethers::signers::Wallet; - -use ethers::providers::{Provider, Ws}; - -use ethers::prelude::SignerMiddleware; - use std::sync::Arc; -use ethers::prelude::Event; - -use crate::Result; +use ethers::{ + prelude::{k256::ecdsa::SigningKey, Event, SignerMiddleware}, + providers::{Provider, Ws}, + signers::Wallet, +}; +use futures::{Stream, TryStreamExt}; +use ports::eth_rpc::FuelBlockCommittedOnEth; use super::connection::CommitSubmittedFilter; +use crate::Result; pub(crate) type EthStreamInitializer = Event< Arc, Wallet>>, diff --git a/packages/fuel_rpc/src/lib.rs b/packages/fuel_rpc/src/lib.rs index c1e359ba..8b5a7576 100644 --- a/packages/fuel_rpc/src/lib.rs +++ b/packages/fuel_rpc/src/lib.rs @@ -32,9 +32,8 @@ mod tests { use prometheus::{proto::Metric, Registry}; use url::Url; - use crate::client::Client; - use super::*; + use crate::client::Client; // TODO: once a sdk release is made these can be adapted // #[tokio::test] diff --git a/packages/fuel_rpc/src/metrics.rs b/packages/fuel_rpc/src/metrics.rs index a70cd6fb..0e8d6543 100644 --- a/packages/fuel_rpc/src/metrics.rs +++ b/packages/fuel_rpc/src/metrics.rs @@ -1,11 +1,5 @@ -use metrics::HealthChecker; -use prometheus::Opts; - -use metrics::Collector; - -use metrics::RegistersMetrics; - -use prometheus::IntCounter; +use metrics::{Collector, HealthChecker, RegistersMetrics}; +use prometheus::{IntCounter, Opts}; use crate::client::Client; diff --git a/packages/metrics/src/connection_health_tracker.rs b/packages/metrics/src/connection_health_tracker.rs index 9601eee7..94767fc4 100644 --- a/packages/metrics/src/connection_health_tracker.rs +++ b/packages/metrics/src/connection_health_tracker.rs @@ -1,11 +1,9 @@ -use super::HealthCheck; +use std::sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, +}; -use super::HealthChecker; - -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; - -use std::sync::Arc; +use super::{HealthCheck, HealthChecker}; #[derive(Debug, Clone)] pub struct ConnectionHealthTracker { diff --git a/packages/ports/src/lib.rs b/packages/ports/src/lib.rs index bb71e5cc..8ffbc488 100644 --- a/packages/ports/src/lib.rs +++ b/packages/ports/src/lib.rs @@ -9,5 +9,4 @@ pub mod storage; pub use ethers_core::types::{H160, U256}; #[cfg(feature = "eth")] pub use futures::Stream; - pub use types::{BlockSubmission, EthHeight, FuelBlock, InvalidEthHeight}; diff --git a/packages/storage/src/postgres.rs b/packages/storage/src/postgres.rs index 11decc76..f72e0f21 100644 --- a/packages/storage/src/postgres.rs +++ b/packages/storage/src/postgres.rs @@ -1,14 +1,8 @@ -use crate::tables; - -use super::Error; - use ports::BlockSubmission; +use sqlx::postgres::{PgConnectOptions, PgPoolOptions}; -use sqlx::postgres::PgPoolOptions; - -use sqlx::postgres::PgConnectOptions; - -use super::Result; +use super::{Error, Result}; +use crate::tables; #[derive(Clone)] pub struct Postgres { diff --git a/packages/storage/src/test_instance.rs b/packages/storage/src/test_instance.rs index f0dac359..954ad32e 100644 --- a/packages/storage/src/test_instance.rs +++ b/packages/storage/src/test_instance.rs @@ -2,10 +2,9 @@ use std::sync::{Arc, Weak}; use testcontainers::{core::WaitFor, runners::AsyncRunner, Image, RunnableImage}; +use super::postgres::{DbConfig, Postgres}; use crate::Result; -use super::{postgres::DbConfig, postgres::Postgres}; - struct PostgresImage; impl Image for PostgresImage { From 270821fa65c4cd2ab3582d3672d7375e96485652 Mon Sep 17 00:00:00 2001 From: segfault_magnet Date: Sat, 4 May 2024 17:13:28 +0200 Subject: [PATCH 05/23] cargo fix --- committer/src/main.rs | 1 - committer/src/services/wallet_balance_tracker.rs | 6 ++---- committer/src/setup.rs | 1 - e2e/src/lib.rs | 3 --- packages/eth_rpc/src/metrics.rs | 4 +--- packages/eth_rpc/src/websocket.rs | 2 +- 6 files changed, 4 insertions(+), 13 deletions(-) diff --git a/committer/src/main.rs b/committer/src/main.rs index 86a5cb60..fe13c8ca 100644 --- a/committer/src/main.rs +++ b/committer/src/main.rs @@ -44,7 +44,6 @@ async fn main() -> Result<()> { create_eth_adapter(&config, &internal_config, &metrics_registry).await?; let wallet_balance_tracker_handle = spawn_wallet_balance_tracker( - &config, &internal_config, &metrics_registry, ethereum_rpc.clone(), diff --git a/committer/src/services/wallet_balance_tracker.rs b/committer/src/services/wallet_balance_tracker.rs index 6095fb00..2fcb3ed5 100644 --- a/committer/src/services/wallet_balance_tracker.rs +++ b/committer/src/services/wallet_balance_tracker.rs @@ -1,7 +1,5 @@ -use std::str::FromStr; - use metrics::RegistersMetrics; -use ports::{eth_rpc::EthereumAdapter, H160, U256}; +use ports::{eth_rpc::EthereumAdapter, U256}; use prometheus::{IntGauge, Opts}; use super::Runner; @@ -70,7 +68,7 @@ impl Runner for WalletBalanceTracker { #[cfg(test)] mod tests { - use mockall::predicate::eq; + use ports::eth_rpc::MockEthereumAdapter; use prometheus::{proto::Metric, Registry}; diff --git a/committer/src/setup.rs b/committer/src/setup.rs index 54e6f2d5..b14c0970 100644 --- a/committer/src/setup.rs +++ b/committer/src/setup.rs @@ -43,7 +43,6 @@ pub fn spawn_block_watcher( } pub fn spawn_wallet_balance_tracker( - config: &Config, internal_config: &InternalConfig, registry: &Registry, ethereum_rpc: WsAdapter, diff --git a/e2e/src/lib.rs b/e2e/src/lib.rs index b027d5d2..0cccbd2b 100644 --- a/e2e/src/lib.rs +++ b/e2e/src/lib.rs @@ -7,10 +7,7 @@ mod tests { use fuel_rpc::client::Client; use ports::fuel_rpc::FuelAdapter; - use super::*; - const FUEL_NODE_PORT: u16 = 4000; - const ETH_NODE_PORT: u16 = 8089; #[tokio::test(flavor = "multi_thread")] async fn submitted_correct_block_and_was_finalized() -> Result<()> { diff --git a/packages/eth_rpc/src/metrics.rs b/packages/eth_rpc/src/metrics.rs index 435679bb..02127c3f 100644 --- a/packages/eth_rpc/src/metrics.rs +++ b/packages/eth_rpc/src/metrics.rs @@ -1,8 +1,6 @@ -use metrics::{HealthChecker, RegistersMetrics}; +use metrics::RegistersMetrics; use prometheus::{IntCounter, Opts}; -use crate::websocket::WsAdapter; - #[derive(Clone)] pub(crate) struct Metrics { pub(crate) eth_network_errors: IntCounter, diff --git a/packages/eth_rpc/src/websocket.rs b/packages/eth_rpc/src/websocket.rs index 30fd65d3..2ac4ebbd 100644 --- a/packages/eth_rpc/src/websocket.rs +++ b/packages/eth_rpc/src/websocket.rs @@ -2,7 +2,7 @@ use std::num::NonZeroU32; use ethers::types::{Address, Chain}; use metrics::{HealthChecker, RegistersMetrics}; -use ports::{FuelBlock, H160, U256}; +use ports::{FuelBlock, U256}; use url::Url; pub(crate) use self::event_streamer::EthEventStreamer; From 0e113810a0fbfea2e5ebf7cb7b656c01f29e8880 Mon Sep 17 00:00:00 2001 From: segfault_magnet Date: Sat, 4 May 2024 17:15:31 +0200 Subject: [PATCH 06/23] hide execute behind feature --- packages/storage/src/postgres.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/storage/src/postgres.rs b/packages/storage/src/postgres.rs index f72e0f21..b7e8e6cd 100644 --- a/packages/storage/src/postgres.rs +++ b/packages/storage/src/postgres.rs @@ -53,6 +53,7 @@ impl Postgres { Ok(()) } + #[cfg(feature = "test-helpers")] pub(crate) async fn execute(&self, query: &str) -> Result<()> { sqlx::query(query).execute(&self.connection_pool).await?; Ok(()) From e3abf9c104d36768fcd939da4f1b07e9302d7e04 Mon Sep 17 00:00:00 2001 From: segfault_magnet Date: Sat, 4 May 2024 17:36:27 +0200 Subject: [PATCH 07/23] tests passing --- committer/tests/eth_test_adapter.rs | 1 - committer/tests/harness.rs | 1 - deployment/Dockerfile | 6 +++--- e2e/src/lib.rs | 2 +- run_tests.sh | 29 ++++++++++++++++------------- 5 files changed, 20 insertions(+), 19 deletions(-) delete mode 100644 committer/tests/eth_test_adapter.rs delete mode 100644 committer/tests/harness.rs diff --git a/committer/tests/eth_test_adapter.rs b/committer/tests/eth_test_adapter.rs deleted file mode 100644 index 8b137891..00000000 --- a/committer/tests/eth_test_adapter.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/committer/tests/harness.rs b/committer/tests/harness.rs deleted file mode 100644 index 8b137891..00000000 --- a/committer/tests/harness.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/deployment/Dockerfile b/deployment/Dockerfile index 76f5866b..00258dbe 100644 --- a/deployment/Dockerfile +++ b/deployment/Dockerfile @@ -6,16 +6,16 @@ WORKDIR /build/ FROM chef as planner ENV CARGO_NET_GIT_FETCH_WITH_CLI=true COPY . . -RUN cargo chef prepare --recipe-path recipe.json +RUN cargo chef prepare --recipe-path recipe.json --bin fuel-block-committer FROM chef as builder COPY --from=planner /build/recipe.json recipe.json # Build our project dependecies, not our application! -RUN cargo chef cook --release --recipe-path recipe.json +RUN cargo chef cook --release --recipe-path recipe.json --bin fuel-block-committer # Up to this point, if our dependency tree stays the same, # all layers should be cached. COPY . . -RUN cargo build --release +RUN cargo build --release --bin fuel-block-committer # Stage 2: Run FROM ubuntu:22.04 as run diff --git a/e2e/src/lib.rs b/e2e/src/lib.rs index 0cccbd2b..4101cf82 100644 --- a/e2e/src/lib.rs +++ b/e2e/src/lib.rs @@ -15,7 +15,7 @@ mod tests { let provider = Client::new(&fuel_node_address.parse()?, 10); let fuel_contract = WsAdapter::connect( - &"ws://eth_node:8099".parse()?, + &"ws://localhost:8089".parse()?, Chain::AnvilHardhat, "0xdAad669b06d79Cb48C8cfef789972436dBe6F24d".parse()?, "0x9e56ccf010fa4073274b8177ccaad46fbaf286645310d03ac9bb6afa922a7c36", diff --git a/run_tests.sh b/run_tests.sh index f2986445..8d652b36 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -1,25 +1,28 @@ #!/usr/bin/env bash while true; do - case "$1" in - --logs) - show_logs="true" - shift 1;; - --| "") - break;; - *) - printf "Unknown option %s\n" "$1" - exit 1;; - esac + case "$1" in + --logs) + show_logs="true" + shift 1 + ;; + -- | "") + break + ;; + *) + printf "Unknown option %s\n" "$1" + exit 1 + ;; + esac done -cargo test --bin fuel-block-committer +cargo test --workspace --exclude e2e docker compose up -d trap 'docker compose down > /dev/null 2>&1' EXIT -cargo test --test e2e -- --nocapture +cargo test --package e2e -- --nocapture if [[ $show_logs = "true" ]]; then - docker compose logs -f block_committer & + docker compose logs -f block_committer & fi From ccaab99e5cef71bd05c462cf671a9961e865797b Mon Sep 17 00:00:00 2001 From: segfault_magnet Date: Sat, 4 May 2024 18:25:30 +0200 Subject: [PATCH 08/23] more private fields --- packages/eth_rpc/src/websocket.rs | 2 +- packages/eth_rpc/src/websocket/connection.rs | 6 ++--- .../eth_rpc/src/websocket/event_streamer.rs | 4 +-- .../websocket/health_tracking_middleware.rs | 2 +- packages/fuel_rpc/src/client.rs | 27 ++++++++++++++++--- packages/fuel_rpc/src/metrics.rs | 25 +---------------- .../metrics/src/connection_health_tracker.rs | 4 +-- packages/storage/src/postgres.rs | 2 +- packages/types/src/eth_height.rs | 2 +- 9 files changed, 35 insertions(+), 39 deletions(-) diff --git a/packages/eth_rpc/src/websocket.rs b/packages/eth_rpc/src/websocket.rs index 2ac4ebbd..25e4f3f0 100644 --- a/packages/eth_rpc/src/websocket.rs +++ b/packages/eth_rpc/src/websocket.rs @@ -78,6 +78,6 @@ impl WsAdapter { // User responsible for registering any metrics T might have impl RegistersMetrics for WsAdapter { fn metrics(&self) -> Vec> { - self.inner.metrics.metrics() + self.inner.metrics() } } diff --git a/packages/eth_rpc/src/websocket/connection.rs b/packages/eth_rpc/src/websocket/connection.rs index c149b626..17682bf3 100644 --- a/packages/eth_rpc/src/websocket/connection.rs +++ b/packages/eth_rpc/src/websocket/connection.rs @@ -25,9 +25,9 @@ abigen!( #[derive(Clone)] pub struct WsConnection { - pub(crate) provider: Provider, + provider: Provider, pub(crate) contract: FUEL_STATE_CONTRACT, LocalWallet>>, - pub(crate) commit_interval: NonZeroU32, + commit_interval: NonZeroU32, address: H160, } @@ -116,7 +116,7 @@ impl WsConnection { (block_height / commit_interval).into() } - pub(crate) async fn _balance(&self, address: H160) -> Result { + async fn _balance(&self, address: H160) -> Result { Ok(self.provider.get_balance(address, None).await?) } } diff --git a/packages/eth_rpc/src/websocket/event_streamer.rs b/packages/eth_rpc/src/websocket/event_streamer.rs index 1c92d4b8..b75fb568 100644 --- a/packages/eth_rpc/src/websocket/event_streamer.rs +++ b/packages/eth_rpc/src/websocket/event_streamer.rs @@ -11,14 +11,14 @@ use ports::eth_rpc::FuelBlockCommittedOnEth; use super::connection::CommitSubmittedFilter; use crate::Result; -pub(crate) type EthStreamInitializer = Event< +type EthStreamInitializer = Event< Arc, Wallet>>, SignerMiddleware, Wallet>, CommitSubmittedFilter, >; pub struct EthEventStreamer { - pub(crate) events: EthStreamInitializer, + events: EthStreamInitializer, } impl EthEventStreamer { diff --git a/packages/eth_rpc/src/websocket/health_tracking_middleware.rs b/packages/eth_rpc/src/websocket/health_tracking_middleware.rs index 3d9ad7f0..2ae55e1f 100644 --- a/packages/eth_rpc/src/websocket/health_tracking_middleware.rs +++ b/packages/eth_rpc/src/websocket/health_tracking_middleware.rs @@ -17,7 +17,7 @@ pub(crate) trait MyAdapter { #[derive(Clone)] pub(crate) struct HealthTrackingMiddleware { adapter: T, - pub(crate) metrics: Metrics, + metrics: Metrics, health_tracker: ConnectionHealthTracker, } diff --git a/packages/fuel_rpc/src/client.rs b/packages/fuel_rpc/src/client.rs index 87569918..be9355f2 100644 --- a/packages/fuel_rpc/src/client.rs +++ b/packages/fuel_rpc/src/client.rs @@ -1,13 +1,13 @@ use fuel_core_client::client::{types::Block, FuelClient as GqlClient}; -use metrics::ConnectionHealthTracker; +use metrics::{Collector, ConnectionHealthTracker, HealthChecker, RegistersMetrics}; use url::Url; use crate::{metrics::Metrics, Error, Result}; pub struct Client { - pub(crate) client: GqlClient, - pub(crate) metrics: Metrics, - pub(crate) health_tracker: ConnectionHealthTracker, + client: GqlClient, + metrics: Metrics, + health_tracker: ConnectionHealthTracker, } impl Client { @@ -52,4 +52,23 @@ impl Client { } } } + + pub fn connection_health_checker(&self) -> HealthChecker { + self.health_tracker.tracker() + } + + fn handle_network_error(&self) { + self.health_tracker.note_failure(); + self.metrics.fuel_network_errors.inc(); + } + + fn handle_network_success(&self) { + self.health_tracker.note_success(); + } +} + +impl RegistersMetrics for Client { + fn metrics(&self) -> Vec> { + self.metrics.metrics() + } } diff --git a/packages/fuel_rpc/src/metrics.rs b/packages/fuel_rpc/src/metrics.rs index 0e8d6543..c5417e43 100644 --- a/packages/fuel_rpc/src/metrics.rs +++ b/packages/fuel_rpc/src/metrics.rs @@ -1,8 +1,6 @@ -use metrics::{Collector, HealthChecker, RegistersMetrics}; +use metrics::{Collector, RegistersMetrics}; use prometheus::{IntCounter, Opts}; -use crate::client::Client; - pub struct Metrics { pub fuel_network_errors: IntCounter, } @@ -25,24 +23,3 @@ impl Default for Metrics { } } } - -impl RegistersMetrics for Client { - fn metrics(&self) -> Vec> { - self.metrics.metrics() - } -} - -impl Client { - pub fn connection_health_checker(&self) -> HealthChecker { - self.health_tracker.tracker() - } - - pub(crate) fn handle_network_error(&self) { - self.health_tracker.note_failure(); - self.metrics.fuel_network_errors.inc(); - } - - pub(crate) fn handle_network_success(&self) { - self.health_tracker.note_success(); - } -} diff --git a/packages/metrics/src/connection_health_tracker.rs b/packages/metrics/src/connection_health_tracker.rs index 94767fc4..8a006c1d 100644 --- a/packages/metrics/src/connection_health_tracker.rs +++ b/packages/metrics/src/connection_health_tracker.rs @@ -8,9 +8,9 @@ use super::{HealthCheck, HealthChecker}; #[derive(Debug, Clone)] pub struct ConnectionHealthTracker { // how many failures are needed before the connection is deemed unhealhty - pub(crate) max_consecutive_failures: usize, + max_consecutive_failures: usize, // how many consecutive failures there currently are - pub(crate) consecutive_failures: Arc, + consecutive_failures: Arc, } impl ConnectionHealthTracker { diff --git a/packages/storage/src/postgres.rs b/packages/storage/src/postgres.rs index b7e8e6cd..d7b833aa 100644 --- a/packages/storage/src/postgres.rs +++ b/packages/storage/src/postgres.rs @@ -6,7 +6,7 @@ use crate::tables; #[derive(Clone)] pub struct Postgres { - pub(crate) connection_pool: sqlx::Pool, + connection_pool: sqlx::Pool, } #[derive(Debug, Clone, serde::Deserialize)] diff --git a/packages/types/src/eth_height.rs b/packages/types/src/eth_height.rs index cda719ac..6267c7fd 100644 --- a/packages/types/src/eth_height.rs +++ b/packages/types/src/eth_height.rs @@ -1,6 +1,6 @@ #[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)] pub struct EthHeight { - pub(crate) height: i64, + height: i64, } #[derive(Debug, Clone)] From 5cf6cc50b226c5ea5eccf27840c86e096e793adf Mon Sep 17 00:00:00 2001 From: segfault_magnet Date: Sat, 4 May 2024 18:28:25 +0200 Subject: [PATCH 09/23] clippy suggestions --- committer/src/errors.rs | 6 +++--- committer/src/services/status_reporter.rs | 2 +- packages/eth_rpc/src/lib.rs | 14 +++++++------- packages/eth_rpc/src/metrics.rs | 2 +- packages/eth_rpc/src/websocket.rs | 3 ++- .../src/websocket/health_tracking_middleware.rs | 4 ++-- packages/fuel_rpc/src/client.rs | 2 ++ packages/metrics/src/connection_health_tracker.rs | 2 ++ packages/ports/src/storage.rs | 1 + packages/storage/src/test_instance.rs | 2 +- packages/types/src/eth_height.rs | 4 ++-- 11 files changed, 24 insertions(+), 18 deletions(-) diff --git a/committer/src/errors.rs b/committer/src/errors.rs index 7109f60a..17a15907 100644 --- a/committer/src/errors.rs +++ b/committer/src/errors.rs @@ -43,7 +43,7 @@ impl From for Error { impl From for Error { fn from(value: ports::eth_rpc::Error) -> Self { match value { - ports::eth_rpc::Error::Network(e) => Self::Network(e.to_string()), + ports::eth_rpc::Error::Network(e) => Self::Network(e), _ => Self::Other(value.to_string()), } } @@ -52,7 +52,7 @@ impl From for Error { impl From for Error { fn from(value: eth_rpc::Error) -> Self { match value { - eth_rpc::Error::Network(e) => Self::Network(e.to_string()), + eth_rpc::Error::Network(e) => Self::Network(e), _ => Self::Other(value.to_string()), } } @@ -61,7 +61,7 @@ impl From for Error { impl From for Error { fn from(value: ports::fuel_rpc::Error) -> Self { match value { - ports::fuel_rpc::Error::Network(e) => Self::Network(e.to_string()), + ports::fuel_rpc::Error::Network(e) => Self::Network(e), } } } diff --git a/committer/src/services/status_reporter.rs b/committer/src/services/status_reporter.rs index a59092e8..fe703581 100644 --- a/committer/src/services/status_reporter.rs +++ b/committer/src/services/status_reporter.rs @@ -36,7 +36,7 @@ where .await? .map(|submission| submission.completed); - let status = if let Some(false) = last_submission_completed { + let status = if last_submission_completed == Some(false) { Status::Committing } else { Status::Idle diff --git a/packages/eth_rpc/src/lib.rs b/packages/eth_rpc/src/lib.rs index 00337253..57a20361 100644 --- a/packages/eth_rpc/src/lib.rs +++ b/packages/eth_rpc/src/lib.rs @@ -29,7 +29,7 @@ pub enum Error { impl From for Error { fn from(err: ethers::providers::ProviderError) -> Self { - Error::Network(err.to_string()) + Self::Network(err.to_string()) } } @@ -38,9 +38,9 @@ type ContractErrorType = impl From for Error { fn from(value: ContractErrorType) -> Self { match value { - ContractError::MiddlewareError { e } => Error::Other(e.to_string()), - ContractError::ProviderError { e } => Error::Network(e.to_string()), - _ => Error::Other(value.to_string()), + ContractError::MiddlewareError { e } => Self::Other(e.to_string()), + ContractError::ProviderError { e } => Self::Network(e.to_string()), + _ => Self::Other(value.to_string()), } } } @@ -50,9 +50,9 @@ pub type Result = std::result::Result; impl From for ports::eth_rpc::Error { fn from(err: Error) -> Self { match err { - Error::Network(err) => ports::eth_rpc::Error::Network(err), - Error::Other(err) => ports::eth_rpc::Error::Other(err), - Error::Wallet(err) => ports::eth_rpc::Error::Other(err.to_string()), + Error::Network(err) => Self::Network(err), + Error::Other(err) => Self::Other(err), + Error::Wallet(err) => Self::Other(err.to_string()), } } } diff --git a/packages/eth_rpc/src/metrics.rs b/packages/eth_rpc/src/metrics.rs index 02127c3f..45a855bc 100644 --- a/packages/eth_rpc/src/metrics.rs +++ b/packages/eth_rpc/src/metrics.rs @@ -2,7 +2,7 @@ use metrics::RegistersMetrics; use prometheus::{IntCounter, Opts}; #[derive(Clone)] -pub(crate) struct Metrics { +pub struct Metrics { pub(crate) eth_network_errors: IntCounter, } diff --git a/packages/eth_rpc/src/websocket.rs b/packages/eth_rpc/src/websocket.rs index 25e4f3f0..657a3720 100644 --- a/packages/eth_rpc/src/websocket.rs +++ b/packages/eth_rpc/src/websocket.rs @@ -5,7 +5,7 @@ use metrics::{HealthChecker, RegistersMetrics}; use ports::{FuelBlock, U256}; use url::Url; -pub(crate) use self::event_streamer::EthEventStreamer; +pub use self::event_streamer::EthEventStreamer; use self::{ connection::WsConnection, health_tracking_middleware::{HealthTrackingMiddleware, MyAdapter}, @@ -44,6 +44,7 @@ impl WsAdapter { }) } + #[must_use] pub fn connection_health_checker(&self) -> HealthChecker { self.inner.connection_health_checker() } diff --git a/packages/eth_rpc/src/websocket/health_tracking_middleware.rs b/packages/eth_rpc/src/websocket/health_tracking_middleware.rs index 2ae55e1f..f2d3e3d5 100644 --- a/packages/eth_rpc/src/websocket/health_tracking_middleware.rs +++ b/packages/eth_rpc/src/websocket/health_tracking_middleware.rs @@ -5,7 +5,7 @@ use crate::{metrics::Metrics, websocket::event_streamer::EthEventStreamer, Error #[cfg_attr(test, mockall::automock)] #[async_trait::async_trait] -pub(crate) trait MyAdapter { +pub trait MyAdapter { async fn submit(&self, block: FuelBlock) -> Result<()>; async fn get_block_number(&self) -> Result; async fn balance(&self) -> Result; @@ -15,7 +15,7 @@ pub(crate) trait MyAdapter { } #[derive(Clone)] -pub(crate) struct HealthTrackingMiddleware { +pub struct HealthTrackingMiddleware { adapter: T, metrics: Metrics, health_tracker: ConnectionHealthTracker, diff --git a/packages/fuel_rpc/src/client.rs b/packages/fuel_rpc/src/client.rs index be9355f2..b6f6b54e 100644 --- a/packages/fuel_rpc/src/client.rs +++ b/packages/fuel_rpc/src/client.rs @@ -11,6 +11,7 @@ pub struct Client { } impl Client { + #[must_use] pub fn new(url: &Url, unhealthy_after_n_errors: usize) -> Self { let client = GqlClient::new(url).expect("Url to be well formed"); Self { @@ -53,6 +54,7 @@ impl Client { } } + #[must_use] pub fn connection_health_checker(&self) -> HealthChecker { self.health_tracker.tracker() } diff --git a/packages/metrics/src/connection_health_tracker.rs b/packages/metrics/src/connection_health_tracker.rs index 8a006c1d..07402ce9 100644 --- a/packages/metrics/src/connection_health_tracker.rs +++ b/packages/metrics/src/connection_health_tracker.rs @@ -14,6 +14,7 @@ pub struct ConnectionHealthTracker { } impl ConnectionHealthTracker { + #[must_use] pub fn new(max_consecutive_failures: usize) -> Self { Self { max_consecutive_failures, @@ -29,6 +30,7 @@ impl ConnectionHealthTracker { self.consecutive_failures.store(0, Ordering::SeqCst); } + #[must_use] pub fn tracker(&self) -> HealthChecker { Box::new(self.clone()) } diff --git a/packages/ports/src/storage.rs b/packages/ports/src/storage.rs index b554e06c..ed0389d0 100644 --- a/packages/ports/src/storage.rs +++ b/packages/ports/src/storage.rs @@ -7,6 +7,7 @@ pub struct Error { } impl Error { + #[must_use] pub fn new(msg: String) -> Self { Self { msg } } diff --git a/packages/storage/src/test_instance.rs b/packages/storage/src/test_instance.rs index 954ad32e..894658c3 100644 --- a/packages/storage/src/test_instance.rs +++ b/packages/storage/src/test_instance.rs @@ -70,10 +70,10 @@ impl PostgresProcess { .await; Ok(Self { - container, username, password, initial_db, + container, }) } diff --git a/packages/types/src/eth_height.rs b/packages/types/src/eth_height.rs index 6267c7fd..d74eac20 100644 --- a/packages/types/src/eth_height.rs +++ b/packages/types/src/eth_height.rs @@ -29,7 +29,7 @@ impl TryFrom for EthHeight { "must be non-negative, got {height}", ))); } - Ok(EthHeight { height }) + Ok(Self { height }) } } @@ -64,6 +64,6 @@ impl From for i64 { impl From for u64 { fn from(height: EthHeight) -> Self { - height.height as u64 + height.height as Self } } From 4c9d8eddaf482e5a11d865b6edc13f6b7e043344 Mon Sep 17 00:00:00 2001 From: segfault_magnet Date: Sat, 4 May 2024 18:45:06 +0200 Subject: [PATCH 10/23] remove unused deps --- Cargo.lock | 3 --- committer/Cargo.toml | 4 +--- committer/src/main.rs | 3 +-- packages/eth_rpc/src/lib.rs | 1 + packages/fuel_rpc/Cargo.toml | 1 - packages/fuel_rpc/src/lib.rs | 1 + packages/metrics/src/lib.rs | 1 + packages/ports/Cargo.toml | 14 +++++++------- packages/ports/src/fuel_rpc.rs | 2 +- packages/ports/src/storage.rs | 1 + packages/storage/src/lib.rs | 1 + packages/types/src/lib.rs | 1 + 12 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 39aa9fe0..c036ab0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1719,8 +1719,6 @@ dependencies = [ "eth_rpc", "fuel_rpc", "futures", - "hex", - "impl-tools", "metrics", "mockall", "ports", @@ -1899,7 +1897,6 @@ dependencies = [ "metrics", "ports", "prometheus", - "thiserror", "tokio", "url", ] diff --git a/committer/Cargo.toml b/committer/Cargo.toml index 892abfda..915156d9 100644 --- a/committer/Cargo.toml +++ b/committer/Cargo.toml @@ -21,12 +21,9 @@ config = { workspace = true, default-features = false, features = [ eth_rpc = { workspace = true } fuel_rpc = { workspace = true } futures = { workspace = true } -hex = "0.4.3" -impl-tools = { workspace = true } metrics = { workspace = true } ports = { workspace = true } prometheus = { workspace = true, default-features = false } -rand = { workspace = true, features = ["std"] } serde = { workspace = true } serde_json = { workspace = true, default-features = false } @@ -46,6 +43,7 @@ url = { workspace = true } anyhow = { workspace = true, default-features = false } mockall = { workspace = true } ports = { workspace = true, features = ["test-helpers"] } +rand = { workspace = true } storage = { workspace = true, features = ["test-helpers"] } #TODO: Once fuels-rs catches up tests will be reenabled #fuels-test-helpers = "0.57" diff --git a/committer/src/main.rs b/committer/src/main.rs index fe13c8ca..c70f0542 100644 --- a/committer/src/main.rs +++ b/committer/src/main.rs @@ -1,5 +1,4 @@ -// TODO: return this -// #![deny(unused_crate_dependencies)] +#![deny(unused_crate_dependencies)] mod api; mod config; mod errors; diff --git a/packages/eth_rpc/src/lib.rs b/packages/eth_rpc/src/lib.rs index 57a20361..89958f99 100644 --- a/packages/eth_rpc/src/lib.rs +++ b/packages/eth_rpc/src/lib.rs @@ -1,3 +1,4 @@ +#![deny(unused_crate_dependencies)] use std::pin::Pin; use async_trait::async_trait; diff --git a/packages/fuel_rpc/Cargo.toml b/packages/fuel_rpc/Cargo.toml index bdafdf19..68f6a908 100644 --- a/packages/fuel_rpc/Cargo.toml +++ b/packages/fuel_rpc/Cargo.toml @@ -11,7 +11,6 @@ fuel-core-client = { workspace = true } metrics = { workspace = true } ports = { workspace = true, features = ["fuel"] } prometheus = { workspace = true } -thiserror = { workspace = true } url = { workspace = true } [dev-dependencies] diff --git a/packages/fuel_rpc/src/lib.rs b/packages/fuel_rpc/src/lib.rs index 8b5a7576..df31a37f 100644 --- a/packages/fuel_rpc/src/lib.rs +++ b/packages/fuel_rpc/src/lib.rs @@ -1,3 +1,4 @@ +#![deny(unused_crate_dependencies)] use fuel_core_client::client::types::Block; use ports::FuelBlock; pub mod client; diff --git a/packages/metrics/src/lib.rs b/packages/metrics/src/lib.rs index 3215ac60..b03768ff 100644 --- a/packages/metrics/src/lib.rs +++ b/packages/metrics/src/lib.rs @@ -1,3 +1,4 @@ +#![deny(unused_crate_dependencies)] mod connection_health_tracker; pub use connection_health_tracker::*; diff --git a/packages/ports/Cargo.toml b/packages/ports/Cargo.toml index 6538fb43..e46d3d02 100644 --- a/packages/ports/Cargo.toml +++ b/packages/ports/Cargo.toml @@ -5,16 +5,16 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-trait = { workspace = true } +async-trait = { workspace = true, optional = true } ethers-core = { workspace = true, optional = true } -futures = { workspace = true } -impl-tools = { workspace = true } +futures = { workspace = true, optional = true } +impl-tools = { workspace = true, optional = true } mockall = { workspace = true, optional = true } -thiserror = { workspace = true } +thiserror = { workspace = true, optional = true } types = { workspace = true } [features] test-helpers = ["dep:mockall", "types/test-helpers"] -eth = ["dep:ethers-core"] -fuel = [] -storage = [] +eth = ["dep:ethers-core", "dep:futures", "dep:thiserror", "dep:async-trait"] +fuel = ["dep:thiserror", "dep:async-trait"] +storage = ["dep:impl-tools", "dep:thiserror", "dep:async-trait"] diff --git a/packages/ports/src/fuel_rpc.rs b/packages/ports/src/fuel_rpc.rs index bb031015..f261cb24 100644 --- a/packages/ports/src/fuel_rpc.rs +++ b/packages/ports/src/fuel_rpc.rs @@ -6,8 +6,8 @@ pub enum Error { pub type Result = std::result::Result; -#[cfg_attr(feature = "test-helpers", mockall::automock)] #[async_trait::async_trait] +#[cfg_attr(feature = "test-helpers", mockall::automock)] pub trait FuelAdapter: Send + Sync { async fn block_at_height(&self, height: u32) -> Result>; async fn latest_block(&self) -> Result; diff --git a/packages/ports/src/storage.rs b/packages/ports/src/storage.rs index ed0389d0..f50029a4 100644 --- a/packages/ports/src/storage.rs +++ b/packages/ports/src/storage.rs @@ -17,6 +17,7 @@ pub type Result = std::result::Result; #[async_trait::async_trait] #[impl_tools::autoimpl(for &T, &mut T, Arc, Box)] +#[cfg_attr(feature = "test-helpers", mockall::automock)] pub trait Storage: Send + Sync { async fn insert(&self, submission: crate::BlockSubmission) -> Result<()>; async fn submission_w_latest_block(&self) -> Result>; diff --git a/packages/storage/src/lib.rs b/packages/storage/src/lib.rs index 0bd1a581..b38b5918 100644 --- a/packages/storage/src/lib.rs +++ b/packages/storage/src/lib.rs @@ -1,3 +1,4 @@ +#![deny(unused_crate_dependencies)] mod tables; #[cfg(feature = "test-helpers")] mod test_instance; diff --git a/packages/types/src/lib.rs b/packages/types/src/lib.rs index 2438368f..9c96698f 100644 --- a/packages/types/src/lib.rs +++ b/packages/types/src/lib.rs @@ -1,3 +1,4 @@ +#![deny(unused_crate_dependencies)] mod block_submission; mod eth_height; mod fuel_block; From 043652b74a4cade0119ae98017256dfed8c0ce5d Mon Sep 17 00:00:00 2001 From: segfault_magnet Date: Sun, 5 May 2024 12:13:57 +0200 Subject: [PATCH 11/23] reorganize --- Cargo.lock | 11 +--- Cargo.toml | 2 - committer/src/services/block_committer.rs | 6 +- committer/src/services/block_watcher.rs | 4 +- committer/src/services/commit_listener.rs | 8 +-- committer/src/services/status_reporter.rs | 2 +- .../src/services/wallet_balance_tracker.rs | 2 +- committer/src/setup.rs | 2 +- packages/eth_rpc/src/lib.rs | 6 +- packages/eth_rpc/src/websocket.rs | 2 +- packages/eth_rpc/src/websocket/connection.rs | 2 +- .../eth_rpc/src/websocket/event_streamer.rs | 2 +- .../websocket/health_tracking_middleware.rs | 2 +- packages/fuel_rpc/src/lib.rs | 2 +- packages/ports/Cargo.toml | 5 +- packages/ports/src/eth_rpc.rs | 55 ------------------- packages/ports/src/lib.rs | 24 ++++---- packages/ports/src/ports/eth_rpc.rs | 36 ++++++++++++ packages/ports/src/{ => ports}/fuel_rpc.rs | 8 ++- packages/ports/src/{ => ports}/storage.rs | 11 ++-- packages/ports/src/types.rs | 16 ++++++ .../src/types}/block_submission.rs | 2 +- .../src => ports/src/types}/eth_height.rs | 0 .../src => ports/src/types}/fuel_block.rs | 0 .../src/types/fuel_block_committed_on_eth.rs | 20 +++++++ packages/storage/Cargo.toml | 2 +- packages/storage/src/lib.rs | 6 +- packages/storage/src/postgres.rs | 2 +- packages/storage/src/tables.rs | 2 +- packages/types/Cargo.toml | 13 ----- packages/types/src/lib.rs | 8 --- 31 files changed, 130 insertions(+), 133 deletions(-) delete mode 100644 packages/ports/src/eth_rpc.rs create mode 100644 packages/ports/src/ports/eth_rpc.rs rename packages/ports/src/{ => ports}/fuel_rpc.rs (77%) rename packages/ports/src/{ => ports}/storage.rs (60%) create mode 100644 packages/ports/src/types.rs rename packages/{types/src => ports/src/types}/block_submission.rs (93%) rename packages/{types/src => ports/src/types}/eth_height.rs (100%) rename packages/{types/src => ports/src/types}/fuel_block.rs (100%) create mode 100644 packages/ports/src/types/fuel_block_committed_on_eth.rs delete mode 100644 packages/types/Cargo.toml delete mode 100644 packages/types/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index c036ab0a..147c985e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3238,8 +3238,9 @@ dependencies = [ "futures", "impl-tools", "mockall", + "rand", + "serde", "thiserror", - "types", ] [[package]] @@ -5016,14 +5017,6 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" -[[package]] -name = "types" -version = "0.1.0" -dependencies = [ - "rand", - "serde", -] - [[package]] name = "uint" version = "0.9.5" diff --git a/Cargo.toml b/Cargo.toml index b87ae07a..9ea46840 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,6 @@ members = [ "packages/metrics", "packages/ports", "packages/storage", - "packages/types", ] [workspace.dependencies] @@ -17,7 +16,6 @@ fuel_rpc = { version = "0.1.0", path = "./packages/fuel_rpc", default-features = metrics = { version = "0.1.0", path = "./packages/metrics", default-features = false } ports = { version = "0.1.0", path = "./packages/ports", default-features = false } storage = { version = "0.1.0", path = "./packages/storage", default-features = false } -types = { version = "0.1.0", path = "./packages/types/", default-features = false } actix-web = { version = "4", default-features = false } anyhow = { version = "1.0", default-features = false } diff --git a/committer/src/services/block_committer.rs b/committer/src/services/block_committer.rs index 66f2ab4f..e678bc56 100644 --- a/committer/src/services/block_committer.rs +++ b/committer/src/services/block_committer.rs @@ -1,6 +1,10 @@ use async_trait::async_trait; use eth_rpc::WsAdapter; -use ports::{eth_rpc::EthereumAdapter, storage::Storage, BlockSubmission, FuelBlock}; +use ports::{ + eth_rpc::EthereumAdapter, + storage::Storage, + types::{BlockSubmission, FuelBlock}, +}; use storage::Postgres; use tokio::sync::mpsc::Receiver; use tracing::{error, info}; diff --git a/committer/src/services/block_watcher.rs b/committer/src/services/block_watcher.rs index 019b05fb..63cd8491 100644 --- a/committer/src/services/block_watcher.rs +++ b/committer/src/services/block_watcher.rs @@ -3,7 +3,7 @@ use std::{num::NonZeroU32, vec}; use async_trait::async_trait; use fuel_rpc::client::Client; use metrics::RegistersMetrics; -use ports::{fuel_rpc::FuelAdapter, storage::Storage, FuelBlock}; +use ports::{fuel_rpc::FuelAdapter, storage::Storage, types::FuelBlock}; use prometheus::{core::Collector, IntGauge, Opts}; use storage::Postgres; use tokio::sync::mpsc::Sender; @@ -138,7 +138,7 @@ mod tests { use std::{sync::Arc, vec}; use mockall::predicate::eq; - use ports::{fuel_rpc::MockFuelAdapter, BlockSubmission}; + use ports::{fuel_rpc::MockFuelAdapter, types::BlockSubmission}; use prometheus::{proto::Metric, Registry}; use rand::Rng; use storage::PostgresProcess; diff --git a/committer/src/services/commit_listener.rs b/committer/src/services/commit_listener.rs index 579eec2a..74fa5c0d 100644 --- a/committer/src/services/commit_listener.rs +++ b/committer/src/services/commit_listener.rs @@ -3,9 +3,9 @@ use eth_rpc::WsAdapter; use futures::{StreamExt, TryStreamExt}; use metrics::RegistersMetrics; use ports::{ - eth_rpc::{EthereumAdapter, FuelBlockCommittedOnEth}, + eth_rpc::EthereumAdapter, storage::Storage, - EthHeight, + types::{EthHeight, FuelBlockCommittedOnEth}, }; use prometheus::{IntGauge, Opts}; use storage::Postgres; @@ -124,9 +124,9 @@ mod tests { use metrics::RegistersMetrics; use mockall::predicate; use ports::{ - eth_rpc::{FuelBlockCommittedOnEth, MockEthereumAdapter, MockEventStreamer}, + eth_rpc::{MockEthereumAdapter, MockEventStreamer}, storage::Storage, - BlockSubmission, EthHeight, U256, + types::{BlockSubmission, EthHeight, FuelBlockCommittedOnEth, U256}, }; use prometheus::{proto::Metric, Registry}; use rand::Rng; diff --git a/committer/src/services/status_reporter.rs b/committer/src/services/status_reporter.rs index fe703581..de0861c4 100644 --- a/committer/src/services/status_reporter.rs +++ b/committer/src/services/status_reporter.rs @@ -50,7 +50,7 @@ where mod tests { use std::sync::Arc; - use ports::BlockSubmission; + use ports::types::BlockSubmission; use rand::Rng; use storage::PostgresProcess; diff --git a/committer/src/services/wallet_balance_tracker.rs b/committer/src/services/wallet_balance_tracker.rs index 2fcb3ed5..6654e0e9 100644 --- a/committer/src/services/wallet_balance_tracker.rs +++ b/committer/src/services/wallet_balance_tracker.rs @@ -1,5 +1,5 @@ use metrics::RegistersMetrics; -use ports::{eth_rpc::EthereumAdapter, U256}; +use ports::{eth_rpc::EthereumAdapter, types::U256}; use prometheus::{IntGauge, Opts}; use super::Runner; diff --git a/committer/src/setup.rs b/committer/src/setup.rs index b14c0970..f9fc9920 100644 --- a/committer/src/setup.rs +++ b/committer/src/setup.rs @@ -3,7 +3,7 @@ use std::time::Duration; use eth_rpc::WsAdapter; use fuel_rpc::client::Client; use metrics::{HealthChecker, RegistersMetrics}; -use ports::{storage::Storage, FuelBlock}; +use ports::{storage::Storage, types::FuelBlock}; use prometheus::Registry; use storage::Postgres; use tokio::{sync::mpsc::Receiver, task::JoinHandle}; diff --git a/packages/eth_rpc/src/lib.rs b/packages/eth_rpc/src/lib.rs index 89958f99..2efc7a20 100644 --- a/packages/eth_rpc/src/lib.rs +++ b/packages/eth_rpc/src/lib.rs @@ -9,7 +9,7 @@ use ethers::{ types::U256, }; use futures::{stream::TryStreamExt, Stream}; -use ports::{eth_rpc::FuelBlockCommittedOnEth, EthHeight}; +use ports::types::{EthHeight, FuelBlockCommittedOnEth}; use websocket::EthEventStreamer; mod metrics; @@ -60,11 +60,11 @@ impl From for ports::eth_rpc::Error { #[async_trait] impl ports::eth_rpc::EthereumAdapter for WsAdapter { - async fn submit(&self, block: ports::FuelBlock) -> ports::eth_rpc::Result<()> { + async fn submit(&self, block: ports::types::FuelBlock) -> ports::eth_rpc::Result<()> { Ok(self.submit(block).await?) } - async fn get_block_number(&self) -> ports::eth_rpc::Result { + async fn get_block_number(&self) -> ports::eth_rpc::Result { let block_num = self.get_block_number().await?; let height = EthHeight::try_from(block_num)?; Ok(height) diff --git a/packages/eth_rpc/src/websocket.rs b/packages/eth_rpc/src/websocket.rs index 657a3720..8bd9f9c7 100644 --- a/packages/eth_rpc/src/websocket.rs +++ b/packages/eth_rpc/src/websocket.rs @@ -2,7 +2,7 @@ use std::num::NonZeroU32; use ethers::types::{Address, Chain}; use metrics::{HealthChecker, RegistersMetrics}; -use ports::{FuelBlock, U256}; +use ports::types::{FuelBlock, U256}; use url::Url; pub use self::event_streamer::EthEventStreamer; diff --git a/packages/eth_rpc/src/websocket/connection.rs b/packages/eth_rpc/src/websocket/connection.rs index 17682bf3..a14b2123 100644 --- a/packages/eth_rpc/src/websocket/connection.rs +++ b/packages/eth_rpc/src/websocket/connection.rs @@ -6,7 +6,7 @@ use ethers::{ signers::{LocalWallet, Signer}, types::{Address, Chain, H160, U256, U64}, }; -use ports::FuelBlock; +use ports::types::FuelBlock; use serde_json::Value; use url::Url; diff --git a/packages/eth_rpc/src/websocket/event_streamer.rs b/packages/eth_rpc/src/websocket/event_streamer.rs index b75fb568..649d08a3 100644 --- a/packages/eth_rpc/src/websocket/event_streamer.rs +++ b/packages/eth_rpc/src/websocket/event_streamer.rs @@ -6,7 +6,7 @@ use ethers::{ signers::Wallet, }; use futures::{Stream, TryStreamExt}; -use ports::eth_rpc::FuelBlockCommittedOnEth; +use ports::types::FuelBlockCommittedOnEth; use super::connection::CommitSubmittedFilter; use crate::Result; diff --git a/packages/eth_rpc/src/websocket/health_tracking_middleware.rs b/packages/eth_rpc/src/websocket/health_tracking_middleware.rs index f2d3e3d5..1a356236 100644 --- a/packages/eth_rpc/src/websocket/health_tracking_middleware.rs +++ b/packages/eth_rpc/src/websocket/health_tracking_middleware.rs @@ -1,5 +1,5 @@ use metrics::{ConnectionHealthTracker, HealthChecker, RegistersMetrics}; -use ports::{FuelBlock, U256}; +use ports::types::{FuelBlock, U256}; use crate::{metrics::Metrics, websocket::event_streamer::EthEventStreamer, Error, Result}; diff --git a/packages/fuel_rpc/src/lib.rs b/packages/fuel_rpc/src/lib.rs index df31a37f..98e52941 100644 --- a/packages/fuel_rpc/src/lib.rs +++ b/packages/fuel_rpc/src/lib.rs @@ -1,6 +1,6 @@ #![deny(unused_crate_dependencies)] use fuel_core_client::client::types::Block; -use ports::FuelBlock; +use ports::types::FuelBlock; pub mod client; pub mod metrics; diff --git a/packages/ports/Cargo.toml b/packages/ports/Cargo.toml index e46d3d02..c56f44aa 100644 --- a/packages/ports/Cargo.toml +++ b/packages/ports/Cargo.toml @@ -10,11 +10,12 @@ ethers-core = { workspace = true, optional = true } futures = { workspace = true, optional = true } impl-tools = { workspace = true, optional = true } mockall = { workspace = true, optional = true } +rand = { workspace = true, optional = true } +serde = { workspace = true, features = ["derive"] } thiserror = { workspace = true, optional = true } -types = { workspace = true } [features] -test-helpers = ["dep:mockall", "types/test-helpers"] +test-helpers = ["dep:mockall", "dep:rand"] eth = ["dep:ethers-core", "dep:futures", "dep:thiserror", "dep:async-trait"] fuel = ["dep:thiserror", "dep:async-trait"] storage = ["dep:impl-tools", "dep:thiserror", "dep:async-trait"] diff --git a/packages/ports/src/eth_rpc.rs b/packages/ports/src/eth_rpc.rs deleted file mode 100644 index bba4cf8a..00000000 --- a/packages/ports/src/eth_rpc.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::pin::Pin; - -use types::InvalidEthHeight; - -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("network error: {0}")] - Network(String), - #[error("{0}")] - Other(String), -} - -pub type Result = std::result::Result; - -impl From for Error { - fn from(err: InvalidEthHeight) -> Self { - Self::Other(err.to_string()) - } -} - -#[cfg_attr(feature = "test-helpers", mockall::automock)] -#[async_trait::async_trait] -pub trait EthereumAdapter: Send + Sync { - async fn submit(&self, block: crate::FuelBlock) -> Result<()>; - async fn get_block_number(&self) -> Result; - async fn balance(&self) -> Result; - fn event_streamer(&self, eth_block_height: u64) -> Box; -} - -#[cfg_attr(feature = "test-helpers", mockall::automock)] -#[async_trait::async_trait] -pub trait EventStreamer { - async fn establish_stream<'a>( - &'a self, - ) -> Result> + 'a + Send>>>; -} - -#[derive(Clone, Copy)] -pub struct FuelBlockCommittedOnEth { - pub fuel_block_hash: [u8; 32], - pub commit_height: crate::U256, -} - -impl std::fmt::Debug for FuelBlockCommittedOnEth { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let hash = self - .fuel_block_hash - .map(|byte| format!("{byte:02x?}")) - .join(""); - f.debug_struct("FuelBlockCommittedOnEth") - .field("hash", &hash) - .field("commit_height", &self.commit_height) - .finish() - } -} diff --git a/packages/ports/src/lib.rs b/packages/ports/src/lib.rs index 8ffbc488..279cc885 100644 --- a/packages/ports/src/lib.rs +++ b/packages/ports/src/lib.rs @@ -1,12 +1,14 @@ -#[cfg(feature = "eth")] -pub mod eth_rpc; -#[cfg(feature = "fuel")] -pub mod fuel_rpc; -#[cfg(feature = "storage")] -pub mod storage; +mod ports { + #[cfg(feature = "eth")] + pub mod eth_rpc; -#[cfg(feature = "eth")] -pub use ethers_core::types::{H160, U256}; -#[cfg(feature = "eth")] -pub use futures::Stream; -pub use types::{BlockSubmission, EthHeight, FuelBlock, InvalidEthHeight}; + #[cfg(feature = "fuel")] + pub mod fuel_rpc; + + #[cfg(feature = "storage")] + pub mod storage; +} + +#[cfg(any(feature = "eth", feature = "fuel", feature = "storage"))] +pub use ports::*; +pub mod types; diff --git a/packages/ports/src/ports/eth_rpc.rs b/packages/ports/src/ports/eth_rpc.rs new file mode 100644 index 00000000..aa67b7ac --- /dev/null +++ b/packages/ports/src/ports/eth_rpc.rs @@ -0,0 +1,36 @@ +use std::pin::Pin; + +use crate::types::{EthHeight, FuelBlock, FuelBlockCommittedOnEth, InvalidEthHeight, Stream, U256}; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("network error: {0}")] + Network(String), + #[error("{0}")] + Other(String), +} + +pub type Result = std::result::Result; + +impl From for Error { + fn from(err: InvalidEthHeight) -> Self { + Self::Other(err.to_string()) + } +} + +#[cfg_attr(feature = "test-helpers", mockall::automock)] +#[async_trait::async_trait] +pub trait EthereumAdapter: Send + Sync { + async fn submit(&self, block: FuelBlock) -> Result<()>; + async fn get_block_number(&self) -> Result; + async fn balance(&self) -> Result; + fn event_streamer(&self, eth_block_height: u64) -> Box; +} + +#[cfg_attr(feature = "test-helpers", mockall::automock)] +#[async_trait::async_trait] +pub trait EventStreamer { + async fn establish_stream<'a>( + &'a self, + ) -> Result> + 'a + Send>>>; +} diff --git a/packages/ports/src/fuel_rpc.rs b/packages/ports/src/ports/fuel_rpc.rs similarity index 77% rename from packages/ports/src/fuel_rpc.rs rename to packages/ports/src/ports/fuel_rpc.rs index f261cb24..98cbe11f 100644 --- a/packages/ports/src/fuel_rpc.rs +++ b/packages/ports/src/ports/fuel_rpc.rs @@ -1,3 +1,5 @@ +use crate::types::FuelBlock; + #[derive(Debug, thiserror::Error)] pub enum Error { #[error("{0}")] @@ -6,9 +8,9 @@ pub enum Error { pub type Result = std::result::Result; -#[async_trait::async_trait] #[cfg_attr(feature = "test-helpers", mockall::automock)] +#[async_trait::async_trait] pub trait FuelAdapter: Send + Sync { - async fn block_at_height(&self, height: u32) -> Result>; - async fn latest_block(&self) -> Result; + async fn block_at_height(&self, height: u32) -> Result>; + async fn latest_block(&self) -> Result; } diff --git a/packages/ports/src/storage.rs b/packages/ports/src/ports/storage.rs similarity index 60% rename from packages/ports/src/storage.rs rename to packages/ports/src/ports/storage.rs index f50029a4..2c1507e0 100644 --- a/packages/ports/src/storage.rs +++ b/packages/ports/src/ports/storage.rs @@ -1,5 +1,7 @@ use std::sync::Arc; +use crate::types::BlockSubmission; + #[derive(Debug, thiserror::Error)] #[error("{msg}")] pub struct Error { @@ -19,10 +21,7 @@ pub type Result = std::result::Result; #[impl_tools::autoimpl(for &T, &mut T, Arc, Box)] #[cfg_attr(feature = "test-helpers", mockall::automock)] pub trait Storage: Send + Sync { - async fn insert(&self, submission: crate::BlockSubmission) -> Result<()>; - async fn submission_w_latest_block(&self) -> Result>; - async fn set_submission_completed( - &self, - fuel_block_hash: [u8; 32], - ) -> Result; + async fn insert(&self, submission: BlockSubmission) -> Result<()>; + async fn submission_w_latest_block(&self) -> Result>; + async fn set_submission_completed(&self, fuel_block_hash: [u8; 32]) -> Result; } diff --git a/packages/ports/src/types.rs b/packages/ports/src/types.rs new file mode 100644 index 00000000..a648bb94 --- /dev/null +++ b/packages/ports/src/types.rs @@ -0,0 +1,16 @@ +#[cfg(feature = "eth")] +pub use ethers_core::types::{H160, U256}; +#[cfg(feature = "eth")] +pub use futures::Stream; + +mod block_submission; +mod eth_height; +mod fuel_block; +#[cfg(feature = "eth")] +mod fuel_block_committed_on_eth; + +pub use block_submission::*; +pub use eth_height::*; +pub use fuel_block::*; +#[cfg(feature = "eth")] +pub use fuel_block_committed_on_eth::*; diff --git a/packages/types/src/block_submission.rs b/packages/ports/src/types/block_submission.rs similarity index 93% rename from packages/types/src/block_submission.rs rename to packages/ports/src/types/block_submission.rs index aed5557a..8eb2d0ce 100644 --- a/packages/types/src/block_submission.rs +++ b/packages/ports/src/types/block_submission.rs @@ -1,4 +1,4 @@ -use crate::{EthHeight, FuelBlock}; +use crate::types::{EthHeight, FuelBlock}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct BlockSubmission { diff --git a/packages/types/src/eth_height.rs b/packages/ports/src/types/eth_height.rs similarity index 100% rename from packages/types/src/eth_height.rs rename to packages/ports/src/types/eth_height.rs diff --git a/packages/types/src/fuel_block.rs b/packages/ports/src/types/fuel_block.rs similarity index 100% rename from packages/types/src/fuel_block.rs rename to packages/ports/src/types/fuel_block.rs diff --git a/packages/ports/src/types/fuel_block_committed_on_eth.rs b/packages/ports/src/types/fuel_block_committed_on_eth.rs new file mode 100644 index 00000000..869622e8 --- /dev/null +++ b/packages/ports/src/types/fuel_block_committed_on_eth.rs @@ -0,0 +1,20 @@ +use crate::types::U256; + +#[derive(Clone, Copy)] +pub struct FuelBlockCommittedOnEth { + pub fuel_block_hash: [u8; 32], + pub commit_height: U256, +} + +impl std::fmt::Debug for FuelBlockCommittedOnEth { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let hash = self + .fuel_block_hash + .map(|byte| format!("{byte:02x?}")) + .join(""); + f.debug_struct("FuelBlockCommittedOnEth") + .field("hash", &hash) + .field("commit_height", &self.commit_height) + .finish() + } +} diff --git a/packages/storage/Cargo.toml b/packages/storage/Cargo.toml index 9f2b6b96..475f1fc2 100644 --- a/packages/storage/Cargo.toml +++ b/packages/storage/Cargo.toml @@ -23,7 +23,7 @@ tokio = { workspace = true, optional = true } [dev-dependencies] ports = { workspace = true, features = ["storage"] } rand = { workspace = true } -storage = { path = ".", features = ["test-helpers"] } +storage = { workspace = true, features = ["test-helpers"] } tokio = { workspace = true } [features] diff --git a/packages/storage/src/lib.rs b/packages/storage/src/lib.rs index b38b5918..ae19556c 100644 --- a/packages/storage/src/lib.rs +++ b/packages/storage/src/lib.rs @@ -38,7 +38,7 @@ impl From for Error { } } -use ports::BlockSubmission; +use ports::types::BlockSubmission; #[async_trait::async_trait] impl ports::storage::Storage for postgres::Postgres { @@ -60,9 +60,11 @@ impl ports::storage::Storage for postgres::Postgres { #[cfg(test)] mod tests { + use ports::types::BlockSubmission; use rand::{thread_rng, Rng}; + use storage as _; - use super::*; + use crate::{Error, PostgresProcess}; fn random_non_zero_height() -> u32 { let mut rng = thread_rng(); diff --git a/packages/storage/src/postgres.rs b/packages/storage/src/postgres.rs index d7b833aa..5161ac1b 100644 --- a/packages/storage/src/postgres.rs +++ b/packages/storage/src/postgres.rs @@ -1,4 +1,4 @@ -use ports::BlockSubmission; +use ports::types::BlockSubmission; use sqlx::postgres::{PgConnectOptions, PgPoolOptions}; use super::{Error, Result}; diff --git a/packages/storage/src/tables.rs b/packages/storage/src/tables.rs index b47bb660..b5550c81 100644 --- a/packages/storage/src/tables.rs +++ b/packages/storage/src/tables.rs @@ -1,4 +1,4 @@ -use ports::{BlockSubmission, FuelBlock}; +use ports::types::{BlockSubmission, FuelBlock}; use crate::Error; diff --git a/packages/types/Cargo.toml b/packages/types/Cargo.toml deleted file mode 100644 index ceeb6571..00000000 --- a/packages/types/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "types" -version = "0.1.0" -edition = "2021" -publish = false -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -rand = { workspace = true, optional = true } -serde = { workspace = true, features = ["derive"] } - -[features] -test-helpers = ["dep:rand"] diff --git a/packages/types/src/lib.rs b/packages/types/src/lib.rs deleted file mode 100644 index 9c96698f..00000000 --- a/packages/types/src/lib.rs +++ /dev/null @@ -1,8 +0,0 @@ -#![deny(unused_crate_dependencies)] -mod block_submission; -mod eth_height; -mod fuel_block; - -pub use block_submission::*; -pub use eth_height::*; -pub use fuel_block::*; From d1d1a2f99cc64ae554e987e986fefebfb249f050 Mon Sep 17 00:00:00 2001 From: segfault_magnet Date: Sun, 5 May 2024 14:42:42 +0200 Subject: [PATCH 12/23] use port errors for public apis --- committer/src/config.rs | 1 - committer/src/errors.rs | 14 ------ packages/eth_rpc/src/error.rs | 46 +++++++++++++++++ packages/eth_rpc/src/lib.rs | 50 ++----------------- packages/eth_rpc/src/websocket.rs | 21 +++++--- packages/eth_rpc/src/websocket/connection.rs | 2 +- .../eth_rpc/src/websocket/event_streamer.rs | 2 +- .../websocket/health_tracking_middleware.rs | 6 ++- packages/fuel_rpc/src/lib.rs | 4 +- packages/ports/src/ports/storage.rs | 15 ++---- packages/storage/src/error.rs | 30 +++++++++++ packages/storage/src/lib.rs | 36 ++----------- packages/storage/src/postgres.rs | 20 +++++--- packages/storage/src/tables.rs | 8 ++- packages/storage/src/test_instance.rs | 7 ++- 15 files changed, 128 insertions(+), 134 deletions(-) create mode 100644 packages/eth_rpc/src/error.rs create mode 100644 packages/storage/src/error.rs diff --git a/committer/src/config.rs b/committer/src/config.rs index 78b8e8f0..66f44f69 100644 --- a/committer/src/config.rs +++ b/committer/src/config.rs @@ -65,7 +65,6 @@ pub struct AppConfig { /// IPv4 address on which the server will listen for connections pub host: Ipv4Addr, /// Postgres database configuration - /// TODO: don't rely on this type it is not robust pub db: DbConfig, } diff --git a/committer/src/errors.rs b/committer/src/errors.rs index 17a15907..ccd8c1ac 100644 --- a/committer/src/errors.rs +++ b/committer/src/errors.rs @@ -34,11 +34,6 @@ impl From for Error { Self::Storage(error.to_string()) } } -impl From for Error { - fn from(error: storage::Error) -> Self { - Self::Storage(error.to_string()) - } -} impl From for Error { fn from(value: ports::eth_rpc::Error) -> Self { @@ -49,15 +44,6 @@ impl From for Error { } } -impl From for Error { - fn from(value: eth_rpc::Error) -> Self { - match value { - eth_rpc::Error::Network(e) => Self::Network(e), - _ => Self::Other(value.to_string()), - } - } -} - impl From for Error { fn from(value: ports::fuel_rpc::Error) -> Self { match value { diff --git a/packages/eth_rpc/src/error.rs b/packages/eth_rpc/src/error.rs new file mode 100644 index 00000000..704e723d --- /dev/null +++ b/packages/eth_rpc/src/error.rs @@ -0,0 +1,46 @@ +use ethers::{ + prelude::{ContractError, SignerMiddleware}, + providers::{Provider, Ws}, + signers::LocalWallet, +}; + +#[derive(Debug, thiserror::Error)] +pub(crate) enum Error { + #[error("wallet error: {0}")] + Wallet(#[from] ethers::signers::WalletError), + #[error("network error: {0}")] + Network(String), + #[error("other error: {0}")] + Other(String), +} + +impl From for Error { + fn from(err: ethers::providers::ProviderError) -> Self { + Self::Network(err.to_string()) + } +} + +pub(crate) type ContractErrorType = + ethers::contract::ContractError, LocalWallet>>; + +impl From for Error { + fn from(value: ContractErrorType) -> Self { + match value { + ContractError::MiddlewareError { e } => Self::Other(e.to_string()), + ContractError::ProviderError { e } => Self::Network(e.to_string()), + _ => Self::Other(value.to_string()), + } + } +} + +pub(crate) type Result = std::result::Result; + +impl From for ports::eth_rpc::Error { + fn from(err: Error) -> Self { + match err { + Error::Network(err) => Self::Network(err), + Error::Other(err) => Self::Other(err), + Error::Wallet(err) => Self::Other(err.to_string()), + } + } +} diff --git a/packages/eth_rpc/src/lib.rs b/packages/eth_rpc/src/lib.rs index 2efc7a20..0eedbffd 100644 --- a/packages/eth_rpc/src/lib.rs +++ b/packages/eth_rpc/src/lib.rs @@ -2,66 +2,22 @@ use std::pin::Pin; use async_trait::async_trait; -use ethers::{ - prelude::{ContractError, SignerMiddleware}, - providers::{Provider, Ws}, - signers::LocalWallet, - types::U256, -}; +use ethers::types::U256; use futures::{stream::TryStreamExt, Stream}; use ports::types::{EthHeight, FuelBlockCommittedOnEth}; use websocket::EthEventStreamer; +mod error; mod metrics; mod websocket; pub use ethers::types::{Address, Chain}; pub use websocket::WsAdapter; -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("wallet error: {0}")] - Wallet(#[from] ethers::signers::WalletError), - #[error("network error: {0}")] - Network(String), - #[error("other error: {0}")] - Other(String), -} - -impl From for Error { - fn from(err: ethers::providers::ProviderError) -> Self { - Self::Network(err.to_string()) - } -} - -type ContractErrorType = - ethers::contract::ContractError, LocalWallet>>; -impl From for Error { - fn from(value: ContractErrorType) -> Self { - match value { - ContractError::MiddlewareError { e } => Self::Other(e.to_string()), - ContractError::ProviderError { e } => Self::Network(e.to_string()), - _ => Self::Other(value.to_string()), - } - } -} - -pub type Result = std::result::Result; - -impl From for ports::eth_rpc::Error { - fn from(err: Error) -> Self { - match err { - Error::Network(err) => Self::Network(err), - Error::Other(err) => Self::Other(err), - Error::Wallet(err) => Self::Other(err.to_string()), - } - } -} - #[async_trait] impl ports::eth_rpc::EthereumAdapter for WsAdapter { async fn submit(&self, block: ports::types::FuelBlock) -> ports::eth_rpc::Result<()> { - Ok(self.submit(block).await?) + self.submit(block).await } async fn get_block_number(&self) -> ports::eth_rpc::Result { diff --git a/packages/eth_rpc/src/websocket.rs b/packages/eth_rpc/src/websocket.rs index 8bd9f9c7..bfda30cd 100644 --- a/packages/eth_rpc/src/websocket.rs +++ b/packages/eth_rpc/src/websocket.rs @@ -2,7 +2,10 @@ use std::num::NonZeroU32; use ethers::types::{Address, Chain}; use metrics::{HealthChecker, RegistersMetrics}; -use ports::types::{FuelBlock, U256}; +use ports::{ + eth_rpc::Result, + types::{FuelBlock, U256}, +}; use url::Url; pub use self::event_streamer::EthEventStreamer; @@ -10,7 +13,6 @@ use self::{ connection::WsConnection, health_tracking_middleware::{HealthTrackingMiddleware, MyAdapter}, }; -use crate::Result; mod connection; mod event_streamer; @@ -29,7 +31,7 @@ impl WsAdapter { ethereum_wallet_key: &str, commit_interval: NonZeroU32, unhealthy_after_n_errors: usize, - ) -> Result { + ) -> ports::eth_rpc::Result { let provider = WsConnection::connect( ethereum_rpc, chain_id, @@ -54,25 +56,28 @@ impl WsAdapter { } pub(crate) async fn submit(&self, block: FuelBlock) -> Result<()> { - self.inner.submit(block).await + Ok(self.inner.submit(block).await?) } pub(crate) async fn get_block_number(&self) -> Result { - self.inner.get_block_number().await + Ok(self.inner.get_block_number().await?) } pub(crate) async fn balance(&self) -> Result { - self.inner.balance().await + Ok(self.inner.balance().await?) } #[cfg(feature = "test-helpers")] pub async fn finalized(&self, block: FuelBlock) -> Result { - self.inner.finalized(block).await + Ok(self.inner.finalized(block).await?) } #[cfg(feature = "test-helpers")] pub async fn block_hash_at_commit_height(&self, commit_height: u32) -> Result<[u8; 32]> { - self.inner.block_hash_at_commit_height(commit_height).await + Ok(self + .inner + .block_hash_at_commit_height(commit_height) + .await?) } } diff --git a/packages/eth_rpc/src/websocket/connection.rs b/packages/eth_rpc/src/websocket/connection.rs index a14b2123..7a1a5c40 100644 --- a/packages/eth_rpc/src/websocket/connection.rs +++ b/packages/eth_rpc/src/websocket/connection.rs @@ -11,7 +11,7 @@ use serde_json::Value; use url::Url; use super::{event_streamer::EthEventStreamer, health_tracking_middleware::MyAdapter}; -use crate::Result; +use crate::error::Result; abigen!( FUEL_STATE_CONTRACT, diff --git a/packages/eth_rpc/src/websocket/event_streamer.rs b/packages/eth_rpc/src/websocket/event_streamer.rs index 649d08a3..b98cbfed 100644 --- a/packages/eth_rpc/src/websocket/event_streamer.rs +++ b/packages/eth_rpc/src/websocket/event_streamer.rs @@ -9,7 +9,7 @@ use futures::{Stream, TryStreamExt}; use ports::types::FuelBlockCommittedOnEth; use super::connection::CommitSubmittedFilter; -use crate::Result; +use crate::error::Result; type EthStreamInitializer = Event< Arc, Wallet>>, diff --git a/packages/eth_rpc/src/websocket/health_tracking_middleware.rs b/packages/eth_rpc/src/websocket/health_tracking_middleware.rs index 1a356236..4f77e7e1 100644 --- a/packages/eth_rpc/src/websocket/health_tracking_middleware.rs +++ b/packages/eth_rpc/src/websocket/health_tracking_middleware.rs @@ -1,7 +1,11 @@ use metrics::{ConnectionHealthTracker, HealthChecker, RegistersMetrics}; use ports::types::{FuelBlock, U256}; -use crate::{metrics::Metrics, websocket::event_streamer::EthEventStreamer, Error, Result}; +use crate::{ + error::{Error, Result}, + metrics::Metrics, + websocket::event_streamer::EthEventStreamer, +}; #[cfg_attr(test, mockall::automock)] #[async_trait::async_trait] diff --git a/packages/fuel_rpc/src/lib.rs b/packages/fuel_rpc/src/lib.rs index 98e52941..404e7add 100644 --- a/packages/fuel_rpc/src/lib.rs +++ b/packages/fuel_rpc/src/lib.rs @@ -4,8 +4,8 @@ use ports::types::FuelBlock; pub mod client; pub mod metrics; -pub type Error = ports::fuel_rpc::Error; -pub type Result = ports::fuel_rpc::Result; +type Error = ports::fuel_rpc::Error; +type Result = ports::fuel_rpc::Result; fn convert_block(block: Block) -> FuelBlock { FuelBlock { diff --git a/packages/ports/src/ports/storage.rs b/packages/ports/src/ports/storage.rs index 2c1507e0..d816b6aa 100644 --- a/packages/ports/src/ports/storage.rs +++ b/packages/ports/src/ports/storage.rs @@ -3,16 +3,11 @@ use std::sync::Arc; use crate::types::BlockSubmission; #[derive(Debug, thiserror::Error)] -#[error("{msg}")] -pub struct Error { - msg: String, -} - -impl Error { - #[must_use] - pub fn new(msg: String) -> Self { - Self { msg } - } +pub enum Error { + #[error("db response: {0}")] + Database(String), + #[error("data conversion app<->db failed: {0}")] + Conversion(String), } pub type Result = std::result::Result; diff --git a/packages/storage/src/error.rs b/packages/storage/src/error.rs new file mode 100644 index 00000000..0f87bd31 --- /dev/null +++ b/packages/storage/src/error.rs @@ -0,0 +1,30 @@ +pub(crate) type Result = std::result::Result; + +#[derive(Debug, thiserror::Error)] +pub(crate) enum Error { + #[error("Database Error {0}")] + Database(String), + #[error("Could not convert to/from domain/db type {0}")] + Conversion(String), +} + +impl From for ports::storage::Error { + fn from(value: Error) -> ports::storage::Error { + match value { + Error::Database(e) => ports::storage::Error::Database(e), + Error::Conversion(e) => ports::storage::Error::Conversion(e), + } + } +} + +impl From for Error { + fn from(e: sqlx::Error) -> Self { + Self::Database(e.to_string()) + } +} + +impl From for Error { + fn from(e: sqlx::migrate::MigrateError) -> Self { + Self::Database(e.to_string()) + } +} diff --git a/packages/storage/src/lib.rs b/packages/storage/src/lib.rs index ae19556c..640598ff 100644 --- a/packages/storage/src/lib.rs +++ b/packages/storage/src/lib.rs @@ -5,40 +5,10 @@ mod test_instance; #[cfg(feature = "test-helpers")] pub use test_instance::*; +mod error; mod postgres; -pub use postgres::*; - -pub type Result = std::result::Result; - -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("Database Error {0}")] - Database(String), - #[error("Could not convert to/from domain/db type {0}")] - Conversion(String), - #[error("{0}")] - Other(String), -} - -impl From for ports::storage::Error { - fn from(value: Error) -> Self { - Self::new(value.to_string()) - } -} - -impl From for Error { - fn from(e: sqlx::Error) -> Self { - Self::Database(e.to_string()) - } -} - -impl From for Error { - fn from(e: sqlx::migrate::MigrateError) -> Self { - Self::Database(e.to_string()) - } -} - use ports::types::BlockSubmission; +pub use postgres::*; #[async_trait::async_trait] impl ports::storage::Storage for postgres::Postgres { @@ -64,7 +34,7 @@ mod tests { use rand::{thread_rng, Rng}; use storage as _; - use crate::{Error, PostgresProcess}; + use crate::{error::Error, PostgresProcess}; fn random_non_zero_height() -> u32 { let mut rng = thread_rng(); diff --git a/packages/storage/src/postgres.rs b/packages/storage/src/postgres.rs index 5161ac1b..b1ebf6ab 100644 --- a/packages/storage/src/postgres.rs +++ b/packages/storage/src/postgres.rs @@ -1,7 +1,7 @@ use ports::types::BlockSubmission; use sqlx::postgres::{PgConnectOptions, PgPoolOptions}; -use super::{Error, Result}; +use super::error::{Error, Result}; use crate::tables; #[derive(Clone)] @@ -26,7 +26,7 @@ pub struct DbConfig { } impl Postgres { - pub async fn connect(opt: &DbConfig) -> Result { + pub async fn connect(opt: &DbConfig) -> ports::storage::Result { let options = PgConnectOptions::new() .username(&opt.username) .password(&opt.password) @@ -37,7 +37,8 @@ impl Postgres { let connection_pool = PgPoolOptions::new() .max_connections(opt.max_connections) .connect_with(options) - .await?; + .await + .map_err(crate::error::Error::from)?; Ok(Self { connection_pool }) } @@ -48,8 +49,11 @@ impl Postgres { self.connection_pool.close().await; } - pub async fn migrate(&self) -> Result<()> { - sqlx::migrate!().run(&self.connection_pool).await?; + pub async fn migrate(&self) -> ports::storage::Result<()> { + sqlx::migrate!() + .run(&self.connection_pool) + .await + .map_err(crate::error::Error::from)?; Ok(()) } @@ -59,7 +63,7 @@ impl Postgres { Ok(()) } - pub(crate) async fn _insert(&self, submission: BlockSubmission) -> Result<()> { + pub(crate) async fn _insert(&self, submission: BlockSubmission) -> crate::error::Result<()> { let row = tables::EthFuelBlockSubmission::from(submission); sqlx::query!( "INSERT INTO eth_fuel_block_submission (fuel_block_hash, fuel_block_height, completed, submittal_height) VALUES ($1, $2, $3, $4)", @@ -71,7 +75,9 @@ impl Postgres { Ok(()) } - pub(crate) async fn _submission_w_latest_block(&self) -> Result> { + pub(crate) async fn _submission_w_latest_block( + &self, + ) -> crate::error::Result> { sqlx::query_as!( tables::EthFuelBlockSubmission, "SELECT * FROM eth_fuel_block_submission ORDER BY fuel_block_height DESC LIMIT 1" diff --git a/packages/storage/src/tables.rs b/packages/storage/src/tables.rs index b5550c81..e6f9fbc6 100644 --- a/packages/storage/src/tables.rs +++ b/packages/storage/src/tables.rs @@ -1,9 +1,7 @@ use ports::types::{BlockSubmission, FuelBlock}; -use crate::Error; - #[derive(sqlx::FromRow)] -pub struct EthFuelBlockSubmission { +pub(crate) struct EthFuelBlockSubmission { pub fuel_block_hash: Vec, pub fuel_block_height: i64, pub completed: bool, @@ -11,13 +9,13 @@ pub struct EthFuelBlockSubmission { } impl TryFrom for BlockSubmission { - type Error = Error; + type Error = crate::error::Error; fn try_from(value: EthFuelBlockSubmission) -> Result { let block_hash = value.fuel_block_hash.as_slice(); macro_rules! bail { ($msg: literal, $($args: expr),*) => { - return Err(Error::Conversion(format!($msg, $($args),*))); + return Err(Self::Error::Conversion(format!($msg, $($args),*))); }; } let Ok(hash) = block_hash.try_into() else { diff --git a/packages/storage/src/test_instance.rs b/packages/storage/src/test_instance.rs index 894658c3..3be1cd7f 100644 --- a/packages/storage/src/test_instance.rs +++ b/packages/storage/src/test_instance.rs @@ -3,7 +3,6 @@ use std::sync::{Arc, Weak}; use testcontainers::{core::WaitFor, runners::AsyncRunner, Image, RunnableImage}; use super::postgres::{DbConfig, Postgres}; -use crate::Result; struct PostgresImage; @@ -37,7 +36,7 @@ pub struct PostgresProcess { } impl PostgresProcess { - pub async fn shared() -> Result> { + pub async fn shared() -> ports::storage::Result> { // If at some point no tests are running, the shared instance will be dropped. If // requested again, it will be recreated. // This is a workaround for the lack of a global setup/teardown for tests. @@ -57,7 +56,7 @@ impl PostgresProcess { Ok(process) } - pub async fn start() -> Result { + pub async fn start() -> ports::storage::Result { let username = "username".to_string(); let password = "password".to_string(); let initial_db = "test".to_string(); @@ -77,7 +76,7 @@ impl PostgresProcess { }) } - pub async fn create_random_db(&self) -> Result { + pub async fn create_random_db(&self) -> ports::storage::Result { let mut config = DbConfig { host: "localhost".to_string(), port: self.container.get_host_port_ipv4(5432).await, From 0b4e848e1b7d8ee4bdcbf4d14ad31b320cabd8b3 Mon Sep 17 00:00:00 2001 From: segfault_magnet Date: Mon, 6 May 2024 06:31:08 +0200 Subject: [PATCH 13/23] move services to separate crate --- .dockerignore | 10 ++++ Cargo.lock | 56 ++++++++++++------- Cargo.toml | 2 + committer/Cargo.toml | 5 +- committer/src/api.rs | 5 +- committer/src/errors.rs | 10 ++++ committer/src/main.rs | 2 - committer/src/services.rs | 18 ------ committer/src/setup.rs | 9 ++- committer/src/setup/helpers.rs | 2 - packages/metrics/src/lib.rs | 2 +- packages/ports/Cargo.toml | 1 + packages/services/Cargo.toml | 23 ++++++++ .../services/src}/block_committer.rs | 6 +- .../services/src}/block_watcher.rs | 13 ++--- .../services/src}/commit_listener.rs | 22 +++----- .../services/src}/health_reporter.rs | 0 packages/services/src/lib.rs | 54 ++++++++++++++++++ .../services/src}/status_reporter.rs | 5 +- .../services/src}/wallet_balance_tracker.rs | 11 ++-- 20 files changed, 169 insertions(+), 87 deletions(-) create mode 100644 .dockerignore delete mode 100644 committer/src/services.rs delete mode 100644 committer/src/setup/helpers.rs create mode 100644 packages/services/Cargo.toml rename {committer/src/services => packages/services/src}/block_committer.rs (96%) rename {committer/src/services => packages/services/src}/block_watcher.rs (96%) rename {committer/src/services => packages/services/src}/commit_listener.rs (92%) rename {committer/src/services => packages/services/src}/health_reporter.rs (100%) create mode 100644 packages/services/src/lib.rs rename {committer/src/services => packages/services/src}/status_reporter.rs (96%) rename {committer/src/services => packages/services/src}/wallet_balance_tracker.rs (90%) diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..4b39ef0c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +target +deployment +configurations +eth_node +fuel_node +helm +compose.yml +.git +*.rs.bk +.dockerignore diff --git a/Cargo.lock b/Cargo.lock index 147c985e..de0a7413 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1713,19 +1713,16 @@ version = "0.4.0" dependencies = [ "actix-web", "anyhow", - "async-trait", "clap", "config", "eth_rpc", "fuel_rpc", - "futures", "metrics", - "mockall", "ports", "prometheus", - "rand", "serde", "serde_json", + "services", "storage", "thiserror", "tokio", @@ -3388,9 +3385,9 @@ dependencies = [ [[package]] name = "prometheus" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" +checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" dependencies = [ "cfg-if", "fnv", @@ -3939,11 +3936,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "core-foundation", "core-foundation-sys", "libc", @@ -3952,9 +3949,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" dependencies = [ "core-foundation-sys", "libc", @@ -4074,6 +4071,24 @@ dependencies = [ "syn 2.0.60", ] +[[package]] +name = "services" +version = "0.1.0" +dependencies = [ + "async-trait", + "futures", + "metrics", + "mockall", + "ports", + "rand", + "serde", + "storage", + "thiserror", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "sha1" version = "0.10.6" @@ -4798,16 +4813,15 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] @@ -4874,7 +4888,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.7", + "winnow 0.6.8", ] [[package]] @@ -5472,9 +5486,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578" +checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" dependencies = [ "memchr", ] @@ -5519,18 +5533,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "087eca3c1eaf8c47b94d02790dd086cd594b912d2043d4de4bfdd466b3befb7c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "6f4b6c273f496d8fd4eaf18853e6b448760225dc030ff2c485a786859aea6393" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 9ea46840..acdf8662 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "packages/fuel_rpc", "packages/metrics", "packages/ports", + "packages/services", "packages/storage", ] @@ -16,6 +17,7 @@ fuel_rpc = { version = "0.1.0", path = "./packages/fuel_rpc", default-features = metrics = { version = "0.1.0", path = "./packages/metrics", default-features = false } ports = { version = "0.1.0", path = "./packages/ports", default-features = false } storage = { version = "0.1.0", path = "./packages/storage", default-features = false } +services = { version = "0.1.0", path = "./packages/services", default-features = false } actix-web = { version = "4", default-features = false } anyhow = { version = "1.0", default-features = false } diff --git a/committer/Cargo.toml b/committer/Cargo.toml index 915156d9..64f29c6a 100644 --- a/committer/Cargo.toml +++ b/committer/Cargo.toml @@ -12,7 +12,6 @@ name = "fuel-block-committer" actix-web = { workspace = true, default-features = false, features = [ "macros", ] } -async-trait = { workspace = true } clap = { workspace = true, features = ["derive"] } config = { workspace = true, default-features = false, features = [ "toml", @@ -20,9 +19,9 @@ config = { workspace = true, default-features = false, features = [ ] } eth_rpc = { workspace = true } fuel_rpc = { workspace = true } -futures = { workspace = true } metrics = { workspace = true } ports = { workspace = true } +services = { workspace = true } prometheus = { workspace = true, default-features = false } serde = { workspace = true } @@ -41,9 +40,7 @@ url = { workspace = true } [dev-dependencies] anyhow = { workspace = true, default-features = false } -mockall = { workspace = true } ports = { workspace = true, features = ["test-helpers"] } -rand = { workspace = true } storage = { workspace = true, features = ["test-helpers"] } #TODO: Once fuels-rs catches up tests will be reenabled #fuels-test-helpers = "0.57" diff --git a/committer/src/api.rs b/committer/src/api.rs index d2167903..daa4a43b 100644 --- a/committer/src/api.rs +++ b/committer/src/api.rs @@ -6,11 +6,12 @@ use actix_web::{ }; use ports::storage::Storage; use prometheus::{Encoder, Registry, TextEncoder}; +use services::{HealthReporter, StatusReporter}; +use storage::Postgres; use crate::{ config::Config, errors::{Error, Result}, - services::{HealthReporter, StatusReporter}, }; pub async fn launch_api_server( @@ -53,7 +54,7 @@ async fn health(data: web::Data>) -> impl Responder { } #[get("/status")] -async fn status(data: web::Data>) -> impl Responder { +async fn status(data: web::Data>>) -> impl Responder { let report = data.current_status().await?; Result::Ok(web::Json(report)) diff --git a/committer/src/errors.rs b/committer/src/errors.rs index ccd8c1ac..e69fa299 100644 --- a/committer/src/errors.rs +++ b/committer/src/errors.rs @@ -52,6 +52,16 @@ impl From for Error { } } +impl From for Error { + fn from(error: services::Error) -> Self { + match error { + services::Error::Network(e) => Self::Network(e), + services::Error::Storage(e) => Self::Storage(e), + services::Error::Other(e) => Self::Other(e), + } + } +} + impl From for Error { fn from(error: config::ConfigError) -> Self { Self::Other(error.to_string()) diff --git a/committer/src/main.rs b/committer/src/main.rs index c70f0542..eedbc06b 100644 --- a/committer/src/main.rs +++ b/committer/src/main.rs @@ -2,7 +2,6 @@ mod api; mod config; mod errors; -mod services; mod setup; use api::launch_api_server; @@ -24,7 +23,6 @@ async fn main() -> Result<()> { let config = config::parse()?; let storage = setup_storage(&config).await?; - storage.migrate().await?; let internal_config = InternalConfig::default(); let cancel_token = CancellationToken::new(); diff --git a/committer/src/services.rs b/committer/src/services.rs deleted file mode 100644 index 37de3995..00000000 --- a/committer/src/services.rs +++ /dev/null @@ -1,18 +0,0 @@ -mod block_committer; -mod block_watcher; -mod commit_listener; -mod health_reporter; -mod status_reporter; -mod wallet_balance_tracker; - -pub use block_committer::BlockCommitter; -pub use block_watcher::BlockWatcher; -pub use commit_listener::CommitListener; -pub use health_reporter::HealthReporter; -pub use status_reporter::StatusReporter; -pub use wallet_balance_tracker::WalletBalanceTracker; - -#[async_trait::async_trait] -pub trait Runner: Send + Sync { - async fn run(&mut self) -> crate::errors::Result<()>; -} diff --git a/committer/src/setup.rs b/committer/src/setup.rs index f9fc9920..eb6016ce 100644 --- a/committer/src/setup.rs +++ b/committer/src/setup.rs @@ -5,6 +5,7 @@ use fuel_rpc::client::Client; use metrics::{HealthChecker, RegistersMetrics}; use ports::{storage::Storage, types::FuelBlock}; use prometheus::Registry; +use services::{BlockCommitter, BlockWatcher, CommitListener, Runner, WalletBalanceTracker}; use storage::Postgres; use tokio::{sync::mpsc::Receiver, task::JoinHandle}; use tokio_util::sync::CancellationToken; @@ -13,7 +14,6 @@ use tracing::{error, info}; use crate::{ config::{Config, InternalConfig}, errors::Result, - services::{BlockCommitter, BlockWatcher, CommitListener, Runner, WalletBalanceTracker}, }; pub fn spawn_block_watcher( @@ -164,7 +164,7 @@ fn create_block_watcher( registry: &Registry, fuel_adapter: Client, storage: Postgres, -) -> (BlockWatcher, Receiver) { +) -> (BlockWatcher, Receiver) { let (tx_fuel_block, rx_fuel_block) = tokio::sync::mpsc::channel(100); let block_watcher = BlockWatcher::new( config.eth.commit_interval, @@ -188,7 +188,10 @@ pub fn setup_logger() { } pub async fn setup_storage(config: &Config) -> Result { - Ok(Postgres::connect(&config.app.db).await?) + let postgres = Postgres::connect(&config.app.db).await?; + postgres.migrate().await?; + + Ok(postgres) } pub async fn shut_down( diff --git a/committer/src/setup/helpers.rs b/committer/src/setup/helpers.rs deleted file mode 100644 index 139597f9..00000000 --- a/committer/src/setup/helpers.rs +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/packages/metrics/src/lib.rs b/packages/metrics/src/lib.rs index b03768ff..16cdaece 100644 --- a/packages/metrics/src/lib.rs +++ b/packages/metrics/src/lib.rs @@ -7,7 +7,7 @@ pub trait HealthCheck: Send + Sync { fn healthy(&self) -> bool; } -pub use prometheus::{core::Collector, Registry}; +pub use prometheus::{core::Collector, proto::Metric, IntGauge, Opts, Registry}; pub trait RegistersMetrics { fn register_metrics(&self, registry: &Registry) { diff --git a/packages/ports/Cargo.toml b/packages/ports/Cargo.toml index c56f44aa..3477d09d 100644 --- a/packages/ports/Cargo.toml +++ b/packages/ports/Cargo.toml @@ -19,3 +19,4 @@ test-helpers = ["dep:mockall", "dep:rand"] eth = ["dep:ethers-core", "dep:futures", "dep:thiserror", "dep:async-trait"] fuel = ["dep:thiserror", "dep:async-trait"] storage = ["dep:impl-tools", "dep:thiserror", "dep:async-trait"] +full = ["eth", "fuel", "storage"] diff --git a/packages/services/Cargo.toml b/packages/services/Cargo.toml new file mode 100644 index 00000000..8a4db011 --- /dev/null +++ b/packages/services/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "services" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +async-trait = { workspace = true } +tracing = { workspace = true } +tokio = { workspace = true, features = ["sync"] } +ports = { workspace = true, features = ["full"] } +thiserror = { workspace = true } +metrics = { workspace = true } +futures = { workspace = true } +tokio-util = { workspace = true } +serde = { workspace = true } + +[dev-dependencies] +mockall = { workspace = true } +storage = { workspace = true, features = ["test-helpers"] } +rand = { workspace = true } diff --git a/committer/src/services/block_committer.rs b/packages/services/src/block_committer.rs similarity index 96% rename from committer/src/services/block_committer.rs rename to packages/services/src/block_committer.rs index e678bc56..2dae07df 100644 --- a/committer/src/services/block_committer.rs +++ b/packages/services/src/block_committer.rs @@ -1,18 +1,16 @@ use async_trait::async_trait; -use eth_rpc::WsAdapter; use ports::{ eth_rpc::EthereumAdapter, storage::Storage, types::{BlockSubmission, FuelBlock}, }; -use storage::Postgres; use tokio::sync::mpsc::Receiver; use tracing::{error, info}; use super::Runner; -use crate::errors::Result; +use crate::Result; -pub struct BlockCommitter { +pub struct BlockCommitter { rx_block: Receiver, ethereum_rpc: A, storage: Db, diff --git a/committer/src/services/block_watcher.rs b/packages/services/src/block_watcher.rs similarity index 96% rename from committer/src/services/block_watcher.rs rename to packages/services/src/block_watcher.rs index 63cd8491..8f9c517a 100644 --- a/committer/src/services/block_watcher.rs +++ b/packages/services/src/block_watcher.rs @@ -1,15 +1,12 @@ use std::{num::NonZeroU32, vec}; use async_trait::async_trait; -use fuel_rpc::client::Client; -use metrics::RegistersMetrics; +use metrics::{Collector, IntGauge, Opts, RegistersMetrics}; use ports::{fuel_rpc::FuelAdapter, storage::Storage, types::FuelBlock}; -use prometheus::{core::Collector, IntGauge, Opts}; -use storage::Postgres; use tokio::sync::mpsc::Sender; use super::Runner; -use crate::errors::{Error, Result}; +use crate::{Error, Result}; struct Metrics { latest_fuel_block: IntGauge, @@ -33,7 +30,7 @@ impl Default for Metrics { } } -pub struct BlockWatcher { +pub struct BlockWatcher { fuel_adapter: A, tx_fuel_block: Sender, storage: Db, @@ -137,11 +134,11 @@ where mod tests { use std::{sync::Arc, vec}; + use metrics::{Metric, Registry}; use mockall::predicate::eq; use ports::{fuel_rpc::MockFuelAdapter, types::BlockSubmission}; - use prometheus::{proto::Metric, Registry}; use rand::Rng; - use storage::PostgresProcess; + use storage::{Postgres, PostgresProcess}; use super::*; diff --git a/committer/src/services/commit_listener.rs b/packages/services/src/commit_listener.rs similarity index 92% rename from committer/src/services/commit_listener.rs rename to packages/services/src/commit_listener.rs index 74fa5c0d..a81d3e9c 100644 --- a/committer/src/services/commit_listener.rs +++ b/packages/services/src/commit_listener.rs @@ -1,20 +1,17 @@ use async_trait::async_trait; -use eth_rpc::WsAdapter; use futures::{StreamExt, TryStreamExt}; -use metrics::RegistersMetrics; +use metrics::{Collector, IntGauge, Opts, RegistersMetrics}; use ports::{ eth_rpc::EthereumAdapter, storage::Storage, types::{EthHeight, FuelBlockCommittedOnEth}, }; -use prometheus::{IntGauge, Opts}; -use storage::Postgres; use tokio_util::sync::CancellationToken; use tracing::{error, info}; use super::Runner; -pub struct CommitListener { +pub struct CommitListener { ethereum_rpc: E, storage: Db, metrics: Metrics, @@ -37,7 +34,7 @@ where E: EthereumAdapter, Db: Storage, { - async fn determine_starting_eth_block(&mut self) -> crate::errors::Result { + async fn determine_starting_eth_block(&mut self) -> crate::Result { Ok(self .storage .submission_w_latest_block() @@ -48,7 +45,7 @@ where async fn handle_block_committed( &self, committed_on_eth: FuelBlockCommittedOnEth, - ) -> crate::errors::Result<()> { + ) -> crate::Result<()> { info!("block comitted on eth {committed_on_eth:?}"); let submission = self @@ -63,7 +60,7 @@ where Ok(()) } - fn log_if_error(result: crate::errors::Result<()>) { + fn log_if_error(result: crate::Result<()>) { if let Err(error) = result { error!("Received an error from block commit event stream: {error}"); } @@ -76,7 +73,7 @@ where E: EthereumAdapter, Db: Storage, { - async fn run(&mut self) -> crate::errors::Result<()> { + async fn run(&mut self) -> crate::Result<()> { let eth_block = self.determine_starting_eth_block().await?; self.ethereum_rpc @@ -99,7 +96,7 @@ struct Metrics { } impl RegistersMetrics for CommitListener { - fn metrics(&self) -> Vec> { + fn metrics(&self) -> Vec> { vec![Box::new(self.metrics.latest_committed_block.clone())] } } @@ -121,19 +118,18 @@ impl Default for Metrics { #[cfg(test)] mod tests { use futures::stream; - use metrics::RegistersMetrics; + use metrics::{Metric, RegistersMetrics, Registry}; use mockall::predicate; use ports::{ eth_rpc::{MockEthereumAdapter, MockEventStreamer}, storage::Storage, types::{BlockSubmission, EthHeight, FuelBlockCommittedOnEth, U256}, }; - use prometheus::{proto::Metric, Registry}; use rand::Rng; use storage::{Postgres, PostgresProcess}; use tokio_util::sync::CancellationToken; - use crate::services::{CommitListener, Runner}; + use crate::{CommitListener, Runner}; #[tokio::test] async fn listener_will_update_storage_if_event_is_emitted() { diff --git a/committer/src/services/health_reporter.rs b/packages/services/src/health_reporter.rs similarity index 100% rename from committer/src/services/health_reporter.rs rename to packages/services/src/health_reporter.rs diff --git a/packages/services/src/lib.rs b/packages/services/src/lib.rs new file mode 100644 index 00000000..1d9afe1c --- /dev/null +++ b/packages/services/src/lib.rs @@ -0,0 +1,54 @@ +#![deny(unused_crate_dependencies)] +mod block_committer; +mod block_watcher; +mod commit_listener; +mod health_reporter; +mod status_reporter; +mod wallet_balance_tracker; + +pub use block_committer::BlockCommitter; +pub use block_watcher::BlockWatcher; +pub use commit_listener::CommitListener; +pub use health_reporter::HealthReporter; +pub use status_reporter::StatusReporter; +pub use wallet_balance_tracker::WalletBalanceTracker; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("{0}")] + Other(String), + #[error("Network Error: {0}")] + Network(String), + #[error("Storage error: {0}")] + Storage(String), +} + +impl From for Error { + fn from(value: ports::eth_rpc::Error) -> Self { + match value { + ports::eth_rpc::Error::Network(e) => Self::Network(e), + _ => Self::Other(value.to_string()), + } + } +} + +impl From for Error { + fn from(value: ports::fuel_rpc::Error) -> Self { + match value { + ports::fuel_rpc::Error::Network(e) => Self::Network(e), + } + } +} + +impl From for Error { + fn from(error: ports::storage::Error) -> Self { + Self::Storage(error.to_string()) + } +} + +pub type Result = std::result::Result; + +#[async_trait::async_trait] +pub trait Runner: Send + Sync { + async fn run(&mut self) -> Result<()>; +} diff --git a/committer/src/services/status_reporter.rs b/packages/services/src/status_reporter.rs similarity index 96% rename from committer/src/services/status_reporter.rs rename to packages/services/src/status_reporter.rs index de0861c4..b6eaf97d 100644 --- a/committer/src/services/status_reporter.rs +++ b/packages/services/src/status_reporter.rs @@ -1,8 +1,7 @@ use ports::storage::Storage; use serde::Serialize; -use storage::Postgres; -use crate::errors::Result; +use crate::Result; #[derive(Debug, Serialize, Default, PartialEq, Eq)] pub struct StatusReport { @@ -16,7 +15,7 @@ pub enum Status { Committing, } -pub struct StatusReporter { +pub struct StatusReporter { storage: Db, } diff --git a/committer/src/services/wallet_balance_tracker.rs b/packages/services/src/wallet_balance_tracker.rs similarity index 90% rename from committer/src/services/wallet_balance_tracker.rs rename to packages/services/src/wallet_balance_tracker.rs index 6654e0e9..c5d25573 100644 --- a/committer/src/services/wallet_balance_tracker.rs +++ b/packages/services/src/wallet_balance_tracker.rs @@ -1,9 +1,8 @@ -use metrics::RegistersMetrics; +use metrics::{Collector, IntGauge, Opts, RegistersMetrics}; use ports::{eth_rpc::EthereumAdapter, types::U256}; -use prometheus::{IntGauge, Opts}; use super::Runner; -use crate::errors::Result; +use crate::Result; pub struct WalletBalanceTracker { eth_adapter: Box, @@ -31,7 +30,7 @@ impl WalletBalanceTracker { } impl RegistersMetrics for WalletBalanceTracker { - fn metrics(&self) -> Vec> { + fn metrics(&self) -> Vec> { self.metrics.metrics() } } @@ -42,7 +41,7 @@ struct Metrics { } impl RegistersMetrics for Metrics { - fn metrics(&self) -> Vec> { + fn metrics(&self) -> Vec> { vec![Box::new(self.eth_wallet_balance.clone())] } } @@ -69,8 +68,8 @@ impl Runner for WalletBalanceTracker { #[cfg(test)] mod tests { + use metrics::{Metric, Registry}; use ports::eth_rpc::MockEthereumAdapter; - use prometheus::{proto::Metric, Registry}; use super::*; From 8e21061ff4fb0e37104b69a7b31010d66511168a Mon Sep 17 00:00:00 2001 From: segfault_magnet Date: Mon, 6 May 2024 06:44:50 +0200 Subject: [PATCH 14/23] sort deps --- committer/Cargo.toml | 2 +- packages/services/Cargo.toml | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/committer/Cargo.toml b/committer/Cargo.toml index 64f29c6a..51e56201 100644 --- a/committer/Cargo.toml +++ b/committer/Cargo.toml @@ -21,11 +21,11 @@ eth_rpc = { workspace = true } fuel_rpc = { workspace = true } metrics = { workspace = true } ports = { workspace = true } -services = { workspace = true } prometheus = { workspace = true, default-features = false } serde = { workspace = true } serde_json = { workspace = true, default-features = false } +services = { workspace = true } storage = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true, features = [ diff --git a/packages/services/Cargo.toml b/packages/services/Cargo.toml index 8a4db011..bcce9a16 100644 --- a/packages/services/Cargo.toml +++ b/packages/services/Cargo.toml @@ -3,21 +3,20 @@ name = "services" version = "0.1.0" edition = "2021" publish = false - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] async-trait = { workspace = true } -tracing = { workspace = true } -tokio = { workspace = true, features = ["sync"] } +futures = { workspace = true } +metrics = { workspace = true } ports = { workspace = true, features = ["full"] } +serde = { workspace = true } thiserror = { workspace = true } -metrics = { workspace = true } -futures = { workspace = true } +tokio = { workspace = true, features = ["sync"] } tokio-util = { workspace = true } -serde = { workspace = true } +tracing = { workspace = true } [dev-dependencies] mockall = { workspace = true } -storage = { workspace = true, features = ["test-helpers"] } rand = { workspace = true } +storage = { workspace = true, features = ["test-helpers"] } From b4ae776eea67f6133ba1f5487c649c6d37785d1d Mon Sep 17 00:00:00 2001 From: segfault_magnet Date: Mon, 6 May 2024 06:56:18 +0200 Subject: [PATCH 15/23] cleanup cargo tomls --- Cargo.lock | 44 ++++++------------------------------ Cargo.toml | 23 +++++++++++++------ committer/Cargo.toml | 43 +++++++++++++---------------------- committer/src/setup.rs | 1 - e2e/Cargo.toml | 10 +++++--- packages/eth_rpc/Cargo.toml | 11 +++++---- packages/fuel_rpc/Cargo.toml | 11 +++++---- packages/metrics/Cargo.toml | 11 +++++---- packages/ports/Cargo.toml | 9 ++++++-- packages/services/Cargo.toml | 11 +++++---- packages/storage/Cargo.toml | 10 +++++--- 11 files changed, 88 insertions(+), 96 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index de0a7413..164f3f57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1185,7 +1185,7 @@ checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] name = "e2e" -version = "0.1.0" +version = "0.4.0" dependencies = [ "anyhow", "eth_rpc", @@ -1341,7 +1341,7 @@ dependencies = [ [[package]] name = "eth_rpc" -version = "0.1.0" +version = "0.4.0" dependencies = [ "async-trait", "ethers", @@ -1887,7 +1887,7 @@ dependencies = [ [[package]] name = "fuel_rpc" -version = "0.1.0" +version = "0.4.0" dependencies = [ "async-trait", "fuel-core-client", @@ -2772,7 +2772,7 @@ checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "metrics" -version = "0.1.0" +version = "0.4.0" dependencies = [ "prometheus", ] @@ -2847,16 +2847,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - [[package]] name = "num-bigint" version = "0.4.4" @@ -3004,12 +2994,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "p256" version = "0.13.2" @@ -3228,7 +3212,7 @@ checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" [[package]] name = "ports" -version = "0.1.0" +version = "0.4.0" dependencies = [ "async-trait", "ethers-core", @@ -4073,7 +4057,7 @@ dependencies = [ [[package]] name = "services" -version = "0.1.0" +version = "0.4.0" dependencies = [ "async-trait", "futures", @@ -4426,7 +4410,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "storage" -version = "0.1.0" +version = "0.4.0" dependencies = [ "async-trait", "hex", @@ -4962,17 +4946,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - [[package]] name = "tracing-serde" version = "0.1.3" @@ -4989,14 +4962,11 @@ version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ - "nu-ansi-term", "serde", "serde_json", "sharded-slab", - "smallvec", "thread_local", "tracing-core", - "tracing-log", "tracing-serde", ] diff --git a/Cargo.toml b/Cargo.toml index acdf8662..a8473ea3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,13 +11,22 @@ members = [ "packages/storage", ] +[workspace.package] +version = "0.4.0" +authors = ["Fuel Labs "] +edition = "2021" +homepage = "https://fuel.network/" +license = "Apache-2.0" +repository = "https://github.com/FuelLabs/fuel-block-committer" +publish = false + [workspace.dependencies] -eth_rpc = { version = "0.1.0", path = "./packages/eth_rpc", default-features = false } -fuel_rpc = { version = "0.1.0", path = "./packages/fuel_rpc", default-features = false } -metrics = { version = "0.1.0", path = "./packages/metrics", default-features = false } -ports = { version = "0.1.0", path = "./packages/ports", default-features = false } -storage = { version = "0.1.0", path = "./packages/storage", default-features = false } -services = { version = "0.1.0", path = "./packages/services", default-features = false } +eth_rpc = { path = "./packages/eth_rpc", default-features = false } +fuel_rpc = { path = "./packages/fuel_rpc", default-features = false } +metrics = { path = "./packages/metrics", default-features = false } +ports = { path = "./packages/ports", default-features = false } +storage = { path = "./packages/storage", default-features = false } +services = { path = "./packages/services", default-features = false } actix-web = { version = "4", default-features = false } anyhow = { version = "1.0", default-features = false } @@ -41,5 +50,5 @@ thiserror = { version = "1.0", default-features = false } tokio = { version = "1.37", default-features = false } tokio-util = { version = "0.7", default-features = false } tracing = { version = "0.1", default-features = false } -tracing-subscriber = { version = "0.3" } +tracing-subscriber = { version = "0.3", default-features = false } url = { version = "2.3", default-features = false } diff --git a/committer/Cargo.toml b/committer/Cargo.toml index 51e56201..d6aa5236 100644 --- a/committer/Cargo.toml +++ b/committer/Cargo.toml @@ -1,46 +1,35 @@ [package] -authors = ["Fuel Labs "] -edition = "2021" -homepage = "https://fuel.network/" -license = "Apache-2.0" -repository = "https://github.com/FuelLabs/fuel-block-committer" -rust-version = "1.77.0" -version = "0.4.0" name = "fuel-block-committer" +authors = { workspace = true } +edition = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +version = { workspace = true } +publish = { workspace = true } [dependencies] -actix-web = { workspace = true, default-features = false, features = [ - "macros", -] } +actix-web = { workspace = true, features = ["macros"] } clap = { workspace = true, features = ["derive"] } -config = { workspace = true, default-features = false, features = [ - "toml", - "async", -] } +config = { workspace = true, features = ["toml", "async"] } eth_rpc = { workspace = true } fuel_rpc = { workspace = true } metrics = { workspace = true } ports = { workspace = true } -prometheus = { workspace = true, default-features = false } +prometheus = { workspace = true } serde = { workspace = true } -serde_json = { workspace = true, default-features = false } +serde_json = { workspace = true } services = { workspace = true } storage = { workspace = true } thiserror = { workspace = true } -tokio = { workspace = true, features = [ - "rt-multi-thread", - "rt", - "macros", -], default-features = false } -tokio-util = { workspace = true, default-features = false } -tracing = { workspace = true, default-features = false } -tracing-subscriber = { workspace = true, features = ["json"] } +tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } +tokio-util = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true, features = ["fmt", "json"] } url = { workspace = true } [dev-dependencies] -anyhow = { workspace = true, default-features = false } +anyhow = { workspace = true } ports = { workspace = true, features = ["test-helpers"] } storage = { workspace = true, features = ["test-helpers"] } -#TODO: Once fuels-rs catches up tests will be reenabled -#fuels-test-helpers = "0.57" diff --git a/committer/src/setup.rs b/committer/src/setup.rs index eb6016ce..afc43ca8 100644 --- a/committer/src/setup.rs +++ b/committer/src/setup.rs @@ -180,7 +180,6 @@ fn create_block_watcher( pub fn setup_logger() { tracing_subscriber::fmt() .with_writer(std::io::stderr) - .with_ansi(false) .with_level(true) .with_line_number(true) .json() diff --git a/e2e/Cargo.toml b/e2e/Cargo.toml index 442a7e93..a0a0fb33 100644 --- a/e2e/Cargo.toml +++ b/e2e/Cargo.toml @@ -1,8 +1,12 @@ [package] name = "e2e" -version = "0.1.0" -edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +authors = { workspace = true } +edition = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +version = { workspace = true } +publish = { workspace = true } [dev-dependencies] anyhow = { workspace = true } diff --git a/packages/eth_rpc/Cargo.toml b/packages/eth_rpc/Cargo.toml index 0d36d5f4..44f6b50f 100644 --- a/packages/eth_rpc/Cargo.toml +++ b/packages/eth_rpc/Cargo.toml @@ -1,9 +1,12 @@ [package] name = "eth_rpc" -version = "0.1.0" -edition = "2021" -publish = false -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +authors = { workspace = true } +edition = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +version = { workspace = true } +publish = { workspace = true } [dependencies] async-trait = { workspace = true } diff --git a/packages/fuel_rpc/Cargo.toml b/packages/fuel_rpc/Cargo.toml index 68f6a908..2589ee30 100644 --- a/packages/fuel_rpc/Cargo.toml +++ b/packages/fuel_rpc/Cargo.toml @@ -1,9 +1,12 @@ [package] name = "fuel_rpc" -version = "0.1.0" -edition = "2021" -publish = false -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +authors = { workspace = true } +edition = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +version = { workspace = true } +publish = { workspace = true } [dependencies] async-trait = { workspace = true } diff --git a/packages/metrics/Cargo.toml b/packages/metrics/Cargo.toml index bcc68a5a..35bd210d 100644 --- a/packages/metrics/Cargo.toml +++ b/packages/metrics/Cargo.toml @@ -1,9 +1,12 @@ [package] name = "metrics" -version = "0.1.0" -edition = "2021" -publish = false -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +authors = { workspace = true } +edition = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +version = { workspace = true } +publish = { workspace = true } [dependencies] prometheus = { workspace = true } diff --git a/packages/ports/Cargo.toml b/packages/ports/Cargo.toml index 3477d09d..fba86ba1 100644 --- a/packages/ports/Cargo.toml +++ b/packages/ports/Cargo.toml @@ -1,7 +1,12 @@ [package] name = "ports" -version = "0.1.0" -edition = "2021" +authors = { workspace = true } +edition = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +version = { workspace = true } +publish = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/packages/services/Cargo.toml b/packages/services/Cargo.toml index bcce9a16..9e10926b 100644 --- a/packages/services/Cargo.toml +++ b/packages/services/Cargo.toml @@ -1,9 +1,12 @@ [package] name = "services" -version = "0.1.0" -edition = "2021" -publish = false -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +authors = { workspace = true } +edition = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +version = { workspace = true } +publish = { workspace = true } [dependencies] async-trait = { workspace = true } diff --git a/packages/storage/Cargo.toml b/packages/storage/Cargo.toml index 475f1fc2..b0bbd045 100644 --- a/packages/storage/Cargo.toml +++ b/packages/storage/Cargo.toml @@ -1,8 +1,12 @@ [package] name = "storage" -version = "0.1.0" -edition = "2021" -publish = false +authors = { workspace = true } +edition = { workspace = true } +homepage = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +version = { workspace = true } +publish = { workspace = true } [dependencies] async-trait = { workspace = true } From c2556723317ff6ee71e0b62e9ad5a11cc563b433 Mon Sep 17 00:00:00 2001 From: segfault_magnet Date: Mon, 6 May 2024 07:54:18 +0200 Subject: [PATCH 16/23] renaming. minimize prometheus dependencies --- Cargo.lock | 3 -- committer/Cargo.toml | 2 - committer/src/api.rs | 6 ++- committer/src/errors.rs | 12 ++--- committer/src/main.rs | 7 ++- committer/src/setup.rs | 45 +++++++++---------- e2e/Cargo.toml | 2 +- e2e/src/lib.rs | 6 +-- packages/eth_rpc/Cargo.toml | 5 +-- packages/eth_rpc/src/error.rs | 2 +- packages/eth_rpc/src/lib.rs | 25 ++++++----- packages/eth_rpc/src/metrics.rs | 8 ++-- packages/eth_rpc/src/websocket.rs | 29 +++++------- .../eth_rpc/src/websocket/event_streamer.rs | 6 +-- .../websocket/health_tracking_middleware.rs | 8 ++-- packages/fuel_rpc/Cargo.toml | 1 - packages/fuel_rpc/src/client.rs | 4 +- packages/fuel_rpc/src/lib.rs | 18 ++++---- packages/fuel_rpc/src/metrics.rs | 6 ++- packages/metrics/src/lib.rs | 6 +-- packages/ports/Cargo.toml | 4 +- packages/ports/src/lib.rs | 8 ++-- .../ports/src/ports/{fuel_rpc.rs => fuel.rs} | 2 +- .../ports/src/ports/{eth_rpc.rs => l1.rs} | 19 +++++--- packages/ports/src/types.rs | 12 ++--- packages/ports/src/types/block_submission.rs | 6 +-- packages/ports/src/types/eth_height.rs | 36 +++++++-------- ...n_eth.rs => fuel_block_committed_on_l1.rs} | 6 +-- packages/services/src/block_committer.rs | 17 ++++--- packages/services/src/block_watcher.rs | 19 ++++---- packages/services/src/commit_listener.rs | 39 +++++++++------- packages/services/src/lib.rs | 12 ++--- .../services/src/wallet_balance_tracker.rs | 39 +++++++++------- 33 files changed, 223 insertions(+), 197 deletions(-) rename packages/ports/src/ports/{fuel_rpc.rs => fuel.rs} (91%) rename packages/ports/src/ports/{eth_rpc.rs => l1.rs} (66%) rename packages/ports/src/types/{fuel_block_committed_on_eth.rs => fuel_block_committed_on_l1.rs} (75%) diff --git a/Cargo.lock b/Cargo.lock index 164f3f57..b7033bdc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1349,7 +1349,6 @@ dependencies = [ "metrics", "mockall", "ports", - "prometheus", "serde_json", "thiserror", "tokio", @@ -1719,7 +1718,6 @@ dependencies = [ "fuel_rpc", "metrics", "ports", - "prometheus", "serde", "serde_json", "services", @@ -1893,7 +1891,6 @@ dependencies = [ "fuel-core-client", "metrics", "ports", - "prometheus", "tokio", "url", ] diff --git a/committer/Cargo.toml b/committer/Cargo.toml index d6aa5236..6945d474 100644 --- a/committer/Cargo.toml +++ b/committer/Cargo.toml @@ -16,8 +16,6 @@ eth_rpc = { workspace = true } fuel_rpc = { workspace = true } metrics = { workspace = true } ports = { workspace = true } -prometheus = { workspace = true } - serde = { workspace = true } serde_json = { workspace = true } services = { workspace = true } diff --git a/committer/src/api.rs b/committer/src/api.rs index daa4a43b..2dd25a42 100644 --- a/committer/src/api.rs +++ b/committer/src/api.rs @@ -1,11 +1,13 @@ use std::sync::Arc; -use ::metrics::HealthChecker; +use ::metrics::{ + prometheus::{self, Encoder, Registry, TextEncoder}, + HealthChecker, +}; use actix_web::{ error::InternalError, get, http::StatusCode, web, App, HttpResponse, HttpServer, Responder, }; use ports::storage::Storage; -use prometheus::{Encoder, Registry, TextEncoder}; use services::{HealthReporter, StatusReporter}; use storage::Postgres; diff --git a/committer/src/errors.rs b/committer/src/errors.rs index e69fa299..da6716b4 100644 --- a/committer/src/errors.rs +++ b/committer/src/errors.rs @@ -35,19 +35,19 @@ impl From for Error { } } -impl From for Error { - fn from(value: ports::eth_rpc::Error) -> Self { +impl From for Error { + fn from(value: ports::l1::Error) -> Self { match value { - ports::eth_rpc::Error::Network(e) => Self::Network(e), + ports::l1::Error::Network(e) => Self::Network(e), _ => Self::Other(value.to_string()), } } } -impl From for Error { - fn from(value: ports::fuel_rpc::Error) -> Self { +impl From for Error { + fn from(value: ports::fuel::Error) -> Self { match value { - ports::fuel_rpc::Error::Network(e) => Self::Network(e), + ports::fuel::Error::Network(e) => Self::Network(e), } } } diff --git a/committer/src/main.rs b/committer/src/main.rs index eedbc06b..8e414a90 100644 --- a/committer/src/main.rs +++ b/committer/src/main.rs @@ -7,7 +7,7 @@ mod setup; use api::launch_api_server; use config::InternalConfig; use errors::Result; -use prometheus::Registry; +use metrics::prometheus::Registry; use setup::{ create_eth_adapter, setup_logger, setup_storage, spawn_block_watcher, spawn_eth_committer_and_listener, spawn_wallet_balance_tracker, @@ -16,6 +16,11 @@ use tokio_util::sync::CancellationToken; use crate::setup::shut_down; +pub type ContractRpc = eth_rpc::WebsocketClient; +pub type Database = storage::Postgres; +pub type L1Api = eth_rpc::WebsocketClient; +pub type FuelApi = fuel_rpc::client::Client; + #[tokio::main] async fn main() -> Result<()> { setup_logger(); diff --git a/committer/src/setup.rs b/committer/src/setup.rs index afc43ca8..50cb86aa 100644 --- a/committer/src/setup.rs +++ b/committer/src/setup.rs @@ -1,12 +1,8 @@ use std::time::Duration; -use eth_rpc::WsAdapter; -use fuel_rpc::client::Client; -use metrics::{HealthChecker, RegistersMetrics}; +use metrics::{prometheus::Registry, HealthChecker, RegistersMetrics}; use ports::{storage::Storage, types::FuelBlock}; -use prometheus::Registry; use services::{BlockCommitter, BlockWatcher, CommitListener, Runner, WalletBalanceTracker}; -use storage::Postgres; use tokio::{sync::mpsc::Receiver, task::JoinHandle}; use tokio_util::sync::CancellationToken; use tracing::{error, info}; @@ -14,12 +10,13 @@ use tracing::{error, info}; use crate::{ config::{Config, InternalConfig}, errors::Result, + ContractRpc, Database, FuelApi, }; pub fn spawn_block_watcher( config: &Config, internal_config: &InternalConfig, - storage: Postgres, + storage: Database, registry: &Registry, cancel_token: CancellationToken, ) -> ( @@ -45,10 +42,10 @@ pub fn spawn_block_watcher( pub fn spawn_wallet_balance_tracker( internal_config: &InternalConfig, registry: &Registry, - ethereum_rpc: WsAdapter, + ethereum_api: ContractRpc, cancel_token: CancellationToken, ) -> tokio::task::JoinHandle<()> { - let wallet_balance_tracker = WalletBalanceTracker::new(ethereum_rpc); + let wallet_balance_tracker = WalletBalanceTracker::new(ethereum_api); wallet_balance_tracker.register_metrics(registry); @@ -63,15 +60,15 @@ pub fn spawn_wallet_balance_tracker( pub fn spawn_eth_committer_and_listener( internal_config: &InternalConfig, rx_fuel_block: Receiver, - ethereum_rpc: WsAdapter, - storage: Postgres, + ethereum_api: ContractRpc, + storage: Database, registry: &Registry, cancel_token: CancellationToken, ) -> (tokio::task::JoinHandle<()>, tokio::task::JoinHandle<()>) { let committer_handler = - create_block_committer(rx_fuel_block, ethereum_rpc.clone(), storage.clone()); + create_block_committer(rx_fuel_block, ethereum_api.clone(), storage.clone()); - let commit_listener = CommitListener::new(ethereum_rpc, storage, cancel_token.clone()); + let commit_listener = CommitListener::new(ethereum_api, storage, cancel_token.clone()); commit_listener.register_metrics(registry); let listener_handle = schedule_polling( @@ -86,10 +83,10 @@ pub fn spawn_eth_committer_and_listener( fn create_block_committer( rx_fuel_block: Receiver, - ethereum_rpc: WsAdapter, + ethereum_api: ContractRpc, storage: impl Storage + 'static, ) -> tokio::task::JoinHandle<()> { - let mut block_committer = BlockCommitter::new(rx_fuel_block, ethereum_rpc, storage); + let mut block_committer = BlockCommitter::new(rx_fuel_block, ethereum_api, storage); tokio::spawn(async move { block_committer .run() @@ -102,8 +99,8 @@ pub async fn create_eth_adapter( config: &Config, internal_config: &InternalConfig, registry: &Registry, -) -> Result<(WsAdapter, HealthChecker)> { - let ws_adapter = WsAdapter::connect( +) -> Result<(ContractRpc, HealthChecker)> { + let ws_adapter = ContractRpc::connect( &config.eth.rpc, config.eth.chain_id, config.eth.state_contract_address, @@ -147,8 +144,8 @@ fn create_fuel_adapter( config: &Config, internal_config: &InternalConfig, registry: &Registry, -) -> (Client, HealthChecker) { - let fuel_adapter = Client::new( +) -> (FuelApi, HealthChecker) { + let fuel_adapter = FuelApi::new( &config.fuel.graphql_endpoint, internal_config.fuel_errors_before_unhealthy, ); @@ -162,9 +159,9 @@ fn create_fuel_adapter( fn create_block_watcher( config: &Config, registry: &Registry, - fuel_adapter: Client, - storage: Postgres, -) -> (BlockWatcher, Receiver) { + fuel_adapter: FuelApi, + storage: Database, +) -> (BlockWatcher, Receiver) { let (tx_fuel_block, rx_fuel_block) = tokio::sync::mpsc::channel(100); let block_watcher = BlockWatcher::new( config.eth.commit_interval, @@ -186,8 +183,8 @@ pub fn setup_logger() { .init(); } -pub async fn setup_storage(config: &Config) -> Result { - let postgres = Postgres::connect(&config.app.db).await?; +pub async fn setup_storage(config: &Config) -> Result { + let postgres = Database::connect(&config.app.db).await?; postgres.migrate().await?; Ok(postgres) @@ -199,7 +196,7 @@ pub async fn shut_down( wallet_balance_tracker_handle: JoinHandle<()>, committer_handle: JoinHandle<()>, listener_handle: JoinHandle<()>, - storage: Postgres, + storage: Database, ) -> Result<()> { cancel_token.cancel(); diff --git a/e2e/Cargo.toml b/e2e/Cargo.toml index a0a0fb33..654e5210 100644 --- a/e2e/Cargo.toml +++ b/e2e/Cargo.toml @@ -12,5 +12,5 @@ publish = { workspace = true } anyhow = { workspace = true } eth_rpc = { workspace = true, features = ["test-helpers"] } fuel_rpc = { workspace = true, features = ["test-helpers"] } -ports = { workspace = true, features = ["fuel", "eth"] } +ports = { workspace = true, features = ["fuel", "l1"] } tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } diff --git a/e2e/src/lib.rs b/e2e/src/lib.rs index 4101cf82..c3b3ff5c 100644 --- a/e2e/src/lib.rs +++ b/e2e/src/lib.rs @@ -3,9 +3,9 @@ mod tests { use std::time::Duration; use anyhow::Result; - use eth_rpc::{Chain, WsAdapter}; + use eth_rpc::{Chain, WebsocketClient}; use fuel_rpc::client::Client; - use ports::fuel_rpc::FuelAdapter; + use ports::fuel::Api; const FUEL_NODE_PORT: u16 = 4000; @@ -14,7 +14,7 @@ mod tests { let fuel_node_address = format!("http://localhost:{FUEL_NODE_PORT}"); let provider = Client::new(&fuel_node_address.parse()?, 10); - let fuel_contract = WsAdapter::connect( + let fuel_contract = WebsocketClient::connect( &"ws://localhost:8089".parse()?, Chain::AnvilHardhat, "0xdAad669b06d79Cb48C8cfef789972436dBe6F24d".parse()?, diff --git a/packages/eth_rpc/Cargo.toml b/packages/eth_rpc/Cargo.toml index 44f6b50f..de123340 100644 --- a/packages/eth_rpc/Cargo.toml +++ b/packages/eth_rpc/Cargo.toml @@ -13,8 +13,7 @@ async-trait = { workspace = true } ethers = { workspace = true, features = ["ws"] } futures = { workspace = true } metrics = { workspace = true } -ports = { workspace = true, features = ["eth"] } -prometheus = { workspace = true } +ports = { workspace = true, features = ["l1"] } serde_json = { workspace = true } thiserror = { workspace = true } tracing = { workspace = true } @@ -22,7 +21,7 @@ url = { workspace = true } [dev-dependencies] mockall = { workspace = true } -ports = { workspace = true, features = ["eth"] } +ports = { workspace = true, features = ["l1"] } tokio = { workspace = true, features = ["macros"] } [features] diff --git a/packages/eth_rpc/src/error.rs b/packages/eth_rpc/src/error.rs index 704e723d..29cb2188 100644 --- a/packages/eth_rpc/src/error.rs +++ b/packages/eth_rpc/src/error.rs @@ -35,7 +35,7 @@ impl From for Error { pub(crate) type Result = std::result::Result; -impl From for ports::eth_rpc::Error { +impl From for ports::l1::Error { fn from(err: Error) -> Self { match err { Error::Network(err) => Self::Network(err), diff --git a/packages/eth_rpc/src/lib.rs b/packages/eth_rpc/src/lib.rs index 0eedbffd..161f50ff 100644 --- a/packages/eth_rpc/src/lib.rs +++ b/packages/eth_rpc/src/lib.rs @@ -4,7 +4,7 @@ use std::pin::Pin; use async_trait::async_trait; use ethers::types::U256; use futures::{stream::TryStreamExt, Stream}; -use ports::types::{EthHeight, FuelBlockCommittedOnEth}; +use ports::types::{FuelBlockCommittedOnL1, L1Height}; use websocket::EthEventStreamer; mod error; @@ -12,39 +12,42 @@ mod metrics; mod websocket; pub use ethers::types::{Address, Chain}; -pub use websocket::WsAdapter; +pub use websocket::WebsocketClient; #[async_trait] -impl ports::eth_rpc::EthereumAdapter for WsAdapter { - async fn submit(&self, block: ports::types::FuelBlock) -> ports::eth_rpc::Result<()> { +impl ports::l1::Contract for WebsocketClient { + async fn submit(&self, block: ports::types::FuelBlock) -> ports::l1::Result<()> { self.submit(block).await } - async fn get_block_number(&self) -> ports::eth_rpc::Result { + async fn get_block_number(&self) -> ports::l1::Result { let block_num = self.get_block_number().await?; - let height = EthHeight::try_from(block_num)?; + let height = L1Height::try_from(block_num)?; Ok(height) } fn event_streamer( &self, eth_block_height: u64, - ) -> Box { + ) -> Box { let stream = self.event_streamer(eth_block_height); Box::new(stream) } +} - async fn balance(&self) -> ports::eth_rpc::Result { +#[async_trait] +impl ports::l1::Api for WebsocketClient { + async fn balance(&self) -> ports::l1::Result { Ok(self.balance().await?) } } #[async_trait::async_trait] -impl ports::eth_rpc::EventStreamer for EthEventStreamer { +impl ports::l1::EventStreamer for EthEventStreamer { async fn establish_stream( &self, - ) -> ports::eth_rpc::Result< - Pin> + '_ + Send>>, + ) -> ports::l1::Result< + Pin> + '_ + Send>>, > { let stream = self.establish_stream().await?.map_err(Into::into); Ok(Box::pin(stream)) diff --git a/packages/eth_rpc/src/metrics.rs b/packages/eth_rpc/src/metrics.rs index 45a855bc..95527198 100644 --- a/packages/eth_rpc/src/metrics.rs +++ b/packages/eth_rpc/src/metrics.rs @@ -1,5 +1,7 @@ -use metrics::RegistersMetrics; -use prometheus::{IntCounter, Opts}; +use metrics::{ + prometheus::{core::Collector, IntCounter, Opts}, + RegistersMetrics, +}; #[derive(Clone)] pub struct Metrics { @@ -7,7 +9,7 @@ pub struct Metrics { } impl RegistersMetrics for Metrics { - fn metrics(&self) -> Vec> { + fn metrics(&self) -> Vec> { vec![Box::new(self.eth_network_errors.clone())] } } diff --git a/packages/eth_rpc/src/websocket.rs b/packages/eth_rpc/src/websocket.rs index bfda30cd..e84718f8 100644 --- a/packages/eth_rpc/src/websocket.rs +++ b/packages/eth_rpc/src/websocket.rs @@ -1,9 +1,9 @@ use std::num::NonZeroU32; use ethers::types::{Address, Chain}; -use metrics::{HealthChecker, RegistersMetrics}; +use metrics::{prometheus::core::Collector, HealthChecker, RegistersMetrics}; use ports::{ - eth_rpc::Result, + l1::Result, types::{FuelBlock, U256}, }; use url::Url; @@ -19,27 +19,22 @@ mod event_streamer; mod health_tracking_middleware; #[derive(Clone)] -pub struct WsAdapter { +pub struct WebsocketClient { inner: HealthTrackingMiddleware, } -impl WsAdapter { +impl WebsocketClient { pub async fn connect( - ethereum_rpc: &Url, + url: &Url, chain_id: Chain, contract_address: Address, - ethereum_wallet_key: &str, + wallet_key: &str, commit_interval: NonZeroU32, unhealthy_after_n_errors: usize, - ) -> ports::eth_rpc::Result { - let provider = WsConnection::connect( - ethereum_rpc, - chain_id, - contract_address, - ethereum_wallet_key, - commit_interval, - ) - .await?; + ) -> ports::l1::Result { + let provider = + WsConnection::connect(url, chain_id, contract_address, wallet_key, commit_interval) + .await?; Ok(Self { inner: HealthTrackingMiddleware::new(provider, unhealthy_after_n_errors), @@ -82,8 +77,8 @@ impl WsAdapter { } // User responsible for registering any metrics T might have -impl RegistersMetrics for WsAdapter { - fn metrics(&self) -> Vec> { +impl RegistersMetrics for WebsocketClient { + fn metrics(&self) -> Vec> { self.inner.metrics() } } diff --git a/packages/eth_rpc/src/websocket/event_streamer.rs b/packages/eth_rpc/src/websocket/event_streamer.rs index b98cbfed..a3629b4d 100644 --- a/packages/eth_rpc/src/websocket/event_streamer.rs +++ b/packages/eth_rpc/src/websocket/event_streamer.rs @@ -6,7 +6,7 @@ use ethers::{ signers::Wallet, }; use futures::{Stream, TryStreamExt}; -use ports::types::FuelBlockCommittedOnEth; +use ports::types::FuelBlockCommittedOnL1; use super::connection::CommitSubmittedFilter; use crate::error::Result; @@ -28,13 +28,13 @@ impl EthEventStreamer { pub(crate) async fn establish_stream( &self, - ) -> Result> + Send + '_> { + ) -> Result> + Send + '_> { let events = self.events.subscribe().await?; let stream = events .map_ok(|event| { let fuel_block_hash = event.block_hash; let commit_height = event.commit_height; - FuelBlockCommittedOnEth { + FuelBlockCommittedOnL1 { fuel_block_hash, commit_height, } diff --git a/packages/eth_rpc/src/websocket/health_tracking_middleware.rs b/packages/eth_rpc/src/websocket/health_tracking_middleware.rs index 4f77e7e1..be0dc690 100644 --- a/packages/eth_rpc/src/websocket/health_tracking_middleware.rs +++ b/packages/eth_rpc/src/websocket/health_tracking_middleware.rs @@ -1,4 +1,6 @@ -use metrics::{ConnectionHealthTracker, HealthChecker, RegistersMetrics}; +use metrics::{ + prometheus::core::Collector, ConnectionHealthTracker, HealthChecker, RegistersMetrics, +}; use ports::types::{FuelBlock, U256}; use crate::{ @@ -54,7 +56,7 @@ impl HealthTrackingMiddleware { // User responsible for registering any metrics T might have impl RegistersMetrics for HealthTrackingMiddleware { - fn metrics(&self) -> Vec> { + fn metrics(&self) -> Vec> { self.metrics.metrics() } } @@ -99,7 +101,7 @@ where #[cfg(test)] mod tests { - use prometheus::{proto::Metric, Registry}; + use metrics::prometheus::{proto::Metric, Registry}; use super::*; diff --git a/packages/fuel_rpc/Cargo.toml b/packages/fuel_rpc/Cargo.toml index 2589ee30..fe18c507 100644 --- a/packages/fuel_rpc/Cargo.toml +++ b/packages/fuel_rpc/Cargo.toml @@ -13,7 +13,6 @@ async-trait = { workspace = true } fuel-core-client = { workspace = true } metrics = { workspace = true } ports = { workspace = true, features = ["fuel"] } -prometheus = { workspace = true } url = { workspace = true } [dev-dependencies] diff --git a/packages/fuel_rpc/src/client.rs b/packages/fuel_rpc/src/client.rs index b6f6b54e..76371d75 100644 --- a/packages/fuel_rpc/src/client.rs +++ b/packages/fuel_rpc/src/client.rs @@ -1,5 +1,7 @@ use fuel_core_client::client::{types::Block, FuelClient as GqlClient}; -use metrics::{Collector, ConnectionHealthTracker, HealthChecker, RegistersMetrics}; +use metrics::{ + prometheus::core::Collector, ConnectionHealthTracker, HealthChecker, RegistersMetrics, +}; use url::Url; use crate::{metrics::Metrics, Error, Result}; diff --git a/packages/fuel_rpc/src/lib.rs b/packages/fuel_rpc/src/lib.rs index 404e7add..c7f0bb85 100644 --- a/packages/fuel_rpc/src/lib.rs +++ b/packages/fuel_rpc/src/lib.rs @@ -4,8 +4,8 @@ use ports::types::FuelBlock; pub mod client; pub mod metrics; -type Error = ports::fuel_rpc::Error; -type Result = ports::fuel_rpc::Result; +type Error = ports::fuel::Error; +type Result = ports::fuel::Result; fn convert_block(block: Block) -> FuelBlock { FuelBlock { @@ -15,12 +15,12 @@ fn convert_block(block: Block) -> FuelBlock { } #[async_trait::async_trait] -impl ports::fuel_rpc::FuelAdapter for client::Client { - async fn block_at_height(&self, height: u32) -> ports::fuel_rpc::Result> { +impl ports::fuel::Api for client::Client { + async fn block_at_height(&self, height: u32) -> ports::fuel::Result> { Ok(self._block_at_height(height).await?.map(convert_block)) } - async fn latest_block(&self) -> ports::fuel_rpc::Result { + async fn latest_block(&self) -> ports::fuel::Result { let block = self._latest_block().await?; Ok(convert_block(block)) } @@ -28,9 +28,11 @@ impl ports::fuel_rpc::FuelAdapter for client::Client { #[cfg(test)] mod tests { - use ::metrics::RegistersMetrics; - use ports::fuel_rpc::FuelAdapter; - use prometheus::{proto::Metric, Registry}; + use ::metrics::{ + prometheus::{proto::Metric, Registry}, + RegistersMetrics, + }; + use ports::fuel::Api; use url::Url; use super::*; diff --git a/packages/fuel_rpc/src/metrics.rs b/packages/fuel_rpc/src/metrics.rs index c5417e43..3984648a 100644 --- a/packages/fuel_rpc/src/metrics.rs +++ b/packages/fuel_rpc/src/metrics.rs @@ -1,5 +1,7 @@ -use metrics::{Collector, RegistersMetrics}; -use prometheus::{IntCounter, Opts}; +use metrics::{ + prometheus::{core::Collector, IntCounter, Opts}, + RegistersMetrics, +}; pub struct Metrics { pub fuel_network_errors: IntCounter, diff --git a/packages/metrics/src/lib.rs b/packages/metrics/src/lib.rs index 16cdaece..f5eb758b 100644 --- a/packages/metrics/src/lib.rs +++ b/packages/metrics/src/lib.rs @@ -7,10 +7,10 @@ pub trait HealthCheck: Send + Sync { fn healthy(&self) -> bool; } -pub use prometheus::{core::Collector, proto::Metric, IntGauge, Opts, Registry}; +pub use prometheus; pub trait RegistersMetrics { - fn register_metrics(&self, registry: &Registry) { + fn register_metrics(&self, registry: &crate::prometheus::Registry) { self.metrics().into_iter().for_each(|metric| { registry .register(metric) @@ -18,5 +18,5 @@ pub trait RegistersMetrics { }); } - fn metrics(&self) -> Vec>; + fn metrics(&self) -> Vec>; } diff --git a/packages/ports/Cargo.toml b/packages/ports/Cargo.toml index fba86ba1..1178bdfc 100644 --- a/packages/ports/Cargo.toml +++ b/packages/ports/Cargo.toml @@ -21,7 +21,7 @@ thiserror = { workspace = true, optional = true } [features] test-helpers = ["dep:mockall", "dep:rand"] -eth = ["dep:ethers-core", "dep:futures", "dep:thiserror", "dep:async-trait"] +l1 = ["dep:ethers-core", "dep:futures", "dep:thiserror", "dep:async-trait"] fuel = ["dep:thiserror", "dep:async-trait"] storage = ["dep:impl-tools", "dep:thiserror", "dep:async-trait"] -full = ["eth", "fuel", "storage"] +full = ["l1", "fuel", "storage"] diff --git a/packages/ports/src/lib.rs b/packages/ports/src/lib.rs index 279cc885..205d7efa 100644 --- a/packages/ports/src/lib.rs +++ b/packages/ports/src/lib.rs @@ -1,14 +1,14 @@ mod ports { - #[cfg(feature = "eth")] - pub mod eth_rpc; + #[cfg(feature = "l1")] + pub mod l1; #[cfg(feature = "fuel")] - pub mod fuel_rpc; + pub mod fuel; #[cfg(feature = "storage")] pub mod storage; } -#[cfg(any(feature = "eth", feature = "fuel", feature = "storage"))] +#[cfg(any(feature = "l1", feature = "fuel", feature = "storage"))] pub use ports::*; pub mod types; diff --git a/packages/ports/src/ports/fuel_rpc.rs b/packages/ports/src/ports/fuel.rs similarity index 91% rename from packages/ports/src/ports/fuel_rpc.rs rename to packages/ports/src/ports/fuel.rs index 98cbe11f..bbda896f 100644 --- a/packages/ports/src/ports/fuel_rpc.rs +++ b/packages/ports/src/ports/fuel.rs @@ -10,7 +10,7 @@ pub type Result = std::result::Result; #[cfg_attr(feature = "test-helpers", mockall::automock)] #[async_trait::async_trait] -pub trait FuelAdapter: Send + Sync { +pub trait Api: Send + Sync { async fn block_at_height(&self, height: u32) -> Result>; async fn latest_block(&self) -> Result; } diff --git a/packages/ports/src/ports/eth_rpc.rs b/packages/ports/src/ports/l1.rs similarity index 66% rename from packages/ports/src/ports/eth_rpc.rs rename to packages/ports/src/ports/l1.rs index aa67b7ac..d3014fe7 100644 --- a/packages/ports/src/ports/eth_rpc.rs +++ b/packages/ports/src/ports/l1.rs @@ -1,6 +1,6 @@ use std::pin::Pin; -use crate::types::{EthHeight, FuelBlock, FuelBlockCommittedOnEth, InvalidEthHeight, Stream, U256}; +use crate::types::{FuelBlock, FuelBlockCommittedOnL1, InvalidL1Height, L1Height, Stream, U256}; #[derive(Debug, thiserror::Error)] pub enum Error { @@ -12,25 +12,30 @@ pub enum Error { pub type Result = std::result::Result; -impl From for Error { - fn from(err: InvalidEthHeight) -> Self { +impl From for Error { + fn from(err: InvalidL1Height) -> Self { Self::Other(err.to_string()) } } #[cfg_attr(feature = "test-helpers", mockall::automock)] #[async_trait::async_trait] -pub trait EthereumAdapter: Send + Sync { +pub trait Contract: Send + Sync { async fn submit(&self, block: FuelBlock) -> Result<()>; - async fn get_block_number(&self) -> Result; - async fn balance(&self) -> Result; + async fn get_block_number(&self) -> Result; fn event_streamer(&self, eth_block_height: u64) -> Box; } +#[cfg_attr(feature = "test-helpers", mockall::automock)] +#[async_trait::async_trait] +pub trait Api { + async fn balance(&self) -> Result; +} + #[cfg_attr(feature = "test-helpers", mockall::automock)] #[async_trait::async_trait] pub trait EventStreamer { async fn establish_stream<'a>( &'a self, - ) -> Result> + 'a + Send>>>; + ) -> Result> + 'a + Send>>>; } diff --git a/packages/ports/src/types.rs b/packages/ports/src/types.rs index a648bb94..ede28015 100644 --- a/packages/ports/src/types.rs +++ b/packages/ports/src/types.rs @@ -1,16 +1,16 @@ -#[cfg(feature = "eth")] +#[cfg(feature = "l1")] pub use ethers_core::types::{H160, U256}; -#[cfg(feature = "eth")] +#[cfg(feature = "l1")] pub use futures::Stream; mod block_submission; mod eth_height; mod fuel_block; -#[cfg(feature = "eth")] -mod fuel_block_committed_on_eth; +#[cfg(feature = "l1")] +mod fuel_block_committed_on_l1; pub use block_submission::*; pub use eth_height::*; pub use fuel_block::*; -#[cfg(feature = "eth")] -pub use fuel_block_committed_on_eth::*; +#[cfg(feature = "l1")] +pub use fuel_block_committed_on_l1::*; diff --git a/packages/ports/src/types/block_submission.rs b/packages/ports/src/types/block_submission.rs index 8eb2d0ce..8eb7c736 100644 --- a/packages/ports/src/types/block_submission.rs +++ b/packages/ports/src/types/block_submission.rs @@ -1,12 +1,12 @@ -use crate::types::{EthHeight, FuelBlock}; +use crate::types::{FuelBlock, L1Height}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct BlockSubmission { pub block: FuelBlock, pub completed: bool, - // Eth block height moments before submitting the fuel block. Used to filter stale events in + // L1 block height moments before submitting the fuel block. Used to filter stale events in // the commit listener. - pub submittal_height: EthHeight, + pub submittal_height: L1Height, } #[cfg(feature = "test-helpers")] diff --git a/packages/ports/src/types/eth_height.rs b/packages/ports/src/types/eth_height.rs index d74eac20..4a54238f 100644 --- a/packages/ports/src/types/eth_height.rs +++ b/packages/ports/src/types/eth_height.rs @@ -1,31 +1,31 @@ #[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd)] -pub struct EthHeight { +pub struct L1Height { height: i64, } #[derive(Debug, Clone)] -pub struct InvalidEthHeight(String); -impl std::fmt::Display for InvalidEthHeight { +pub struct InvalidL1Height(String); +impl std::fmt::Display for InvalidL1Height { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "Invalid eth height: {}", self.0) + write!(f, "Invalid l1 height: {}", self.0) } } -impl std::error::Error for InvalidEthHeight {} +impl std::error::Error for InvalidL1Height {} #[cfg(feature = "test-helpers")] -impl rand::distributions::Distribution for rand::distributions::Standard { - fn sample(&self, rng: &mut R) -> EthHeight { +impl rand::distributions::Distribution for rand::distributions::Standard { + fn sample(&self, rng: &mut R) -> L1Height { let height: i64 = rng.gen_range(0..=i64::MAX); height.try_into().expect("Must be valid EthHeight") } } -impl TryFrom for EthHeight { - type Error = InvalidEthHeight; +impl TryFrom for L1Height { + type Error = InvalidL1Height; fn try_from(height: i64) -> Result { if height < 0 { - return Err(InvalidEthHeight(format!( + return Err(InvalidL1Height(format!( "must be non-negative, got {height}", ))); } @@ -33,11 +33,11 @@ impl TryFrom for EthHeight { } } -impl TryFrom for EthHeight { - type Error = InvalidEthHeight; +impl TryFrom for L1Height { + type Error = InvalidL1Height; fn try_from(height: u64) -> Result { if height >= i64::MAX as u64 { - return Err(InvalidEthHeight(format!( + return Err(InvalidL1Height(format!( "{height} too large. DB can handle at most {}", i64::MAX ))); @@ -48,7 +48,7 @@ impl TryFrom for EthHeight { } } -impl From for EthHeight { +impl From for L1Height { fn from(height: u32) -> Self { Self { height: i64::from(height), @@ -56,14 +56,14 @@ impl From for EthHeight { } } -impl From for i64 { - fn from(height: EthHeight) -> Self { +impl From for i64 { + fn from(height: L1Height) -> Self { height.height } } -impl From for u64 { - fn from(height: EthHeight) -> Self { +impl From for u64 { + fn from(height: L1Height) -> Self { height.height as Self } } diff --git a/packages/ports/src/types/fuel_block_committed_on_eth.rs b/packages/ports/src/types/fuel_block_committed_on_l1.rs similarity index 75% rename from packages/ports/src/types/fuel_block_committed_on_eth.rs rename to packages/ports/src/types/fuel_block_committed_on_l1.rs index 869622e8..fe04df52 100644 --- a/packages/ports/src/types/fuel_block_committed_on_eth.rs +++ b/packages/ports/src/types/fuel_block_committed_on_l1.rs @@ -1,18 +1,18 @@ use crate::types::U256; #[derive(Clone, Copy)] -pub struct FuelBlockCommittedOnEth { +pub struct FuelBlockCommittedOnL1 { pub fuel_block_hash: [u8; 32], pub commit_height: U256, } -impl std::fmt::Debug for FuelBlockCommittedOnEth { +impl std::fmt::Debug for FuelBlockCommittedOnL1 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let hash = self .fuel_block_hash .map(|byte| format!("{byte:02x?}")) .join(""); - f.debug_struct("FuelBlockCommittedOnEth") + f.debug_struct("FuelBlockCommittedOnL1") .field("hash", &hash) .field("commit_height", &self.commit_height) .finish() diff --git a/packages/services/src/block_committer.rs b/packages/services/src/block_committer.rs index 2dae07df..25a53697 100644 --- a/packages/services/src/block_committer.rs +++ b/packages/services/src/block_committer.rs @@ -1,6 +1,5 @@ use async_trait::async_trait; use ports::{ - eth_rpc::EthereumAdapter, storage::Storage, types::{BlockSubmission, FuelBlock}, }; @@ -10,9 +9,9 @@ use tracing::{error, info}; use super::Runner; use crate::Result; -pub struct BlockCommitter { +pub struct BlockCommitter { rx_block: Receiver, - ethereum_rpc: A, + ethereum_rpc: C, storage: Db, } @@ -32,7 +31,7 @@ impl BlockCommitter { impl BlockCommitter where - A: EthereumAdapter, + A: ports::l1::Contract, Db: Storage, { async fn submit_block(&self, fuel_block: FuelBlock) -> Result<()> { @@ -56,7 +55,7 @@ where #[async_trait] impl Runner for BlockCommitter where - A: EthereumAdapter, + A: ports::l1::Contract, Db: Storage, { async fn run(&mut self) -> Result<()> { @@ -79,7 +78,7 @@ mod tests { use std::time::Duration; use mockall::predicate; - use ports::eth_rpc::MockEthereumAdapter; + use ports::l1::MockContract; use rand::Rng; use storage::PostgresProcess; @@ -105,8 +104,8 @@ mod tests { assert_eq!(expeted_height, last_submission.block.height); } - fn given_eth_rpc_that_expects(block: FuelBlock) -> MockEthereumAdapter { - let mut eth_rpc_mock = MockEthereumAdapter::new(); + fn given_eth_rpc_that_expects(block: FuelBlock) -> MockContract { + let mut eth_rpc_mock = MockContract::new(); eth_rpc_mock .expect_submit() .with(predicate::eq(block)) @@ -121,7 +120,7 @@ mod tests { async fn spawn_committer_and_run_until_timeout( rx: Receiver, - eth_rpc_mock: MockEthereumAdapter, + eth_rpc_mock: MockContract, storage: Db, ) { let _ = tokio::time::timeout(Duration::from_millis(250), async move { diff --git a/packages/services/src/block_watcher.rs b/packages/services/src/block_watcher.rs index 8f9c517a..a7d85bf0 100644 --- a/packages/services/src/block_watcher.rs +++ b/packages/services/src/block_watcher.rs @@ -1,8 +1,11 @@ use std::{num::NonZeroU32, vec}; use async_trait::async_trait; -use metrics::{Collector, IntGauge, Opts, RegistersMetrics}; -use ports::{fuel_rpc::FuelAdapter, storage::Storage, types::FuelBlock}; +use metrics::{ + prometheus::{core::Collector, IntGauge, Opts}, + RegistersMetrics, +}; +use ports::{storage::Storage, types::FuelBlock}; use tokio::sync::mpsc::Sender; use super::Runner; @@ -56,7 +59,7 @@ impl BlockWatcher { } impl BlockWatcher where - A: FuelAdapter, + A: ports::fuel::Api, Db: Storage, { async fn fetch_latest_block(&self) -> Result { @@ -104,7 +107,7 @@ where #[async_trait] impl Runner for BlockWatcher where - A: FuelAdapter, + A: ports::fuel::Api, Db: Storage, { async fn run(&mut self) -> Result<()> { @@ -134,9 +137,9 @@ where mod tests { use std::{sync::Arc, vec}; - use metrics::{Metric, Registry}; + use metrics::prometheus::{proto::Metric, Registry}; use mockall::predicate::eq; - use ports::{fuel_rpc::MockFuelAdapter, types::BlockSubmission}; + use ports::{fuel::MockApi, types::BlockSubmission}; use rand::Rng; use storage::{Postgres, PostgresProcess}; @@ -273,8 +276,8 @@ mod tests { db } - fn given_fetcher(available_blocks: Vec) -> MockFuelAdapter { - let mut fetcher = MockFuelAdapter::new(); + fn given_fetcher(available_blocks: Vec) -> MockApi { + let mut fetcher = MockApi::new(); for block in available_blocks.clone() { fetcher .expect_block_at_height() diff --git a/packages/services/src/commit_listener.rs b/packages/services/src/commit_listener.rs index a81d3e9c..021c1d56 100644 --- a/packages/services/src/commit_listener.rs +++ b/packages/services/src/commit_listener.rs @@ -1,10 +1,12 @@ use async_trait::async_trait; use futures::{StreamExt, TryStreamExt}; -use metrics::{Collector, IntGauge, Opts, RegistersMetrics}; +use metrics::{ + prometheus::{core::Collector, IntGauge, Opts}, + RegistersMetrics, +}; use ports::{ - eth_rpc::EthereumAdapter, storage::Storage, - types::{EthHeight, FuelBlockCommittedOnEth}, + types::{FuelBlockCommittedOnL1, L1Height}, }; use tokio_util::sync::CancellationToken; use tracing::{error, info}; @@ -31,10 +33,10 @@ impl CommitListener { impl CommitListener where - E: EthereumAdapter, + E: ports::l1::Contract, Db: Storage, { - async fn determine_starting_eth_block(&mut self) -> crate::Result { + async fn determine_starting_eth_block(&mut self) -> crate::Result { Ok(self .storage .submission_w_latest_block() @@ -44,13 +46,13 @@ where async fn handle_block_committed( &self, - committed_on_eth: FuelBlockCommittedOnEth, + committed_on_l1: FuelBlockCommittedOnL1, ) -> crate::Result<()> { - info!("block comitted on eth {committed_on_eth:?}"); + info!("block comitted on l1 {committed_on_l1:?}"); let submission = self .storage - .set_submission_completed(committed_on_eth.fuel_block_hash) + .set_submission_completed(committed_on_l1.fuel_block_hash) .await?; self.metrics @@ -68,9 +70,9 @@ where } #[async_trait] -impl Runner for CommitListener +impl Runner for CommitListener where - E: EthereumAdapter, + C: ports::l1::Contract, Db: Storage, { async fn run(&mut self) -> crate::Result<()> { @@ -118,12 +120,15 @@ impl Default for Metrics { #[cfg(test)] mod tests { use futures::stream; - use metrics::{Metric, RegistersMetrics, Registry}; + use metrics::{ + prometheus::{proto::Metric, Registry}, + RegistersMetrics, + }; use mockall::predicate; use ports::{ - eth_rpc::{MockEthereumAdapter, MockEventStreamer}, + l1::{MockContract, MockEventStreamer}, storage::Storage, - types::{BlockSubmission, EthHeight, FuelBlockCommittedOnEth, U256}, + types::{BlockSubmission, FuelBlockCommittedOnL1, L1Height, U256}, }; use rand::Rng; use storage::{Postgres, PostgresProcess}; @@ -249,9 +254,9 @@ mod tests { fn given_eth_rpc_that_will_stream( events: Vec<[u8; 32]>, - starting_from_height: EthHeight, - ) -> MockEthereumAdapter { - let mut eth_rpc = MockEthereumAdapter::new(); + starting_from_height: L1Height, + ) -> MockContract { + let mut eth_rpc = MockContract::new(); let event_streamer = Box::new(given_event_streamer_w_events(events)); eth_rpc @@ -266,7 +271,7 @@ mod tests { let mut streamer = MockEventStreamer::new(); let events = events .into_iter() - .map(|block_hash| FuelBlockCommittedOnEth { + .map(|block_hash| FuelBlockCommittedOnL1 { fuel_block_hash: block_hash, commit_height: U256::default(), }) diff --git a/packages/services/src/lib.rs b/packages/services/src/lib.rs index 1d9afe1c..6fc3ae01 100644 --- a/packages/services/src/lib.rs +++ b/packages/services/src/lib.rs @@ -23,19 +23,19 @@ pub enum Error { Storage(String), } -impl From for Error { - fn from(value: ports::eth_rpc::Error) -> Self { +impl From for Error { + fn from(value: ports::l1::Error) -> Self { match value { - ports::eth_rpc::Error::Network(e) => Self::Network(e), + ports::l1::Error::Network(e) => Self::Network(e), _ => Self::Other(value.to_string()), } } } -impl From for Error { - fn from(value: ports::fuel_rpc::Error) -> Self { +impl From for Error { + fn from(value: ports::fuel::Error) -> Self { match value { - ports::fuel_rpc::Error::Network(e) => Self::Network(e), + ports::fuel::Error::Network(e) => Self::Network(e), } } } diff --git a/packages/services/src/wallet_balance_tracker.rs b/packages/services/src/wallet_balance_tracker.rs index c5d25573..ce75e5e3 100644 --- a/packages/services/src/wallet_balance_tracker.rs +++ b/packages/services/src/wallet_balance_tracker.rs @@ -1,24 +1,30 @@ -use metrics::{Collector, IntGauge, Opts, RegistersMetrics}; -use ports::{eth_rpc::EthereumAdapter, types::U256}; +use metrics::{ + prometheus::{core::Collector, IntGauge, Opts}, + RegistersMetrics, +}; +use ports::types::U256; use super::Runner; use crate::Result; -pub struct WalletBalanceTracker { - eth_adapter: Box, +pub struct WalletBalanceTracker { + api: Api, metrics: Metrics, } -impl WalletBalanceTracker { - pub fn new(adapter: impl EthereumAdapter + 'static) -> Self { +impl WalletBalanceTracker +where + Api: ports::l1::Api, +{ + pub fn new(api: Api) -> Self { Self { - eth_adapter: Box::new(adapter), + api, metrics: Metrics::default(), } } pub async fn update_balance(&self) -> Result<()> { - let balance = self.eth_adapter.balance().await?; + let balance = self.api.balance().await?; let balance_gwei = balance / U256::from(1_000_000_000); self.metrics @@ -29,7 +35,7 @@ impl WalletBalanceTracker { } } -impl RegistersMetrics for WalletBalanceTracker { +impl RegistersMetrics for WalletBalanceTracker { fn metrics(&self) -> Vec> { self.metrics.metrics() } @@ -59,7 +65,10 @@ impl Default for Metrics { } #[async_trait::async_trait] -impl Runner for WalletBalanceTracker { +impl Runner for WalletBalanceTracker +where + Api: Send + Sync + ports::l1::Api, +{ async fn run(&mut self) -> Result<()> { self.update_balance().await } @@ -68,15 +77,15 @@ impl Runner for WalletBalanceTracker { #[cfg(test)] mod tests { - use metrics::{Metric, Registry}; - use ports::eth_rpc::MockEthereumAdapter; + use metrics::prometheus::{proto::Metric, Registry}; + use ports::l1; use super::*; #[tokio::test] async fn updates_metrics() { // given - let eth_adapter = given_eth_adapter("500000000000000000000"); + let eth_adapter = given_l1_api("500000000000000000000"); let registry = Registry::new(); let sut = WalletBalanceTracker::new(eth_adapter); @@ -97,10 +106,10 @@ mod tests { assert_eq!(eth_balance_metric.get_value(), 500_000_000_000_f64); } - fn given_eth_adapter(wei_balance: &str) -> MockEthereumAdapter { + fn given_l1_api(wei_balance: &str) -> l1::MockApi { let balance = U256::from_dec_str(wei_balance).unwrap(); - let mut eth_adapter = MockEthereumAdapter::new(); + let mut eth_adapter = l1::MockApi::new(); eth_adapter .expect_balance() .return_once(move || Ok(balance)); From 8a70b2243f9cf89ffd6d60de8bc460ade05b936b Mon Sep 17 00:00:00 2001 From: segfault_magnet Date: Mon, 6 May 2024 08:07:42 +0200 Subject: [PATCH 17/23] separate contract and general api --- ...e3f45511e91468b32f5ef0024f92730588a6.json} | 4 +- ...8be642af65ed848f53a191ef58c2e02f227c.json} | 4 +- ...d241dd9a24d99d28ff14db70a93200ff869c6.json | 17 +++++ ...c5956c3187e2e72e4b873eac1621ff1b69e3e.json | 17 ----- committer/src/main.rs | 11 ++- committer/src/setup.rs | 25 ++++--- packages/eth_rpc/src/lib.rs | 19 +++--- packages/eth_rpc/src/metrics.rs | 2 +- packages/eth_rpc/src/websocket.rs | 4 +- packages/eth_rpc/src/websocket/connection.rs | 12 ++-- .../websocket/health_tracking_middleware.rs | 18 ++--- packages/ports/src/ports/l1.rs | 4 +- packages/ports/src/types.rs | 4 +- .../src/types/{eth_height.rs => l1_height.rs} | 0 packages/services/src/block_committer.rs | 68 ++++++++++++++----- packages/services/src/commit_listener.rs | 45 ++++++------ .../storage/migrations/0001_initial.down.sql | 2 +- .../storage/migrations/0001_initial.up.sql | 2 +- packages/storage/src/postgres.rs | 12 ++-- packages/storage/src/tables.rs | 8 +-- 20 files changed, 151 insertions(+), 127 deletions(-) rename .sqlx/{query-6389313221aa32ab73e62573b11d26eb631abbd9e7e114d6ef34207dea8152d3.json => query-2207b448e46117ad64084feefc49e3f45511e91468b32f5ef0024f92730588a6.json} (77%) rename .sqlx/{query-9774b1f03a332c68b0c646ba48723205201c7a3faf38bb0362959e8ae1ebf989.json => query-6f7e6ba876d49bef1bf870514ed38be642af65ed848f53a191ef58c2e02f227c.json} (77%) create mode 100644 .sqlx/query-a9bb121820d80a7aeadd87ce55ad241dd9a24d99d28ff14db70a93200ff869c6.json delete mode 100644 .sqlx/query-e6619efca0fbd3ae785f2cd13aac5956c3187e2e72e4b873eac1621ff1b69e3e.json rename packages/ports/src/types/{eth_height.rs => l1_height.rs} (100%) diff --git a/.sqlx/query-6389313221aa32ab73e62573b11d26eb631abbd9e7e114d6ef34207dea8152d3.json b/.sqlx/query-2207b448e46117ad64084feefc49e3f45511e91468b32f5ef0024f92730588a6.json similarity index 77% rename from .sqlx/query-6389313221aa32ab73e62573b11d26eb631abbd9e7e114d6ef34207dea8152d3.json rename to .sqlx/query-2207b448e46117ad64084feefc49e3f45511e91468b32f5ef0024f92730588a6.json index 9a105428..8b32735b 100644 --- a/.sqlx/query-6389313221aa32ab73e62573b11d26eb631abbd9e7e114d6ef34207dea8152d3.json +++ b/.sqlx/query-2207b448e46117ad64084feefc49e3f45511e91468b32f5ef0024f92730588a6.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "UPDATE eth_fuel_block_submission SET completed = true WHERE fuel_block_hash = $1 RETURNING *", + "query": "UPDATE l1_fuel_block_submission SET completed = true WHERE fuel_block_hash = $1 RETURNING *", "describe": { "columns": [ { @@ -36,5 +36,5 @@ false ] }, - "hash": "6389313221aa32ab73e62573b11d26eb631abbd9e7e114d6ef34207dea8152d3" + "hash": "2207b448e46117ad64084feefc49e3f45511e91468b32f5ef0024f92730588a6" } diff --git a/.sqlx/query-9774b1f03a332c68b0c646ba48723205201c7a3faf38bb0362959e8ae1ebf989.json b/.sqlx/query-6f7e6ba876d49bef1bf870514ed38be642af65ed848f53a191ef58c2e02f227c.json similarity index 77% rename from .sqlx/query-9774b1f03a332c68b0c646ba48723205201c7a3faf38bb0362959e8ae1ebf989.json rename to .sqlx/query-6f7e6ba876d49bef1bf870514ed38be642af65ed848f53a191ef58c2e02f227c.json index 3d2fe072..5c81b625 100644 --- a/.sqlx/query-9774b1f03a332c68b0c646ba48723205201c7a3faf38bb0362959e8ae1ebf989.json +++ b/.sqlx/query-6f7e6ba876d49bef1bf870514ed38be642af65ed848f53a191ef58c2e02f227c.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT * FROM eth_fuel_block_submission ORDER BY fuel_block_height DESC LIMIT 1", + "query": "SELECT * FROM l1_fuel_block_submission ORDER BY fuel_block_height DESC LIMIT 1", "describe": { "columns": [ { @@ -34,5 +34,5 @@ false ] }, - "hash": "9774b1f03a332c68b0c646ba48723205201c7a3faf38bb0362959e8ae1ebf989" + "hash": "6f7e6ba876d49bef1bf870514ed38be642af65ed848f53a191ef58c2e02f227c" } diff --git a/.sqlx/query-a9bb121820d80a7aeadd87ce55ad241dd9a24d99d28ff14db70a93200ff869c6.json b/.sqlx/query-a9bb121820d80a7aeadd87ce55ad241dd9a24d99d28ff14db70a93200ff869c6.json new file mode 100644 index 00000000..27479ad2 --- /dev/null +++ b/.sqlx/query-a9bb121820d80a7aeadd87ce55ad241dd9a24d99d28ff14db70a93200ff869c6.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO l1_fuel_block_submission (fuel_block_hash, fuel_block_height, completed, submittal_height) VALUES ($1, $2, $3, $4)", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Bytea", + "Int8", + "Bool", + "Int8" + ] + }, + "nullable": [] + }, + "hash": "a9bb121820d80a7aeadd87ce55ad241dd9a24d99d28ff14db70a93200ff869c6" +} diff --git a/.sqlx/query-e6619efca0fbd3ae785f2cd13aac5956c3187e2e72e4b873eac1621ff1b69e3e.json b/.sqlx/query-e6619efca0fbd3ae785f2cd13aac5956c3187e2e72e4b873eac1621ff1b69e3e.json deleted file mode 100644 index 6e2691bf..00000000 --- a/.sqlx/query-e6619efca0fbd3ae785f2cd13aac5956c3187e2e72e4b873eac1621ff1b69e3e.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "INSERT INTO eth_fuel_block_submission (fuel_block_hash, fuel_block_height, completed, submittal_height) VALUES ($1, $2, $3, $4)", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Bytea", - "Int8", - "Bool", - "Int8" - ] - }, - "nullable": [] - }, - "hash": "e6619efca0fbd3ae785f2cd13aac5956c3187e2e72e4b873eac1621ff1b69e3e" -} diff --git a/committer/src/main.rs b/committer/src/main.rs index 8e414a90..25bf48f1 100644 --- a/committer/src/main.rs +++ b/committer/src/main.rs @@ -9,16 +9,15 @@ use config::InternalConfig; use errors::Result; use metrics::prometheus::Registry; use setup::{ - create_eth_adapter, setup_logger, setup_storage, spawn_block_watcher, - spawn_eth_committer_and_listener, spawn_wallet_balance_tracker, + create_l1_adapter, setup_logger, setup_storage, spawn_block_watcher, + spawn_l1_committer_and_listener, spawn_wallet_balance_tracker, }; use tokio_util::sync::CancellationToken; use crate::setup::shut_down; -pub type ContractRpc = eth_rpc::WebsocketClient; +pub type L1 = eth_rpc::WebsocketClient; pub type Database = storage::Postgres; -pub type L1Api = eth_rpc::WebsocketClient; pub type FuelApi = fuel_rpc::client::Client; #[tokio::main] @@ -43,7 +42,7 @@ async fn main() -> Result<()> { ); let (ethereum_rpc, eth_health_check) = - create_eth_adapter(&config, &internal_config, &metrics_registry).await?; + create_l1_adapter(&config, &internal_config, &metrics_registry).await?; let wallet_balance_tracker_handle = spawn_wallet_balance_tracker( &internal_config, @@ -52,7 +51,7 @@ async fn main() -> Result<()> { cancel_token.clone(), ); - let (committer_handle, listener_handle) = spawn_eth_committer_and_listener( + let (committer_handle, listener_handle) = spawn_l1_committer_and_listener( &internal_config, rx_fuel_block, ethereum_rpc, diff --git a/committer/src/setup.rs b/committer/src/setup.rs index 50cb86aa..c9ca9e2e 100644 --- a/committer/src/setup.rs +++ b/committer/src/setup.rs @@ -10,7 +10,7 @@ use tracing::{error, info}; use crate::{ config::{Config, InternalConfig}, errors::Result, - ContractRpc, Database, FuelApi, + Database, FuelApi, L1, }; pub fn spawn_block_watcher( @@ -42,10 +42,10 @@ pub fn spawn_block_watcher( pub fn spawn_wallet_balance_tracker( internal_config: &InternalConfig, registry: &Registry, - ethereum_api: ContractRpc, + l1: L1, cancel_token: CancellationToken, ) -> tokio::task::JoinHandle<()> { - let wallet_balance_tracker = WalletBalanceTracker::new(ethereum_api); + let wallet_balance_tracker = WalletBalanceTracker::new(l1); wallet_balance_tracker.register_metrics(registry); @@ -57,18 +57,17 @@ pub fn spawn_wallet_balance_tracker( ) } -pub fn spawn_eth_committer_and_listener( +pub fn spawn_l1_committer_and_listener( internal_config: &InternalConfig, rx_fuel_block: Receiver, - ethereum_api: ContractRpc, + l1: L1, storage: Database, registry: &Registry, cancel_token: CancellationToken, ) -> (tokio::task::JoinHandle<()>, tokio::task::JoinHandle<()>) { - let committer_handler = - create_block_committer(rx_fuel_block, ethereum_api.clone(), storage.clone()); + let committer_handler = create_block_committer(rx_fuel_block, l1.clone(), storage.clone()); - let commit_listener = CommitListener::new(ethereum_api, storage, cancel_token.clone()); + let commit_listener = CommitListener::new(l1, storage, cancel_token.clone()); commit_listener.register_metrics(registry); let listener_handle = schedule_polling( @@ -83,10 +82,10 @@ pub fn spawn_eth_committer_and_listener( fn create_block_committer( rx_fuel_block: Receiver, - ethereum_api: ContractRpc, + l1: L1, storage: impl Storage + 'static, ) -> tokio::task::JoinHandle<()> { - let mut block_committer = BlockCommitter::new(rx_fuel_block, ethereum_api, storage); + let mut block_committer = BlockCommitter::new(rx_fuel_block, l1, storage); tokio::spawn(async move { block_committer .run() @@ -95,12 +94,12 @@ fn create_block_committer( }) } -pub async fn create_eth_adapter( +pub async fn create_l1_adapter( config: &Config, internal_config: &InternalConfig, registry: &Registry, -) -> Result<(ContractRpc, HealthChecker)> { - let ws_adapter = ContractRpc::connect( +) -> Result<(L1, HealthChecker)> { + let ws_adapter = L1::connect( &config.eth.rpc, config.eth.chain_id, config.eth.state_contract_address, diff --git a/packages/eth_rpc/src/lib.rs b/packages/eth_rpc/src/lib.rs index 161f50ff..528ab67c 100644 --- a/packages/eth_rpc/src/lib.rs +++ b/packages/eth_rpc/src/lib.rs @@ -20,17 +20,8 @@ impl ports::l1::Contract for WebsocketClient { self.submit(block).await } - async fn get_block_number(&self) -> ports::l1::Result { - let block_num = self.get_block_number().await?; - let height = L1Height::try_from(block_num)?; - Ok(height) - } - - fn event_streamer( - &self, - eth_block_height: u64, - ) -> Box { - let stream = self.event_streamer(eth_block_height); + fn event_streamer(&self, height: L1Height) -> Box { + let stream = self.event_streamer(height.into()); Box::new(stream) } } @@ -40,6 +31,12 @@ impl ports::l1::Api for WebsocketClient { async fn balance(&self) -> ports::l1::Result { Ok(self.balance().await?) } + + async fn get_block_number(&self) -> ports::l1::Result { + let block_num = self.get_block_number().await?; + let height = L1Height::try_from(block_num)?; + Ok(height) + } } #[async_trait::async_trait] diff --git a/packages/eth_rpc/src/metrics.rs b/packages/eth_rpc/src/metrics.rs index 95527198..bc38957a 100644 --- a/packages/eth_rpc/src/metrics.rs +++ b/packages/eth_rpc/src/metrics.rs @@ -1,4 +1,4 @@ -use metrics::{ +use ::metrics::{ prometheus::{core::Collector, IntCounter, Opts}, RegistersMetrics, }; diff --git a/packages/eth_rpc/src/websocket.rs b/packages/eth_rpc/src/websocket.rs index e84718f8..08b29249 100644 --- a/packages/eth_rpc/src/websocket.rs +++ b/packages/eth_rpc/src/websocket.rs @@ -1,7 +1,7 @@ use std::num::NonZeroU32; +use ::metrics::{prometheus::core::Collector, HealthChecker, RegistersMetrics}; use ethers::types::{Address, Chain}; -use metrics::{prometheus::core::Collector, HealthChecker, RegistersMetrics}; use ports::{ l1::Result, types::{FuelBlock, U256}, @@ -11,7 +11,7 @@ use url::Url; pub use self::event_streamer::EthEventStreamer; use self::{ connection::WsConnection, - health_tracking_middleware::{HealthTrackingMiddleware, MyAdapter}, + health_tracking_middleware::{EthApi, HealthTrackingMiddleware}, }; mod connection; diff --git a/packages/eth_rpc/src/websocket/connection.rs b/packages/eth_rpc/src/websocket/connection.rs index 7a1a5c40..4927c3bc 100644 --- a/packages/eth_rpc/src/websocket/connection.rs +++ b/packages/eth_rpc/src/websocket/connection.rs @@ -10,7 +10,7 @@ use ports::types::FuelBlock; use serde_json::Value; use url::Url; -use super::{event_streamer::EthEventStreamer, health_tracking_middleware::MyAdapter}; +use super::{event_streamer::EthEventStreamer, health_tracking_middleware::EthApi}; use crate::error::Result; abigen!( @@ -32,7 +32,7 @@ pub struct WsConnection { } #[async_trait::async_trait] -impl MyAdapter for WsConnection { +impl EthApi for WsConnection { async fn submit(&self, block: FuelBlock) -> Result<()> { let commit_height = Self::calculate_commit_height(block.height, self.commit_interval); let contract_call = self.contract.commit(block.hash, commit_height); @@ -88,15 +88,15 @@ impl MyAdapter for WsConnection { impl WsConnection { pub async fn connect( - ethereum_rpc: &Url, + url: &Url, chain_id: Chain, contract_address: Address, - ethereum_wallet_key: &str, + wallet_key: &str, commit_interval: NonZeroU32, ) -> Result { - let provider = Provider::::connect(ethereum_rpc.to_string()).await?; + let provider = Provider::::connect(url.to_string()).await?; - let wallet = LocalWallet::from_str(ethereum_wallet_key)?.with_chain_id(chain_id); + let wallet = LocalWallet::from_str(wallet_key)?.with_chain_id(chain_id); let address = wallet.address(); let signer = SignerMiddleware::new(provider.clone(), wallet); diff --git a/packages/eth_rpc/src/websocket/health_tracking_middleware.rs b/packages/eth_rpc/src/websocket/health_tracking_middleware.rs index be0dc690..79171ade 100644 --- a/packages/eth_rpc/src/websocket/health_tracking_middleware.rs +++ b/packages/eth_rpc/src/websocket/health_tracking_middleware.rs @@ -1,4 +1,4 @@ -use metrics::{ +use ::metrics::{ prometheus::core::Collector, ConnectionHealthTracker, HealthChecker, RegistersMetrics, }; use ports::types::{FuelBlock, U256}; @@ -11,7 +11,7 @@ use crate::{ #[cfg_attr(test, mockall::automock)] #[async_trait::async_trait] -pub trait MyAdapter { +pub trait EthApi { async fn submit(&self, block: FuelBlock) -> Result<()>; async fn get_block_number(&self) -> Result; async fn balance(&self) -> Result; @@ -62,9 +62,9 @@ impl RegistersMetrics for HealthTrackingMiddleware { } #[async_trait::async_trait] -impl MyAdapter for HealthTrackingMiddleware +impl EthApi for HealthTrackingMiddleware where - T: MyAdapter + Send + Sync, + T: EthApi + Send + Sync, { async fn submit(&self, block: FuelBlock) -> Result<()> { let response = self.adapter.submit(block).await; @@ -101,14 +101,14 @@ where #[cfg(test)] mod tests { - use metrics::prometheus::{proto::Metric, Registry}; + use ::metrics::prometheus::{proto::Metric, Registry}; use super::*; #[tokio::test] async fn recovers_after_successful_network_request() { // given - let mut eth_adapter = MockMyAdapter::new(); + let mut eth_adapter = MockEthApi::new(); eth_adapter .expect_submit() .returning(|_| Err(Error::Network("An error".into()))); @@ -132,7 +132,7 @@ mod tests { #[tokio::test] async fn other_errors_dont_impact_health_status() { // given - let mut eth_adapter = MockMyAdapter::new(); + let mut eth_adapter = MockEthApi::new(); eth_adapter .expect_submit() .returning(|_| Err(Error::Other("An error".into()))); @@ -155,7 +155,7 @@ mod tests { #[tokio::test] async fn network_errors_impact_health_status() { - let mut eth_adapter = MockMyAdapter::new(); + let mut eth_adapter = MockEthApi::new(); eth_adapter .expect_submit() .returning(|_| Err(Error::Network("An error".into()))); @@ -180,7 +180,7 @@ mod tests { #[tokio::test] async fn network_errors_seen_in_metrics() { - let mut eth_adapter = MockMyAdapter::new(); + let mut eth_adapter = MockEthApi::new(); eth_adapter .expect_submit() .returning(|_| Err(Error::Network("An error".into()))); diff --git a/packages/ports/src/ports/l1.rs b/packages/ports/src/ports/l1.rs index d3014fe7..4f149b15 100644 --- a/packages/ports/src/ports/l1.rs +++ b/packages/ports/src/ports/l1.rs @@ -22,13 +22,13 @@ impl From for Error { #[async_trait::async_trait] pub trait Contract: Send + Sync { async fn submit(&self, block: FuelBlock) -> Result<()>; - async fn get_block_number(&self) -> Result; - fn event_streamer(&self, eth_block_height: u64) -> Box; + fn event_streamer(&self, height: L1Height) -> Box; } #[cfg_attr(feature = "test-helpers", mockall::automock)] #[async_trait::async_trait] pub trait Api { + async fn get_block_number(&self) -> Result; async fn balance(&self) -> Result; } diff --git a/packages/ports/src/types.rs b/packages/ports/src/types.rs index ede28015..298c0df6 100644 --- a/packages/ports/src/types.rs +++ b/packages/ports/src/types.rs @@ -4,13 +4,13 @@ pub use ethers_core::types::{H160, U256}; pub use futures::Stream; mod block_submission; -mod eth_height; mod fuel_block; #[cfg(feature = "l1")] mod fuel_block_committed_on_l1; +mod l1_height; pub use block_submission::*; -pub use eth_height::*; pub use fuel_block::*; #[cfg(feature = "l1")] pub use fuel_block_committed_on_l1::*; +pub use l1_height::*; diff --git a/packages/ports/src/types/eth_height.rs b/packages/ports/src/types/l1_height.rs similarity index 100% rename from packages/ports/src/types/eth_height.rs rename to packages/ports/src/types/l1_height.rs diff --git a/packages/services/src/block_committer.rs b/packages/services/src/block_committer.rs index 25a53697..2d3e1015 100644 --- a/packages/services/src/block_committer.rs +++ b/packages/services/src/block_committer.rs @@ -11,15 +11,15 @@ use crate::Result; pub struct BlockCommitter { rx_block: Receiver, - ethereum_rpc: C, + l1: C, storage: Db, } -impl BlockCommitter { - pub fn new(rx_block: Receiver, ethereum_rpc: A, storage: Db) -> Self { +impl BlockCommitter { + pub fn new(rx_block: Receiver, l1: L1, storage: Db) -> Self { Self { rx_block, - ethereum_rpc, + l1, storage, } } @@ -31,11 +31,11 @@ impl BlockCommitter { impl BlockCommitter where - A: ports::l1::Contract, + A: ports::l1::Contract + ports::l1::Api, Db: Storage, { async fn submit_block(&self, fuel_block: FuelBlock) -> Result<()> { - let submittal_height = self.ethereum_rpc.get_block_number().await?; + let submittal_height = self.l1.get_block_number().await?; let submission = BlockSubmission { block: fuel_block, @@ -46,7 +46,7 @@ where self.storage.insert(submission).await?; // if we have a network failure the DB entry will be left at completed:false. - self.ethereum_rpc.submit(fuel_block).await?; + self.l1.submit(fuel_block).await?; Ok(()) } @@ -55,7 +55,7 @@ where #[async_trait] impl Runner for BlockCommitter where - A: ports::l1::Contract, + A: ports::l1::Contract + ports::l1::Api, Db: Storage, { async fn run(&mut self) -> Result<()> { @@ -78,12 +78,41 @@ mod tests { use std::time::Duration; use mockall::predicate; - use ports::l1::MockContract; + use ports::{ + l1::{Contract, EventStreamer, MockApi, MockContract}, + types::{L1Height, U256}, + }; use rand::Rng; use storage::PostgresProcess; use super::*; + struct MockL1 { + api: MockApi, + contract: MockContract, + } + + #[async_trait::async_trait] + impl Contract for MockL1 { + async fn submit(&self, block: FuelBlock) -> ports::l1::Result<()> { + self.contract.submit(block).await + } + fn event_streamer(&self, height: L1Height) -> Box { + self.contract.event_streamer(height) + } + } + + #[cfg_attr(feature = "test-helpers", mockall::automock)] + #[async_trait::async_trait] + impl ports::l1::Api for MockL1 { + async fn get_block_number(&self) -> ports::l1::Result { + self.api.get_block_number().await + } + async fn balance(&self) -> ports::l1::Result { + self.api.balance().await + } + } + #[tokio::test] async fn block_committer_will_submit_and_write_block() { // given @@ -93,38 +122,41 @@ mod tests { let process = PostgresProcess::shared().await.unwrap(); let db = process.create_random_db().await.unwrap(); - let eth_rpc_mock = given_eth_rpc_that_expects(block); + let mock_l1 = given_l1_that_expects_submission(block); tx.try_send(block).unwrap(); // when - spawn_committer_and_run_until_timeout(rx, eth_rpc_mock, db.clone()).await; + spawn_committer_and_run_until_timeout(rx, mock_l1, db.clone()).await; // then let last_submission = db.submission_w_latest_block().await.unwrap().unwrap(); assert_eq!(expeted_height, last_submission.block.height); } - fn given_eth_rpc_that_expects(block: FuelBlock) -> MockContract { - let mut eth_rpc_mock = MockContract::new(); - eth_rpc_mock + fn given_l1_that_expects_submission(block: FuelBlock) -> MockL1 { + let mut l1 = MockL1 { + api: MockApi::new(), + contract: MockContract::new(), + }; + l1.contract .expect_submit() .with(predicate::eq(block)) .return_once(move |_| Ok(())); - eth_rpc_mock + l1.api .expect_get_block_number() .return_once(move || Ok(0u32.into())); - eth_rpc_mock + l1 } async fn spawn_committer_and_run_until_timeout( rx: Receiver, - eth_rpc_mock: MockContract, + mock_l1: MockL1, storage: Db, ) { let _ = tokio::time::timeout(Duration::from_millis(250), async move { - let mut block_committer = BlockCommitter::new(rx, eth_rpc_mock, storage); + let mut block_committer = BlockCommitter::new(rx, mock_l1, storage); block_committer .run() .await diff --git a/packages/services/src/commit_listener.rs b/packages/services/src/commit_listener.rs index 021c1d56..6f60c0de 100644 --- a/packages/services/src/commit_listener.rs +++ b/packages/services/src/commit_listener.rs @@ -14,16 +14,16 @@ use tracing::{error, info}; use super::Runner; pub struct CommitListener { - ethereum_rpc: E, + contract: E, storage: Db, metrics: Metrics, cancel_token: CancellationToken, } -impl CommitListener { - pub fn new(ethereum_rpc: E, storage: Db, cancel_token: CancellationToken) -> Self { +impl CommitListener { + pub fn new(contract: C, storage: Db, cancel_token: CancellationToken) -> Self { Self { - ethereum_rpc, + contract, storage, metrics: Metrics::default(), cancel_token, @@ -31,12 +31,12 @@ impl CommitListener { } } -impl CommitListener +impl CommitListener where - E: ports::l1::Contract, + C: ports::l1::Contract, Db: Storage, { - async fn determine_starting_eth_block(&mut self) -> crate::Result { + async fn determine_starting_l1_height(&mut self) -> crate::Result { Ok(self .storage .submission_w_latest_block() @@ -76,10 +76,10 @@ where Db: Storage, { async fn run(&mut self) -> crate::Result<()> { - let eth_block = self.determine_starting_eth_block().await?; + let height = self.determine_starting_l1_height().await?; - self.ethereum_rpc - .event_streamer(eth_block.into()) + self.contract + .event_streamer(height) .establish_stream() .await? .map_err(Into::into) @@ -147,14 +147,13 @@ mod tests { }; let block_hash = submission.block.hash; - let eth_rpc_mock = - given_eth_rpc_that_will_stream(vec![block_hash], submission.submittal_height); + let contract = given_contract_with_events(vec![block_hash], submission.submittal_height); let process = PostgresProcess::shared().await.unwrap(); let db = db_with_submission(&process, submission).await; let mut commit_listener = - CommitListener::new(eth_rpc_mock, db.clone(), CancellationToken::default()); + CommitListener::new(contract, db.clone(), CancellationToken::default()); // when commit_listener.run().await.unwrap(); @@ -176,14 +175,12 @@ mod tests { let block_hash = submission.block.hash; let fuel_block_height = submission.block.height; - let eth_rpc_mock = - given_eth_rpc_that_will_stream(vec![block_hash], submission.submittal_height); + let contract = given_contract_with_events(vec![block_hash], submission.submittal_height); let process = PostgresProcess::shared().await.unwrap(); let db = db_with_submission(&process, submission).await; - let mut commit_listener = - CommitListener::new(eth_rpc_mock, db, CancellationToken::default()); + let mut commit_listener = CommitListener::new(contract, db, CancellationToken::default()); let registry = Registry::new(); commit_listener.register_metrics(®istry); @@ -216,7 +213,7 @@ mod tests { let missing_hash = block_missing_from_db.block.hash; let incoming_hash = incoming_block.block.hash; - let eth_rpc_mock = given_eth_rpc_that_will_stream( + let contract = given_contract_with_events( vec![missing_hash, incoming_hash], incoming_block.submittal_height, ); @@ -225,7 +222,7 @@ mod tests { let db = db_with_submission(&process, incoming_block.clone()).await; let mut commit_listener = - CommitListener::new(eth_rpc_mock, db.clone(), CancellationToken::default()); + CommitListener::new(contract, db.clone(), CancellationToken::default()); // when commit_listener.run().await.unwrap(); @@ -252,19 +249,19 @@ mod tests { db } - fn given_eth_rpc_that_will_stream( + fn given_contract_with_events( events: Vec<[u8; 32]>, starting_from_height: L1Height, ) -> MockContract { - let mut eth_rpc = MockContract::new(); + let mut contract = MockContract::new(); let event_streamer = Box::new(given_event_streamer_w_events(events)); - eth_rpc + contract .expect_event_streamer() - .with(predicate::eq(u64::from(starting_from_height))) + .with(predicate::eq(starting_from_height)) .return_once(move |_| event_streamer); - eth_rpc + contract } fn given_event_streamer_w_events(events: Vec<[u8; 32]>) -> MockEventStreamer { diff --git a/packages/storage/migrations/0001_initial.down.sql b/packages/storage/migrations/0001_initial.down.sql index 6a564cc0..ffec93bf 100644 --- a/packages/storage/migrations/0001_initial.down.sql +++ b/packages/storage/migrations/0001_initial.down.sql @@ -1 +1 @@ -DROP TABLE IF EXISTS eth_fuel_block_submission; +DROP TABLE IF EXISTS l1_fuel_block_submission; diff --git a/packages/storage/migrations/0001_initial.up.sql b/packages/storage/migrations/0001_initial.up.sql index 6f021689..8a6876c2 100644 --- a/packages/storage/migrations/0001_initial.up.sql +++ b/packages/storage/migrations/0001_initial.up.sql @@ -1,4 +1,4 @@ -CREATE TABLE IF NOT EXISTS eth_fuel_block_submission ( +CREATE TABLE IF NOT EXISTS l1_fuel_block_submission ( fuel_block_hash BYTEA PRIMARY KEY NOT NULL, fuel_block_height BIGINT NOT NULL UNIQUE CHECK (fuel_block_height >= 0), completed BOOLEAN NOT NULL, diff --git a/packages/storage/src/postgres.rs b/packages/storage/src/postgres.rs index b1ebf6ab..de4531ea 100644 --- a/packages/storage/src/postgres.rs +++ b/packages/storage/src/postgres.rs @@ -64,9 +64,9 @@ impl Postgres { } pub(crate) async fn _insert(&self, submission: BlockSubmission) -> crate::error::Result<()> { - let row = tables::EthFuelBlockSubmission::from(submission); + let row = tables::L1FuelBlockSubmission::from(submission); sqlx::query!( - "INSERT INTO eth_fuel_block_submission (fuel_block_hash, fuel_block_height, completed, submittal_height) VALUES ($1, $2, $3, $4)", + "INSERT INTO l1_fuel_block_submission (fuel_block_hash, fuel_block_height, completed, submittal_height) VALUES ($1, $2, $3, $4)", row.fuel_block_hash, row.fuel_block_height, row.completed, @@ -79,8 +79,8 @@ impl Postgres { &self, ) -> crate::error::Result> { sqlx::query_as!( - tables::EthFuelBlockSubmission, - "SELECT * FROM eth_fuel_block_submission ORDER BY fuel_block_height DESC LIMIT 1" + tables::L1FuelBlockSubmission, + "SELECT * FROM l1_fuel_block_submission ORDER BY fuel_block_height DESC LIMIT 1" ) .fetch_optional(&self.connection_pool) .await? @@ -93,8 +93,8 @@ impl Postgres { fuel_block_hash: [u8; 32], ) -> Result { let updated_row = sqlx::query_as!( - tables::EthFuelBlockSubmission, - "UPDATE eth_fuel_block_submission SET completed = true WHERE fuel_block_hash = $1 RETURNING *", + tables::L1FuelBlockSubmission, + "UPDATE l1_fuel_block_submission SET completed = true WHERE fuel_block_hash = $1 RETURNING *", fuel_block_hash.as_slice(), ).fetch_optional(&self.connection_pool).await?; diff --git a/packages/storage/src/tables.rs b/packages/storage/src/tables.rs index e6f9fbc6..a3719eb2 100644 --- a/packages/storage/src/tables.rs +++ b/packages/storage/src/tables.rs @@ -1,17 +1,17 @@ use ports::types::{BlockSubmission, FuelBlock}; #[derive(sqlx::FromRow)] -pub(crate) struct EthFuelBlockSubmission { +pub(crate) struct L1FuelBlockSubmission { pub fuel_block_hash: Vec, pub fuel_block_height: i64, pub completed: bool, pub submittal_height: i64, } -impl TryFrom for BlockSubmission { +impl TryFrom for BlockSubmission { type Error = crate::error::Error; - fn try_from(value: EthFuelBlockSubmission) -> Result { + fn try_from(value: L1FuelBlockSubmission) -> Result { let block_hash = value.fuel_block_hash.as_slice(); macro_rules! bail { ($msg: literal, $($args: expr),*) => { @@ -42,7 +42,7 @@ impl TryFrom for BlockSubmission { } } -impl From for EthFuelBlockSubmission { +impl From for L1FuelBlockSubmission { fn from(value: BlockSubmission) -> Self { Self { fuel_block_hash: value.block.hash.to_vec(), From ee057068a2151286acc5841659f8c93dbc9871eb Mon Sep 17 00:00:00 2001 From: segfault_magnet Date: Mon, 6 May 2024 08:28:25 +0200 Subject: [PATCH 18/23] rename packages --- Cargo.lock | 66 +++++++++---------- Cargo.toml | 8 +-- committer/Cargo.toml | 4 +- committer/src/config.rs | 2 +- committer/src/main.rs | 4 +- e2e/Cargo.toml | 4 +- e2e/src/lib.rs | 6 +- packages/{eth_rpc => eth}/Cargo.toml | 2 +- packages/{eth_rpc => eth}/src/error.rs | 0 packages/{eth_rpc => eth}/src/lib.rs | 0 packages/{eth_rpc => eth}/src/metrics.rs | 0 packages/{eth_rpc => eth}/src/websocket.rs | 0 .../src/websocket/connection.rs | 0 .../src/websocket/event_streamer.rs | 0 .../websocket/health_tracking_middleware.rs | 0 packages/{fuel_rpc => fuel}/Cargo.toml | 2 +- packages/{fuel_rpc => fuel}/src/client.rs | 6 +- packages/{fuel_rpc => fuel}/src/lib.rs | 14 ++-- packages/{fuel_rpc => fuel}/src/metrics.rs | 2 +- 19 files changed, 61 insertions(+), 59 deletions(-) rename packages/{eth_rpc => eth}/Cargo.toml (97%) rename packages/{eth_rpc => eth}/src/error.rs (100%) rename packages/{eth_rpc => eth}/src/lib.rs (100%) rename packages/{eth_rpc => eth}/src/metrics.rs (100%) rename packages/{eth_rpc => eth}/src/websocket.rs (100%) rename packages/{eth_rpc => eth}/src/websocket/connection.rs (100%) rename packages/{eth_rpc => eth}/src/websocket/event_streamer.rs (100%) rename packages/{eth_rpc => eth}/src/websocket/health_tracking_middleware.rs (100%) rename packages/{fuel_rpc => fuel}/Cargo.toml (96%) rename packages/{fuel_rpc => fuel}/src/client.rs (96%) rename packages/{fuel_rpc => fuel}/src/lib.rs (94%) rename packages/{fuel_rpc => fuel}/src/metrics.rs (96%) diff --git a/Cargo.lock b/Cargo.lock index b7033bdc..b262574e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1188,8 +1188,8 @@ name = "e2e" version = "0.4.0" dependencies = [ "anyhow", - "eth_rpc", - "fuel_rpc", + "eth", + "fuel", "ports", "tokio", ] @@ -1317,6 +1317,23 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "eth" +version = "0.4.0" +dependencies = [ + "async-trait", + "ethers", + "futures", + "metrics", + "mockall", + "ports", + "serde_json", + "thiserror", + "tokio", + "tracing", + "url", +] + [[package]] name = "eth-keystore" version = "0.5.0" @@ -1339,23 +1356,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "eth_rpc" -version = "0.4.0" -dependencies = [ - "async-trait", - "ethers", - "futures", - "metrics", - "mockall", - "ports", - "serde_json", - "thiserror", - "tokio", - "tracing", - "url", -] - [[package]] name = "ethabi" version = "18.0.0" @@ -1694,6 +1694,18 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" +[[package]] +name = "fuel" +version = "0.4.0" +dependencies = [ + "async-trait", + "fuel-core-client", + "metrics", + "ports", + "tokio", + "url", +] + [[package]] name = "fuel-asm" version = "0.49.0" @@ -1714,8 +1726,8 @@ dependencies = [ "anyhow", "clap", "config", - "eth_rpc", - "fuel_rpc", + "eth", + "fuel", "metrics", "ports", "serde", @@ -1883,18 +1895,6 @@ dependencies = [ "tai64", ] -[[package]] -name = "fuel_rpc" -version = "0.4.0" -dependencies = [ - "async-trait", - "fuel-core-client", - "metrics", - "ports", - "tokio", - "url", -] - [[package]] name = "funty" version = "2.0.0" diff --git a/Cargo.toml b/Cargo.toml index a8473ea3..7bdf9957 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,8 +3,8 @@ resolver = "2" members = [ "committer", "e2e", - "packages/eth_rpc", - "packages/fuel_rpc", + "packages/eth", + "packages/fuel", "packages/metrics", "packages/ports", "packages/services", @@ -21,8 +21,8 @@ repository = "https://github.com/FuelLabs/fuel-block-committer" publish = false [workspace.dependencies] -eth_rpc = { path = "./packages/eth_rpc", default-features = false } -fuel_rpc = { path = "./packages/fuel_rpc", default-features = false } +eth = { path = "./packages/eth", default-features = false } +fuel = { path = "./packages/fuel", default-features = false } metrics = { path = "./packages/metrics", default-features = false } ports = { path = "./packages/ports", default-features = false } storage = { path = "./packages/storage", default-features = false } diff --git a/committer/Cargo.toml b/committer/Cargo.toml index 6945d474..47487a64 100644 --- a/committer/Cargo.toml +++ b/committer/Cargo.toml @@ -12,8 +12,8 @@ publish = { workspace = true } actix-web = { workspace = true, features = ["macros"] } clap = { workspace = true, features = ["derive"] } config = { workspace = true, features = ["toml", "async"] } -eth_rpc = { workspace = true } -fuel_rpc = { workspace = true } +eth = { workspace = true } +fuel = { workspace = true } metrics = { workspace = true } ports = { workspace = true } serde = { workspace = true } diff --git a/committer/src/config.rs b/committer/src/config.rs index 66f44f69..3d582309 100644 --- a/committer/src/config.rs +++ b/committer/src/config.rs @@ -1,7 +1,7 @@ use std::{net::Ipv4Addr, num::NonZeroU32, path::PathBuf, str::FromStr, time::Duration}; use clap::{command, Parser}; -use eth_rpc::{Address, Chain}; +use eth::{Address, Chain}; use serde::Deserialize; use storage::DbConfig; use url::Url; diff --git a/committer/src/main.rs b/committer/src/main.rs index 25bf48f1..a2ae945d 100644 --- a/committer/src/main.rs +++ b/committer/src/main.rs @@ -16,9 +16,9 @@ use tokio_util::sync::CancellationToken; use crate::setup::shut_down; -pub type L1 = eth_rpc::WebsocketClient; +pub type L1 = eth::WebsocketClient; pub type Database = storage::Postgres; -pub type FuelApi = fuel_rpc::client::Client; +pub type FuelApi = fuel::HttpClient; #[tokio::main] async fn main() -> Result<()> { diff --git a/e2e/Cargo.toml b/e2e/Cargo.toml index 654e5210..4871fc69 100644 --- a/e2e/Cargo.toml +++ b/e2e/Cargo.toml @@ -10,7 +10,7 @@ publish = { workspace = true } [dev-dependencies] anyhow = { workspace = true } -eth_rpc = { workspace = true, features = ["test-helpers"] } -fuel_rpc = { workspace = true, features = ["test-helpers"] } +eth = { workspace = true, features = ["test-helpers"] } +fuel = { workspace = true, features = ["test-helpers"] } ports = { workspace = true, features = ["fuel", "l1"] } tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } diff --git a/e2e/src/lib.rs b/e2e/src/lib.rs index c3b3ff5c..e6087d94 100644 --- a/e2e/src/lib.rs +++ b/e2e/src/lib.rs @@ -3,8 +3,8 @@ mod tests { use std::time::Duration; use anyhow::Result; - use eth_rpc::{Chain, WebsocketClient}; - use fuel_rpc::client::Client; + use eth::{Chain, WebsocketClient}; + use fuel::HttpClient; use ports::fuel::Api; const FUEL_NODE_PORT: u16 = 4000; @@ -12,7 +12,7 @@ mod tests { #[tokio::test(flavor = "multi_thread")] async fn submitted_correct_block_and_was_finalized() -> Result<()> { let fuel_node_address = format!("http://localhost:{FUEL_NODE_PORT}"); - let provider = Client::new(&fuel_node_address.parse()?, 10); + let provider = HttpClient::new(&fuel_node_address.parse()?, 10); let fuel_contract = WebsocketClient::connect( &"ws://localhost:8089".parse()?, diff --git a/packages/eth_rpc/Cargo.toml b/packages/eth/Cargo.toml similarity index 97% rename from packages/eth_rpc/Cargo.toml rename to packages/eth/Cargo.toml index de123340..c51ffb26 100644 --- a/packages/eth_rpc/Cargo.toml +++ b/packages/eth/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "eth_rpc" +name = "eth" authors = { workspace = true } edition = { workspace = true } homepage = { workspace = true } diff --git a/packages/eth_rpc/src/error.rs b/packages/eth/src/error.rs similarity index 100% rename from packages/eth_rpc/src/error.rs rename to packages/eth/src/error.rs diff --git a/packages/eth_rpc/src/lib.rs b/packages/eth/src/lib.rs similarity index 100% rename from packages/eth_rpc/src/lib.rs rename to packages/eth/src/lib.rs diff --git a/packages/eth_rpc/src/metrics.rs b/packages/eth/src/metrics.rs similarity index 100% rename from packages/eth_rpc/src/metrics.rs rename to packages/eth/src/metrics.rs diff --git a/packages/eth_rpc/src/websocket.rs b/packages/eth/src/websocket.rs similarity index 100% rename from packages/eth_rpc/src/websocket.rs rename to packages/eth/src/websocket.rs diff --git a/packages/eth_rpc/src/websocket/connection.rs b/packages/eth/src/websocket/connection.rs similarity index 100% rename from packages/eth_rpc/src/websocket/connection.rs rename to packages/eth/src/websocket/connection.rs diff --git a/packages/eth_rpc/src/websocket/event_streamer.rs b/packages/eth/src/websocket/event_streamer.rs similarity index 100% rename from packages/eth_rpc/src/websocket/event_streamer.rs rename to packages/eth/src/websocket/event_streamer.rs diff --git a/packages/eth_rpc/src/websocket/health_tracking_middleware.rs b/packages/eth/src/websocket/health_tracking_middleware.rs similarity index 100% rename from packages/eth_rpc/src/websocket/health_tracking_middleware.rs rename to packages/eth/src/websocket/health_tracking_middleware.rs diff --git a/packages/fuel_rpc/Cargo.toml b/packages/fuel/Cargo.toml similarity index 96% rename from packages/fuel_rpc/Cargo.toml rename to packages/fuel/Cargo.toml index fe18c507..5621f4bc 100644 --- a/packages/fuel_rpc/Cargo.toml +++ b/packages/fuel/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "fuel_rpc" +name = "fuel" authors = { workspace = true } edition = { workspace = true } homepage = { workspace = true } diff --git a/packages/fuel_rpc/src/client.rs b/packages/fuel/src/client.rs similarity index 96% rename from packages/fuel_rpc/src/client.rs rename to packages/fuel/src/client.rs index 76371d75..262f5481 100644 --- a/packages/fuel_rpc/src/client.rs +++ b/packages/fuel/src/client.rs @@ -6,13 +6,13 @@ use url::Url; use crate::{metrics::Metrics, Error, Result}; -pub struct Client { +pub struct HttpClient { client: GqlClient, metrics: Metrics, health_tracker: ConnectionHealthTracker, } -impl Client { +impl HttpClient { #[must_use] pub fn new(url: &Url, unhealthy_after_n_errors: usize) -> Self { let client = GqlClient::new(url).expect("Url to be well formed"); @@ -71,7 +71,7 @@ impl Client { } } -impl RegistersMetrics for Client { +impl RegistersMetrics for HttpClient { fn metrics(&self) -> Vec> { self.metrics.metrics() } diff --git a/packages/fuel_rpc/src/lib.rs b/packages/fuel/src/lib.rs similarity index 94% rename from packages/fuel_rpc/src/lib.rs rename to packages/fuel/src/lib.rs index c7f0bb85..267256bd 100644 --- a/packages/fuel_rpc/src/lib.rs +++ b/packages/fuel/src/lib.rs @@ -1,8 +1,10 @@ #![deny(unused_crate_dependencies)] use fuel_core_client::client::types::Block; use ports::types::FuelBlock; -pub mod client; -pub mod metrics; +mod client; +mod metrics; + +pub use client::*; type Error = ports::fuel::Error; type Result = ports::fuel::Result; @@ -15,7 +17,7 @@ fn convert_block(block: Block) -> FuelBlock { } #[async_trait::async_trait] -impl ports::fuel::Api for client::Client { +impl ports::fuel::Api for client::HttpClient { async fn block_at_height(&self, height: u32) -> ports::fuel::Result> { Ok(self._block_at_height(height).await?.map(convert_block)) } @@ -36,7 +38,7 @@ mod tests { use url::Url; use super::*; - use crate::client::Client; + use crate::client::HttpClient; // TODO: once a sdk release is made these can be adapted // #[tokio::test] @@ -96,7 +98,7 @@ mod tests { // killing the node once the SDK supports it. let url = Url::parse("localhost:12344").unwrap(); - let fuel_adapter = Client::new(&url, 1); + let fuel_adapter = HttpClient::new(&url, 1); let registry = Registry::default(); fuel_adapter.register_metrics(®istry); @@ -123,7 +125,7 @@ mod tests { // killing the node once the SDK supports it. let url = Url::parse("http://localhost:12344").unwrap(); - let fuel_adapter = client::Client::new(&url, 3); + let fuel_adapter = client::HttpClient::new(&url, 3); let health_check = fuel_adapter.connection_health_checker(); assert!(health_check.healthy()); diff --git a/packages/fuel_rpc/src/metrics.rs b/packages/fuel/src/metrics.rs similarity index 96% rename from packages/fuel_rpc/src/metrics.rs rename to packages/fuel/src/metrics.rs index 3984648a..a91e823a 100644 --- a/packages/fuel_rpc/src/metrics.rs +++ b/packages/fuel/src/metrics.rs @@ -3,7 +3,7 @@ use metrics::{ RegistersMetrics, }; -pub struct Metrics { +pub(crate) struct Metrics { pub fuel_network_errors: IntCounter, } From 65dc49cad01631548866b2eeb57378bfeb1cb52d Mon Sep 17 00:00:00 2001 From: segfault_magnet Date: Mon, 6 May 2024 09:33:12 +0200 Subject: [PATCH 19/23] nits --- .github/CODEOWNERS | 1 + committer/src/api.rs | 4 ++-- committer/src/setup.rs | 8 ++++---- packages/eth/src/websocket/connection.rs | 4 +++- packages/eth/src/websocket/health_tracking_middleware.rs | 6 +++++- packages/services/src/commit_listener.rs | 4 ++-- 6 files changed, 17 insertions(+), 10 deletions(-) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..ccbfa9d7 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @digorithm @hal3e @MujkicA @segfault-magnet @Salka1988 @Br1ght0ne diff --git a/committer/src/api.rs b/committer/src/api.rs index 2dd25a42..9deb3c47 100644 --- a/committer/src/api.rs +++ b/committer/src/api.rs @@ -9,11 +9,11 @@ use actix_web::{ }; use ports::storage::Storage; use services::{HealthReporter, StatusReporter}; -use storage::Postgres; use crate::{ config::Config, errors::{Error, Result}, + Database, }; pub async fn launch_api_server( @@ -56,7 +56,7 @@ async fn health(data: web::Data>) -> impl Responder { } #[get("/status")] -async fn status(data: web::Data>>) -> impl Responder { +async fn status(data: web::Data>>) -> impl Responder { let report = data.current_status().await?; Result::Ok(web::Json(report)) diff --git a/committer/src/setup.rs b/committer/src/setup.rs index c9ca9e2e..a7d40e46 100644 --- a/committer/src/setup.rs +++ b/committer/src/setup.rs @@ -99,7 +99,7 @@ pub async fn create_l1_adapter( internal_config: &InternalConfig, registry: &Registry, ) -> Result<(L1, HealthChecker)> { - let ws_adapter = L1::connect( + let l1 = L1::connect( &config.eth.rpc, config.eth.chain_id, config.eth.state_contract_address, @@ -109,11 +109,11 @@ pub async fn create_l1_adapter( ) .await?; - ws_adapter.register_metrics(registry); + l1.register_metrics(registry); - let health_check = ws_adapter.connection_health_checker(); + let health_check = l1.connection_health_checker(); - Ok((ws_adapter, health_check)) + Ok((l1, health_check)) } fn schedule_polling( diff --git a/packages/eth/src/websocket/connection.rs b/packages/eth/src/websocket/connection.rs index 4927c3bc..611f8ca4 100644 --- a/packages/eth/src/websocket/connection.rs +++ b/packages/eth/src/websocket/connection.rs @@ -26,7 +26,7 @@ abigen!( #[derive(Clone)] pub struct WsConnection { provider: Provider, - pub(crate) contract: FUEL_STATE_CONTRACT, LocalWallet>>, + contract: FUEL_STATE_CONTRACT, LocalWallet>>, commit_interval: NonZeroU32, address: H160, } @@ -69,6 +69,7 @@ impl EthApi for WsConnection { EthEventStreamer::new(events) } + #[cfg(feature = "test-helpers")] async fn finalized(&self, block: FuelBlock) -> Result { Ok(self .contract @@ -77,6 +78,7 @@ impl EthApi for WsConnection { .await?) } + #[cfg(feature = "test-helpers")] async fn block_hash_at_commit_height(&self, commit_height: u32) -> Result<[u8; 32]> { Ok(self .contract diff --git a/packages/eth/src/websocket/health_tracking_middleware.rs b/packages/eth/src/websocket/health_tracking_middleware.rs index 79171ade..92d962d6 100644 --- a/packages/eth/src/websocket/health_tracking_middleware.rs +++ b/packages/eth/src/websocket/health_tracking_middleware.rs @@ -11,12 +11,14 @@ use crate::{ #[cfg_attr(test, mockall::automock)] #[async_trait::async_trait] -pub trait EthApi { +pub(crate) trait EthApi { async fn submit(&self, block: FuelBlock) -> Result<()>; async fn get_block_number(&self) -> Result; async fn balance(&self) -> Result; fn event_streamer(&self, eth_block_height: u64) -> EthEventStreamer; + #[cfg(feature = "test-helpers")] async fn finalized(&self, block: FuelBlock) -> Result; + #[cfg(feature = "test-helpers")] async fn block_hash_at_commit_height(&self, commit_height: u32) -> Result<[u8; 32]>; } @@ -88,10 +90,12 @@ where response } + #[cfg(feature = "test-helpers")] async fn finalized(&self, block: FuelBlock) -> Result { self.adapter.finalized(block).await } + #[cfg(feature = "test-helpers")] async fn block_hash_at_commit_height(&self, commit_height: u32) -> Result<[u8; 32]> { self.adapter .block_hash_at_commit_height(commit_height) diff --git a/packages/services/src/commit_listener.rs b/packages/services/src/commit_listener.rs index 6f60c0de..2e3d5314 100644 --- a/packages/services/src/commit_listener.rs +++ b/packages/services/src/commit_listener.rs @@ -13,8 +13,8 @@ use tracing::{error, info}; use super::Runner; -pub struct CommitListener { - contract: E, +pub struct CommitListener { + contract: C, storage: Db, metrics: Metrics, cancel_token: CancellationToken, From 7e8d07e97e158b58422bb294b8edca13f8397e57 Mon Sep 17 00:00:00 2001 From: segfault_magnet Date: Mon, 6 May 2024 09:34:00 +0200 Subject: [PATCH 20/23] nits --- packages/eth/src/error.rs | 6 +++--- .../eth/src/websocket/health_tracking_middleware.rs | 2 +- packages/fuel/src/metrics.rs | 2 +- packages/services/src/health_reporter.rs | 2 ++ packages/storage/src/error.rs | 10 +++++----- packages/storage/src/postgres.rs | 10 +++++----- packages/storage/src/tables.rs | 2 +- 7 files changed, 18 insertions(+), 16 deletions(-) diff --git a/packages/eth/src/error.rs b/packages/eth/src/error.rs index 29cb2188..272e700e 100644 --- a/packages/eth/src/error.rs +++ b/packages/eth/src/error.rs @@ -5,7 +5,7 @@ use ethers::{ }; #[derive(Debug, thiserror::Error)] -pub(crate) enum Error { +pub enum Error { #[error("wallet error: {0}")] Wallet(#[from] ethers::signers::WalletError), #[error("network error: {0}")] @@ -20,7 +20,7 @@ impl From for Error { } } -pub(crate) type ContractErrorType = +pub type ContractErrorType = ethers::contract::ContractError, LocalWallet>>; impl From for Error { @@ -33,7 +33,7 @@ impl From for Error { } } -pub(crate) type Result = std::result::Result; +pub type Result = std::result::Result; impl From for ports::l1::Error { fn from(err: Error) -> Self { diff --git a/packages/eth/src/websocket/health_tracking_middleware.rs b/packages/eth/src/websocket/health_tracking_middleware.rs index 92d962d6..b6ea61e2 100644 --- a/packages/eth/src/websocket/health_tracking_middleware.rs +++ b/packages/eth/src/websocket/health_tracking_middleware.rs @@ -11,7 +11,7 @@ use crate::{ #[cfg_attr(test, mockall::automock)] #[async_trait::async_trait] -pub(crate) trait EthApi { +pub trait EthApi { async fn submit(&self, block: FuelBlock) -> Result<()>; async fn get_block_number(&self) -> Result; async fn balance(&self) -> Result; diff --git a/packages/fuel/src/metrics.rs b/packages/fuel/src/metrics.rs index a91e823a..3984648a 100644 --- a/packages/fuel/src/metrics.rs +++ b/packages/fuel/src/metrics.rs @@ -3,7 +3,7 @@ use metrics::{ RegistersMetrics, }; -pub(crate) struct Metrics { +pub struct Metrics { pub fuel_network_errors: IntCounter, } diff --git a/packages/services/src/health_reporter.rs b/packages/services/src/health_reporter.rs index 0f724792..6aa8b330 100644 --- a/packages/services/src/health_reporter.rs +++ b/packages/services/src/health_reporter.rs @@ -19,6 +19,7 @@ pub struct HealthReporter { } impl HealthReporter { + #[must_use] pub fn new(fuel_health_check: HealthChecker, eth_health_check: HealthChecker) -> Self { Self { fuel_connection: fuel_health_check, @@ -26,6 +27,7 @@ impl HealthReporter { } } + #[must_use] pub fn report(&self) -> HealthReport { HealthReport { fuel_connection_up: self.fuel_connection.healthy(), diff --git a/packages/storage/src/error.rs b/packages/storage/src/error.rs index 0f87bd31..b7e12b0a 100644 --- a/packages/storage/src/error.rs +++ b/packages/storage/src/error.rs @@ -1,7 +1,7 @@ -pub(crate) type Result = std::result::Result; +pub type Result = std::result::Result; #[derive(Debug, thiserror::Error)] -pub(crate) enum Error { +pub enum Error { #[error("Database Error {0}")] Database(String), #[error("Could not convert to/from domain/db type {0}")] @@ -9,10 +9,10 @@ pub(crate) enum Error { } impl From for ports::storage::Error { - fn from(value: Error) -> ports::storage::Error { + fn from(value: Error) -> Self { match value { - Error::Database(e) => ports::storage::Error::Database(e), - Error::Conversion(e) => ports::storage::Error::Conversion(e), + Error::Database(e) => Self::Database(e), + Error::Conversion(e) => Self::Conversion(e), } } } diff --git a/packages/storage/src/postgres.rs b/packages/storage/src/postgres.rs index de4531ea..3bd2505a 100644 --- a/packages/storage/src/postgres.rs +++ b/packages/storage/src/postgres.rs @@ -11,15 +11,15 @@ pub struct Postgres { #[derive(Debug, Clone, serde::Deserialize)] pub struct DbConfig { - /// The hostname or IP address of the PostgreSQL server. + /// The hostname or IP address of the `PostgreSQL` server. pub host: String, - /// The port number on which the PostgreSQL server is listening. + /// The port number on which the `PostgreSQL` server is listening. pub port: u16, - /// The username used to authenticate with the PostgreSQL server. + /// The username used to authenticate with the `PostgreSQL` server. pub username: String, - /// The password used to authenticate with the PostgreSQL server. + /// The password used to authenticate with the `PostgreSQL` server. pub password: String, - /// The name of the database to connect to on the PostgreSQL server. + /// The name of the database to connect to on the `PostgreSQL` server. pub database: String, /// The maximum number of connections allowed in the connection pool. pub max_connections: u32, diff --git a/packages/storage/src/tables.rs b/packages/storage/src/tables.rs index a3719eb2..db405d23 100644 --- a/packages/storage/src/tables.rs +++ b/packages/storage/src/tables.rs @@ -1,7 +1,7 @@ use ports::types::{BlockSubmission, FuelBlock}; #[derive(sqlx::FromRow)] -pub(crate) struct L1FuelBlockSubmission { +pub struct L1FuelBlockSubmission { pub fuel_block_hash: Vec, pub fuel_block_height: i64, pub completed: bool, From 1431543b380a91d3c6d00e84cbb6cc789f490059 Mon Sep 17 00:00:00 2001 From: segfault_magnet Date: Mon, 6 May 2024 09:51:39 +0200 Subject: [PATCH 21/23] fix version checks --- .github/scripts/verify_chart_version.sh | 14 +++++++------- .github/workflows/ci.yml | 2 +- Cargo.toml | 1 + committer/Cargo.toml | 1 + e2e/Cargo.toml | 1 + packages/eth/Cargo.toml | 1 + packages/fuel/Cargo.toml | 1 + packages/metrics/Cargo.toml | 1 + packages/ports/Cargo.toml | 2 +- packages/services/Cargo.toml | 1 + packages/storage/Cargo.toml | 1 + 11 files changed, 17 insertions(+), 9 deletions(-) diff --git a/.github/scripts/verify_chart_version.sh b/.github/scripts/verify_chart_version.sh index dcc16b68..c229527c 100755 --- a/.github/scripts/verify_chart_version.sh +++ b/.github/scripts/verify_chart_version.sh @@ -2,12 +2,12 @@ set -e err() { - echo -e "\e[31m\e[1merror:\e[0m $@" 1>&2; + echo -e "\e[31m\e[1merror:\e[0m $@" 1>&2 } status() { - WIDTH=12 - printf "\e[32m\e[1m%${WIDTH}s\e[0m %s\n" "$1" "$2" + WIDTH=12 + printf "\e[32m\e[1m%${WIDTH}s\e[0m %s\n" "$1" "$2" } # install dasel curl -sSLf "https://github.com/TomWright/dasel/releases/download/v1.24.3/dasel_linux_amd64" -L -o dasel @@ -15,10 +15,10 @@ chmod +x dasel mv ./dasel /usr/local/bin/dasel # check appVersion with crate package metadata HELM_APP_VERSION=$(cat deployment/charts/Chart.yaml | dasel -r yaml 'appVersion') -CRATE_VERSION=$(cat Cargo.toml | dasel -r toml 'package.version') +CRATE_VERSION=$(cat Cargo.toml | dasel -r toml 'workspace.package.version') if [ "$HELM_APP_VERSION" != "$CRATE_VERSION" ]; then - err "crate version $CRATE_VERSION, doesn't match helm app version $HELM_APP_VERSION" - exit 1 + err "crate version $CRATE_VERSION, doesn't match helm app version $HELM_APP_VERSION" + exit 1 else - status "crate version matches helm chart app version $HELM_APP_VERSION" + status "crate version matches helm chart app version $HELM_APP_VERSION" fi diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 99b72565..8bb30e57 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,7 @@ jobs: - run: | curl -sSLf "$DASEL_VERSION" -L -o dasel && chmod +x dasel mv ./dasel /usr/local/bin/dasel - MIN_VERSION=$(cat Cargo.toml | dasel -r toml 'package.rust-version') + MIN_VERSION=$(cat Cargo.toml | dasel -r toml 'workspace.package.rust-version') RUST_VERSION="${{ env.RUST_VERSION }}" echo "Comparing minimum supported toolchain ($MIN_VERSION) with ci toolchain (RUST_VERSION)" test "$MIN_VERSION" == "$RUST_VERSION" diff --git a/Cargo.toml b/Cargo.toml index 7bdf9957..68477a11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ edition = "2021" homepage = "https://fuel.network/" license = "Apache-2.0" repository = "https://github.com/FuelLabs/fuel-block-committer" +rust-version = "1.77" publish = false [workspace.dependencies] diff --git a/committer/Cargo.toml b/committer/Cargo.toml index 47487a64..a3a9befa 100644 --- a/committer/Cargo.toml +++ b/committer/Cargo.toml @@ -7,6 +7,7 @@ license = { workspace = true } repository = { workspace = true } version = { workspace = true } publish = { workspace = true } +rust-version = { workspace = true } [dependencies] actix-web = { workspace = true, features = ["macros"] } diff --git a/e2e/Cargo.toml b/e2e/Cargo.toml index 4871fc69..fff6e70d 100644 --- a/e2e/Cargo.toml +++ b/e2e/Cargo.toml @@ -7,6 +7,7 @@ license = { workspace = true } repository = { workspace = true } version = { workspace = true } publish = { workspace = true } +rust-version = { workspace = true } [dev-dependencies] anyhow = { workspace = true } diff --git a/packages/eth/Cargo.toml b/packages/eth/Cargo.toml index c51ffb26..e5360619 100644 --- a/packages/eth/Cargo.toml +++ b/packages/eth/Cargo.toml @@ -7,6 +7,7 @@ license = { workspace = true } repository = { workspace = true } version = { workspace = true } publish = { workspace = true } +rust-version = { workspace = true } [dependencies] async-trait = { workspace = true } diff --git a/packages/fuel/Cargo.toml b/packages/fuel/Cargo.toml index 5621f4bc..13836028 100644 --- a/packages/fuel/Cargo.toml +++ b/packages/fuel/Cargo.toml @@ -7,6 +7,7 @@ license = { workspace = true } repository = { workspace = true } version = { workspace = true } publish = { workspace = true } +rust-version = { workspace = true } [dependencies] async-trait = { workspace = true } diff --git a/packages/metrics/Cargo.toml b/packages/metrics/Cargo.toml index 35bd210d..658ac3ed 100644 --- a/packages/metrics/Cargo.toml +++ b/packages/metrics/Cargo.toml @@ -7,6 +7,7 @@ license = { workspace = true } repository = { workspace = true } version = { workspace = true } publish = { workspace = true } +rust-version = { workspace = true } [dependencies] prometheus = { workspace = true } diff --git a/packages/ports/Cargo.toml b/packages/ports/Cargo.toml index 1178bdfc..dc738cf4 100644 --- a/packages/ports/Cargo.toml +++ b/packages/ports/Cargo.toml @@ -7,7 +7,7 @@ license = { workspace = true } repository = { workspace = true } version = { workspace = true } publish = { workspace = true } -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +rust-version = { workspace = true } [dependencies] async-trait = { workspace = true, optional = true } diff --git a/packages/services/Cargo.toml b/packages/services/Cargo.toml index 9e10926b..db8c1d25 100644 --- a/packages/services/Cargo.toml +++ b/packages/services/Cargo.toml @@ -7,6 +7,7 @@ license = { workspace = true } repository = { workspace = true } version = { workspace = true } publish = { workspace = true } +rust-version = { workspace = true } [dependencies] async-trait = { workspace = true } diff --git a/packages/storage/Cargo.toml b/packages/storage/Cargo.toml index b0bbd045..91a61f99 100644 --- a/packages/storage/Cargo.toml +++ b/packages/storage/Cargo.toml @@ -7,6 +7,7 @@ license = { workspace = true } repository = { workspace = true } version = { workspace = true } publish = { workspace = true } +rust-version = { workspace = true } [dependencies] async-trait = { workspace = true } From 80be4228ce5bb9a7db836d8318f7921cb4ef88d9 Mon Sep 17 00:00:00 2001 From: segfault_magnet Date: Mon, 6 May 2024 09:52:54 +0200 Subject: [PATCH 22/23] fix ci rust version --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8bb30e57..df8bf36b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ concurrency: env: DASEL_VERSION: https://github.com/TomWright/dasel/releases/download/v1.24.3/dasel_linux_amd64 - RUST_VERSION: 1.77.0 + RUST_VERSION: 1.77 FUEL_CORE_VERSION: 0.26.0 IMAGE_NAME: ${{ github.repository }} From e9d1eace938d6351ac093540ffa10f41a16c95b8 Mon Sep 17 00:00:00 2001 From: segfault_magnet Date: Mon, 6 May 2024 13:35:29 +0200 Subject: [PATCH 23/23] use port in tests --- packages/storage/src/lib.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/storage/src/lib.rs b/packages/storage/src/lib.rs index 640598ff..880e04fc 100644 --- a/packages/storage/src/lib.rs +++ b/packages/storage/src/lib.rs @@ -30,11 +30,14 @@ impl ports::storage::Storage for postgres::Postgres { #[cfg(test)] mod tests { - use ports::types::BlockSubmission; + use ports::{ + storage::{Error, Storage}, + types::BlockSubmission, + }; use rand::{thread_rng, Rng}; use storage as _; - use crate::{error::Error, PostgresProcess}; + use crate::PostgresProcess; fn random_non_zero_height() -> u32 { let mut rng = thread_rng(); @@ -49,13 +52,13 @@ mod tests { let latest_height = random_non_zero_height(); let latest_submission = given_incomplete_submission(latest_height); - db._insert(latest_submission.clone()).await.unwrap(); + db.insert(latest_submission.clone()).await.unwrap(); let older_submission = given_incomplete_submission(latest_height - 1); - db._insert(older_submission).await.unwrap(); + db.insert(older_submission).await.unwrap(); // when - let actual = db._submission_w_latest_block().await.unwrap().unwrap(); + let actual = db.submission_w_latest_block().await.unwrap().unwrap(); // then assert_eq!(actual, latest_submission); @@ -70,10 +73,10 @@ mod tests { let height = random_non_zero_height(); let submission = given_incomplete_submission(height); let block_hash = submission.block.hash; - db._insert(submission).await.unwrap(); + db.insert(submission).await.unwrap(); // when - let submission = db._set_submission_completed(block_hash).await.unwrap(); + let submission = db.set_submission_completed(block_hash).await.unwrap(); // then assert!(submission.completed); @@ -90,7 +93,7 @@ mod tests { let block_hash = submission.block.hash; // when - let result = db._set_submission_completed(block_hash).await; + let result = db.set_submission_completed(block_hash).await; // then let Err(Error::Database(msg)) = result else {