From 6871d13aa166ef5b50d1ccf951949393b285f4c3 Mon Sep 17 00:00:00 2001 From: Aatif Syed Date: Mon, 29 Apr 2024 18:37:48 +0100 Subject: [PATCH 1/9] mark: aatifsyed/refactor-api-info From 2dd86885f384bb3320889be111a3bb639e0fca6e Mon Sep 17 00:00:00 2001 From: Aatif Syed Date: Mon, 29 Apr 2024 18:37:50 +0100 Subject: [PATCH 2/9] wip --- src/cli/main.rs | 2 +- src/cli/subcommands/snapshot_cmd.rs | 2 +- src/cli/subcommands/state_cmd.rs | 7 +++---- src/rpc_client/mod.rs | 21 --------------------- src/tool/subcommands/api_cmd.rs | 8 ++++---- 5 files changed, 9 insertions(+), 31 deletions(-) diff --git a/src/cli/main.rs b/src/cli/main.rs index 37d13d39cc89..0ed33a29b899 100644 --- a/src/cli/main.rs +++ b/src/cli/main.rs @@ -44,7 +44,7 @@ where Subcommand::Net(cmd) => cmd.run(rpc::Client::from(api)).await, Subcommand::Sync(cmd) => cmd.run(rpc::Client::from(api)).await, Subcommand::Mpool(cmd) => cmd.run(api).await, - Subcommand::State(cmd) => cmd.run(api).await, + Subcommand::State(cmd) => cmd.run(rpc::Client::from(api)).await, Subcommand::Config(cmd) => cmd.run(&mut std::io::stdout()), Subcommand::Send(cmd) => cmd.run(rpc::Client::from(api)).await, Subcommand::Info(cmd) => cmd.run(api).await, diff --git a/src/cli/subcommands/snapshot_cmd.rs b/src/cli/subcommands/snapshot_cmd.rs index fa9cc3442ebf..7cd610e66f5d 100644 --- a/src/cli/subcommands/snapshot_cmd.rs +++ b/src/cli/subcommands/snapshot_cmd.rs @@ -114,7 +114,7 @@ impl SnapshotCommands { }); // Manually construct RpcRequest because snapshot export could // take a few hours on mainnet - let hash_result = api + let hash_result = client .call(ChainExport::request((params,))?.with_timeout(Duration::MAX)) .await?; diff --git a/src/cli/subcommands/state_cmd.rs b/src/cli/subcommands/state_cmd.rs index 1d499bc30d6a..24939d69c9a1 100644 --- a/src/cli/subcommands/state_cmd.rs +++ b/src/cli/subcommands/state_cmd.rs @@ -4,10 +4,9 @@ use std::path::PathBuf; use std::time::Duration; -use crate::rpc::RpcMethodExt as _; +use crate::rpc::{self, prelude::*}; use crate::shim::clock::ChainEpoch; use crate::shim::econ::TokenAmount; -use crate::{rpc::state::StateFetchRoot, rpc_client::ApiInfo}; use cid::Cid; use clap::Subcommand; use serde_tuple::{self, Deserialize_tuple, Serialize_tuple}; @@ -34,10 +33,10 @@ pub enum StateCommands { } impl StateCommands { - pub async fn run(self, api: ApiInfo) -> anyhow::Result<()> { + pub async fn run(self, client: rpc::Client) -> anyhow::Result<()> { match self { Self::Fetch { root, save_to_file } => { - let ret = api + let ret = client .call( StateFetchRoot::request((root, save_to_file))?.with_timeout(Duration::MAX), ) diff --git a/src/rpc_client/mod.rs b/src/rpc_client/mod.rs index 7d065e46f32e..194154633e70 100644 --- a/src/rpc_client/mod.rs +++ b/src/rpc_client/mod.rs @@ -89,27 +89,6 @@ impl ApiInfo { Err(it @ env::VarError::NotUnicode(_)) => Err(it.into()), } } - - // TODO(aatifsyed): https://github.com/ChainSafe/forest/issues/4032 - // This function should return rpc::ClientError, - // but that change should wait until _after_ all the methods - // have been migrated. - // - // In the limit, only rpc::Client should be making calls, - // and ApiInfo should be removed. - pub async fn call( - &self, - req: RpcRequest, - ) -> Result { - use jsonrpsee::core::ClientError; - match rpc::Client::from(self.clone()).call(req).await { - Ok(it) => Ok(it), - Err(e) => match e { - ClientError::Call(it) => Err(it.into()), - other => Err(ServerError::internal_error(other, None)), - }, - } - } } impl From for rpc::Client { diff --git a/src/tool/subcommands/api_cmd.rs b/src/tool/subcommands/api_cmd.rs index 36dadc8e6f9f..849e19f32345 100644 --- a/src/tool/subcommands/api_cmd.rs +++ b/src/tool/subcommands/api_cmd.rs @@ -15,9 +15,9 @@ use crate::message_pool::{MessagePool, MpoolRpcProvider}; use crate::networks::{parse_bootstrap_peers, ChainConfig, NetworkChain}; use crate::rpc::beacon::BeaconGetEntry; use crate::rpc::eth::Address as EthAddress; -use crate::rpc::eth::*; use crate::rpc::gas::GasEstimateGasLimit; use crate::rpc::types::{ApiTipsetKey, MessageFilter, MessageLookup}; +use crate::rpc::{self, eth::*}; use crate::rpc::{prelude::*, start_rpc, RPCState, ServerError}; use crate::rpc_client::{ApiInfo, RpcRequest, DEFAULT_PORT}; use crate::shim::address::{CurrentNetwork, Network}; @@ -367,9 +367,9 @@ impl RpcTest { self } - async fn run(&self, forest_api: &ApiInfo, lotus_api: &ApiInfo) -> TestResult { - let forest_resp = forest_api.call(self.request.clone()).await; - let lotus_resp = lotus_api.call(self.request.clone()).await; + async fn run(&self, forest: &rpc::Client, lotus: &rpc::Client) -> TestResult { + let forest_resp = forest.call(self.request.clone()).await; + let lotus_resp = lotus.call(self.request.clone()).await; let forest_json_str = if let Ok(forest_resp) = forest_resp.as_ref() { serde_json::to_string_pretty(forest_resp).ok() From 7ce4a7d30f636040c03b018b895420cb29123d48 Mon Sep 17 00:00:00 2001 From: Aatif Syed Date: Tue, 30 Apr 2024 12:12:24 +0100 Subject: [PATCH 3/9] refactor: EndpointStatus -> TestSummary --- src/tool/subcommands/api_cmd.rs | 102 ++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 44 deletions(-) diff --git a/src/tool/subcommands/api_cmd.rs b/src/tool/subcommands/api_cmd.rs index 849e19f32345..318d527fe83b 100644 --- a/src/tool/subcommands/api_cmd.rs +++ b/src/tool/subcommands/api_cmd.rs @@ -18,7 +18,7 @@ use crate::rpc::eth::Address as EthAddress; use crate::rpc::gas::GasEstimateGasLimit; use crate::rpc::types::{ApiTipsetKey, MessageFilter, MessageLookup}; use crate::rpc::{self, eth::*}; -use crate::rpc::{prelude::*, start_rpc, RPCState, ServerError}; +use crate::rpc::{prelude::*, start_rpc, RPCState}; use crate::rpc_client::{ApiInfo, RpcRequest, DEFAULT_PORT}; use crate::shim::address::{CurrentNetwork, Network}; use crate::shim::{ @@ -177,34 +177,44 @@ pub enum RunIgnored { All, } +/// Brief description of a single method call against a single host #[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] -enum EndpointStatus { - // RPC method is missing +enum TestSummary { + /// Server spoke JSON-RPC: no such method MissingMethod, - // Request isn't valid according to jsonrpc spec - InvalidRequest, - // Catch-all for errors on the node - InternalServerError, - // Unexpected JSON schema - InvalidJSON, - // Got response with the right JSON schema but it failed sanity checking - InvalidResponse, + /// Server spoke JSON-RPC: bad request (or other error) + Rejected, + /// Server doesn't seem to be speaking JSON-RPC + NotJsonRPC, + /// Transport or ask task management errors + InfraError, + /// Server returned JSON-RPC and it didn't match our schema + BadJson, + /// Server returned JSON-RPC and it matched our schema, but failed validation + CustomCheckFailed, Timeout, Valid, } -impl EndpointStatus { - fn from_json_error(err: ServerError) -> Self { - match err.known_code() { - ErrorCode::ParseError => Self::InvalidResponse, - ErrorCode::OversizedRequest => Self::InvalidRequest, - ErrorCode::InvalidRequest => Self::InvalidRequest, - ErrorCode::MethodNotFound => Self::MissingMethod, - it if it.code() == 0 && it.message().contains("timed out") => Self::Timeout, - _ => { - tracing::debug!(?err); - Self::InternalServerError - } +impl TestSummary { + fn from_err(err: &rpc::ClientError) -> Self { + match err { + rpc::ClientError::Call(it) => match it.code().into() { + ErrorCode::MethodNotFound => Self::MissingMethod, + _ => Self::Rejected, + }, + rpc::ClientError::ParseError(_) => Self::NotJsonRPC, + rpc::ClientError::RequestTimeout => Self::Timeout, + + rpc::ClientError::Transport(_) + | rpc::ClientError::RestartNeeded(_) + | rpc::ClientError::InvalidSubscriptionId + | rpc::ClientError::InvalidRequestId(_) + | rpc::ClientError::MaxSlotsExceeded + | rpc::ClientError::Custom(_) + | rpc::ClientError::HttpNotImplemented + | rpc::ClientError::EmptyBatchRequest(_) + | rpc::ClientError::RegisterMethod(_) => Self::InfraError, } } } @@ -250,9 +260,9 @@ impl std::fmt::Display for TestDump { struct TestResult { /// Forest result after calling the RPC method. - forest_status: EndpointStatus, + forest_status: TestSummary, /// Lotus result after calling the RPC method. - lotus_status: EndpointStatus, + lotus_status: TestSummary, /// Optional data dump if either status was invalid. test_dump: Option, } @@ -388,35 +398,39 @@ impl RpcTest { if (self.check_syntax)(forest.clone()) && (self.check_syntax)(lotus.clone()) => { let forest_status = if (self.check_semantics)(forest, lotus) { - EndpointStatus::Valid + TestSummary::Valid } else { - EndpointStatus::InvalidResponse + TestSummary::CustomCheckFailed }; - (forest_status, EndpointStatus::Valid) + (forest_status, TestSummary::Valid) } (forest_resp, lotus_resp) => { - let forest_status = - forest_resp.map_or_else(EndpointStatus::from_json_error, |value| { + let forest_status = forest_resp.map_or_else( + |e| TestSummary::from_err(&e), + |value| { if (self.check_syntax)(value) { - EndpointStatus::Valid + TestSummary::Valid } else { - EndpointStatus::InvalidJSON + TestSummary::BadJson } - }); - let lotus_status = - lotus_resp.map_or_else(EndpointStatus::from_json_error, |value| { + }, + ); + let lotus_status = lotus_resp.map_or_else( + |e| TestSummary::from_err(&e), + |value| { if (self.check_syntax)(value) { - EndpointStatus::Valid + TestSummary::Valid } else { - EndpointStatus::InvalidJSON + TestSummary::BadJson } - }); + }, + ); (forest_status, lotus_status) } }; - if forest_status == EndpointStatus::Valid && lotus_status == EndpointStatus::Valid { + if forest_status == TestSummary::Valid && lotus_status == TestSummary::Valid { TestResult { forest_status, lotus_status, @@ -1212,8 +1226,8 @@ async fn run_tests( let forest_status = test_result.forest_status; let lotus_status = test_result.lotus_status; let result_entry = (method_name, forest_status, lotus_status); - if (forest_status == EndpointStatus::Valid && lotus_status == EndpointStatus::Valid) - || (forest_status == EndpointStatus::Timeout && lotus_status == EndpointStatus::Timeout) + if (forest_status == TestSummary::Valid && lotus_status == TestSummary::Valid) + || (forest_status == TestSummary::Timeout && lotus_status == TestSummary::Timeout) { success_results .entry(result_entry) @@ -1250,8 +1264,8 @@ fn print_error_details(fail_details: &[TestDump]) { } fn print_test_results( - success_results: &HashMap<(&'static str, EndpointStatus, EndpointStatus), u32>, - failed_results: &HashMap<(&'static str, EndpointStatus, EndpointStatus), u32>, + success_results: &HashMap<(&'static str, TestSummary, TestSummary), u32>, + failed_results: &HashMap<(&'static str, TestSummary, TestSummary), u32>, ) { // Combine all results let mut combined_results = success_results.clone(); @@ -1265,7 +1279,7 @@ fn print_test_results( println!("{}", format_as_markdown(&results)); } -fn format_as_markdown(results: &[((&'static str, EndpointStatus, EndpointStatus), u32)]) -> String { +fn format_as_markdown(results: &[((&'static str, TestSummary, TestSummary), u32)]) -> String { let mut builder = Builder::default(); builder.push_record(["RPC Method", "Forest", "Lotus"]); From 68a302d4e42b9b47e905dcafd58667158b8e6176 Mon Sep 17 00:00:00 2001 From: Aatif Syed Date: Tue, 30 Apr 2024 19:04:52 +0100 Subject: [PATCH 4/9] wip --- src/auth/mod.rs | 8 +- src/cli/main.rs | 32 +++---- src/cli/subcommands/attach_cmd.rs | 1 - src/cli/subcommands/auth_cmd.rs | 17 ++-- src/cli/subcommands/info_cmd.rs | 4 +- src/cli/subcommands/mpool_cmd.rs | 4 +- src/cli/subcommands/snapshot_cmd.rs | 4 +- src/rpc/client.rs | 64 +++++++++---- src/rpc_client/chain_ops.rs | 8 +- src/rpc_client/mod.rs | 133 +-------------------------- src/rpc_client/state_ops.rs | 21 ----- src/tool/subcommands/api_cmd.rs | 53 +++++------ src/tool/subcommands/shed_cmd.rs | 4 +- src/utils/mod.rs | 86 ++++++++++++++++- src/wallet/main.rs | 10 +- src/wallet/subcommands/wallet_cmd.rs | 13 +-- 16 files changed, 205 insertions(+), 257 deletions(-) delete mode 100644 src/rpc_client/state_ops.rs diff --git a/src/auth/mod.rs b/src/auth/mod.rs index 06ea3e538c2c..0baf010c5fee 100644 --- a/src/auth/mod.rs +++ b/src/auth/mod.rs @@ -12,13 +12,13 @@ use thiserror::Error; /// constant string that is used to identify the JWT secret key in `KeyStore` pub const JWT_IDENTIFIER: &str = "auth-jwt-private"; /// Admin permissions -pub static ADMIN: &[&str] = &["read", "write", "sign", "admin"]; +pub const ADMIN: &[&str] = &["read", "write", "sign", "admin"]; /// Signing permissions -pub static SIGN: &[&str] = &["read", "write", "sign"]; +pub const SIGN: &[&str] = &["read", "write", "sign"]; /// Writing permissions -pub static WRITE: &[&str] = &["read", "write"]; +pub const WRITE: &[&str] = &["read", "write"]; /// Reading permissions -pub static READ: &[&str] = &["read"]; +pub const READ: &[&str] = &["read"]; /// Error enumeration for Authentication #[derive(Debug, Error, Serialize, Deserialize)] diff --git a/src/cli/main.rs b/src/cli/main.rs index 0ed33a29b899..d10839596cad 100644 --- a/src/cli/main.rs +++ b/src/cli/main.rs @@ -6,11 +6,8 @@ use std::ffi::OsString; use crate::cli::subcommands::Cli; use crate::cli_shared::logger; use crate::daemon::get_actual_chain_name; +use crate::rpc::{self, prelude::*}; use crate::shim::address::{CurrentNetwork, Network}; -use crate::{ - rpc::{self, prelude::*}, - rpc_client::ApiInfo, -}; use clap::Parser; use super::subcommands::Subcommand; @@ -22,7 +19,8 @@ where // Capture Cli inputs let Cli { token, cmd } = Cli::parse_from(args); - let api = ApiInfo::from_env()?.set_token(token); + let client = + rpc::Client::from_env_with_override("http://127.0.0.1:2345/".parse()?, token.as_deref())?; tokio::runtime::Builder::new_multi_thread() .enable_all() @@ -31,7 +29,7 @@ where .block_on(async { logger::setup_logger(&crate::cli_shared::cli::CliOpts::default()); - if let Ok(name) = StateNetworkName::call(&rpc::Client::from(api.clone()), ()).await { + if let Ok(name) = StateNetworkName::call(&client, ()).await { if get_actual_chain_name(&name) != "mainnet" { CurrentNetwork::set_global(Network::Testnet); } @@ -39,18 +37,18 @@ where // Run command match cmd { - Subcommand::Chain(cmd) => cmd.run(rpc::Client::from(api)).await, - Subcommand::Auth(cmd) => cmd.run(api).await, - Subcommand::Net(cmd) => cmd.run(rpc::Client::from(api)).await, - Subcommand::Sync(cmd) => cmd.run(rpc::Client::from(api)).await, - Subcommand::Mpool(cmd) => cmd.run(api).await, - Subcommand::State(cmd) => cmd.run(rpc::Client::from(api)).await, + Subcommand::Chain(cmd) => cmd.run(client).await, + Subcommand::Auth(cmd) => cmd.run(client).await, + Subcommand::Net(cmd) => cmd.run(client).await, + Subcommand::Sync(cmd) => cmd.run(client).await, + Subcommand::Mpool(cmd) => cmd.run(client).await, + Subcommand::State(cmd) => cmd.run(client).await, Subcommand::Config(cmd) => cmd.run(&mut std::io::stdout()), - Subcommand::Send(cmd) => cmd.run(rpc::Client::from(api)).await, - Subcommand::Info(cmd) => cmd.run(api).await, - Subcommand::Snapshot(cmd) => cmd.run(api).await, - Subcommand::Attach(cmd) => cmd.run(api), - Subcommand::Shutdown(cmd) => cmd.run(rpc::Client::from(api)).await, + Subcommand::Send(cmd) => cmd.run(client).await, + Subcommand::Info(cmd) => cmd.run(client).await, + Subcommand::Snapshot(cmd) => cmd.run(client).await, + Subcommand::Attach(cmd) => cmd.run(client), + Subcommand::Shutdown(cmd) => cmd.run(client).await, } }) } diff --git a/src/cli/subcommands/attach_cmd.rs b/src/cli/subcommands/attach_cmd.rs index ae13c50936c4..29e1f21766d7 100644 --- a/src/cli/subcommands/attach_cmd.rs +++ b/src/cli/subcommands/attach_cmd.rs @@ -1,7 +1,6 @@ // Copyright 2019-2024 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT -use crate::rpc_client::ApiInfo; use std::path::PathBuf; #[derive(Debug, clap::Args)] diff --git a/src/cli/subcommands/auth_cmd.rs b/src/cli/subcommands/auth_cmd.rs index 3c8f12139f96..bc15fe954508 100644 --- a/src/cli/subcommands/auth_cmd.rs +++ b/src/cli/subcommands/auth_cmd.rs @@ -1,14 +1,12 @@ // Copyright 2019-2024 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT -use crate::rpc_client::ApiInfo; use crate::{ auth::*, rpc::{self, auth::AuthNewParams, prelude::*}, }; use chrono::Duration; use clap::Subcommand; -use std::str::FromStr; use super::print_rpc_res_bytes; @@ -20,7 +18,7 @@ pub enum AuthCommands { #[arg(short, long)] perm: String, /// Token is revoked after this duration - #[arg(long, default_value_t = humantime::Duration::from_str("2 months").expect("infallible"))] + #[arg(long, default_value = "2 months")] expire_in: humantime::Duration, }, /// Get RPC API Information @@ -29,7 +27,7 @@ pub enum AuthCommands { #[arg(short, long)] perm: String, /// Token is revoked after this duration - #[arg(long, default_value_t = humantime::Duration::from_str("2 months").expect("infallible"))] + #[arg(long, default_value = "2 months")] expire_in: humantime::Duration, }, } @@ -48,8 +46,7 @@ fn process_perms(perm: String) -> Result, rpc::ServerError> { } impl AuthCommands { - pub async fn run(self, api: ApiInfo) -> anyhow::Result<()> { - let client = rpc::Client::from(api.clone()); + pub async fn run(self, client: rpc::Client) -> anyhow::Result<()> { match self { Self::CreateToken { perm, expire_in } => { let perm: String = perm.parse()?; @@ -62,9 +59,11 @@ impl AuthCommands { let perm: String = perm.parse()?; let perms = process_perms(perm)?; let token_exp = Duration::from_std(expire_in.into())?; - let token = AuthNew::call(&client, (AuthNewParams { perms, token_exp },)).await?; - let new_api = api.set_token(Some(String::from_utf8(token)?)); - println!("FULLNODE_API_INFO=\"{}\"", new_api); + let token = String::from_utf8( + AuthNew::call(&client, (AuthNewParams { perms, token_exp },)).await?, + )?; + let addr = multiaddr::from_url(client.base_url().as_str())?; + println!("FULLNODE_API_INFO=\"{}:{}\"", token, addr); Ok(()) } } diff --git a/src/cli/subcommands/info_cmd.rs b/src/cli/subcommands/info_cmd.rs index bd966ea6bb01..ebc436fccf47 100644 --- a/src/cli/subcommands/info_cmd.rs +++ b/src/cli/subcommands/info_cmd.rs @@ -6,7 +6,6 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH}; use crate::blocks::Tipset; use crate::cli::humantoken::TokenAmountPretty; use crate::rpc::{self, prelude::*}; -use crate::rpc_client::ApiInfo; use crate::shim::address::Address; use crate::shim::clock::{ChainEpoch, BLOCKS_PER_EPOCH, EPOCH_DURATION_SECONDS}; use crate::shim::econ::TokenAmount; @@ -148,8 +147,7 @@ impl NodeStatusInfo { } impl InfoCommand { - pub async fn run(self, api: ApiInfo) -> anyhow::Result<()> { - let client = rpc::Client::from(api.clone()); + pub async fn run(self, client: rpc::Client) -> anyhow::Result<()> { let (node_status, head, network, start_time, default_wallet_address) = tokio::try_join!( NodeStatus::call(&client, ()), ChainHead::call(&client, ()), diff --git a/src/cli/subcommands/mpool_cmd.rs b/src/cli/subcommands/mpool_cmd.rs index 10ee8bd0ee86..f6832febfdcf 100644 --- a/src/cli/subcommands/mpool_cmd.rs +++ b/src/cli/subcommands/mpool_cmd.rs @@ -7,7 +7,6 @@ use crate::blocks::Tipset; use crate::lotus_json::HasLotusJson as _; use crate::message::SignedMessage; use crate::rpc::{self, prelude::*, types::ApiTipsetKey}; -use crate::rpc_client::ApiInfo; use crate::shim::address::StrictAddress; use crate::shim::message::Message; use crate::shim::{address::Address, econ::TokenAmount}; @@ -208,8 +207,7 @@ fn print_stats(stats: &[MpStat], basefee_lookback: u32) { } impl MpoolCommands { - pub async fn run(self, api: ApiInfo) -> anyhow::Result<()> { - let client = rpc::Client::from(api.clone()); + pub async fn run(self, client: rpc::Client) -> anyhow::Result<()> { match self { Self::Pending { local, diff --git a/src/cli/subcommands/snapshot_cmd.rs b/src/cli/subcommands/snapshot_cmd.rs index 7cd610e66f5d..6c7dd2b74b94 100644 --- a/src/cli/subcommands/snapshot_cmd.rs +++ b/src/cli/subcommands/snapshot_cmd.rs @@ -6,7 +6,6 @@ use crate::chain_sync::SyncConfig; use crate::cli_shared::snapshot::{self, TrustedVendor}; use crate::rpc::types::ApiTipsetKey; use crate::rpc::{self, chain::ChainExportParams, prelude::*}; -use crate::rpc_client::ApiInfo; use anyhow::Context as _; use chrono::DateTime; use clap::Subcommand; @@ -39,8 +38,7 @@ pub enum SnapshotCommands { } impl SnapshotCommands { - pub async fn run(self, api: ApiInfo) -> anyhow::Result<()> { - let client = rpc::Client::from(api.clone()); + pub async fn run(self, client: rpc::Client) -> anyhow::Result<()> { match self { Self::Export { output_path, diff --git a/src/rpc/client.rs b/src/rpc/client.rs index b6b82cbf6bca..0143b104859a 100644 --- a/src/rpc/client.rs +++ b/src/rpc/client.rs @@ -11,9 +11,11 @@ //! - communication protocols (`ws`, `http`). //! - Support per-request timeouts. +use std::env; use std::fmt::{self, Debug}; use std::time::Duration; +use anyhow::bail; use http02::{header, HeaderMap, HeaderValue}; use jsonrpsee::core::client::ClientT as _; use jsonrpsee::core::params::{ArrayParams, ObjectParams}; @@ -36,14 +38,44 @@ pub struct Client { } impl Client { - pub fn new(base_url: Url, token: impl Into>) -> Self { + /// Use either the url in the environment or the provided default. + /// + /// If `token` is provided, use that over a token in the above. + pub fn from_env_with_override(mut default: Url, token: Option<&str>) -> anyhow::Result { + let override_token = |url: &mut Url| -> anyhow::Result<()> { + if token.is_some() { + if url.set_password(token).is_err() { + bail!("couldn't set override password") + } + } + Ok(()) + }; + match env::var("FULLNODE_API_INFO") { + Ok(it) => { + let crate::utils::UrlFromMultiAddr(mut url) = it.parse()?; + override_token(&mut url)?; + todo!() + } + Err(env::VarError::NotPresent) => { + override_token(&mut default)?; + Ok(Self::from_url(default)) + } + Err(e @ env::VarError::NotUnicode(_)) => bail!(e), + } + } + pub fn from_url(mut base_url: Url) -> Self { + let token = base_url.password().map(Into::into); + let _defer = base_url.set_password(None); Self { + token, base_url, - token: token.into(), v0: Default::default(), v1: Default::default(), } } + pub fn base_url(&self) -> &Url { + &self.base_url + } pub async fn call( &self, req: crate::rpc_client::RpcRequest, @@ -140,7 +172,7 @@ fn trace_params(params: impl jsonrpsee::core::traits::ToRpcParams) { /// can be made using [`jsonrpsee`] primitives. struct UrlClient { url: Url, - inner: OneClientInner, + inner: UrlClientInner, } impl Debug for UrlClient { @@ -169,7 +201,7 @@ impl UrlClient { None => HeaderMap::new(), }; let inner = match url.scheme() { - "ws" | "wss" => OneClientInner::Ws( + "ws" | "wss" => UrlClientInner::Ws( jsonrpsee::ws_client::WsClientBuilder::new() .set_headers(headers) .max_request_size(MAX_REQUEST_BODY_SIZE) @@ -178,7 +210,7 @@ impl UrlClient { .build(&url) .await?, ), - "http" | "https" => OneClientInner::Https( + "http" | "https" => UrlClientInner::Https( jsonrpsee::http_client::HttpClientBuilder::new() .set_headers(headers) .max_request_size(MAX_REQUEST_BODY_SIZE) @@ -197,7 +229,7 @@ impl UrlClient { } } -enum OneClientInner { +enum UrlClientInner { Ws(jsonrpsee::ws_client::WsClient), Https(jsonrpsee::http_client::HttpClient), } @@ -210,8 +242,8 @@ impl jsonrpsee::core::client::ClientT for UrlClient { params: P, ) -> Result<(), jsonrpsee::core::ClientError> { match &self.inner { - OneClientInner::Ws(it) => it.notification(method, params).await, - OneClientInner::Https(it) => it.notification(method, params).await, + UrlClientInner::Ws(it) => it.notification(method, params).await, + UrlClientInner::Https(it) => it.notification(method, params).await, } } async fn request( @@ -220,8 +252,8 @@ impl jsonrpsee::core::client::ClientT for UrlClient { params: P, ) -> Result { match &self.inner { - OneClientInner::Ws(it) => it.request(method, params).await, - OneClientInner::Https(it) => it.request(method, params).await, + UrlClientInner::Ws(it) => it.request(method, params).await, + UrlClientInner::Https(it) => it.request(method, params).await, } } async fn batch_request<'a, R: DeserializeOwned + 'a + std::fmt::Debug>( @@ -229,8 +261,8 @@ impl jsonrpsee::core::client::ClientT for UrlClient { batch: jsonrpsee::core::params::BatchRequestBuilder<'a>, ) -> Result, jsonrpsee::core::ClientError> { match &self.inner { - OneClientInner::Ws(it) => it.batch_request(batch).await, - OneClientInner::Https(it) => it.batch_request(batch).await, + UrlClientInner::Ws(it) => it.batch_request(batch).await, + UrlClientInner::Https(it) => it.batch_request(batch).await, } } } @@ -248,11 +280,11 @@ impl jsonrpsee::core::client::SubscriptionClientT for UrlClient { Notif: DeserializeOwned, { match &self.inner { - OneClientInner::Ws(it) => { + UrlClientInner::Ws(it) => { it.subscribe(subscribe_method, params, unsubscribe_method) .await } - OneClientInner::Https(it) => { + UrlClientInner::Https(it) => { it.subscribe(subscribe_method, params, unsubscribe_method) .await } @@ -266,8 +298,8 @@ impl jsonrpsee::core::client::SubscriptionClientT for UrlClient { Notif: DeserializeOwned, { match &self.inner { - OneClientInner::Ws(it) => it.subscribe_to_method(method).await, - OneClientInner::Https(it) => it.subscribe_to_method(method).await, + UrlClientInner::Ws(it) => it.subscribe_to_method(method).await, + UrlClientInner::Https(it) => it.subscribe_to_method(method).await, } } } diff --git a/src/rpc_client/chain_ops.rs b/src/rpc_client/chain_ops.rs index 7209893cc3a9..764f60fdb60b 100644 --- a/src/rpc_client/chain_ops.rs +++ b/src/rpc_client/chain_ops.rs @@ -1,14 +1,12 @@ // Copyright 2019-2024 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT -use super::{ApiInfo, RpcRequest}; +use super::RpcRequest; /// Client calls should use [`crate::rpc::RpcMethod`]'s way of constructing [`RpcRequest`]. /// `Filecoin.ChainNotify` is an exception because it is a subscription method, so falls outside /// of that abstraction. /// See for more information. -impl ApiInfo { - pub fn chain_notify_req() -> RpcRequest<()> { - RpcRequest::new(crate::rpc::chain::CHAIN_NOTIFY, ()) - } +pub fn chain_notify_req() -> RpcRequest<()> { + RpcRequest::new(crate::rpc::chain::CHAIN_NOTIFY, ()) } diff --git a/src/rpc_client/mod.rs b/src/rpc_client/mod.rs index 194154633e70..7a9961fd8d35 100644 --- a/src/rpc_client/mod.rs +++ b/src/rpc_client/mod.rs @@ -2,16 +2,12 @@ // SPDX-License-Identifier: Apache-2.0, MIT mod chain_ops; -mod state_ops; -use crate::libp2p::{Multiaddr, Protocol}; use crate::lotus_json::HasLotusJson; -use crate::rpc::{self, ApiVersion, ServerError}; -use anyhow::Context as _; +use crate::rpc::ApiVersion; use jsonrpsee::core::traits::ToRpcParams; use once_cell::sync::Lazy; -use std::{env, fmt, marker::PhantomData, str::FromStr, time::Duration}; -use url::Url; +use std::{env, marker::PhantomData, time::Duration}; pub const API_INFO_KEY: &str = "FULLNODE_API_INFO"; pub const DEFAULT_PORT: u16 = 2345; @@ -19,84 +15,12 @@ pub const DEFAULT_PORT: u16 = 2345; /// Default timeout for RPC requests. Doesn't apply to all requests, e.g., snapshot export which /// has no timeout. pub static DEFAULT_TIMEOUT: Lazy = Lazy::new(|| { - std::env::var("FOREST_RPC_DEFAULT_TIMEOUT") + env::var("FOREST_RPC_DEFAULT_TIMEOUT") .ok() .and_then(|it| Duration::from_secs(it.parse().ok()?).into()) .unwrap_or(Duration::from_secs(60)) }); -/// Token and URL for an [`rpc::Client`]. -#[derive(Clone, Debug)] -pub struct ApiInfo { - multiaddr: Multiaddr, - url: Url, - pub token: Option, -} - -impl fmt::Display for ApiInfo { - /// Convert an [`ApiInfo`] to a string - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(token) = &self.token { - token.fmt(f)?; - write!(f, ":")?; - } - self.multiaddr.fmt(f)?; - Ok(()) - } -} - -impl FromStr for ApiInfo { - type Err = anyhow::Error; - fn from_str(s: &str) -> Result { - let (token, host) = match s.split_once(':') { - Some((token, host)) => (Some(token), host), - None => (None, s), - }; - let multiaddr = host.parse()?; - let url = multiaddr2url(&multiaddr).context("couldn't convert multiaddr to URL")?; - Ok(ApiInfo { - multiaddr, - url, - token: token.map(String::from), - }) - } -} - -impl Default for ApiInfo { - fn default() -> Self { - "/ip4/127.0.0.1/tcp/2345/http".parse().unwrap() - } -} - -impl ApiInfo { - pub fn scheme(&self) -> &str { - self.url.scheme() - } - // Update API handle with new (optional) token - pub fn set_token(self, token: Option) -> Self { - ApiInfo { - token: token.or(self.token), - ..self - } - } - - // Get API_INFO environment variable if exists, otherwise, use default - // multiaddress. Fails if the environment variable is malformed. - pub fn from_env() -> anyhow::Result { - match env::var(API_INFO_KEY) { - Ok(it) => it.parse(), - Err(env::VarError::NotPresent) => Ok(Self::default()), - Err(it @ env::VarError::NotUnicode(_)) => Err(it.into()), - } - } -} - -impl From for rpc::Client { - fn from(value: ApiInfo) -> Self { - rpc::Client::new(value.url, value.token) - } -} - /// An `RpcRequest` is an at-rest description of a remote procedure call. It can /// be invoked using `ApiInfo::call`. /// @@ -152,54 +76,3 @@ impl ToRpcParams for RpcRequest { Ok(Some(serde_json::value::to_raw_value(&self.params)?)) } } - -/// `"/dns/example.com/tcp/8080/http" -> "http://example.com:8080/"` -/// -/// Returns [`None`] on unsupported formats, or if there is a URL parsing error. -/// -/// Note that [`Multiaddr`]s do NOT support a (URL) `path`, so that must be handled -/// out-of-band. -fn multiaddr2url(m: &Multiaddr) -> Option { - let mut components = m.iter().peekable(); - let host = match components.next()? { - Protocol::Dns(it) | Protocol::Dns4(it) | Protocol::Dns6(it) | Protocol::Dnsaddr(it) => { - it.to_string() - } - Protocol::Ip4(it) => it.to_string(), - Protocol::Ip6(it) => it.to_string(), - _ => return None, - }; - let port = components - .next_if(|it| matches!(it, Protocol::Tcp(_))) - .map(|it| match it { - Protocol::Tcp(port) => port, - _ => unreachable!(), - }); - // ENHANCEMENT: could recognise `Tcp/443/Tls` as `https` - let scheme = match components.next()? { - Protocol::Http => "http", - Protocol::Https => "https", - Protocol::Ws(it) if it == "/" => "ws", - Protocol::Wss(it) if it == "/" => "wss", - _ => return None, - }; - let None = components.next() else { return None }; - let parse_me = match port { - Some(port) => format!("{}://{}:{}", scheme, host, port), - None => format!("{}://{}", scheme, host), - }; - parse_me.parse().ok() -} - -#[test] -fn test_multiaddr2url() { - #[track_caller] - fn do_test(input: &str, expected: &str) { - let multiaddr = input.parse().unwrap(); - let url = multiaddr2url(&multiaddr).unwrap(); - assert_eq!(url.as_str(), expected); - } - do_test("/dns/example.com/http", "http://example.com/"); - do_test("/dns/example.com/tcp/8080/http", "http://example.com:8080/"); - do_test("/ip4/127.0.0.1/wss", "wss://127.0.0.1/"); -} diff --git a/src/rpc_client/state_ops.rs b/src/rpc_client/state_ops.rs deleted file mode 100644 index e2d2101292bf..000000000000 --- a/src/rpc_client/state_ops.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2019-2024 ChainSafe Systems -// SPDX-License-Identifier: Apache-2.0, MIT - -use super::{ApiInfo, RpcRequest}; -use crate::rpc::types::*; -use crate::{ - rpc::state::*, - shim::{address::Address, message::MethodNum}, -}; -use libipld_core::ipld::Ipld; - -impl ApiInfo { - pub fn state_decode_params_req( - recipient: Address, - method_number: MethodNum, - params: Vec, - tsk: ApiTipsetKey, - ) -> RpcRequest { - RpcRequest::new(STATE_DECODE_PARAMS, (recipient, method_number, params, tsk)) - } -} diff --git a/src/tool/subcommands/api_cmd.rs b/src/tool/subcommands/api_cmd.rs index 318d527fe83b..5b37d5462b62 100644 --- a/src/tool/subcommands/api_cmd.rs +++ b/src/tool/subcommands/api_cmd.rs @@ -19,7 +19,7 @@ use crate::rpc::gas::GasEstimateGasLimit; use crate::rpc::types::{ApiTipsetKey, MessageFilter, MessageLookup}; use crate::rpc::{self, eth::*}; use crate::rpc::{prelude::*, start_rpc, RPCState}; -use crate::rpc_client::{ApiInfo, RpcRequest, DEFAULT_PORT}; +use crate::rpc_client::{RpcRequest, DEFAULT_PORT}; use crate::shim::address::{CurrentNetwork, Network}; use crate::shim::{ address::{Address, Protocol}, @@ -29,6 +29,7 @@ use crate::shim::{ state_tree::StateTree, }; use crate::state_manager::StateManager; +use crate::utils::UrlFromMultiAddr; use ahash::HashMap; use anyhow::Context as _; use cid::Cid; @@ -86,11 +87,11 @@ pub enum ApiCommands { /// Compare Compare { /// Forest address - #[clap(long, default_value_t = ApiInfo::from_str("/ip4/127.0.0.1/tcp/2345/http").expect("infallible"))] - forest: ApiInfo, + #[clap(long, default_value = "/ip4/127.0.0.1/tcp/2345/http")] + forest: UrlFromMultiAddr, /// Lotus address - #[clap(long, default_value_t = ApiInfo::from_str("/ip4/127.0.0.1/tcp/1234/http").expect("infallible"))] - lotus: ApiInfo, + #[clap(long, default_value = "/ip4/127.0.0.1/tcp/1234/http")] + lotus: UrlFromMultiAddr, /// Snapshot input paths. Supports `.car`, `.car.zst`, and `.forest.car.zst`. #[arg()] snapshot_files: Vec, @@ -143,8 +144,8 @@ impl ApiCommands { .await?; } Self::Compare { - forest, - lotus, + forest: UrlFromMultiAddr(forest), + lotus: UrlFromMultiAddr(lotus), snapshot_files, filter, filter_file, @@ -162,7 +163,13 @@ impl ApiCommands { max_concurrent_requests, }; - compare_apis(forest, lotus, snapshot_files, config).await? + compare_apis( + rpc::Client::from_url(forest), + rpc::Client::from_url(lotus), + snapshot_files, + config, + ) + .await? } } Ok(()) @@ -795,15 +802,6 @@ fn state_tests_with_tipset( ))?), RpcTest::identity(StateCall::request((msg.clone(), tipset.key().into()))?), ]); - if !msg.params().is_empty() { - tests.extend([RpcTest::identity(ApiInfo::state_decode_params_req( - msg.to(), - msg.method_num(), - msg.params().to_vec(), - tipset.key().into(), - )).ignore("Difficult to implement. Tracking issue: https://github.com/ChainSafe/forest/issues/3769") - ]); - } } } @@ -937,11 +935,6 @@ fn snapshot_tests(store: Arc, n_tipsets: usize) -> anyhow::Result Vec { - let test = RpcTest::identity(ApiInfo::chain_notify_req()).ignore("Not implemented yet"); - vec![test] -} - fn sample_message_cids<'a>( bls_messages: impl Iterator + 'a, secp_messages: impl Iterator + 'a, @@ -994,8 +987,8 @@ fn sample_messages<'a>( /// The number after a method name indicates how many times an RPC call was tested. #[allow(clippy::too_many_arguments)] async fn compare_apis( - forest: ApiInfo, - lotus: ApiInfo, + forest: rpc::Client, + lotus: rpc::Client, snapshot_files: Vec, config: ApiTestFlags, ) -> anyhow::Result<()> { @@ -1017,13 +1010,9 @@ async fn compare_apis( tests.extend(snapshot_tests(store, config.n_tipsets)?); } - if matches!(forest.scheme(), "ws" | "wss") && matches!(lotus.scheme(), "ws" | "wss") { - tests.extend(websocket_tests()) - } - tests.sort_by_key(|test| test.request.method_name); - run_tests(tests, &forest, &lotus, &config).await + run_tests(tests, forest, lotus, &config).await } async fn start_offline_server( @@ -1178,10 +1167,12 @@ where async fn run_tests( tests: impl IntoIterator, - forest: &ApiInfo, - lotus: &ApiInfo, + forest: impl Into>, + lotus: impl Into>, config: &ApiTestFlags, ) -> anyhow::Result<()> { + let forest = Into::>::into(forest); + let lotus = Into::>::into(lotus); let semaphore = Arc::new(Semaphore::new(config.max_concurrent_requests)); let mut futures = FuturesUnordered::new(); diff --git a/src/tool/subcommands/shed_cmd.rs b/src/tool/subcommands/shed_cmd.rs index 2c25a427f63a..83851fa2b5db 100644 --- a/src/tool/subcommands/shed_cmd.rs +++ b/src/tool/subcommands/shed_cmd.rs @@ -11,7 +11,6 @@ use crate::{ types::ApiTipsetKey, RpcMethodExt as _, }, - rpc_client::ApiInfo, }; use anyhow::Context as _; use base64::{prelude::BASE64_STANDARD, Engine}; @@ -54,10 +53,9 @@ pub enum ShedCommands { } impl ShedCommands { - pub async fn run(self) -> anyhow::Result<()> { + pub async fn run(self, client: rpc::Client) -> anyhow::Result<()> { match self { ShedCommands::SummarizeTipsets { height, ancestors } => { - let client = rpc::Client::from(ApiInfo::from_env()?); let head = ChainHead::call(&client, ()).await?; let end_height = match height { Some(it) => it, diff --git a/src/utils/mod.rs b/src/utils/mod.rs index af43e7c95c2d..e7bdfe480a37 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -13,13 +13,97 @@ pub mod reqwest_resume; pub mod stream; pub mod version; +use anyhow::{bail, Context as _}; use futures::{ future::{pending, FusedFuture}, select, Future, FutureExt, }; -use std::{pin::Pin, time::Duration}; +use multiaddr::{Multiaddr, Protocol}; +use std::{pin::Pin, str::FromStr, time::Duration}; use tokio::time::sleep; use tracing::error; +use url::Url; + +/// "hunter2:/ip4/127.0.0.1/wss" -> "wss://:hunter2@127.0.0.1/" +pub struct UrlFromMultiAddr(pub Url); + +impl FromStr for UrlFromMultiAddr { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + let (p, s) = match s.split_once(':') { + Some((first, rest)) => (Some(first), rest), + None => (None, s), + }; + let m = Multiaddr::from_str(s).context("invalid multiaddr")?; + let mut u = multiaddr2url(&m).context("unsupported multiaddr")?; + if u.set_password(p).is_err() { + bail!("unsupported password") + } + Ok(Self(u)) + } +} + +/// `"/dns/example.com/tcp/8080/http" -> "http://example.com:8080/"` +/// +/// Returns [`None`] on unsupported formats, or if there is a URL parsing error. +/// +/// Note that [`Multiaddr`]s do NOT support a (URL) `path`, so that must be handled +/// out-of-band. +fn multiaddr2url(m: &Multiaddr) -> Option { + let mut components = m.iter().peekable(); + let host = match components.next()? { + Protocol::Dns(it) | Protocol::Dns4(it) | Protocol::Dns6(it) | Protocol::Dnsaddr(it) => { + it.to_string() + } + Protocol::Ip4(it) => it.to_string(), + Protocol::Ip6(it) => it.to_string(), + _ => return None, + }; + let port = components + .next_if(|it| matches!(it, Protocol::Tcp(_))) + .map(|it| match it { + Protocol::Tcp(port) => port, + _ => unreachable!(), + }); + // ENHANCEMENT: could recognise `Tcp/443/Tls` as `https` + let scheme = match components.next()? { + Protocol::Http => "http", + Protocol::Https => "https", + Protocol::Ws(it) if it == "/" => "ws", + Protocol::Wss(it) if it == "/" => "wss", + _ => return None, + }; + let None = components.next() else { return None }; + let parse_me = match port { + Some(port) => format!("{}://{}:{}", scheme, host, port), + None => format!("{}://{}", scheme, host), + }; + parse_me.parse().ok() +} + +#[test] +fn test_url_from_multiaddr() { + #[track_caller] + fn do_test(input: &str, expected: &str) { + let UrlFromMultiAddr(url) = input.parse().unwrap(); + assert_eq!(url.as_str(), expected); + } + do_test("/dns/example.com/http", "http://example.com/"); + do_test("/dns/example.com/tcp/8080/http", "http://example.com:8080/"); + do_test("/ip4/127.0.0.1/wss", "wss://127.0.0.1/"); + + // with password + do_test( + "hunter2:/dns/example.com/http", + "http://:hunter2@example.com/", + ); + do_test( + "hunter2:/dns/example.com/tcp/8080/http", + "http://:hunter2@example.com:8080/", + ); + do_test("hunter2:/ip4/127.0.0.1/wss", "wss://:hunter2@127.0.0.1/"); +} /// Keep running the future created by `make_fut` until the timeout or retry /// limit in `args` is reached. diff --git a/src/wallet/main.rs b/src/wallet/main.rs index 7b7f337a5bef..5851b9a42b7f 100644 --- a/src/wallet/main.rs +++ b/src/wallet/main.rs @@ -6,7 +6,6 @@ use std::ffi::OsString; use super::subcommands::Cli; use crate::networks::NetworkChain; use crate::rpc::{self, prelude::*}; -use crate::rpc_client::ApiInfo; use crate::shim::address::{CurrentNetwork, Network}; use clap::Parser; use std::str::FromStr; @@ -23,18 +22,21 @@ where cmd, } = Cli::parse_from(args); - let api = ApiInfo::from_env()?.set_token(opts.token.clone()); + let client = rpc::Client::from_env_with_override( + "http://127.0.0.1:2345/".parse()?, + opts.token.as_deref(), + )?; tokio::runtime::Builder::new_multi_thread() .enable_all() .build()? .block_on(async { - let name = StateNetworkName::call(&rpc::Client::from(api.clone()), ()).await?; + let name = StateNetworkName::call(&client, ()).await?; let chain = NetworkChain::from_str(&name)?; if chain.is_testnet() { CurrentNetwork::set_global(Network::Testnet); } // Run command - cmd.run(api, remote_wallet, encrypt).await + cmd.run(client, remote_wallet, encrypt).await }) } diff --git a/src/wallet/subcommands/wallet_cmd.rs b/src/wallet/subcommands/wallet_cmd.rs index 40c1b63f63d2..2fe26595a1bc 100644 --- a/src/wallet/subcommands/wallet_cmd.rs +++ b/src/wallet/subcommands/wallet_cmd.rs @@ -7,6 +7,7 @@ use std::{ str::{self, FromStr}, }; +use crate::key_management::{Key, KeyInfo}; use crate::{ cli::humantoken, message::SignedMessage, @@ -17,10 +18,6 @@ use crate::{ shim::address::Address, ENCRYPTED_KEYSTORE_NAME, }; -use crate::{ - key_management::{Key, KeyInfo}, - rpc_client::ApiInfo, -}; use crate::{ lotus_json::HasLotusJson as _, rpc::{self, prelude::*}, @@ -307,8 +304,12 @@ pub enum WalletCommands { }, } impl WalletCommands { - pub async fn run(self, api: ApiInfo, remote_wallet: bool, encrypt: bool) -> anyhow::Result<()> { - let client = rpc::Client::from(api.clone()); + pub async fn run( + self, + client: rpc::Client, + remote_wallet: bool, + encrypt: bool, + ) -> anyhow::Result<()> { let mut backend = if remote_wallet { WalletBackend::new_remote(client) } else { From 61d4c12cd942087aa4393c5812f06b4acfa7cf15 Mon Sep 17 00:00:00 2001 From: Aatif Syed Date: Tue, 30 Apr 2024 20:02:33 +0100 Subject: [PATCH 5/9] wip --- src/cli/main.rs | 3 +- src/cli/subcommands/attach_cmd.rs | 47 ++++++++++++++++--------------- src/rpc/client.rs | 35 +++++++++++------------ src/tool/main.rs | 4 ++- src/utils/mod.rs | 1 + src/wallet/main.rs | 5 +--- 6 files changed, 46 insertions(+), 49 deletions(-) diff --git a/src/cli/main.rs b/src/cli/main.rs index d10839596cad..52145840d48e 100644 --- a/src/cli/main.rs +++ b/src/cli/main.rs @@ -19,8 +19,7 @@ where // Capture Cli inputs let Cli { token, cmd } = Cli::parse_from(args); - let client = - rpc::Client::from_env_with_override("http://127.0.0.1:2345/".parse()?, token.as_deref())?; + let client = rpc::Client::default_or_from_env(token.as_deref())?; tokio::runtime::Builder::new_multi_thread() .enable_all() diff --git a/src/cli/subcommands/attach_cmd.rs b/src/cli/subcommands/attach_cmd.rs index 29e1f21766d7..0d935408c247 100644 --- a/src/cli/subcommands/attach_cmd.rs +++ b/src/cli/subcommands/attach_cmd.rs @@ -1,6 +1,7 @@ // Copyright 2019-2024 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT +use crate::rpc::{self, prelude::*}; use std::path::PathBuf; #[derive(Debug, clap::Args)] @@ -16,14 +17,14 @@ pub struct AttachCommand { impl AttachCommand { #[cfg(not(feature = "attach"))] - pub fn run(self, _api: ApiInfo) -> anyhow::Result<()> { + pub fn run(self, _: rpc::Client) -> anyhow::Result<()> { tracing::warn!("`attach` command is unavailable, forest binaries need to be recompiled with `attach` feature"); Ok(()) } #[cfg(feature = "attach")] - pub fn run(self, api: ApiInfo) -> anyhow::Result<()> { - self.run_inner(api) + pub fn run(self, client: rpc::Client) -> anyhow::Result<()> { + self.run_inner(client) } } @@ -32,16 +33,13 @@ mod inner { use std::{ fs::{canonicalize, read_to_string, OpenOptions}, str::FromStr, + sync::Arc, }; use super::*; + use crate::chain::ChainEpochDelta; use crate::chain_sync::SyncStage; - use crate::rpc_client::*; use crate::shim::{address::Address, message::Message}; - use crate::{ - chain::ChainEpochDelta, - rpc::{self, prelude::*}, - }; use crate::{cli::humantoken, message::SignedMessage}; use boa_engine::{ object::{builtins::JsArray, FunctionObjectBuilder}, @@ -211,9 +209,9 @@ mod inner { fn bind_async( context: &mut Context, - api: &ApiInfo, + client: Arc, name: &'static str, - req: impl Fn(T, ApiInfo) -> Fut + 'static, + req: impl Fn(T, Arc) -> Fut + 'static, ) where Fut: Future>, { @@ -222,7 +220,7 @@ mod inner { // not get traced. We're safe because we do not use any GC'ed variables. let js_func = FunctionObjectBuilder::new(context.realm(), unsafe { NativeFunction::from_closure({ - let api = api.clone(); + let client = client.clone(); move |_this, params, context| { let handle = tokio::runtime::Handle::current(); @@ -237,7 +235,7 @@ mod inner { let args = serde_json::from_value( value.to_json(context).map_err(|e| anyhow::anyhow!("{e}"))?, )?; - handle.block_on(req(args, api.clone())) + handle.block_on(req(args, client.clone())) }); check_result(context, result) } @@ -269,7 +267,7 @@ mod inner { async fn send_message( params: SendMessageParams, - api: ApiInfo, + client: Arc, ) -> anyhow::Result { let (from, to, value) = params; @@ -278,23 +276,25 @@ mod inner { Address::from_str(&to)?, humantoken::parse(&value)?, // Convert forest_shim::TokenAmount to TokenAmount3 ); - Ok(MpoolPushMessage::call(&rpc::Client::from(api), (message, None)).await?) + Ok(MpoolPushMessage::call(&*client, (message, None)).await?) } type SleepParams = (u64,); type SleepResult = (); - async fn sleep(params: SleepParams, _api: ApiInfo) -> anyhow::Result { + async fn sleep(params: SleepParams, _: Arc) -> anyhow::Result { let secs = params.0; time::sleep(time::Duration::from_secs(secs)).await; Ok(()) } - async fn sleep_tipsets(epochs: ChainEpochDelta, api: ApiInfo) -> anyhow::Result<()> { - let client = rpc::Client::from(api); + async fn sleep_tipsets( + epochs: ChainEpochDelta, + client: Arc, + ) -> anyhow::Result<()> { let mut epoch = None; loop { - let state = SyncState::call(&client, ()).await?; + let state = SyncState::call(&*client, ()).await?; if state.active_syncs.first().stage() == SyncStage::Complete { if let Some(prev) = epoch { let curr = state.active_syncs.first().epoch(); @@ -310,7 +310,8 @@ mod inner { } impl AttachCommand { - fn setup_context(&self, context: &mut Context, api: ApiInfo) { + fn setup_context(&self, context: &mut Context, client: rpc::Client) { + let client = Arc::new(client); let console = Console::init(context); context .register_global_property(JsString::from(Console::NAME), console, Attribute::all()) @@ -343,9 +344,9 @@ mod inner { bind_request!(context, api,); // Bind send_message, sleep, sleep_tipsets - bind_async(context, &api, "send_message", send_message); - bind_async(context, &api, "seep", sleep); - bind_async(context, &api, "sleep_tipsets", sleep_tipsets); + bind_async(context, client.clone(), "send_message", send_message); + bind_async(context, client.clone(), "seep", sleep); + bind_async(context, client.clone(), "sleep_tipsets", sleep_tipsets); } fn import_prelude(&self, context: &mut Context) -> anyhow::Result<()> { @@ -367,7 +368,7 @@ mod inner { Ok(()) } - pub(super) fn run_inner(self, api: ApiInfo) -> anyhow::Result<()> { + pub(super) fn run_inner(self, api: rpc::Client) -> anyhow::Result<()> { let mut context = Context::default(); self.setup_context(&mut context, api); diff --git a/src/rpc/client.rs b/src/rpc/client.rs index 0143b104859a..415e1924f8dc 100644 --- a/src/rpc/client.rs +++ b/src/rpc/client.rs @@ -20,6 +20,7 @@ use http02::{header, HeaderMap, HeaderValue}; use jsonrpsee::core::client::ClientT as _; use jsonrpsee::core::params::{ArrayParams, ObjectParams}; use jsonrpsee::core::ClientError; +use once_cell::sync::Lazy; use serde::de::DeserializeOwned; use tracing::{debug, Instrument, Level}; use url::Url; @@ -38,30 +39,26 @@ pub struct Client { } impl Client { - /// Use either the url in the environment or the provided default. + /// Use either the url in the environment or a default. /// - /// If `token` is provided, use that over a token in the above. - pub fn from_env_with_override(mut default: Url, token: Option<&str>) -> anyhow::Result { - let override_token = |url: &mut Url| -> anyhow::Result<()> { - if token.is_some() { - if url.set_password(token).is_err() { - bail!("couldn't set override password") - } - } - Ok(()) - }; - match env::var("FULLNODE_API_INFO") { + /// If `token` is provided, use that over the token in either of the above. + pub fn default_or_from_env(token: Option<&str>) -> anyhow::Result { + static DEFAULT: Lazy = Lazy::new(|| "http://127.0.0.1:2345/".parse().unwrap()); + + let mut base_url = match env::var("FULLNODE_API_INFO") { Ok(it) => { - let crate::utils::UrlFromMultiAddr(mut url) = it.parse()?; - override_token(&mut url)?; - todo!() - } - Err(env::VarError::NotPresent) => { - override_token(&mut default)?; - Ok(Self::from_url(default)) + let crate::utils::UrlFromMultiAddr(url) = it.parse()?; + url } + Err(env::VarError::NotPresent) => DEFAULT.clone(), Err(e @ env::VarError::NotUnicode(_)) => bail!(e), + }; + if token.is_some() { + if base_url.set_password(token).is_err() { + bail!("couldn't set override password") + } } + Ok(Self::from_url(base_url)) } pub fn from_url(mut base_url: Url) -> Self { let token = base_url.password().map(Into::into); diff --git a/src/tool/main.rs b/src/tool/main.rs index 613c064ad1a2..f8c2efcdfebd 100644 --- a/src/tool/main.rs +++ b/src/tool/main.rs @@ -16,6 +16,8 @@ where let Cli { cmd } = Cli::parse_from(args); setup_minimal_logger(); + let client = crate::rpc::Client::default_or_from_env(None)?; + tokio::runtime::Builder::new_multi_thread() .enable_all() .build()? @@ -32,7 +34,7 @@ where Subcommand::Car(cmd) => cmd.run().await, Subcommand::Api(cmd) => cmd.run().await, Subcommand::Net(cmd) => cmd.run().await, - Subcommand::Shed(cmd) => cmd.run().await, + Subcommand::Shed(cmd) => cmd.run(client).await, } }) } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index e7bdfe480a37..e9f4fde881f5 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -25,6 +25,7 @@ use tracing::error; use url::Url; /// "hunter2:/ip4/127.0.0.1/wss" -> "wss://:hunter2@127.0.0.1/" +#[derive(Clone, Debug)] pub struct UrlFromMultiAddr(pub Url); impl FromStr for UrlFromMultiAddr { diff --git a/src/wallet/main.rs b/src/wallet/main.rs index 5851b9a42b7f..b73bb7dda5f5 100644 --- a/src/wallet/main.rs +++ b/src/wallet/main.rs @@ -22,10 +22,7 @@ where cmd, } = Cli::parse_from(args); - let client = rpc::Client::from_env_with_override( - "http://127.0.0.1:2345/".parse()?, - opts.token.as_deref(), - )?; + let client = rpc::Client::default_or_from_env(opts.token.as_deref())?; tokio::runtime::Builder::new_multi_thread() .enable_all() From 63a262daf7d939e3319da257205bf23cad743e8c Mon Sep 17 00:00:00 2001 From: Aatif Syed Date: Tue, 30 Apr 2024 20:28:21 +0100 Subject: [PATCH 6/9] wip --- src/cli_shared/cli/client.rs | 4 +- src/lib.rs | 1 - src/rpc/client.rs | 6 +-- src/rpc/methods/state.rs | 2 - src/rpc/mod.rs | 21 +++++++-- src/rpc/reflect/mod.rs | 8 ++-- src/rpc/request.rs | 45 +++++++++++++++++++ src/rpc_client/chain_ops.rs | 12 ----- src/rpc_client/mod.rs | 78 --------------------------------- src/tool/subcommands/api_cmd.rs | 19 ++++---- 10 files changed, 78 insertions(+), 118 deletions(-) create mode 100644 src/rpc/request.rs delete mode 100644 src/rpc_client/chain_ops.rs delete mode 100644 src/rpc_client/mod.rs diff --git a/src/cli_shared/cli/client.rs b/src/cli_shared/cli/client.rs index b897edb5aff7..7a424ec27493 100644 --- a/src/cli_shared/cli/client.rs +++ b/src/cli_shared/cli/client.rs @@ -7,8 +7,6 @@ use std::{ str::FromStr, }; -// todo: move this to healthcheck module -use crate::rpc_client::DEFAULT_PORT; use chrono::Duration; use directories::ProjectDirs; use serde::{Deserialize, Serialize}; @@ -98,7 +96,7 @@ impl Default for Client { buffer_size: BufferSize::default(), encrypt_keystore: true, metrics_address: FromStr::from_str("0.0.0.0:6116").unwrap(), - rpc_address: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), DEFAULT_PORT), + rpc_address: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), crate::rpc::DEFAULT_PORT), healthcheck_address: SocketAddr::new( IpAddr::V4(Ipv4Addr::LOCALHOST), crate::health::DEFAULT_HEALTHCHECK_PORT, diff --git a/src/lib.rs b/src/lib.rs index d50428c3ad4f..87611d300abc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,7 +63,6 @@ mod message_pool; mod metrics; mod networks; mod rpc; -mod rpc_client; mod shim; mod state_manager; mod state_migration; diff --git a/src/rpc/client.rs b/src/rpc/client.rs index 415e1924f8dc..3c175e4f1e06 100644 --- a/src/rpc/client.rs +++ b/src/rpc/client.rs @@ -25,7 +25,7 @@ use serde::de::DeserializeOwned; use tracing::{debug, Instrument, Level}; use url::Url; -use super::{ApiVersion, MAX_REQUEST_BODY_SIZE, MAX_RESPONSE_BODY_SIZE}; +use super::{ApiVersion, Request, MAX_REQUEST_BODY_SIZE, MAX_RESPONSE_BODY_SIZE}; /// A JSON-RPC client that can dispatch either a [`crate::rpc_client::RpcRequest`] /// or a [`crate::rpc::RpcMethod`] to a single URL. @@ -75,9 +75,9 @@ impl Client { } pub async fn call( &self, - req: crate::rpc_client::RpcRequest, + req: Request, ) -> Result { - let crate::rpc_client::RpcRequest { + let Request { method_name, params, api_version, diff --git a/src/rpc/methods/state.rs b/src/rpc/methods/state.rs index 3b9869419162..ecd30f0f7343 100644 --- a/src/rpc/methods/state.rs +++ b/src/rpc/methods/state.rs @@ -95,8 +95,6 @@ macro_rules! for_each_method { } pub(crate) use for_each_method; -pub const STATE_DECODE_PARAMS: &str = "Filecoin.StateDecodeParams"; - pub enum MinerGetBaseInfo {} impl RpcMethod<3> for MinerGetBaseInfo { const NAME: &'static str = "Filecoin.MinerGetBaseInfo"; diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index 2d2504e89f1b..89400de809f5 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -4,11 +4,13 @@ mod auth_layer; mod channel; mod client; +mod request; pub use client::Client; pub use error::ServerError; use reflect::Ctx; pub use reflect::{ApiVersion, RpcMethod, RpcMethodExt}; +pub use request::Request; mod error; mod reflect; pub mod types; @@ -16,7 +18,6 @@ pub use methods::*; use reflect::Permission; /// Protocol or transport-specific error -#[allow(unused)] pub use jsonrpsee::core::ClientError; #[allow(unused)] @@ -113,9 +114,6 @@ mod methods { pub mod wallet; } -use std::net::SocketAddr; -use std::sync::Arc; - use crate::key_management::KeyStore; use crate::rpc::auth_layer::AuthLayer; use crate::rpc::channel::RpcModule as FilRpcModule; @@ -129,12 +127,27 @@ use jsonrpsee::{ server::{stop_channel, RpcModule, RpcServiceBuilder, Server, StopHandle, TowerServiceBuilder}, Methods, }; +use once_cell::sync::Lazy; +use std::env; +use std::net::SocketAddr; +use std::sync::Arc; +use std::time::Duration; use tokio::sync::{mpsc, RwLock}; use tower::Service; use tracing::info; use self::reflect::openrpc_types::ParamStructure; +pub const DEFAULT_PORT: u16 = 2345; + +/// Request timeout read from environment variables +static DEFAULT_REQUEST_TIMEOUT: Lazy = Lazy::new(|| { + env::var("FOREST_RPC_DEFAULT_TIMEOUT") + .ok() + .and_then(|it| Duration::from_secs(it.parse().ok()?).into()) + .unwrap_or(Duration::from_secs(60)) +}); + const MAX_REQUEST_BODY_SIZE: u32 = 64 * 1024 * 1024; const MAX_RESPONSE_BODY_SIZE: u32 = MAX_REQUEST_BODY_SIZE; diff --git a/src/rpc/reflect/mod.rs b/src/rpc/reflect/mod.rs index ba67da1fd11a..854f9b443a9c 100644 --- a/src/rpc/reflect/mod.rs +++ b/src/rpc/reflect/mod.rs @@ -198,20 +198,18 @@ pub trait RpcMethodExt: RpcMethod { } } /// Returns [`Err`] if any of the parameters fail to serialize. - fn request( - params: Self::Params, - ) -> Result, serde_json::Error> { + fn request(params: Self::Params) -> Result, serde_json::Error> { // hardcode calling convention because lotus is by-position only let params = match Self::build_params(params, ConcreteCallingConvention::ByPosition)? { RequestParameters::ByPosition(it) => serde_json::Value::Array(it), RequestParameters::ByName(it) => serde_json::Value::Object(it), }; - Ok(crate::rpc_client::RpcRequest { + Ok(crate::rpc::Request { method_name: Self::NAME, params, result_type: std::marker::PhantomData, api_version: Self::API_VERSION, - timeout: *crate::rpc_client::DEFAULT_TIMEOUT, + timeout: *crate::rpc::DEFAULT_REQUEST_TIMEOUT, }) } fn call_raw( diff --git a/src/rpc/request.rs b/src/rpc/request.rs new file mode 100644 index 000000000000..28beb8932015 --- /dev/null +++ b/src/rpc/request.rs @@ -0,0 +1,45 @@ +// Copyright 2019-2024 ChainSafe Systems +// SPDX-License-Identifier: Apache-2.0, MIT + +use super::ApiVersion; +use jsonrpsee::core::traits::ToRpcParams; +use std::{marker::PhantomData, time::Duration}; + +/// An at-rest description of a remote procedure call, created using +/// [`rpc::RpcMethodExt`](crate::rpc::RpcMethodExt::request), and called using [`rpc::Client::call`](crate::rpc::Client::call). +#[derive(Debug, Clone)] +pub struct Request { + pub method_name: &'static str, + pub params: serde_json::Value, + pub result_type: PhantomData, + pub api_version: ApiVersion, + pub timeout: Duration, +} + +impl Request { + pub fn set_timeout(&mut self, timeout: Duration) { + self.timeout = timeout; + } + + pub fn with_timeout(mut self, timeout: Duration) -> Self { + self.set_timeout(timeout); + self + } + + /// Map type information about the response. + pub fn map_ty(self) -> Request { + Request { + method_name: self.method_name, + params: self.params, + result_type: PhantomData, + api_version: self.api_version, + timeout: self.timeout, + } + } +} + +impl ToRpcParams for Request { + fn to_rpc_params(self) -> Result>, serde_json::Error> { + Ok(Some(serde_json::value::to_raw_value(&self.params)?)) + } +} diff --git a/src/rpc_client/chain_ops.rs b/src/rpc_client/chain_ops.rs deleted file mode 100644 index 764f60fdb60b..000000000000 --- a/src/rpc_client/chain_ops.rs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2019-2024 ChainSafe Systems -// SPDX-License-Identifier: Apache-2.0, MIT - -use super::RpcRequest; - -/// Client calls should use [`crate::rpc::RpcMethod`]'s way of constructing [`RpcRequest`]. -/// `Filecoin.ChainNotify` is an exception because it is a subscription method, so falls outside -/// of that abstraction. -/// See for more information. -pub fn chain_notify_req() -> RpcRequest<()> { - RpcRequest::new(crate::rpc::chain::CHAIN_NOTIFY, ()) -} diff --git a/src/rpc_client/mod.rs b/src/rpc_client/mod.rs deleted file mode 100644 index 7a9961fd8d35..000000000000 --- a/src/rpc_client/mod.rs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2019-2024 ChainSafe Systems -// SPDX-License-Identifier: Apache-2.0, MIT - -mod chain_ops; - -use crate::lotus_json::HasLotusJson; -use crate::rpc::ApiVersion; -use jsonrpsee::core::traits::ToRpcParams; -use once_cell::sync::Lazy; -use std::{env, marker::PhantomData, time::Duration}; - -pub const API_INFO_KEY: &str = "FULLNODE_API_INFO"; -pub const DEFAULT_PORT: u16 = 2345; - -/// Default timeout for RPC requests. Doesn't apply to all requests, e.g., snapshot export which -/// has no timeout. -pub static DEFAULT_TIMEOUT: Lazy = Lazy::new(|| { - env::var("FOREST_RPC_DEFAULT_TIMEOUT") - .ok() - .and_then(|it| Duration::from_secs(it.parse().ok()?).into()) - .unwrap_or(Duration::from_secs(60)) -}); - -/// An `RpcRequest` is an at-rest description of a remote procedure call. It can -/// be invoked using `ApiInfo::call`. -/// -/// When adding support for a new RPC method, the corresponding `RpcRequest` -/// value should be public for use in testing. -#[derive(Debug, Clone)] -pub struct RpcRequest { - pub method_name: &'static str, - pub params: serde_json::Value, - pub result_type: PhantomData, - pub api_version: ApiVersion, - pub timeout: Duration, -} - -impl RpcRequest { - pub fn new(method_name: &'static str, params: P) -> Self { - RpcRequest { - method_name, - params: params - .into_lotus_json_value() - .unwrap_or(serde_json::Value::String( - "INTERNAL ERROR: Parameters could not be serialized as JSON".to_string(), - )), - result_type: PhantomData, - api_version: ApiVersion::V0, - timeout: *DEFAULT_TIMEOUT, - } - } - - pub fn set_timeout(&mut self, timeout: Duration) { - self.timeout = timeout; - } - - pub fn with_timeout(mut self, timeout: Duration) -> Self { - self.set_timeout(timeout); - self - } - - /// Map type information about the response. - pub fn map_ty(self) -> RpcRequest { - RpcRequest { - method_name: self.method_name, - params: self.params, - result_type: PhantomData, - api_version: self.api_version, - timeout: self.timeout, - } - } -} - -impl ToRpcParams for RpcRequest { - fn to_rpc_params(self) -> Result>, serde_json::Error> { - Ok(Some(serde_json::value::to_raw_value(&self.params)?)) - } -} diff --git a/src/tool/subcommands/api_cmd.rs b/src/tool/subcommands/api_cmd.rs index 5b37d5462b62..8ffdf21cc0a4 100644 --- a/src/tool/subcommands/api_cmd.rs +++ b/src/tool/subcommands/api_cmd.rs @@ -19,7 +19,6 @@ use crate::rpc::gas::GasEstimateGasLimit; use crate::rpc::types::{ApiTipsetKey, MessageFilter, MessageLookup}; use crate::rpc::{self, eth::*}; use crate::rpc::{prelude::*, start_rpc, RPCState}; -use crate::rpc_client::{RpcRequest, DEFAULT_PORT}; use crate::shim::address::{CurrentNetwork, Network}; use crate::shim::{ address::{Address, Protocol}, @@ -74,7 +73,7 @@ pub enum ApiCommands { #[arg(long, default_value = "mainnet")] chain: NetworkChain, // RPC port - #[arg(long, default_value_t = DEFAULT_PORT)] + #[arg(long, default_value_t = crate::rpc::DEFAULT_PORT)] port: u16, // Allow downloading snapshot automatically #[arg(long)] @@ -228,7 +227,7 @@ impl TestSummary { /// Data about a failed test. Used for debugging. struct TestDump { - request: RpcRequest, + request: rpc::Request, forest_response: Option, lotus_response: Option, } @@ -296,7 +295,7 @@ impl From<&RpcTest> for RpcTestHashable { } struct RpcTest { - request: RpcRequest, + request: rpc::Request, check_syntax: Arc bool + Send + Sync>, check_semantics: Arc bool + Send + Sync>, ignore: Option<&'static str>, @@ -308,14 +307,14 @@ struct RpcTest { impl RpcTest { /// Check that an endpoint exists and that both the Lotus and Forest JSON /// response follows the same schema. - fn basic(request: RpcRequest) -> Self + fn basic(request: rpc::Request) -> Self where T: HasLotusJson, { Self::basic_raw(request.map_ty::()) } /// See [Self::basic], and note on this `impl` block. - fn basic_raw(request: RpcRequest) -> Self { + fn basic_raw(request: rpc::Request) -> Self { Self { request: request.map_ty(), check_syntax: Arc::new(|it| match serde_json::from_value::(it) { @@ -332,7 +331,7 @@ impl RpcTest { /// Check that an endpoint exists, has the same JSON schema, and do custom /// validation over both responses. fn validate( - request: RpcRequest, + request: rpc::Request, validate: impl Fn(T, T) -> bool + Send + Sync + 'static, ) -> Self { Self::validate_raw(request.map_ty::(), move |l, r| { @@ -341,7 +340,7 @@ impl RpcTest { } /// See [Self::validate], and note on this `impl` block. fn validate_raw( - request: RpcRequest, + request: rpc::Request, validate: impl Fn(T, T) -> bool + Send + Sync + 'static, ) -> Self { Self { @@ -375,7 +374,7 @@ impl RpcTest { } /// Check that an endpoint exists and that Forest returns exactly the same /// JSON as Lotus. - fn identity(request: RpcRequest) -> RpcTest { + fn identity(request: rpc::Request) -> RpcTest { Self::validate(request, |forest, lotus| forest == lotus) } @@ -1290,7 +1289,7 @@ fn format_as_markdown(results: &[((&'static str, TestSummary, TestSummary), u32) builder.build().with(Style::markdown()).to_string() } -fn validate_message_lookup(req: RpcRequest) -> RpcTest { +fn validate_message_lookup(req: rpc::Request) -> RpcTest { use libipld_core::ipld::Ipld; RpcTest::validate(req, |mut forest, mut lotus| { From fe60446222e68b0a5d37b1c84fc092a51b854dc2 Mon Sep 17 00:00:00 2001 From: Aatif Syed Date: Tue, 30 Apr 2024 20:41:07 +0100 Subject: [PATCH 7/9] wip --- src/cli/subcommands/attach_cmd.rs | 4 ++-- src/rpc/client.rs | 13 ++++--------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/cli/subcommands/attach_cmd.rs b/src/cli/subcommands/attach_cmd.rs index 0d935408c247..914ddfd34753 100644 --- a/src/cli/subcommands/attach_cmd.rs +++ b/src/cli/subcommands/attach_cmd.rs @@ -276,7 +276,7 @@ mod inner { Address::from_str(&to)?, humantoken::parse(&value)?, // Convert forest_shim::TokenAmount to TokenAmount3 ); - Ok(MpoolPushMessage::call(&*client, (message, None)).await?) + Ok(MpoolPushMessage::call(&client, (message, None)).await?) } type SleepParams = (u64,); @@ -294,7 +294,7 @@ mod inner { ) -> anyhow::Result<()> { let mut epoch = None; loop { - let state = SyncState::call(&*client, ()).await?; + let state = SyncState::call(&client, ()).await?; if state.active_syncs.first().stage() == SyncStage::Complete { if let Some(prev) = epoch { let curr = state.active_syncs.first().epoch(); diff --git a/src/rpc/client.rs b/src/rpc/client.rs index 3c175e4f1e06..ee529c2f429b 100644 --- a/src/rpc/client.rs +++ b/src/rpc/client.rs @@ -3,9 +3,7 @@ //! # Design Goals //! - use [`jsonrpsee`] clients and primitives. -//! - Support different call formats -//! - [`crate::rpc_client::RpcRequest`] -//! - [`crate::rpc::RpcMethod`] +//! - Support [`rpc::Request`](crate::rpc::Request). //! - Support different //! - endpoint paths (`v0`, `v1`). //! - communication protocols (`ws`, `http`). @@ -27,8 +25,7 @@ use url::Url; use super::{ApiVersion, Request, MAX_REQUEST_BODY_SIZE, MAX_RESPONSE_BODY_SIZE}; -/// A JSON-RPC client that can dispatch either a [`crate::rpc_client::RpcRequest`] -/// or a [`crate::rpc::RpcMethod`] to a single URL. +/// A JSON-RPC client that can dispatch either a [`crate::rpc::Request`] to a single URL. pub struct Client { /// SHOULD end in a slash, due to our use of [`Url::join`]. base_url: Url, @@ -53,10 +50,8 @@ impl Client { Err(env::VarError::NotPresent) => DEFAULT.clone(), Err(e @ env::VarError::NotUnicode(_)) => bail!(e), }; - if token.is_some() { - if base_url.set_password(token).is_err() { - bail!("couldn't set override password") - } + if token.is_some() && base_url.set_password(token).is_err() { + bail!("couldn't set override password") } Ok(Self::from_url(base_url)) } From 91d8b3d8abf6caa87e2fc457dba31027dd24291b Mon Sep 17 00:00:00 2001 From: Aatif Syed Date: Tue, 30 Apr 2024 21:37:54 +0100 Subject: [PATCH 8/9] fix: clippy features --- src/cli/subcommands/attach_cmd.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cli/subcommands/attach_cmd.rs b/src/cli/subcommands/attach_cmd.rs index 914ddfd34753..94918c757ca7 100644 --- a/src/cli/subcommands/attach_cmd.rs +++ b/src/cli/subcommands/attach_cmd.rs @@ -1,7 +1,7 @@ // Copyright 2019-2024 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT -use crate::rpc::{self, prelude::*}; +use crate::rpc; use std::path::PathBuf; #[derive(Debug, clap::Args)] @@ -39,6 +39,7 @@ mod inner { use super::*; use crate::chain::ChainEpochDelta; use crate::chain_sync::SyncStage; + use crate::rpc::prelude::*; use crate::shim::{address::Address, message::Message}; use crate::{cli::humantoken, message::SignedMessage}; use boa_engine::{ From 187db135fa58301bda92576c0b62134d4a639868 Mon Sep 17 00:00:00 2001 From: Aatif Syed Date: Tue, 30 Apr 2024 21:48:30 +0100 Subject: [PATCH 9/9] fucking spellchecker --- src/rpc/client.rs | 2 +- src/utils/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rpc/client.rs b/src/rpc/client.rs index ee529c2f429b..a13a75e3b25e 100644 --- a/src/rpc/client.rs +++ b/src/rpc/client.rs @@ -36,7 +36,7 @@ pub struct Client { } impl Client { - /// Use either the url in the environment or a default. + /// Use either the URL in the environment or a default. /// /// If `token` is provided, use that over the token in either of the above. pub fn default_or_from_env(token: Option<&str>) -> anyhow::Result { diff --git a/src/utils/mod.rs b/src/utils/mod.rs index e9f4fde881f5..bf7d6ee39753 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -24,7 +24,7 @@ use tokio::time::sleep; use tracing::error; use url::Url; -/// "hunter2:/ip4/127.0.0.1/wss" -> "wss://:hunter2@127.0.0.1/" +/// `"hunter2:/ip4/127.0.0.1/wss" -> "wss://:hunter2@127.0.0.1/"` #[derive(Clone, Debug)] pub struct UrlFromMultiAddr(pub Url);