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}