Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(config): Make gRPC http connections configurable #832

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading