1use std::borrow::Cow;
4use std::collections::HashMap;
5use std::fmt;
6use std::num::{
7 NonZeroU64,
8 NonZeroUsize,
9};
10use std::sync::atomic::{
11 AtomicBool,
12 AtomicU64,
13 Ordering,
14};
15use std::time::Duration;
16
17pub(crate) use network::{
18 Network,
19 NetworkData,
20};
21pub(crate) use operator::Operator;
22use parking_lot::RwLock;
23use tokio::sync::watch;
24use triomphe::Arc;
25
26use self::network::managed::ManagedNetwork;
27use self::network::mirror::MirrorNetwork;
28pub(crate) use self::network::mirror::MirrorNetworkData;
29use crate::ping_query::PingQuery;
30use crate::signer::AnySigner;
31use crate::{
32 AccountId,
33 ArcSwapOption,
34 Error,
35 Hbar,
36 LedgerId,
37 NodeAddressBook,
38 NodeAddressBookQuery,
39 PrivateKey,
40 PublicKey,
41};
42
43#[cfg(feature = "serde")]
44mod config;
45
46mod network;
47mod operator;
48
49pub(crate) const DEFAULT_GRPC_DEADLINE: Duration = Duration::from_secs(10);
51
52pub(crate) const DEFAULT_REQUEST_TIMEOUT: Duration = Duration::from_secs(120);
54
55#[derive(Copy, Clone)]
56pub(crate) struct ClientBackoff {
57 pub(crate) max_backoff: Duration,
58 pub(crate) initial_backoff: Duration,
60 pub(crate) max_attempts: usize,
61 pub(crate) request_timeout: Option<Duration>,
62 pub(crate) grpc_deadline: Duration,
63}
64
65impl Default for ClientBackoff {
66 fn default() -> Self {
67 Self {
68 max_backoff: Duration::from_millis(backoff::default::MAX_INTERVAL_MILLIS),
69 initial_backoff: Duration::from_millis(backoff::default::INITIAL_INTERVAL_MILLIS),
70 max_attempts: 10,
71 request_timeout: Some(DEFAULT_REQUEST_TIMEOUT),
72 grpc_deadline: DEFAULT_GRPC_DEADLINE,
73 }
74 }
75}
76
77struct ClientBuilder {
79 network: ManagedNetwork,
80 operator: Option<Operator>,
81 max_transaction_fee: Option<NonZeroU64>,
82 max_query_payment: Option<NonZeroU64>,
83 ledger_id: Option<LedgerId>,
84 auto_validate_checksums: bool,
85 regenerate_transaction_ids: bool,
86 update_network: bool,
87 backoff: ClientBackoff,
88}
89
90impl ClientBuilder {
91 #[must_use]
92 fn new(network: ManagedNetwork) -> Self {
93 Self {
94 network,
95 operator: None,
96 max_transaction_fee: None,
97 max_query_payment: None,
98 ledger_id: None,
99 auto_validate_checksums: false,
100 regenerate_transaction_ids: true,
101 update_network: true,
102 backoff: ClientBackoff::default(),
103 }
104 }
105
106 fn disable_network_updating(self) -> Self {
107 Self { update_network: false, ..self }
108 }
109
110 fn ledger_id(self, ledger_id: Option<LedgerId>) -> Self {
111 Self { ledger_id, ..self }
112 }
113
114 fn build(self) -> Client {
115 let Self {
116 network,
117 operator,
118 max_transaction_fee,
119 max_query_payment,
120 ledger_id,
121 auto_validate_checksums,
122 regenerate_transaction_ids,
123 update_network,
124 backoff,
125 } = self;
126
127 let network_update_tx = match update_network {
128 true => network::managed::spawn_network_update(
129 network.clone(),
130 Some(Duration::from_secs(24 * 60 * 60)),
131 ),
132 false => watch::channel(None).0,
134 };
135
136 Client(Arc::new(ClientInner {
137 network,
138 operator: ArcSwapOption::new(operator.map(Arc::new)),
139 max_transaction_fee_tinybar: AtomicU64::new(
140 max_transaction_fee.map_or(0, NonZeroU64::get),
141 ),
142 max_query_payment_tinybar: AtomicU64::new(max_query_payment.map_or(0, NonZeroU64::get)),
143 ledger_id: ArcSwapOption::new(ledger_id.map(Arc::new)),
144 auto_validate_checksums: AtomicBool::new(auto_validate_checksums),
145 regenerate_transaction_ids: AtomicBool::new(regenerate_transaction_ids),
146 enable_receipt_record_query_failover: AtomicBool::new(false),
147 network_update_tx,
148 backoff: RwLock::new(backoff),
149 }))
150 }
151}
152
153struct ClientInner {
154 network: ManagedNetwork,
155 operator: ArcSwapOption<Operator>,
156 max_transaction_fee_tinybar: AtomicU64,
157 max_query_payment_tinybar: AtomicU64,
158 ledger_id: ArcSwapOption<LedgerId>,
159 auto_validate_checksums: AtomicBool,
160 regenerate_transaction_ids: AtomicBool,
161 enable_receipt_record_query_failover: AtomicBool,
162 network_update_tx: watch::Sender<Option<Duration>>,
163 backoff: RwLock<ClientBackoff>,
164}
165
166#[derive(Clone)]
168pub struct Client(Arc<ClientInner>);
169
170impl fmt::Debug for Client {
171 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172 f.debug_struct("Client").finish_non_exhaustive()
174 }
175}
176
177impl Client {
178 #[cfg(feature = "serde")]
179 fn from_config_data(config: config::ClientConfig) -> crate::Result<Self> {
180 let config::ClientConfig { operator, network, mirror_network } = config;
181
182 let client = match network {
184 config::Either::Left(network) => Client::for_network(network)?,
185 config::Either::Right(it) => match it {
186 config::NetworkName::Mainnet => Client::for_mainnet(),
187 config::NetworkName::Testnet => Client::for_testnet(),
188 config::NetworkName::Previewnet => Client::for_previewnet(),
189 },
190 };
191
192 let mirror_network = mirror_network.map(|mirror_network| match mirror_network {
193 config::Either::Left(mirror_network) => {
194 MirrorNetwork::from_addresses(mirror_network.into_iter().map(Cow::Owned).collect())
195 }
196 config::Either::Right(it) => match it {
197 config::NetworkName::Mainnet => MirrorNetwork::mainnet(),
198 config::NetworkName::Testnet => MirrorNetwork::testnet(),
199 config::NetworkName::Previewnet => MirrorNetwork::previewnet(),
200 },
201 });
202
203 if let Some(operator) = operator {
204 client.0.operator.store(Some(Arc::new(operator)));
205 }
206
207 if let Some(mirror_network) = mirror_network {
208 client.set_mirror_network(mirror_network.load().addresses());
209 }
210
211 Ok(client)
212 }
213
214 #[cfg(feature = "serde")]
219 pub fn from_config(json: &str) -> crate::Result<Self> {
220 let config = serde_json::from_str::<config::ClientConfigInner>(json)
221 .map_err(crate::Error::basic_parse)?
222 .into();
223
224 Self::from_config_data(config)
225 }
226
227 #[must_use]
254 pub fn mirror_network(&self) -> Vec<String> {
255 self.mirrornet().load().addresses().collect()
256 }
257
258 pub fn set_mirror_network<I: IntoIterator<Item = String>>(&self, addresses: I) {
262 self.mirrornet().store(
263 MirrorNetworkData::from_addresses(addresses.into_iter().map(Cow::Owned).collect())
264 .into(),
265 );
266 }
267
268 #[allow(clippy::needless_pass_by_value)]
276 pub fn for_network(network: HashMap<String, AccountId>) -> crate::Result<Self> {
277 let network =
278 ManagedNetwork::new(Network::from_addresses(&network)?, MirrorNetwork::default());
279
280 Ok(ClientBuilder::new(network).disable_network_updating().build())
281 }
282
283 pub async fn for_mirror_network(mirror_networks: Vec<String>) -> crate::Result<Self> {
285 Self::for_mirror_network_with_shard_realm(mirror_networks, 0, 0).await
286 }
287
288 pub async fn for_mirror_network_with_shard_realm(
290 mirror_networks: Vec<String>,
291 shard: u64,
292 realm: u64,
293 ) -> crate::Result<Self> {
294 let network_addresses: HashMap<String, AccountId> = HashMap::new();
295 let network = ManagedNetwork::new(
296 Network::from_addresses(&network_addresses)?,
297 MirrorNetwork::from_addresses(mirror_networks.into_iter().map(Cow::Owned).collect()),
298 );
299
300 let client = ClientBuilder::new(network).build();
301 let address_book = if shard == 0 && realm == 0 {
302 NodeAddressBookQuery::default().execute(&client).await?
303 } else {
304 NodeAddressBookQuery::new().shard(shard).realm(realm).execute(&client).await?
305 };
306
307 client.set_network_from_address_book(address_book);
308
309 Ok(client)
310 }
311
312 #[must_use]
314 pub fn for_mainnet() -> Self {
315 ClientBuilder::new(ManagedNetwork::mainnet()).ledger_id(Some(LedgerId::mainnet())).build()
316 }
317
318 #[must_use]
320 pub fn for_testnet() -> Self {
321 ClientBuilder::new(ManagedNetwork::testnet()).ledger_id(Some(LedgerId::testnet())).build()
322 }
323
324 #[must_use]
326 pub fn for_previewnet() -> Self {
327 ClientBuilder::new(ManagedNetwork::previewnet())
328 .ledger_id(Some(LedgerId::previewnet()))
329 .build()
330 }
331
332 #[allow(clippy::needless_pass_by_value)]
339 pub fn set_network_from_address_book(&self, address_book: NodeAddressBook) {
340 self.net().update_from_address_book(&address_book);
341 }
342
343 #[allow(clippy::needless_pass_by_value)]
355 pub fn set_network(&self, network: HashMap<String, AccountId>) -> crate::Result<()> {
356 self.net().update_from_addresses(&network)?;
357
358 Ok(())
359 }
360
361 #[must_use]
363 pub fn network(&self) -> HashMap<String, AccountId> {
364 self.net().0.load().addresses()
365 }
366
367 pub fn max_node_attempts(&self) -> Option<NonZeroUsize> {
369 self.net().0.load().max_node_attempts()
370 }
371
372 pub fn set_max_node_attempts(&self, attempts: usize) {
374 self.net().0.load().set_max_node_attempts(NonZeroUsize::new(attempts))
375 }
376
377 pub fn max_node_backoff(&self) -> Duration {
379 self.net().0.load().max_backoff()
380 }
381
382 pub fn set_max_node_backoff(&self, max_node_backoff: Duration) {
384 self.net().0.load().set_max_backoff(max_node_backoff)
385 }
386
387 pub fn min_node_backoff(&self) -> Duration {
389 self.net().0.load().min_backoff()
390 }
391
392 pub fn set_min_node_backoff(&self, min_node_backoff: Duration) {
394 self.net().0.load().set_min_backoff(min_node_backoff)
395 }
396
397 pub fn max_nodes_per_request(&self) -> Option<u32> {
399 self.net().0.load().max_nodes_per_request()
400 }
401
402 pub fn set_max_nodes_per_request(&self, max_nodes: Option<u32>) {
406 self.net().0.load().set_max_nodes_per_request(max_nodes)
407 }
408
409 pub fn for_name(name: &str) -> crate::Result<Self> {
416 match name {
417 "mainnet" => Ok(Self::for_mainnet()),
418 "testnet" => Ok(Self::for_testnet()),
419 "previewnet" => Ok(Self::for_previewnet()),
420 "localhost" => {
421 let mut network: HashMap<String, AccountId> = HashMap::new();
422 network.insert("127.0.0.1:50211".to_string(), AccountId::new(0, 0, 3));
423
424 let client = Client::for_network(network).unwrap();
425 client.set_mirror_network(["127.0.0.1:5600".to_string()]);
426 Ok(client)
427 }
428 _ => Err(Error::basic_parse(format!("Unknown network name {name}"))),
429 }
430 }
431
432 pub(crate) fn ledger_id_internal(&self) -> arc_swap::Guard<Option<Arc<LedgerId>>> {
435 self.0.ledger_id.load()
436 }
437
438 pub fn set_ledger_id(&self, ledger_id: Option<LedgerId>) {
440 self.0.ledger_id.store(ledger_id.map(Arc::new));
441 }
442
443 #[must_use]
445 pub fn auto_validate_checksums(&self) -> bool {
446 self.0.auto_validate_checksums.load(Ordering::Relaxed)
447 }
448
449 pub fn set_auto_validate_checksums(&self, value: bool) {
451 self.0.auto_validate_checksums.store(value, Ordering::Relaxed);
452 }
453
454 #[must_use]
458 pub fn default_regenerate_transaction_id(&self) -> bool {
459 self.0.regenerate_transaction_ids.load(Ordering::Relaxed)
460 }
461
462 pub fn set_default_regenerate_transaction_id(&self, value: bool) {
464 self.0.regenerate_transaction_ids.store(value, Ordering::Relaxed);
465 }
466
467 #[must_use]
475 pub fn get_enable_receipt_record_query_failover(&self) -> bool {
476 self.0.enable_receipt_record_query_failover.load(Ordering::Relaxed)
477 }
478
479 pub fn set_enable_receipt_record_query_failover(&self, enable: bool) {
498 self.0.enable_receipt_record_query_failover.store(enable, Ordering::Relaxed);
499 }
500
501 pub fn set_operator(&self, id: AccountId, key: PrivateKey) {
509 self.0
510 .operator
511 .store(Some(Arc::new(Operator { account_id: id, signer: AnySigner::PrivateKey(key) })));
512 }
513
514 pub fn set_operator_with<F: Fn(&[u8]) -> Vec<u8> + Send + Sync + 'static>(
522 &self,
523 id: AccountId,
524 public_key: PublicKey,
525 f: F,
526 ) {
527 self.0.operator.store(Some(Arc::new(Operator {
528 account_id: id,
529 signer: AnySigner::arbitrary(Box::new(public_key), f),
530 })));
531 }
532
533 pub(crate) fn net(&self) -> &Network {
535 &self.0.network.primary
536 }
537
538 pub(crate) fn mirrornet(&self) -> &MirrorNetwork {
540 &self.0.network.mirror
541 }
542
543 pub fn set_default_max_transaction_fee(&self, amount: Hbar) {
549 assert!(amount >= Hbar::ZERO);
550 self.0.max_transaction_fee_tinybar.store(amount.to_tinybars() as u64, Ordering::Relaxed);
551 }
552
553 #[must_use]
555 pub fn default_max_transaction_fee(&self) -> Option<Hbar> {
556 let val = self.0.max_transaction_fee_tinybar.load(Ordering::Relaxed);
557
558 (val > 0).then(|| Hbar::from_tinybars(val as i64))
559 }
560
561 #[must_use]
563 pub fn default_max_query_payment(&self) -> Option<Hbar> {
564 let val = self.0.max_query_payment_tinybar.load(Ordering::Relaxed);
565
566 (val > 0).then(|| Hbar::from_tinybars(val as i64))
567 }
568
569 pub fn set_default_max_query_payment(&self, amount: Hbar) {
575 assert!(amount >= Hbar::ZERO);
576 self.0.max_query_payment_tinybar.store(amount.to_tinybars() as u64, Ordering::Relaxed);
577 }
578
579 #[must_use]
581 pub fn request_timeout(&self) -> Option<Duration> {
582 self.backoff().request_timeout
583 }
584
585 pub fn set_request_timeout(&self, timeout: Option<Duration>) {
587 self.0.backoff.write().request_timeout = timeout;
588 }
589
590 #[must_use]
592 pub fn max_attempts(&self) -> usize {
593 self.backoff().max_attempts
594 }
595
596 pub fn set_max_attempts(&self, max_attempts: usize) {
598 self.0.backoff.write().max_attempts = max_attempts;
599 }
600
601 #[doc(alias = "initial_backoff")]
603 #[must_use]
604 pub fn min_backoff(&self) -> Duration {
605 self.backoff().initial_backoff
606 }
607
608 #[doc(alias = "set_initial_backoff")]
610 pub fn set_min_backoff(&self, max_backoff: Duration) {
611 self.0.backoff.write().max_backoff = max_backoff;
612 }
613
614 #[must_use]
616 pub fn max_backoff(&self) -> Duration {
617 self.backoff().max_backoff
618 }
619
620 pub fn set_max_backoff(&self, max_backoff: Duration) {
622 self.0.backoff.write().max_backoff = max_backoff;
623 }
624
625 #[must_use]
627 pub fn grpc_deadline(&self) -> Duration {
628 self.backoff().grpc_deadline
629 }
630
631 pub fn set_grpc_deadline(&self, grpc_deadline: Duration) {
635 self.0.backoff.write().grpc_deadline = grpc_deadline;
636 }
637
638 #[must_use]
639 pub(crate) fn backoff(&self) -> ClientBackoff {
640 *self.0.backoff.read()
641 }
642
643 pub(crate) fn load_operator(&self) -> arc_swap::Guard<Option<Arc<Operator>>> {
645 self.0.operator.load()
646 }
647
648 pub(crate) fn full_load_operator(&self) -> Option<Arc<Operator>> {
650 self.0.operator.load_full()
651 }
652
653 pub async fn ping(&self, node_account_id: AccountId) -> crate::Result<()> {
655 PingQuery::new(node_account_id).execute(self, None).await
656 }
657
658 pub async fn ping_with_timeout(
660 &self,
661 node_account_id: AccountId,
662 timeout: Duration,
663 ) -> crate::Result<()> {
664 PingQuery::new(node_account_id).execute(self, Some(timeout)).await
665 }
666
667 pub async fn ping_all(&self) -> crate::Result<()> {
669 futures_util::future::try_join_all(
670 self.net().0.load().node_ids().iter().map(|it| self.ping(*it)),
671 )
672 .await?;
673
674 Ok(())
675 }
676
677 pub async fn ping_all_with_timeout(&self, timeout: Duration) -> crate::Result<()> {
679 futures_util::future::try_join_all(
680 self.net().0.load().node_ids().iter().map(|it| self.ping_with_timeout(*it, timeout)),
681 )
682 .await?;
683
684 Ok(())
685 }
686
687 #[must_use = "this function has no side-effects"]
689 pub fn network_update_period(&self) -> Option<Duration> {
690 *self.0.network_update_tx.borrow()
691 }
692
693 pub fn set_network_update_period(&self, period: Option<Duration>) {
697 self.0.network_update_tx.send_if_modified(|place| {
698 let changed = *place == period;
699 if changed {
700 *place = period;
701 }
702
703 changed
704 });
705 }
706
707 pub(crate) async fn refresh_network(&self) {
710 match NodeAddressBookQuery::new()
711 .execute_mirrornet(self.mirrornet().load().channel(self.grpc_deadline()), None)
712 .await
713 {
714 Ok(address_book) => {
715 log::info!("Successfully updated network address book");
716 self.set_network_from_address_book(address_book);
717 }
718 Err(e) => {
719 log::warn!("Failed to update network address book: {e:?}");
720 }
721 }
722 }
723
724 #[must_use]
726 pub fn get_operator_account_id(&self) -> Option<AccountId> {
727 self.load_operator().as_deref().map(|it| it.account_id)
728 }
729
730 #[must_use]
732 pub fn get_operator_public_key(&self) -> Option<PublicKey> {
733 self.load_operator().as_deref().map(|it| it.signer.public_key())
734 }
735}