1use std::collections::HashMap;
2
3use fedimint_core::config::FederationId;
4use fedimint_core::core::OperationId;
5use fedimint_core::db::{AutocommitError, Database, IDatabaseTransactionOpsCoreTyped};
6use fedimint_core::encoding::{Decodable, Encodable};
7use fedimint_core::secp256k1::rand::thread_rng;
8use fedimint_core::{impl_db_lookup, impl_db_record};
9use fedimint_ln_client::recurring::{PaymentCodeId, PaymentCodeRootKey, RecurringPaymentProtocol};
10use futures::stream::StreamExt;
11use rand::Rng;
12
13use crate::PaymentCodeInvoice;
14
15#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Encodable, Decodable)]
16pub struct FederationDbPrefix([u8; 16]);
17
18impl FederationDbPrefix {
19 pub fn random() -> FederationDbPrefix {
20 FederationDbPrefix(thread_rng().r#gen())
21 }
22
23 fn prepend(&self, byte: u8) -> Vec<u8> {
24 let mut full_prefix = Vec::with_capacity(17);
25 full_prefix.push(byte);
26 full_prefix.extend(&self.0);
27 full_prefix
28 }
29}
30
31async fn load_federation_clients(db: &Database) -> Vec<(FederationId, FederationDbPrefix)> {
32 let mut dbtx = db.begin_transaction_nc().await;
33 dbtx.find_by_prefix(&FederationClientPrefix)
34 .await
35 .map(|(k, v)| (k.federation_id, v.db_prefix))
36 .collect::<Vec<_>>()
37 .await
38}
39
40pub fn open_client_db(db: &Database, db_prefix: FederationDbPrefix) -> Database {
41 db.with_prefix(db_prefix.prepend(DbKeyPrefix::ClientDB as u8))
42}
43
44pub async fn try_add_federation_database(
45 db: &Database,
46 federation_id: FederationId,
47 db_prefix: FederationDbPrefix,
48) -> Result<(), FederationDbPrefix> {
49 db.autocommit(
50 |dbtx, _| {
51 Box::pin(async move {
52 if let Some(federation_db_entry) =
53 dbtx.get_value(&FederationClientKey { federation_id }).await
54 {
55 return Err(federation_db_entry.db_prefix);
56 }
57
58 dbtx.insert_new_entry(
59 &FederationClientKey { federation_id },
60 &FederationClientEntry { db_prefix },
61 )
62 .await;
63
64 Ok(())
65 })
66 },
67 None,
68 )
69 .await
70 .map_err(|e| match e {
71 AutocommitError::CommitFailed { .. } => unreachable!("will keep retrying"),
72 AutocommitError::ClosureError { error, .. } => {
73 error
75 }
76 })
77}
78
79pub async fn load_federation_client_databases(db: &Database) -> HashMap<FederationId, Database> {
80 load_federation_clients(db)
81 .await
82 .into_iter()
83 .map(|(federation_id, db_prefix)| (federation_id, open_client_db(db, db_prefix)))
84 .collect()
85}
86
87#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
88enum DbKeyPrefix {
89 ClientList = 0x00,
90 ClientDB = 0x01,
91 PaymentCodes = 0x02,
92 PaymentCodeNextInvoiceIndex = 0x03,
93 PaymentCodeInvoices = 0x04,
94}
95
96#[derive(Debug, Clone, Eq, PartialEq, Hash, Encodable, Decodable)]
97pub struct FederationClientKey {
98 pub federation_id: FederationId,
99}
100
101#[derive(Debug, Clone, Eq, PartialEq, Hash, Encodable, Decodable)]
102pub struct FederationClientPrefix;
103
104impl_db_record!(
105 key = FederationClientKey,
106 value = FederationClientEntry,
107 db_prefix = DbKeyPrefix::ClientList,
108);
109impl_db_lookup!(
110 key = FederationClientKey,
111 query_prefix = FederationClientPrefix
112);
113
114#[derive(Debug, Clone, Eq, PartialEq, Hash, Encodable, Decodable)]
115pub struct FederationClientEntry {
116 pub db_prefix: FederationDbPrefix,
117}
118
119#[derive(Debug, Clone, Eq, PartialEq, Hash, Encodable, Decodable)]
120pub struct PaymentCodeKey {
121 pub payment_code_id: PaymentCodeId,
122}
123
124#[derive(Debug, Clone, Eq, PartialEq, Hash, Encodable, Decodable)]
125pub struct PaymentCodePrefix;
126
127#[derive(Debug, Clone, Eq, PartialEq, Hash, Encodable, Decodable)]
128pub enum PaymentCodeVariant {
129 Lnurl { meta: String },
130}
131
132#[derive(Debug, Clone, Eq, PartialEq, Hash, Encodable, Decodable)]
133pub struct PaymentCodeEntry {
134 pub root_key: PaymentCodeRootKey,
135 pub federation_id: FederationId,
136 pub protocol: RecurringPaymentProtocol,
137 pub payment_code: String,
138 pub variant: PaymentCodeVariant,
139}
140
141impl_db_record!(
142 key = PaymentCodeKey,
143 value = PaymentCodeEntry,
144 db_prefix = DbKeyPrefix::PaymentCodes,
145);
146impl_db_lookup!(key = PaymentCodeKey, query_prefix = PaymentCodePrefix);
147
148#[derive(Debug, Clone, Eq, PartialEq, Hash, Encodable, Decodable)]
149pub struct PaymentCodeNextInvoiceIndexKey {
150 pub payment_code_id: PaymentCodeId,
151}
152
153impl_db_record!(
154 key = PaymentCodeNextInvoiceIndexKey,
155 value = u64,
156 db_prefix = DbKeyPrefix::PaymentCodeNextInvoiceIndex,
157);
158
159#[derive(Debug, Clone, Eq, PartialEq, Hash, Encodable, Decodable)]
160pub struct PaymentCodeInvoiceKey {
161 pub payment_code_id: PaymentCodeId,
162 pub index: u64,
163}
164
165#[derive(Debug, Clone, Eq, PartialEq, Hash, Encodable, Decodable)]
166pub struct PaymentCodeInvoicePrefix {
167 payment_code_id: PaymentCodeId,
168}
169
170#[derive(Debug, Clone, Eq, PartialEq, Hash, Encodable, Decodable)]
171pub struct PaymentCodeInvoiceEntry {
172 pub operation_id: OperationId,
173 pub invoice: PaymentCodeInvoice,
174}
175
176impl_db_record!(
177 key = PaymentCodeInvoiceKey,
178 value = PaymentCodeInvoiceEntry,
179 db_prefix = DbKeyPrefix::PaymentCodeInvoices,
180);
181impl_db_lookup!(
182 key = PaymentCodeInvoiceKey,
183 query_prefix = PaymentCodeInvoicePrefix
184);