Skip to content

Commit

Permalink
feat(config): Make gRPC http connections configurable
Browse files Browse the repository at this point in the history
Signed-off-by: Joshua Potts <[email protected]>
  • Loading branch information
iamjpotts committed Sep 3, 2024
1 parent 40cc835 commit ac877f1
Show file tree
Hide file tree
Showing 19 changed files with 247 additions and 86 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ license = "Apache-2.0"
name = "hedera"
readme = "README.md"
repository = "https://github.com/hashgraph/hedera-sdk-rust"
version = "0.28.0"
version = "0.29.0-alpha"

[lib]
bench = false
Expand Down
2 changes: 1 addition & 1 deletion examples/consensus_pub_sub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,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);

Expand Down
2 changes: 1 addition & 1 deletion examples/create_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,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);

Expand Down
2 changes: 1 addition & 1 deletion examples/create_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,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);

Expand Down
2 changes: 1 addition & 1 deletion examples/create_topic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,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);

Expand Down
2 changes: 1 addition & 1 deletion examples/get_account_balance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,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?
Expand Down
2 changes: 1 addition & 1 deletion examples/get_account_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,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);

Expand Down
2 changes: 1 addition & 1 deletion examples/get_file_contents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,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);

Expand Down
2 changes: 1 addition & 1 deletion examples/transfer_crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,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);

Expand Down
2 changes: 1 addition & 1 deletion examples/transfer_crypto_cost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,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);

Expand Down
4 changes: 2 additions & 2 deletions src/account/account_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,15 +376,15 @@ 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"
);
}

#[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!(
Expand Down
64 changes: 64 additions & 0 deletions src/client/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@

use std::collections::HashMap;
use std::str::FromStr;
use std::time::Duration;

use tonic::transport::Endpoint;

use crate::signer::AnySigner;
use crate::{
Expand Down Expand Up @@ -97,3 +100,64 @@ pub(super) struct ClientConfig {
pub(super) network: Either<HashMap<String, AccountId>, NetworkName>,
pub(super) mirror_network: Option<Either<Vec<String>, 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<Duration>,

/// HTTP/2 keep alive interval
pub http2_keep_alive_interval: Option<Duration>,

/// HTTP/2 keep alive timeout
pub http2_keep_alive_timeout: Option<Duration>,

/// HTTP/2 keep alive while idle
pub http2_keep_alive_while_idle: Option<bool>,

/// TCP keep alive time threshold
pub tcp_keepalive: Option<Duration>,
}

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)
}
}
93 changes: 63 additions & 30 deletions src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,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::{
Expand Down Expand Up @@ -85,9 +87,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<EndpointConfig>,
network: ManagedNetworkBuilder,
operator: Option<Operator>,
max_transaction_fee: Option<NonZeroU64>,
max_query_payment: Option<NonZeroU64>,
Expand All @@ -99,9 +102,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<String, AccountId>) -> 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,
Expand All @@ -122,8 +156,10 @@ impl ClientBuilder {
Self { ledger_id, ..self }
}

fn build(self) -> Client {
/// Build configured client
pub fn build(self) -> crate::Result<Client> {
let Self {
endpoint_config,
network,
operator,
max_transaction_fee,
Expand All @@ -135,6 +171,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(),
Expand All @@ -144,7 +184,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(
Expand All @@ -156,7 +196,9 @@ impl ClientBuilder {
regenerate_transaction_ids: AtomicBool::new(regenerate_transaction_ids),
network_update_tx,
backoff: RwLock::new(backoff),
}))
}));

Ok(client)
}
}

Expand Down Expand Up @@ -192,9 +234,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()?,
},
};

Expand Down Expand Up @@ -251,7 +293,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()]);
Expand Down Expand Up @@ -281,32 +323,23 @@ 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<String, AccountId>) -> crate::Result<Self> {
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 Hedera 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<Self> {
ClientBuilder::for_mainnet().build()
}

/// Construct a Hedera 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<Self> {
ClientBuilder::for_testnet().build()
}

/// Construct a Hedera 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<Self> {
ClientBuilder::for_previewnet().build()
}

/// Updates the network to use the given address book.
Expand Down Expand Up @@ -382,9 +415,9 @@ impl Client {
/// - [`Error::BasicParse`] if the network name is not a supported network name.
pub fn for_name(name: &str) -> crate::Result<Self> {
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<String, AccountId> = HashMap::new();
network.insert("127.0.0.1:50211".to_string(), AccountId::new(0, 0, 3));
Expand Down
Loading

0 comments on commit ac877f1

Please sign in to comment.