Skip to main content

chio_store_sqlite/
clearing_lifecycle_store.rs

1use std::sync::Arc;
2
3use chio_core::economic_continuity::{EconomicResourceKeyV1, VerifiedEconomicStateBatchAdvance};
4use chio_core::StoreMutationFence;
5use chio_credit::clearing::{
6    verify_clearing_lifecycle_replay_authority_verification_with_outcome,
7    verify_clearing_lifecycle_replay_with_outcome, ClearingDisputeWindowResolver, ClearingError,
8    ClearingLifecycleAuthorityPinsV1, ClearingLifecycleAuthorityVerificationV1,
9    ClearingLifecycleAuthorityVerifier, ClearingLifecycleBatchVerifier,
10    ClearingLifecycleProofResolver, ClearingLifecycleReplayV1, ClearingRoundTransitionProofV1,
11    ClearingSettlementOutcomeVerifier, CLEARING_LIFECYCLE_REPLAY_DESCRIPTOR_KIND,
12    CLEARING_ROUND_RESOURCE_FAMILY,
13};
14use chio_federation::frost::FrostArtifactTrustStore;
15
16use crate::economic_state_cache::EconomicStageAdmissionCheckpoint;
17use crate::{
18    EconomicStateCacheError, EconomicStateStageDescriptor, EconomicStateStageRecord,
19    SqliteEconomicStateCache,
20};
21
22#[derive(Debug, thiserror::Error)]
23pub enum ClearingLifecycleStoreError {
24    #[error(transparent)]
25    Cache(#[from] EconomicStateCacheError),
26    #[error(transparent)]
27    Clearing(#[from] ClearingError),
28}
29
30#[derive(Clone)]
31pub struct SqliteClearingLifecycleStore {
32    cache: SqliteEconomicStateCache,
33    pins: ClearingLifecycleAuthorityPinsV1,
34    frost_trust: Option<Arc<FrostArtifactTrustStore>>,
35    dispute_resolver: Arc<dyn ClearingDisputeWindowResolver>,
36    settlement_outcome_verifier: Option<Arc<dyn ClearingSettlementOutcomeVerifier>>,
37}
38
39impl SqliteClearingLifecycleStore {
40    pub fn new(
41        cache: SqliteEconomicStateCache,
42        pins: ClearingLifecycleAuthorityPinsV1,
43        frost_trust: Option<Arc<FrostArtifactTrustStore>>,
44        dispute_resolver: Arc<dyn ClearingDisputeWindowResolver>,
45    ) -> Result<Self, ClearingLifecycleStoreError> {
46        pins.validate()?;
47        Ok(Self {
48            cache,
49            pins,
50            frost_trust,
51            dispute_resolver,
52            settlement_outcome_verifier: None,
53        })
54    }
55
56    #[must_use]
57    pub fn with_settlement_outcome_verifier(
58        mut self,
59        verifier: Arc<dyn ClearingSettlementOutcomeVerifier>,
60    ) -> Self {
61        self.settlement_outcome_verifier = Some(verifier);
62        self
63    }
64
65    pub fn stage(
66        &self,
67        advance: &VerifiedEconomicStateBatchAdvance,
68        replay: &ClearingLifecycleReplayV1,
69        active_fence: &StoreMutationFence,
70        trusted_now_unix_ms: u64,
71    ) -> Result<EconomicStateStageRecord, ClearingLifecycleStoreError> {
72        verify_clearing_lifecycle_replay_with_outcome(
73            advance.current(),
74            advance.batch(),
75            replay,
76            &self.pins,
77            self.frost_trust.as_deref(),
78            Some(self.dispute_resolver.as_ref()),
79            self.settlement_outcome_verifier.as_deref(),
80        )?;
81        if replay.authorized_at_unix_ms() > trusted_now_unix_ms {
82            return Err(ClearingError::AuthorityVerification.into());
83        }
84        let proof_digest = replay.proof_digest()?;
85        let descriptor = EconomicStateStageDescriptor::new(
86            CLEARING_LIFECYCLE_REPLAY_DESCRIPTOR_KIND,
87            &proof_digest,
88            replay,
89        )?;
90        let checkpoint = replay
91            .admission_checkpoint()
92            .map(
93                |(store_id, sequence, digest)| EconomicStageAdmissionCheckpoint {
94                    store_id,
95                    sequence,
96                    digest,
97                },
98            );
99        let record = self.cache.stage_clearing_lifecycle_batch(
100            advance,
101            descriptor,
102            checkpoint,
103            active_fence,
104            trusted_now_unix_ms,
105        )?;
106        Ok(record)
107    }
108
109    #[must_use]
110    pub fn recovery_verifier(self: &Arc<Self>) -> ClearingLifecycleBatchVerifier {
111        ClearingLifecycleBatchVerifier::new(self.clone(), self.clone())
112    }
113
114    fn load_replay(
115        &self,
116        proof_digest: &str,
117    ) -> Result<(EconomicStateStageRecord, ClearingLifecycleReplayV1), ClearingLifecycleStoreError>
118    {
119        let record = self
120            .cache
121            .load_stage_by_descriptor(CLEARING_LIFECYCLE_REPLAY_DESCRIPTOR_KIND, proof_digest)?
122            .ok_or(EconomicStateCacheError::NotFound)?;
123        let descriptor = record
124            .descriptor()
125            .ok_or(EconomicStateCacheError::Conflict)?;
126        if descriptor.kind() != CLEARING_LIFECYCLE_REPLAY_DESCRIPTOR_KIND
127            || descriptor.key() != proof_digest
128        {
129            return Err(EconomicStateCacheError::Conflict.into());
130        }
131        let replay = descriptor.decode::<ClearingLifecycleReplayV1>()?;
132        if replay.proof_digest()? != proof_digest {
133            return Err(EconomicStateCacheError::Conflict.into());
134        }
135        Ok((record, replay))
136    }
137}
138
139impl ClearingLifecycleProofResolver for SqliteClearingLifecycleStore {
140    fn resolve(&self, proof_digest: &str) -> Result<ClearingRoundTransitionProofV1, ClearingError> {
141        self.load_replay(proof_digest)
142            .map(|(_, replay)| replay.proof)
143            .map_err(|_| ClearingError::AuthorityVerification)
144    }
145}
146
147impl ClearingLifecycleAuthorityVerifier for SqliteClearingLifecycleStore {
148    fn verify(
149        &self,
150        proof: &ClearingRoundTransitionProofV1,
151    ) -> Result<ClearingLifecycleAuthorityVerificationV1, ClearingError> {
152        let proof_digest = proof.digest()?;
153        let (record, replay) = self
154            .load_replay(&proof_digest)
155            .map_err(|_| ClearingError::AuthorityVerification)?;
156        if replay.proof != *proof {
157            return Err(ClearingError::AuthorityVerification);
158        }
159        let round_key = EconomicResourceKeyV1 {
160            resource_family: CLEARING_ROUND_RESOURCE_FAMILY.to_owned(),
161            scope_id: proof.governance_scope_id.clone(),
162            resource_id: proof.round_id.clone(),
163        };
164        let source_round_head = record
165            .base_view()
166            .head(&round_key)
167            .ok_or(ClearingError::IncompleteLifecycleProjection)?;
168        verify_clearing_lifecycle_replay_authority_verification_with_outcome(
169            source_round_head,
170            &replay,
171            &self.pins,
172            self.frost_trust.as_deref(),
173            Some(self.dispute_resolver.as_ref()),
174            self.settlement_outcome_verifier.as_deref(),
175        )
176    }
177}