1use std::collections::HashMap;
4use std::str::FromStr;
5
6use async_trait::async_trait;
7use cdk_common::database::{self, Error, MintSignatureTransaction, MintSignaturesDatabase};
8use cdk_common::quote_id::QuoteId;
9use cdk_common::util::unix_time;
10use cdk_common::{Amount, BlindSignature, BlindSignatureDleq, Id, PublicKey, SecretKey};
11
12use super::proofs::sql_row_to_hashmap_amount;
13use super::{SQLMintDatabase, SQLTransaction};
14use crate::pool::DatabasePool;
15use crate::stmt::{query, Column};
16use crate::{column_as_nullable_string, column_as_number, column_as_string, unpack_into};
17
18pub(crate) fn sql_row_to_blind_signature(row: Vec<Column>) -> Result<BlindSignature, Error> {
19 unpack_into!(
20 let (
21 keyset_id, amount, c, dleq_e, dleq_s
22 ) = row
23 );
24
25 let dleq = match (
26 column_as_nullable_string!(dleq_e),
27 column_as_nullable_string!(dleq_s),
28 ) {
29 (Some(e), Some(s)) => Some(BlindSignatureDleq {
30 e: SecretKey::from_hex(e)?,
31 s: SecretKey::from_hex(s)?,
32 }),
33 _ => None,
34 };
35
36 let amount: u64 = column_as_number!(amount);
37
38 Ok(BlindSignature {
39 amount: Amount::from(amount),
40 keyset_id: column_as_string!(keyset_id, Id::from_str, Id::from_bytes),
41 c: column_as_string!(c, PublicKey::from_hex, PublicKey::from_slice),
42 dleq,
43 })
44}
45
46#[async_trait]
47impl<RM> MintSignatureTransaction for SQLTransaction<RM>
48where
49 RM: DatabasePool + 'static,
50{
51 type Err = Error;
52
53 async fn add_blind_signatures(
54 &mut self,
55 blinded_messages: &[PublicKey],
56 blind_signatures: &[BlindSignature],
57 quote_id: Option<QuoteId>,
58 ) -> Result<(), Self::Err> {
59 let current_time = unix_time();
60
61 if blinded_messages.len() != blind_signatures.len() {
62 return Err(database::Error::Internal(
63 "Mismatched array lengths for blinded messages and blind signatures".to_string(),
64 ));
65 }
66
67 let mut existing_rows = query(
69 r#"
70 SELECT blinded_message, c, dleq_e, dleq_s
71 FROM blind_signature
72 WHERE blinded_message IN (:blinded_messages)
73 ORDER BY blinded_message
74 FOR UPDATE
75 "#,
76 )?
77 .bind_vec(
78 "blinded_messages",
79 blinded_messages
80 .iter()
81 .map(|message| message.to_bytes().to_vec())
82 .collect(),
83 )?
84 .fetch_all(&self.inner)
85 .await?
86 .into_iter()
87 .map(|mut row| {
88 Ok((
89 column_as_string!(&row.remove(0), PublicKey::from_hex, PublicKey::from_slice),
90 (row[0].clone(), row[1].clone(), row[2].clone()),
91 ))
92 })
93 .collect::<Result<HashMap<_, _>, Error>>()?;
94
95 let mut ordered_signatures = blinded_messages
96 .iter()
97 .zip(blind_signatures)
98 .enumerate()
99 .collect::<Vec<_>>();
100 ordered_signatures.sort_unstable_by(|(_, (left, _)), (_, (right, _))| {
101 left.to_bytes().cmp(&right.to_bytes())
102 });
103
104 for (i, (message, signature)) in ordered_signatures {
106 match existing_rows.remove(message) {
107 None => {
108 query(
110 r#"
111 INSERT INTO blind_signature
112 (blinded_message, amount, keyset_id, c, quote_id, dleq_e, dleq_s, created_time, signed_time, order_index)
113 VALUES
114 (:blinded_message, :amount, :keyset_id, :c, :quote_id, :dleq_e, :dleq_s, :created_time, :signed_time, :order_index)
115 "#,
116 )?
117 .bind("blinded_message", message.to_bytes().to_vec())
118 .bind("amount", u64::from(signature.amount) as i64)
119 .bind("keyset_id", signature.keyset_id.to_string())
120 .bind("c", signature.c.to_bytes().to_vec())
121 .bind("quote_id", quote_id.as_ref().map(|q| q.to_string()))
122 .bind(
123 "dleq_e",
124 signature.dleq.as_ref().map(|dleq| dleq.e.to_secret_hex()),
125 )
126 .bind(
127 "dleq_s",
128 signature.dleq.as_ref().map(|dleq| dleq.s.to_secret_hex()),
129 )
130 .bind("created_time", current_time as i64)
131 .bind("signed_time", current_time as i64)
132 .bind("order_index", i as i64)
133 .execute(&self.inner)
134 .await?;
135
136 query(
137 r#"
138 INSERT INTO keyset_amounts (keyset_id, total_issued, total_redeemed)
139 VALUES (:keyset_id, :amount, 0)
140 ON CONFLICT (keyset_id)
141 DO UPDATE SET total_issued = keyset_amounts.total_issued + EXCLUDED.total_issued
142 "#,
143 )?
144 .bind("amount", u64::from(signature.amount) as i64)
145 .bind("keyset_id", signature.keyset_id.to_string())
146 .execute(&self.inner)
147 .await?;
148 }
149 Some((c, _dleq_e, _dleq_s)) => {
150 match c {
152 Column::Null => {
153 query(
155 r#"
156 UPDATE blind_signature
157 SET c = :c, dleq_e = :dleq_e, dleq_s = :dleq_s, signed_time = :signed_time, amount = :amount
158 WHERE blinded_message = :blinded_message
159 "#,
160 )?
161 .bind("c", signature.c.to_bytes().to_vec())
162 .bind(
163 "dleq_e",
164 signature.dleq.as_ref().map(|dleq| dleq.e.to_secret_hex()),
165 )
166 .bind(
167 "dleq_s",
168 signature.dleq.as_ref().map(|dleq| dleq.s.to_secret_hex()),
169 )
170 .bind("blinded_message", message.to_bytes().to_vec())
171 .bind("signed_time", current_time as i64)
172 .bind("amount", u64::from(signature.amount) as i64)
173 .execute(&self.inner)
174 .await?;
175
176 query(
177 r#"
178 INSERT INTO keyset_amounts (keyset_id, total_issued, total_redeemed)
179 VALUES (:keyset_id, :amount, 0)
180 ON CONFLICT (keyset_id)
181 DO UPDATE SET total_issued = keyset_amounts.total_issued + EXCLUDED.total_issued
182 "#,
183 )?
184 .bind("amount", u64::from(signature.amount) as i64)
185 .bind("keyset_id", signature.keyset_id.to_string())
186 .execute(&self.inner)
187 .await?;
188 }
189 _ => {
190 tracing::error!(
192 "Attempting to add signature to message already signed {}",
193 message
194 );
195
196 return Err(database::Error::Duplicate);
197 }
198 }
199 }
200 }
201 }
202
203 debug_assert!(
204 existing_rows.is_empty(),
205 "Unexpected existing rows remain: {:?}",
206 existing_rows.keys().collect::<Vec<_>>()
207 );
208
209 if !existing_rows.is_empty() {
210 tracing::error!("Did not check all existing rows");
211 return Err(Error::Internal(
212 "Did not check all existing rows".to_string(),
213 ));
214 }
215
216 Ok(())
217 }
218
219 async fn get_blind_signatures(
220 &mut self,
221 blinded_messages: &[PublicKey],
222 ) -> Result<Vec<Option<BlindSignature>>, Self::Err> {
223 let mut blinded_signatures = query(
224 r#"SELECT
225 keyset_id,
226 amount,
227 c,
228 dleq_e,
229 dleq_s,
230 blinded_message
231 FROM
232 blind_signature
233 WHERE blinded_message IN (:b) AND c IS NOT NULL
234 "#,
235 )?
236 .bind_vec(
237 "b",
238 blinded_messages
239 .iter()
240 .map(|b| b.to_bytes().to_vec())
241 .collect(),
242 )?
243 .fetch_all(&self.inner)
244 .await?
245 .into_iter()
246 .map(|mut row| {
247 Ok((
248 column_as_string!(
249 &row.pop().ok_or(Error::InvalidDbResponse)?,
250 PublicKey::from_hex,
251 PublicKey::from_slice
252 ),
253 sql_row_to_blind_signature(row)?,
254 ))
255 })
256 .collect::<Result<HashMap<_, _>, Error>>()?;
257 Ok(blinded_messages
258 .iter()
259 .map(|y| blinded_signatures.remove(y))
260 .collect())
261 }
262}
263
264#[async_trait]
265impl<RM> MintSignaturesDatabase for SQLMintDatabase<RM>
266where
267 RM: DatabasePool + 'static,
268{
269 type Err = Error;
270
271 async fn get_blind_signatures(
272 &self,
273 blinded_messages: &[PublicKey],
274 ) -> Result<Vec<Option<BlindSignature>>, Self::Err> {
275 let conn = self
276 .pool
277 .get()
278 .await
279 .map_err(|e| Error::Database(Box::new(e)))?;
280 let mut blinded_signatures = query(
281 r#"SELECT
282 keyset_id,
283 amount,
284 c,
285 dleq_e,
286 dleq_s,
287 blinded_message
288 FROM
289 blind_signature
290 WHERE blinded_message IN (:b) AND c IS NOT NULL
291 "#,
292 )?
293 .bind_vec(
294 "b",
295 blinded_messages
296 .iter()
297 .map(|b_| b_.to_bytes().to_vec())
298 .collect(),
299 )?
300 .fetch_all(&*conn)
301 .await?
302 .into_iter()
303 .map(|mut row| {
304 Ok((
305 column_as_string!(
306 &row.pop().ok_or(Error::InvalidDbResponse)?,
307 PublicKey::from_hex,
308 PublicKey::from_slice
309 ),
310 sql_row_to_blind_signature(row)?,
311 ))
312 })
313 .collect::<Result<HashMap<_, _>, Error>>()?;
314 Ok(blinded_messages
315 .iter()
316 .map(|y| blinded_signatures.remove(y))
317 .collect())
318 }
319
320 async fn get_blind_signatures_for_keyset(
321 &self,
322 keyset_id: &Id,
323 ) -> Result<Vec<BlindSignature>, Self::Err> {
324 let conn = self
325 .pool
326 .get()
327 .await
328 .map_err(|e| Error::Database(Box::new(e)))?;
329 Ok(query(
330 r#"
331 SELECT
332 keyset_id,
333 amount,
334 c,
335 dleq_e,
336 dleq_s
337 FROM
338 blind_signature
339 WHERE
340 keyset_id=:keyset_id AND c IS NOT NULL
341 "#,
342 )?
343 .bind("keyset_id", keyset_id.to_string())
344 .fetch_all(&*conn)
345 .await?
346 .into_iter()
347 .map(sql_row_to_blind_signature)
348 .collect::<Result<Vec<BlindSignature>, _>>()?)
349 }
350
351 async fn get_blind_signatures_for_quote(
353 &self,
354 quote_id: &QuoteId,
355 ) -> Result<Vec<BlindSignature>, Self::Err> {
356 let conn = self
357 .pool
358 .get()
359 .await
360 .map_err(|e| Error::Database(Box::new(e)))?;
361 Ok(query(
362 r#"
363 SELECT
364 keyset_id,
365 amount,
366 c,
367 dleq_e,
368 dleq_s
369 FROM
370 blind_signature
371 WHERE
372 quote_id=:quote_id AND c IS NOT NULL
373 ORDER BY order_index ASC
374 "#,
375 )?
376 .bind("quote_id", quote_id.to_string())
377 .fetch_all(&*conn)
378 .await?
379 .into_iter()
380 .map(sql_row_to_blind_signature)
381 .collect::<Result<Vec<BlindSignature>, _>>()?)
382 }
383
384 async fn get_total_issued(&self) -> Result<HashMap<Id, Amount>, Self::Err> {
386 let conn = self
387 .pool
388 .get()
389 .await
390 .map_err(|e| Error::Database(Box::new(e)))?;
391 query(
392 r#"
393 SELECT
394 keyset_id,
395 total_issued as amount
396 FROM
397 keyset_amounts
398 "#,
399 )?
400 .fetch_all(&*conn)
401 .await?
402 .into_iter()
403 .map(sql_row_to_hashmap_amount)
404 .collect()
405 }
406
407 async fn get_blinded_secrets_by_operation_id(
408 &self,
409 operation_id: &uuid::Uuid,
410 ) -> Result<Vec<PublicKey>, Self::Err> {
411 let conn = self
412 .pool
413 .get()
414 .await
415 .map_err(|e| Error::Database(Box::new(e)))?;
416 query(
417 r#"
418 SELECT
419 blinded_message
420 FROM
421 blind_signature
422 WHERE
423 operation_id = :operation_id
424 "#,
425 )?
426 .bind("operation_id", operation_id.to_string())
427 .fetch_all(&*conn)
428 .await?
429 .into_iter()
430 .map(|row| -> Result<PublicKey, Error> {
431 Ok(column_as_string!(
432 &row[0],
433 PublicKey::from_hex,
434 PublicKey::from_slice
435 ))
436 })
437 .collect::<Result<Vec<_>, _>>()
438 }
439}