fedimint_server/consensus/
session_cache.rs1use std::future::pending;
7use std::sync::Arc;
8
9use fedimint_core::db::{
10 Committable, Database, DatabaseTransaction, IDatabaseTransactionOpsCoreTyped,
11};
12use fedimint_core::module::SerdeModuleEncoding;
13use fedimint_core::session_outcome::SignedSessionOutcome;
14use futures::StreamExt;
15use tokio::sync::watch;
16
17use super::db::{SignedSessionOutcomeKey, SignedSessionOutcomePrefix};
18
19pub struct CachedSessionOutcome {
25 pub index: u64,
26 pub signed: Arc<serde_json::Value>,
27 pub unsigned: Arc<serde_json::Value>,
28}
29
30impl CachedSessionOutcome {
31 fn new(index: u64, outcome: &SignedSessionOutcome) -> Self {
32 Self {
33 index,
34 signed: OutcomeFormat::Signed.encode(outcome),
35 unsigned: OutcomeFormat::Unsigned.encode(outcome),
36 }
37 }
38}
39
40#[derive(Clone, Copy)]
42pub enum OutcomeFormat {
43 Signed,
44 Unsigned,
45}
46
47impl OutcomeFormat {
48 fn encode(self, outcome: &SignedSessionOutcome) -> Arc<serde_json::Value> {
49 Arc::new(
50 match self {
51 Self::Signed => serde_json::to_value(SerdeModuleEncoding::from(outcome)),
52 Self::Unsigned => {
53 serde_json::to_value(SerdeModuleEncoding::from(&outcome.session_outcome))
54 }
55 }
56 .expect("Session outcome encoding is infallible"),
57 )
58 }
59
60 fn cached(self, outcome: &CachedSessionOutcome) -> Arc<serde_json::Value> {
61 match self {
62 Self::Signed => outcome.signed.clone(),
63 Self::Unsigned => outcome.unsigned.clone(),
64 }
65 }
66}
67
68pub type CompletedSession = Option<Arc<CachedSessionOutcome>>;
69
70pub async fn load(db: &Database) -> CompletedSession {
73 db.begin_transaction_nc()
74 .await
75 .find_by_prefix_sorted_descending(&SignedSessionOutcomePrefix)
76 .await
77 .next()
78 .await
79 .map(|(key, outcome)| Arc::new(CachedSessionOutcome::new(key.0, &outcome)))
80}
81
82pub fn publish_on_commit(
84 dbtx: &mut DatabaseTransaction<'_, Committable>,
85 sender: watch::Sender<CompletedSession>,
86 index: u64,
87 outcome: &SignedSessionOutcome,
88) {
89 let completed = Arc::new(CachedSessionOutcome::new(index, outcome));
90 dbtx.on_commit(move || {
91 sender.send_replace(Some(completed));
92 });
93}
94
95pub async fn await_outcome(
97 db: &Database,
98 receiver: &watch::Receiver<CompletedSession>,
99 index: u64,
100 format: OutcomeFormat,
101) -> Arc<serde_json::Value> {
102 let mut receiver = receiver.clone();
103 loop {
104 let latest = receiver.borrow_and_update().clone();
107 if let Some(latest) = latest {
108 if latest.index == index {
109 return format.cached(&latest);
110 }
111 if latest.index > index {
112 drop(latest);
113 let outcome = db
114 .begin_transaction_nc()
115 .await
116 .get_value(&SignedSessionOutcomeKey(index))
117 .await
118 .expect("There are no gaps in completed session outcomes");
119 return format.encode(&outcome);
122 }
123 }
124 if receiver.changed().await.is_err() {
127 pending::<()>().await;
128 }
129 }
130}
131
132#[cfg(test)]
133mod tests;