migrations.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. use core::str;
  2. use std::collections::HashMap;
  3. use std::str::FromStr;
  4. use std::sync::Arc;
  5. use cdk_common::mint::MintQuote;
  6. use cdk_common::mint_url::MintUrl;
  7. use cdk_common::util::unix_time;
  8. use cdk_common::{Amount, CurrencyUnit, MintQuoteState, Proof, State};
  9. use lightning_invoice::Bolt11Invoice;
  10. use redb::{
  11. Database, MultimapTableDefinition, ReadableMultimapTable, ReadableTable, TableDefinition,
  12. };
  13. use serde::{Deserialize, Serialize};
  14. use uuid::Uuid;
  15. use super::{Error, PROOFS_STATE_TABLE, PROOFS_TABLE, QUOTE_SIGNATURES_TABLE};
  16. const ID_STR_MINT_QUOTES_TABLE: TableDefinition<&str, &str> = TableDefinition::new("mint_quotes");
  17. const PENDING_PROOFS_TABLE: TableDefinition<[u8; 33], &str> =
  18. TableDefinition::new("pending_proofs");
  19. const SPENT_PROOFS_TABLE: TableDefinition<[u8; 33], &str> = TableDefinition::new("spent_proofs");
  20. const QUOTE_PROOFS_TABLE: MultimapTableDefinition<&str, [u8; 33]> =
  21. MultimapTableDefinition::new("quote_proofs");
  22. pub fn migrate_01_to_02(db: Arc<Database>) -> Result<u32, Error> {
  23. migrate_mint_quotes_01_to_02(db)?;
  24. Ok(2)
  25. }
  26. pub fn migrate_02_to_03(db: Arc<Database>) -> Result<u32, Error> {
  27. migrate_mint_proofs_02_to_03(db)?;
  28. Ok(3)
  29. }
  30. pub fn migrate_03_to_04(db: Arc<Database>) -> Result<u32, Error> {
  31. let write_txn = db.begin_write()?;
  32. let _ = write_txn.open_multimap_table(QUOTE_PROOFS_TABLE)?;
  33. let _ = write_txn.open_multimap_table(QUOTE_SIGNATURES_TABLE)?;
  34. Ok(4)
  35. }
  36. pub fn migrate_04_to_05(db: Arc<Database>) -> Result<u32, Error> {
  37. let write_txn = db.begin_write()?;
  38. // Mint quotes
  39. {
  40. const MINT_QUOTE_TABLE_NAME: &str = "mint_quotes";
  41. const OLD_TABLE: TableDefinition<&str, &str> = TableDefinition::new(MINT_QUOTE_TABLE_NAME);
  42. const NEW_TABLE: TableDefinition<[u8; 16], &str> =
  43. TableDefinition::new(MINT_QUOTE_TABLE_NAME);
  44. let old_table = write_txn.open_table(OLD_TABLE)?;
  45. let mut tmp_hashmap = HashMap::new();
  46. for (k, v) in old_table.iter().map_err(Error::from)?.flatten() {
  47. let quote_id = Uuid::try_parse(k.value()).unwrap();
  48. tmp_hashmap.insert(quote_id, v.value().to_string());
  49. }
  50. write_txn.delete_table(old_table).map_err(Error::from)?;
  51. let mut new_table = write_txn.open_table(NEW_TABLE)?;
  52. for (k, v) in tmp_hashmap.into_iter() {
  53. new_table
  54. .insert(k.as_bytes(), v.as_str())
  55. .map_err(Error::from)?;
  56. }
  57. }
  58. // Melt quotes
  59. {
  60. const MELT_QUOTE_TABLE_NAME: &str = "melt_quotes";
  61. const OLD_TABLE: TableDefinition<&str, &str> = TableDefinition::new(MELT_QUOTE_TABLE_NAME);
  62. const NEW_TABLE: TableDefinition<[u8; 16], &str> =
  63. TableDefinition::new(MELT_QUOTE_TABLE_NAME);
  64. let old_table = write_txn.open_table(OLD_TABLE)?;
  65. let mut tmp_hashmap = HashMap::new();
  66. for (k, v) in old_table.iter().map_err(Error::from)?.flatten() {
  67. let quote_id = Uuid::try_parse(k.value()).unwrap();
  68. tmp_hashmap.insert(quote_id, v.value().to_string());
  69. }
  70. write_txn.delete_table(old_table).map_err(Error::from)?;
  71. let mut new_table = write_txn.open_table(NEW_TABLE)?;
  72. for (k, v) in tmp_hashmap.into_iter() {
  73. new_table
  74. .insert(k.as_bytes(), v.as_str())
  75. .map_err(Error::from)?;
  76. }
  77. }
  78. // Quote proofs
  79. {
  80. const QUOTE_PROOFS_TABLE_NAME: &str = "quote_proofs";
  81. const OLD_TABLE: MultimapTableDefinition<&str, [u8; 33]> =
  82. MultimapTableDefinition::new(QUOTE_PROOFS_TABLE_NAME);
  83. const NEW_TABLE: MultimapTableDefinition<[u8; 16], [u8; 33]> =
  84. MultimapTableDefinition::new(QUOTE_PROOFS_TABLE_NAME);
  85. let old_table = write_txn.open_multimap_table(OLD_TABLE)?;
  86. let mut tmp_hashmap = HashMap::new();
  87. for (k, v) in old_table.iter().map_err(Error::from)?.flatten() {
  88. let quote_id = Uuid::try_parse(k.value()).unwrap();
  89. let ys: Vec<[u8; 33]> = v.into_iter().flatten().map(|v| v.value()).collect();
  90. tmp_hashmap.insert(quote_id, ys);
  91. }
  92. write_txn
  93. .delete_multimap_table(old_table)
  94. .map_err(Error::from)?;
  95. let mut new_table = write_txn.open_multimap_table(NEW_TABLE)?;
  96. for (quote_id, blind_messages) in tmp_hashmap.into_iter() {
  97. for blind_message in blind_messages {
  98. new_table
  99. .insert(quote_id.as_bytes(), blind_message)
  100. .map_err(Error::from)?;
  101. }
  102. }
  103. }
  104. // Quote signatures
  105. {
  106. const QUOTE_SIGNATURES_TABLE_NAME: &str = "quote_signatures";
  107. const OLD_TABLE: MultimapTableDefinition<&str, [u8; 33]> =
  108. MultimapTableDefinition::new(QUOTE_SIGNATURES_TABLE_NAME);
  109. const NEW_TABLE: MultimapTableDefinition<[u8; 16], [u8; 33]> =
  110. MultimapTableDefinition::new(QUOTE_SIGNATURES_TABLE_NAME);
  111. let old_table = write_txn.open_multimap_table(OLD_TABLE)?;
  112. let mut tmp_hashmap = HashMap::new();
  113. for (k, v) in old_table.iter().map_err(Error::from)?.flatten() {
  114. let quote_id = Uuid::try_parse(k.value()).unwrap();
  115. let ys: Vec<[u8; 33]> = v.into_iter().flatten().map(|v| v.value()).collect();
  116. tmp_hashmap.insert(quote_id, ys);
  117. }
  118. write_txn
  119. .delete_multimap_table(old_table)
  120. .map_err(Error::from)?;
  121. let mut new_table = write_txn.open_multimap_table(NEW_TABLE)?;
  122. for (quote_id, signatures) in tmp_hashmap.into_iter() {
  123. for signature in signatures {
  124. new_table
  125. .insert(quote_id.as_bytes(), signature)
  126. .map_err(Error::from)?;
  127. }
  128. }
  129. }
  130. // Melt requests
  131. {
  132. const MELT_REQUESTS_TABLE_NAME: &str = "melt_requests";
  133. const OLD_TABLE: TableDefinition<&str, (&str, &str)> =
  134. TableDefinition::new(MELT_REQUESTS_TABLE_NAME);
  135. const NEW_TABLE: TableDefinition<[u8; 16], (&str, &str)> =
  136. TableDefinition::new(MELT_REQUESTS_TABLE_NAME);
  137. let old_table = write_txn.open_table(OLD_TABLE)?;
  138. let mut tmp_hashmap = HashMap::new();
  139. for (k, v) in old_table.iter().map_err(Error::from)?.flatten() {
  140. let quote_id = Uuid::try_parse(k.value()).unwrap();
  141. let value = v.value();
  142. tmp_hashmap.insert(quote_id, (value.0.to_string(), value.1.to_string()));
  143. }
  144. write_txn.delete_table(old_table).map_err(Error::from)?;
  145. let mut new_table = write_txn.open_table(NEW_TABLE)?;
  146. for (k, v) in tmp_hashmap.into_iter() {
  147. new_table
  148. .insert(k.as_bytes(), (v.0.as_str(), v.1.as_str()))
  149. .map_err(Error::from)?;
  150. }
  151. }
  152. write_txn.commit().map_err(Error::from)?;
  153. Ok(5)
  154. }
  155. /// Mint Quote Info
  156. #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
  157. struct V1MintQuote {
  158. pub id: Uuid,
  159. pub mint_url: MintUrl,
  160. pub amount: Amount,
  161. pub unit: CurrencyUnit,
  162. pub request: String,
  163. pub state: MintQuoteState,
  164. pub expiry: u64,
  165. }
  166. impl From<V1MintQuote> for MintQuote {
  167. fn from(quote: V1MintQuote) -> MintQuote {
  168. MintQuote {
  169. id: quote.id,
  170. amount: quote.amount,
  171. unit: quote.unit,
  172. request: quote.request.clone(),
  173. state: quote.state,
  174. expiry: quote.expiry,
  175. request_lookup_id: Bolt11Invoice::from_str(&quote.request).unwrap().to_string(),
  176. pubkey: None,
  177. created_time: unix_time(),
  178. paid_time: None,
  179. issued_time: None,
  180. }
  181. }
  182. }
  183. fn migrate_mint_quotes_01_to_02(db: Arc<Database>) -> Result<(), Error> {
  184. let read_txn = db.begin_read().map_err(Error::from)?;
  185. let table = read_txn
  186. .open_table(ID_STR_MINT_QUOTES_TABLE)
  187. .map_err(Error::from)?;
  188. let mint_quotes: HashMap<String, Option<V1MintQuote>>;
  189. {
  190. mint_quotes = table
  191. .iter()
  192. .map_err(Error::from)?
  193. .flatten()
  194. .map(|(quote_id, mint_quote)| {
  195. (
  196. quote_id.value().to_string(),
  197. serde_json::from_str(mint_quote.value()).ok(),
  198. )
  199. })
  200. .collect();
  201. }
  202. let migrated_mint_quotes: HashMap<String, Option<MintQuote>> = mint_quotes
  203. .into_iter()
  204. .map(|(quote_id, quote)| (quote_id, quote.map(|q| q.into())))
  205. .collect();
  206. {
  207. let write_txn = db.begin_write()?;
  208. {
  209. let mut table = write_txn
  210. .open_table(ID_STR_MINT_QUOTES_TABLE)
  211. .map_err(Error::from)?;
  212. for (quote_id, quote) in migrated_mint_quotes {
  213. match quote {
  214. Some(quote) => {
  215. let quote_str = serde_json::to_string(&quote)?;
  216. table.insert(quote_id.as_str(), quote_str.as_str())?;
  217. }
  218. None => {
  219. table.remove(quote_id.as_str())?;
  220. }
  221. }
  222. }
  223. }
  224. write_txn.commit()?;
  225. }
  226. Ok(())
  227. }
  228. fn migrate_mint_proofs_02_to_03(db: Arc<Database>) -> Result<(), Error> {
  229. let pending_proofs: Vec<([u8; 33], Option<Proof>)>;
  230. let spent_proofs: Vec<([u8; 33], Option<Proof>)>;
  231. {
  232. let read_txn = db.begin_read().map_err(Error::from)?;
  233. let table = read_txn
  234. .open_table(PENDING_PROOFS_TABLE)
  235. .map_err(Error::from)?;
  236. pending_proofs = table
  237. .iter()
  238. .map_err(Error::from)?
  239. .flatten()
  240. .map(|(quote_id, mint_quote)| {
  241. (
  242. quote_id.value(),
  243. serde_json::from_str(mint_quote.value()).ok(),
  244. )
  245. })
  246. .collect();
  247. }
  248. {
  249. let read_txn = db.begin_read().map_err(Error::from)?;
  250. let table = read_txn
  251. .open_table(SPENT_PROOFS_TABLE)
  252. .map_err(Error::from)?;
  253. spent_proofs = table
  254. .iter()
  255. .map_err(Error::from)?
  256. .flatten()
  257. .map(|(quote_id, mint_quote)| {
  258. (
  259. quote_id.value(),
  260. serde_json::from_str(mint_quote.value()).ok(),
  261. )
  262. })
  263. .collect();
  264. }
  265. let write_txn = db.begin_write().map_err(Error::from)?;
  266. {
  267. let mut proofs_table = write_txn.open_table(PROOFS_TABLE).map_err(Error::from)?;
  268. let mut state_table = write_txn
  269. .open_table(PROOFS_STATE_TABLE)
  270. .map_err(Error::from)?;
  271. for (y, proof) in pending_proofs {
  272. if let Some(proof) = proof {
  273. proofs_table.insert(y, serde_json::to_string(&proof)?.as_str())?;
  274. state_table.insert(y, State::Pending.to_string().as_str())?;
  275. }
  276. }
  277. for (y, proof) in spent_proofs {
  278. if let Some(proof) = proof {
  279. proofs_table.insert(y, serde_json::to_string(&proof)?.as_str())?;
  280. state_table.insert(y, State::Spent.to_string().as_str())?;
  281. }
  282. }
  283. }
  284. write_txn.commit()?;
  285. Ok(())
  286. }