nym_credential_proxy_lib/
quorum_checker.rs1use crate::error::CredentialProxyError;
5use crate::shared_state::nyxd_client::ChainClient;
6use nym_ecash_signer_check::{check_known_dealers, dkg_details_with_client};
7use std::ops::Deref;
8use std::sync::Arc;
9use std::sync::atomic::{AtomicBool, Ordering};
10use std::time::Duration;
11use tokio_util::sync::CancellationToken;
12use tracing::{error, info, warn};
13
14#[derive(Clone)]
15pub struct QuorumState {
16 available: Arc<AtomicBool>,
17}
18
19impl QuorumState {
20 pub fn available(&self) -> bool {
21 self.available.load(Ordering::Acquire)
22 }
23}
24
25pub struct QuorumStateChecker {
26 client: ChainClient,
27 cancellation_token: CancellationToken,
28 check_interval: Duration,
29 quorum_state: QuorumState,
30}
31
32impl QuorumStateChecker {
33 pub async fn new(
34 client: ChainClient,
35 check_interval: Duration,
36 cancellation_token: CancellationToken,
37 ) -> Result<Self, CredentialProxyError> {
38 let this = QuorumStateChecker {
39 client,
40 cancellation_token,
41 check_interval,
42 quorum_state: QuorumState {
43 available: Arc::new(Default::default()),
44 },
45 };
46
47 let quorum_available = this.check_quorum_state().await?;
49 this.quorum_state
50 .available
51 .store(quorum_available, Ordering::Relaxed);
52 Ok(this)
53 }
54
55 pub fn quorum_state_ref(&self) -> QuorumState {
56 self.quorum_state.clone()
57 }
58
59 async fn check_quorum_state(&self) -> Result<bool, CredentialProxyError> {
60 let client_guard = self.client.query_chain().await;
61
62 let dkg_details = dkg_details_with_client(client_guard.deref()).await?;
65 drop(client_guard);
66
67 let res = check_known_dealers(dkg_details).await?;
68
69 let Some(signing_threshold) = res.threshold else {
70 warn!(
71 "signing threshold is currently unavailable and we have not yet implemented credential issuance during DKG transition"
72 );
73 return Ok(false);
74 };
75
76 let mut working_issuer = 0;
77
78 for result in res.results {
79 if result.chain_available() && result.signing_available() {
80 working_issuer += 1;
81 }
82 }
83
84 Ok((working_issuer as u64) >= signing_threshold)
85 }
86
87 pub async fn run_forever(self) {
88 info!("starting quorum state checker");
89 loop {
90 tokio::select! {
91 biased;
92 _ = self.cancellation_token.cancelled() => {
93 break
94 }
95 _ = tokio::time::sleep(self.check_interval) => {
96 match self.check_quorum_state().await {
97 Ok(available) => self.quorum_state.available.store(available, Ordering::SeqCst),
98 Err(err) => error!("failed to check current quorum state: {err}"),
99 }
100 }
101 }
102 }
103 }
104}