From 3bfff0a13a286401cbb2fed074026801e9d7b83a Mon Sep 17 00:00:00 2001 From: Joshua Potts <8704475+iamjpotts@users.noreply.github.com> Date: Sun, 1 Sep 2024 19:59:57 -0400 Subject: [PATCH] feat(config): Make gRPC http connections configurable Signed-off-by: Joshua Potts <8704475+iamjpotts@users.noreply.github.com> --- .gitignore | 2 + Cargo.lock | 2 +- Cargo.toml | 2 +- examples/consensus_pub_sub.rs | 2 +- examples/create_account.rs | 2 +- examples/create_file.rs | 2 +- examples/create_topic.rs | 2 +- examples/get_account_balance.rs | 2 +- examples/get_account_info.rs | 2 +- examples/get_file_contents.rs | 2 +- examples/transfer_crypto.rs | 2 +- examples/transfer_crypto_cost.rs | 2 +- src/account/account_id.rs | 4 +- src/client/config.rs | 64 +++++++++++++++++++++ src/client/mod.rs | 99 +++++++++++++++++++++----------- src/client/network/managed.rs | 45 ++++++++++++--- src/client/network/mod.rs | 90 +++++++++++++++++++---------- src/lib.rs | 6 +- src/transaction/tests.rs | 2 +- tests/e2e/common/mod.rs | 8 +-- 20 files changed, 255 insertions(+), 87 deletions(-) diff --git a/.gitignore b/.gitignore index 06d24ab13..37583f1b8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ .idea/ .task/ target + +protobufs/protobufs diff --git a/Cargo.lock b/Cargo.lock index 01133c581..3258ac807 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -974,7 +974,7 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hedera" -version = "0.32.0" +version = "0.33.0-alpha" dependencies = [ "aes", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index 13fb312be..a80dcc92d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ license = "Apache-2.0" name = "hedera" readme = "README.md" repository = "https://github.com/hashgraph/hedera-sdk-rust" -version = "0.32.0" +version = "0.33.0-alpha" [lib] bench = false diff --git a/examples/consensus_pub_sub.rs b/examples/consensus_pub_sub.rs index 4b5f96504..fbe9b91b9 100644 --- a/examples/consensus_pub_sub.rs +++ b/examples/consensus_pub_sub.rs @@ -30,7 +30,7 @@ async fn main() -> anyhow::Result<()> { let _ = dotenvy::dotenv(); let args = Args::parse(); - let client = Client::for_testnet(); + let client = Client::for_testnet()?; client.set_operator(args.operator_account_id, args.operator_key); diff --git a/examples/create_account.rs b/examples/create_account.rs index 90b916351..d12e4e93f 100644 --- a/examples/create_account.rs +++ b/examples/create_account.rs @@ -18,7 +18,7 @@ async fn main() -> anyhow::Result<()> { let _ = dotenvy::dotenv(); let args = Args::parse(); - let client = Client::for_testnet(); + let client = Client::for_testnet()?; client.set_operator(args.operator_account_id, args.operator_key); diff --git a/examples/create_file.rs b/examples/create_file.rs index dd8be5c0b..48e950125 100644 --- a/examples/create_file.rs +++ b/examples/create_file.rs @@ -18,7 +18,7 @@ async fn main() -> anyhow::Result<()> { let _ = dotenvy::dotenv(); let args = Args::parse(); - let client = Client::for_testnet(); + let client = Client::for_testnet()?; client.set_operator(args.operator_account_id, args.operator_key); diff --git a/examples/create_topic.rs b/examples/create_topic.rs index 7ca16bcb4..885f725de 100644 --- a/examples/create_topic.rs +++ b/examples/create_topic.rs @@ -18,7 +18,7 @@ async fn main() -> anyhow::Result<()> { let _ = dotenvy::dotenv(); let args = Args::parse(); - let client = Client::for_testnet(); + let client = Client::for_testnet()?; client.set_operator(args.operator_account_id, args.operator_key); diff --git a/examples/get_account_balance.rs b/examples/get_account_balance.rs index 3dade13d5..c3072d9ea 100644 --- a/examples/get_account_balance.rs +++ b/examples/get_account_balance.rs @@ -5,7 +5,7 @@ use hedera::{AccountBalanceQuery, AccountId, Client, NodeAddressBookQuery}; #[tokio::main] async fn main() -> anyhow::Result<()> { // let client = Client::for_mainnet(); - let client = Client::for_testnet(); + let client = Client::for_testnet()?; dbg!(NodeAddressBookQuery::new() .execute(&client) .await? diff --git a/examples/get_account_info.rs b/examples/get_account_info.rs index b341cb58a..b8808fc7a 100644 --- a/examples/get_account_info.rs +++ b/examples/get_account_info.rs @@ -17,7 +17,7 @@ async fn main() -> anyhow::Result<()> { let _ = dotenvy::dotenv(); let args = Args::parse(); - let client = Client::for_testnet(); + let client = Client::for_testnet()?; client.set_operator(args.operator_account_id, args.operator_key); diff --git a/examples/get_file_contents.rs b/examples/get_file_contents.rs index 22d715f27..be0593f08 100644 --- a/examples/get_file_contents.rs +++ b/examples/get_file_contents.rs @@ -20,7 +20,7 @@ async fn main() -> anyhow::Result<()> { let _ = dotenvy::dotenv(); let args = Args::parse(); - let client = Client::for_testnet(); + let client = Client::for_testnet()?; client.set_operator(args.operator_account_id, args.operator_key); diff --git a/examples/transfer_crypto.rs b/examples/transfer_crypto.rs index 08c706ec4..50dfb6561 100644 --- a/examples/transfer_crypto.rs +++ b/examples/transfer_crypto.rs @@ -26,7 +26,7 @@ async fn main() -> anyhow::Result<()> { let _ = dotenvy::dotenv(); let args = Args::parse(); - let client = Client::for_testnet(); + let client = Client::for_testnet()?; client.set_operator(args.operator_account_id, args.operator_key); diff --git a/examples/transfer_crypto_cost.rs b/examples/transfer_crypto_cost.rs index ae8608127..b1104683e 100644 --- a/examples/transfer_crypto_cost.rs +++ b/examples/transfer_crypto_cost.rs @@ -26,7 +26,7 @@ async fn main() -> anyhow::Result<()> { let _ = dotenvy::dotenv(); let args = Args::parse(); - let client = Client::for_testnet(); + let client = Client::for_testnet()?; client.set_operator(args.operator_account_id, args.operator_key); diff --git a/src/account/account_id.rs b/src/account/account_id.rs index a1b110045..7f835bf83 100644 --- a/src/account/account_id.rs +++ b/src/account/account_id.rs @@ -351,7 +351,7 @@ mod tests { assert_eq!( AccountId::from_str("0.0.123") .unwrap() - .to_string_with_checksum(&Client::for_testnet()) + .to_string_with_checksum(&Client::for_testnet().unwrap()) .unwrap(), "0.0.123-esxsf" ); @@ -359,7 +359,7 @@ mod tests { #[tokio::test] async fn bad_checksum_on_previewnet() { - let client = Client::for_previewnet(); + let client = Client::for_previewnet().unwrap(); let id = AccountId::from_str("0.0.123-ntjli").unwrap(); assert_matches!( diff --git a/src/client/config.rs b/src/client/config.rs index ec4dab261..724f5d052 100644 --- a/src/client/config.rs +++ b/src/client/config.rs @@ -2,6 +2,9 @@ use std::collections::HashMap; use std::str::FromStr; +use std::time::Duration; + +use tonic::transport::Endpoint; use crate::signer::AnySigner; use crate::{ @@ -79,3 +82,64 @@ pub(super) struct ClientConfig { pub(super) network: Either, NetworkName>, pub(super) mirror_network: Option, NetworkName>>, } + +/// gRPC channel connection configuration. Values which are set will override +/// the defaults established by tonic, which are typically hyper defaults. +pub struct EndpointConfig { + /// Initial connect timeout + pub connect_timeout: Option, + + /// HTTP/2 keep alive interval + pub http2_keep_alive_interval: Option, + + /// HTTP/2 keep alive timeout + pub http2_keep_alive_timeout: Option, + + /// HTTP/2 keep alive while idle + pub http2_keep_alive_while_idle: Option, + + /// TCP keep alive time threshold + pub tcp_keepalive: Option, +} + +impl Default for EndpointConfig { + fn default() -> Self { + Self { + connect_timeout: Some(Duration::from_secs(10)), + http2_keep_alive_interval: None, + http2_keep_alive_timeout: Some(Duration::from_secs(10)), + http2_keep_alive_while_idle: Some(true), + tcp_keepalive: Some(Duration::from_secs(10)), + } + } +} + +impl EndpointConfig { + pub(crate) fn apply(&self, endpoint: Endpoint) -> Endpoint { + let endpoint = if let Some(value) = self.connect_timeout { + endpoint.connect_timeout(value) + } else { + endpoint + }; + + let endpoint = if let Some(value) = self.http2_keep_alive_interval { + endpoint.http2_keep_alive_interval(value) + } else { + endpoint + }; + + let endpoint = if let Some(value) = self.http2_keep_alive_timeout { + endpoint.keep_alive_timeout(value) + } else { + endpoint + }; + + let endpoint = if let Some(value) = self.http2_keep_alive_while_idle { + endpoint.keep_alive_while_idle(value) + } else { + endpoint + }; + + endpoint.tcp_keepalive(self.tcp_keepalive) + } +} diff --git a/src/client/mod.rs b/src/client/mod.rs index 0501effcb..7b9741ada 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -26,6 +26,8 @@ use triomphe::Arc; use self::network::managed::ManagedNetwork; use self::network::mirror::MirrorNetwork; pub(crate) use self::network::mirror::MirrorNetworkData; +pub use crate::client::config::EndpointConfig; +use crate::client::network::managed::ManagedNetworkBuilder; use crate::ping_query::PingQuery; use crate::signer::AnySigner; use crate::{ @@ -68,9 +70,10 @@ impl Default for ClientBackoff { } } -// yes, client is complicated enough for this, even if it's only internal. -struct ClientBuilder { - network: ManagedNetwork, +/// Builder pattern for creating a client +pub struct ClientBuilder { + endpoint_config: Option, + network: ManagedNetworkBuilder, operator: Option, max_transaction_fee: Option, max_query_payment: Option, @@ -82,9 +85,40 @@ struct ClientBuilder { } impl ClientBuilder { + /// Construct a client with the given nodes configured. + /// + /// Note that this disables network auto-updating. + pub fn for_addresses(addresses: HashMap) -> Self { + let network = ManagedNetworkBuilder::Addresses(addresses); + + ClientBuilder::new(network).disable_network_updating() + } + + /// Set defaults for a mainnet client + pub fn for_mainnet() -> Self { + ClientBuilder::new(ManagedNetworkBuilder::Mainnet).ledger_id(Some(LedgerId::mainnet())) + } + + /// Set defaults for a previewnet client + pub fn for_previewnet() -> Self { + ClientBuilder::new(ManagedNetworkBuilder::Previewnet) + .ledger_id(Some(LedgerId::previewnet())) + } + + /// Set defaults for a testnet client + pub fn for_testnet() -> Self { + ClientBuilder::new(ManagedNetworkBuilder::Testnet).ledger_id(Some(LedgerId::testnet())) + } + + /// Set non-default endpoint configuration + pub fn endpoint_config(self, endpoint_config: EndpointConfig) -> Self { + Self { endpoint_config: Some(endpoint_config), ..self } + } + #[must_use] - fn new(network: ManagedNetwork) -> Self { + fn new(network: ManagedNetworkBuilder) -> Self { Self { + endpoint_config: None, network, operator: None, max_transaction_fee: None, @@ -105,8 +139,10 @@ impl ClientBuilder { Self { ledger_id, ..self } } - fn build(self) -> Client { + /// Build configured client + pub fn build(self) -> crate::Result { let Self { + endpoint_config, network, operator, max_transaction_fee, @@ -118,6 +154,10 @@ impl ClientBuilder { backoff, } = self; + let endpoint_config = endpoint_config.unwrap_or_default(); + + let network = network.build(endpoint_config)?; + let network_update_tx = match update_network { true => network::managed::spawn_network_update( network.clone(), @@ -127,7 +167,7 @@ impl ClientBuilder { false => watch::channel(None).0, }; - Client(Arc::new(ClientInner { + let client = Client(Arc::new(ClientInner { network, operator: ArcSwapOption::new(operator.map(Arc::new)), max_transaction_fee_tinybar: AtomicU64::new( @@ -139,7 +179,9 @@ impl ClientBuilder { regenerate_transaction_ids: AtomicBool::new(regenerate_transaction_ids), network_update_tx, backoff: RwLock::new(backoff), - })) + })); + + Ok(client) } } @@ -175,9 +217,9 @@ impl Client { let client = match network { config::Either::Left(network) => Client::for_network(network)?, config::Either::Right(it) => match it { - config::NetworkName::Mainnet => Client::for_mainnet(), - config::NetworkName::Testnet => Client::for_testnet(), - config::NetworkName::Previewnet => Client::for_previewnet(), + config::NetworkName::Mainnet => Client::for_mainnet()?, + config::NetworkName::Testnet => Client::for_testnet()?, + config::NetworkName::Previewnet => Client::for_previewnet()?, }, }; @@ -234,7 +276,7 @@ impl Client { /// # async fn main() { /// use hedera::Client; /// - /// let client = Client::for_testnet(); + /// let client = Client::for_testnet().unwrap(); /// /// // note: This isn't *guaranteed* in a semver sense, but this is the current result. /// let expected = Vec::from(["testnet.mirrornode.hedera.com:443".to_owned()]); @@ -264,23 +306,21 @@ impl Client { /// # Errors /// - [`Error::BasicParse`] if an error occurs parsing the configuration. // allowed for API compatibility. - #[allow(clippy::needless_pass_by_value)] pub fn for_network(network: HashMap) -> crate::Result { - let network = - ManagedNetwork::new(Network::from_addresses(&network)?, MirrorNetwork::default()); - - Ok(ClientBuilder::new(network).disable_network_updating().build()) + ClientBuilder::for_addresses(network).build() } /// Construct a client from a select mirror network pub async fn for_mirror_network(mirror_networks: Vec) -> crate::Result { let network_addresses: HashMap = HashMap::new(); let network = ManagedNetwork::new( - Network::from_addresses(&network_addresses)?, + Network::from_addresses(EndpointConfig::default(), &network_addresses)?, MirrorNetwork::from_addresses(mirror_networks.into_iter().map(Cow::Owned).collect()), ); - let client = ClientBuilder::new(network).build(); + let network_builder = ManagedNetworkBuilder::Network(network); + + let client = ClientBuilder::new(network_builder).build()?; let address_book = NodeAddressBookQuery::default().execute(&client).await?; client.set_network_from_address_book(address_book); @@ -289,23 +329,18 @@ impl Client { } /// Construct a Hiero client pre-configured for mainnet access. - #[must_use] - pub fn for_mainnet() -> Self { - ClientBuilder::new(ManagedNetwork::mainnet()).ledger_id(Some(LedgerId::mainnet())).build() + pub fn for_mainnet() -> crate::Result { + ClientBuilder::for_mainnet().build() } /// Construct a Hiero client pre-configured for testnet access. - #[must_use] - pub fn for_testnet() -> Self { - ClientBuilder::new(ManagedNetwork::testnet()).ledger_id(Some(LedgerId::testnet())).build() + pub fn for_testnet() -> crate::Result { + ClientBuilder::for_testnet().build() } /// Construct a Hiero client pre-configured for previewnet access. - #[must_use] - pub fn for_previewnet() -> Self { - ClientBuilder::new(ManagedNetwork::previewnet()) - .ledger_id(Some(LedgerId::previewnet())) - .build() + pub fn for_previewnet() -> crate::Result { + ClientBuilder::for_previewnet().build() } /// Updates the network to use the given address book. @@ -381,9 +416,9 @@ impl Client { /// - [`Error::BasicParse`] if the network name is not a supported network name. pub fn for_name(name: &str) -> crate::Result { match name { - "mainnet" => Ok(Self::for_mainnet()), - "testnet" => Ok(Self::for_testnet()), - "previewnet" => Ok(Self::for_previewnet()), + "mainnet" => Ok(Self::for_mainnet()?), + "testnet" => Ok(Self::for_testnet()?), + "previewnet" => Ok(Self::for_previewnet()?), "localhost" => { let mut network: HashMap = HashMap::new(); network.insert("127.0.0.1:50211".to_string(), AccountId::new(0, 0, 3)); diff --git a/src/client/network/managed.rs b/src/client/network/managed.rs index c9dc1bdaa..03fb05aff 100644 --- a/src/client/network/managed.rs +++ b/src/client/network/managed.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::time::Duration; use rand::Rng; @@ -6,7 +7,37 @@ use triomphe::Arc; use super::mirror::MirrorNetwork; use super::Network; -use crate::NodeAddressBookQuery; +use crate::client::config::EndpointConfig; +use crate::{ + AccountId, + NodeAddressBookQuery, +}; + +pub(crate) enum ManagedNetworkBuilder { + Addresses(HashMap), + Network(ManagedNetwork), + Mainnet, + Previewnet, + Testnet, +} + +impl ManagedNetworkBuilder { + pub(crate) fn build(self, endpoint_config: EndpointConfig) -> crate::Result { + let managed = match self { + Self::Addresses(addresses) => { + let network = Network::from_addresses(endpoint_config, &addresses)?; + + ManagedNetwork::new(network, MirrorNetwork::default()) + } + Self::Network(network) => network, + Self::Mainnet => ManagedNetwork::mainnet(endpoint_config), + Self::Previewnet => ManagedNetwork::previewnet(endpoint_config), + Self::Testnet => ManagedNetwork::testnet(endpoint_config), + }; + + Ok(managed) + } +} #[derive(Clone)] pub(crate) struct ManagedNetwork(Arc); @@ -23,16 +54,16 @@ impl ManagedNetwork { Self(Arc::new(ManagedNetworkInner { primary, mirror })) } - pub(crate) fn mainnet() -> Self { - Self::new(Network::mainnet(), MirrorNetwork::mainnet()) + pub(crate) fn mainnet(endpoint_config: EndpointConfig) -> Self { + Self::new(Network::mainnet(endpoint_config), MirrorNetwork::mainnet()) } - pub(crate) fn testnet() -> Self { - Self::new(Network::testnet(), MirrorNetwork::testnet()) + pub(crate) fn testnet(endpoint_config: EndpointConfig) -> Self { + Self::new(Network::testnet(endpoint_config), MirrorNetwork::testnet()) } - pub(crate) fn previewnet() -> Self { - Self::new(Network::previewnet(), MirrorNetwork::previewnet()) + pub(crate) fn previewnet(endpoint_config: EndpointConfig) -> Self { + Self::new(Network::previewnet(endpoint_config), MirrorNetwork::previewnet()) } } diff --git a/src/client/network/mod.rs b/src/client/network/mod.rs index 43381e5cf..40631a80b 100644 --- a/src/client/network/mod.rs +++ b/src/client/network/mod.rs @@ -27,6 +27,7 @@ use tonic::transport::{ }; use triomphe::Arc; +use crate::client::config::EndpointConfig; use crate::{ AccountId, ArcSwap, @@ -90,20 +91,23 @@ pub(crate) const PREVIEWNET: &[(u64, &[&str])] = &[ pub(crate) struct Network(pub(crate) ArcSwap); impl Network { - pub(super) fn mainnet() -> Self { - NetworkData::from_static(MAINNET).into() + pub(super) fn mainnet(endpoint_config: EndpointConfig) -> Self { + NetworkData::from_static(endpoint_config, MAINNET).into() } - pub(super) fn testnet() -> Self { - NetworkData::from_static(TESTNET).into() + pub(super) fn testnet(endpoint_config: EndpointConfig) -> Self { + NetworkData::from_static(endpoint_config, TESTNET).into() } - pub(super) fn previewnet() -> Self { - NetworkData::from_static(PREVIEWNET).into() + pub(super) fn previewnet(endpoint_config: EndpointConfig) -> Self { + NetworkData::from_static(endpoint_config, PREVIEWNET).into() } - pub(super) fn from_addresses(addresses: &HashMap) -> crate::Result { - Ok(NetworkData::from_addresses(addresses)?.into()) + pub(super) fn from_addresses( + endpoint_config: EndpointConfig, + addresses: &HashMap, + ) -> crate::Result { + Ok(NetworkData::from_addresses(endpoint_config, addresses)?.into()) } fn try_rcu>, E, F: FnMut(&Arc) -> Result>( @@ -155,9 +159,9 @@ impl From for Network { } } -// note: `Default` here is mostly only useful so that we don't need to implement `from_addresses` twice, notably this doesn't allocate. #[derive(Default)] pub(crate) struct NetworkData { + endpoint_config: Arc, map: HashMap, node_ids: Box<[AccountId]>, backoff: RwLock, @@ -167,11 +171,31 @@ pub(crate) struct NetworkData { } impl NetworkData { - pub(crate) fn from_addresses(addresses: &HashMap) -> crate::Result { - Self::default().with_addresses(addresses) + fn empty(endpoint_config: EndpointConfig) -> Self { + Self { + endpoint_config: Arc::new(endpoint_config), + map: Default::default(), + node_ids: Default::default(), + backoff: Default::default(), + health: Default::default(), + connections: Default::default(), + } + } + + pub(crate) fn from_addresses( + endpoint_config: EndpointConfig, + addresses: &HashMap, + ) -> crate::Result { + let this = Self::empty(endpoint_config); + + this.with_addresses(addresses) } - pub(crate) fn from_static(network: &'static [(u64, &'static [&'static str])]) -> Self { + pub(crate) fn from_static( + endpoint_config: EndpointConfig, + network: &'static [(u64, &'static [&'static str])], + ) -> Self { + let endpoint_config = Arc::new(endpoint_config); let mut map = HashMap::with_capacity(network.len()); let mut node_ids = Vec::with_capacity(network.len()); let mut connections = Vec::with_capacity(network.len()); @@ -183,10 +207,11 @@ impl NetworkData { map.insert(node_account_id, i); node_ids.push(node_account_id); health.push(Arc::default()); - connections.push(NodeConnection::new_static(address)); + connections.push(NodeConnection::new_static(endpoint_config.clone(), address)); } Self { + endpoint_config, map, node_ids: node_ids.into_boxed_slice(), health: health.into_boxed_slice(), @@ -211,24 +236,29 @@ impl NetworkData { .map(|it| (*it.ip()).into()) .collect(); + let config = old.endpoint_config.clone(); + // if the node is the exact same we want to reuse everything (namely the connections and `healthy`). // if the node has different routes then we still want to reuse `healthy` but replace the channel with a new channel. // if the node just flat out doesn't exist in `old`, we want to add the new node. // and, last but not least, if the node doesn't exist in `new` we want to get rid of it. let upsert = match old.map.get(&address.node_account_id) { Some(&account) => { - let connection = - match old.connections[account].addresses.symmetric_difference(&new).count() - { - 0 => old.connections[account].clone(), - _ => NodeConnection { addresses: new, channel: OnceCell::new() }, - }; + let connection = match old.connections[account] + .addresses + .symmetric_difference(&new) + .count() + { + 0 => old.connections[account].clone(), + _ => NodeConnection { config, addresses: new, channel: OnceCell::new() }, + }; (old.health[account].clone(), connection) } - None => { - (Arc::default(), NodeConnection { addresses: new, channel: OnceCell::new() }) - } + None => ( + Arc::default(), + NodeConnection { config, addresses: new, channel: OnceCell::new() }, + ), }; map.insert(address.node_account_id, i); @@ -238,6 +268,7 @@ impl NetworkData { } Self { + endpoint_config: old.endpoint_config.clone(), map, node_ids: node_ids.into_boxed_slice(), health: health.into_boxed_slice(), @@ -267,6 +298,7 @@ impl NetworkData { node_ids.push(*node); // fixme: keep the channel around more. connections.push(NodeConnection { + config: self.endpoint_config.clone(), addresses: BTreeSet::from([address]), channel: OnceCell::new(), }); @@ -280,6 +312,7 @@ impl NetworkData { } Ok(Self { + endpoint_config: self.endpoint_config.clone(), map, node_ids: node_ids.into_boxed_slice(), health: health.into_boxed_slice(), @@ -553,6 +586,7 @@ impl From for HostAndPort { #[derive(Clone)] struct NodeConnection { + config: Arc, addresses: BTreeSet, channel: OnceCell, } @@ -560,8 +594,9 @@ struct NodeConnection { impl NodeConnection { const PLAINTEXT_PORT: u16 = 50211; - fn new_static(addresses: &[&'static str]) -> NodeConnection { + fn new_static(config: Arc, addresses: &[&'static str]) -> NodeConnection { Self { + config, addresses: addresses.iter().copied().map(HostAndPort::from_static).collect(), channel: OnceCell::default(), } @@ -572,12 +607,9 @@ impl NodeConnection { .channel .get_or_init(|| { let addresses = self.addresses.iter().map(|it| { - Endpoint::from_shared(format!("tcp://{it}")) - .unwrap() - .keep_alive_timeout(Duration::from_secs(10)) - .keep_alive_while_idle(true) - .tcp_keepalive(Some(Duration::from_secs(10))) - .connect_timeout(Duration::from_secs(10)) + let endpoint = Endpoint::from_shared(format!("tcp://{it}")).unwrap(); + + self.config.apply(endpoint) }); Channel::balance_list(addresses) diff --git a/src/lib.rs b/src/lib.rs index 845bdb50b..fbbb9bd51 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -171,8 +171,12 @@ pub use address_book::{ NodeDeleteTransaction, NodeUpdateTransaction, }; -pub use client::Client; pub(crate) use client::Operator; +pub use client::{ + Client, + ClientBuilder, + EndpointConfig, +}; pub use contract::{ ContractBytecodeQuery, ContractCallQuery, diff --git a/src/transaction/tests.rs b/src/transaction/tests.rs index 5cf3886d2..989f08f9c 100644 --- a/src/transaction/tests.rs +++ b/src/transaction/tests.rs @@ -95,7 +95,7 @@ fn from_bytes_sign_to_bytes() -> crate::Result<()> { #[tokio::test] async fn chunked_to_from_bytes() -> crate::Result<()> { - let client = Client::for_testnet(); + let client = Client::for_testnet()?; client.set_operator(0.into(), PrivateKey::generate_ed25519()); let bytes = TopicMessageSubmitTransaction::new() diff --git a/tests/e2e/common/mod.rs b/tests/e2e/common/mod.rs index ab9c3ebe2..d086ab7ec 100644 --- a/tests/e2e/common/mod.rs +++ b/tests/e2e/common/mod.rs @@ -31,9 +31,9 @@ fn client() -> Client { let config = &*CONFIG; let client = match &*config.network_name { - "mainnet" => Client::for_mainnet(), - "testnet" => Client::for_testnet(), - "previewnet" => Client::for_previewnet(), + "mainnet" => Client::for_mainnet().unwrap(), + "testnet" => Client::for_testnet().unwrap(), + "previewnet" => Client::for_previewnet().unwrap(), "localhost" => for_local_node(), _ => { // to ensure we don't spam the logs with `Error creating client: ...`, @@ -48,7 +48,7 @@ fn client() -> Client { ); } - Client::for_testnet() + Client::for_testnet().unwrap() } };