mod.rs 62 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893
  1. //! Cashu Wallet
  2. //!
  3. //! Each wallet is single mint and single unit
  4. use std::collections::{HashMap, HashSet};
  5. use std::str::FromStr;
  6. use std::sync::Arc;
  7. use bitcoin::bip32::ExtendedPrivKey;
  8. use bitcoin::hashes::sha256::Hash as Sha256Hash;
  9. use bitcoin::hashes::Hash;
  10. use bitcoin::key::XOnlyPublicKey;
  11. use bitcoin::Network;
  12. use error::Error;
  13. use tracing::instrument;
  14. use crate::amount::SplitTarget;
  15. use crate::cdk_database::{self, WalletDatabase};
  16. use crate::dhke::{construct_proofs, hash_to_curve};
  17. use crate::nuts::nut00::token::Token;
  18. use crate::nuts::{
  19. nut10, nut12, Conditions, CurrencyUnit, Id, KeySetInfo, Keys, Kind, MeltQuoteBolt11Response,
  20. MeltQuoteState, MintInfo, MintQuoteBolt11Response, MintQuoteState, PreMintSecrets, PreSwap,
  21. Proof, ProofState, Proofs, PublicKey, RestoreRequest, SecretKey, SigFlag, SpendingConditions,
  22. State, SwapRequest,
  23. };
  24. use crate::types::{Melted, ProofInfo};
  25. use crate::url::UncheckedUrl;
  26. use crate::util::{hex, unix_time};
  27. use crate::{Amount, Bolt11Invoice, HttpClient, SECP256K1};
  28. pub mod client;
  29. pub mod error;
  30. pub mod multi_mint_wallet;
  31. pub mod types;
  32. pub mod util;
  33. pub use types::{MeltQuote, MintQuote, SendKind};
  34. /// CDK Wallet
  35. #[derive(Debug, Clone)]
  36. pub struct Wallet {
  37. /// Mint Url
  38. pub mint_url: UncheckedUrl,
  39. /// Unit
  40. pub unit: CurrencyUnit,
  41. /// Storage backend
  42. pub localstore: Arc<dyn WalletDatabase<Err = cdk_database::Error> + Send + Sync>,
  43. /// The targeted amount of proofs to have at each size
  44. pub target_proof_count: usize,
  45. xpriv: ExtendedPrivKey,
  46. client: HttpClient,
  47. }
  48. impl Wallet {
  49. /// Create new [`Wallet`]
  50. pub fn new(
  51. mint_url: &str,
  52. unit: CurrencyUnit,
  53. localstore: Arc<dyn WalletDatabase<Err = cdk_database::Error> + Send + Sync>,
  54. seed: &[u8],
  55. target_proof_count: Option<usize>,
  56. ) -> Self {
  57. let xpriv = ExtendedPrivKey::new_master(Network::Bitcoin, seed)
  58. .expect("Could not create master key");
  59. Self {
  60. mint_url: UncheckedUrl::from(mint_url),
  61. unit,
  62. client: HttpClient::new(),
  63. localstore,
  64. xpriv,
  65. target_proof_count: target_proof_count.unwrap_or(3),
  66. }
  67. }
  68. /// Fee required for proof set
  69. #[instrument(skip_all)]
  70. pub async fn get_proofs_fee(&self, proofs: &Proofs) -> Result<Amount, Error> {
  71. let mut sum_fee = 0;
  72. for proof in proofs {
  73. let input_fee_ppk = self
  74. .localstore
  75. .get_keyset_by_id(&proof.keyset_id)
  76. .await?
  77. .ok_or(Error::UnknownKey)?;
  78. sum_fee += input_fee_ppk.input_fee_ppk;
  79. }
  80. let fee = (sum_fee + 999) / 1000;
  81. Ok(Amount::from(fee))
  82. }
  83. /// Get fee for count of proofs in a keyset
  84. #[instrument(skip_all)]
  85. pub async fn get_keyset_count_fee(&self, keyset_id: &Id, count: u64) -> Result<Amount, Error> {
  86. let input_fee_ppk = self
  87. .localstore
  88. .get_keyset_by_id(keyset_id)
  89. .await?
  90. .ok_or(Error::UnknownKey)?
  91. .input_fee_ppk;
  92. let fee = (input_fee_ppk * count + 999) / 1000;
  93. Ok(Amount::from(fee))
  94. }
  95. /// Total unspent balance of wallet
  96. #[instrument(skip(self))]
  97. pub async fn total_balance(&self) -> Result<Amount, Error> {
  98. if let Some(proofs) = self
  99. .localstore
  100. .get_proofs(
  101. Some(self.mint_url.clone()),
  102. Some(self.unit),
  103. Some(vec![State::Unspent]),
  104. None,
  105. )
  106. .await?
  107. {
  108. let balance = proofs.iter().map(|p| p.proof.amount).sum::<Amount>();
  109. return Ok(balance);
  110. }
  111. Ok(Amount::ZERO)
  112. }
  113. /// Total pending balance
  114. #[instrument(skip(self))]
  115. pub async fn total_pending_balance(&self) -> Result<HashMap<CurrencyUnit, Amount>, Error> {
  116. let mut balances = HashMap::new();
  117. if let Some(proofs) = self
  118. .localstore
  119. .get_proofs(
  120. Some(self.mint_url.clone()),
  121. Some(self.unit),
  122. Some(vec![State::Pending]),
  123. None,
  124. )
  125. .await?
  126. {
  127. for proof in proofs {
  128. balances
  129. .entry(proof.unit)
  130. .and_modify(|ps| *ps += proof.proof.amount)
  131. .or_insert(proof.proof.amount);
  132. }
  133. }
  134. Ok(balances)
  135. }
  136. /// Total reserved balance
  137. #[instrument(skip(self))]
  138. pub async fn total_reserved_balance(&self) -> Result<HashMap<CurrencyUnit, Amount>, Error> {
  139. let mut balances = HashMap::new();
  140. if let Some(proofs) = self
  141. .localstore
  142. .get_proofs(
  143. Some(self.mint_url.clone()),
  144. Some(self.unit),
  145. Some(vec![State::Reserved]),
  146. None,
  147. )
  148. .await?
  149. {
  150. for proof in proofs {
  151. balances
  152. .entry(proof.unit)
  153. .and_modify(|ps| *ps += proof.proof.amount)
  154. .or_insert(proof.proof.amount);
  155. }
  156. }
  157. Ok(balances)
  158. }
  159. /// Update Mint information and related entries in the event a mint changes its URL
  160. #[instrument(skip(self))]
  161. pub async fn update_mint_url(&mut self, new_mint_url: UncheckedUrl) -> Result<(), Error> {
  162. self.mint_url = new_mint_url.clone();
  163. // Where the mint_url is in the database it must be updated
  164. self.localstore
  165. .update_mint_url(self.mint_url.clone(), new_mint_url)
  166. .await?;
  167. self.localstore.remove_mint(self.mint_url.clone()).await?;
  168. Ok(())
  169. }
  170. /// Get unspent proofs for mint
  171. #[instrument(skip(self))]
  172. pub async fn get_proofs(&self) -> Result<Proofs, Error> {
  173. Ok(self
  174. .localstore
  175. .get_proofs(
  176. Some(self.mint_url.clone()),
  177. Some(self.unit),
  178. Some(vec![State::Unspent]),
  179. None,
  180. )
  181. .await?
  182. .map(|p| p.into_iter().map(|p| p.proof).collect())
  183. .unwrap_or_default())
  184. }
  185. /// Get pending [`Proofs`]
  186. #[instrument(skip(self))]
  187. pub async fn get_pending_proofs(&self) -> Result<Proofs, Error> {
  188. Ok(self
  189. .localstore
  190. .get_proofs(
  191. Some(self.mint_url.clone()),
  192. Some(self.unit),
  193. Some(vec![State::Pending]),
  194. None,
  195. )
  196. .await?
  197. .map(|p| p.into_iter().map(|p| p.proof).collect())
  198. .unwrap_or_default())
  199. }
  200. /// Get reserved [`Proofs`]
  201. #[instrument(skip(self))]
  202. pub async fn get_reserved_proofs(&self) -> Result<Proofs, Error> {
  203. Ok(self
  204. .localstore
  205. .get_proofs(
  206. Some(self.mint_url.clone()),
  207. Some(self.unit),
  208. Some(vec![State::Reserved]),
  209. None,
  210. )
  211. .await?
  212. .map(|p| p.into_iter().map(|p| p.proof).collect())
  213. .unwrap_or_default())
  214. }
  215. /// Return proofs to unspent allowing them to be selected and spent
  216. #[instrument(skip(self))]
  217. pub async fn unreserve_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Error> {
  218. for y in ys {
  219. self.localstore.set_proof_state(y, State::Unspent).await?;
  220. }
  221. Ok(())
  222. }
  223. /// Add mint to wallet
  224. #[instrument(skip(self))]
  225. pub async fn get_mint_info(&self) -> Result<Option<MintInfo>, Error> {
  226. let mint_info = match self
  227. .client
  228. .get_mint_info(self.mint_url.clone().try_into()?)
  229. .await
  230. {
  231. Ok(mint_info) => Some(mint_info),
  232. Err(err) => {
  233. tracing::warn!("Could not get mint info {}", err);
  234. None
  235. }
  236. };
  237. self.localstore
  238. .add_mint(self.mint_url.clone(), mint_info.clone())
  239. .await?;
  240. Ok(mint_info)
  241. }
  242. /// Get keys for mint keyset
  243. #[instrument(skip(self))]
  244. pub async fn get_keyset_keys(&self, keyset_id: Id) -> Result<Keys, Error> {
  245. let keys = if let Some(keys) = self.localstore.get_keys(&keyset_id).await? {
  246. keys
  247. } else {
  248. let keys = self
  249. .client
  250. .get_mint_keyset(self.mint_url.clone().try_into()?, keyset_id)
  251. .await?;
  252. self.localstore.add_keys(keys.keys.clone()).await?;
  253. keys.keys
  254. };
  255. Ok(keys)
  256. }
  257. /// Get keysets for mint
  258. #[instrument(skip(self))]
  259. pub async fn get_mint_keysets(&self) -> Result<Vec<KeySetInfo>, Error> {
  260. let keysets = self
  261. .client
  262. .get_mint_keysets(self.mint_url.clone().try_into()?)
  263. .await?;
  264. self.localstore
  265. .add_mint_keysets(self.mint_url.clone(), keysets.keysets.clone())
  266. .await?;
  267. Ok(keysets.keysets)
  268. }
  269. /// Get active keyset for mint
  270. /// Quieries mint for current keysets then gets Keys for any unknown keysets
  271. #[instrument(skip(self))]
  272. pub async fn get_active_mint_keyset(&self) -> Result<KeySetInfo, Error> {
  273. let keysets = self
  274. .client
  275. .get_mint_keysets(self.mint_url.clone().try_into()?)
  276. .await?;
  277. let keysets = keysets.keysets;
  278. self.localstore
  279. .add_mint_keysets(self.mint_url.clone(), keysets.clone())
  280. .await?;
  281. let active_keysets = keysets
  282. .clone()
  283. .into_iter()
  284. .filter(|k| k.active && k.unit == self.unit)
  285. .collect::<Vec<KeySetInfo>>();
  286. match self
  287. .localstore
  288. .get_mint_keysets(self.mint_url.clone())
  289. .await?
  290. {
  291. Some(known_keysets) => {
  292. let unknown_keysets: Vec<&KeySetInfo> = keysets
  293. .iter()
  294. .filter(|k| known_keysets.contains(k))
  295. .collect();
  296. for keyset in unknown_keysets {
  297. self.get_keyset_keys(keyset.id).await?;
  298. }
  299. }
  300. None => {
  301. for keyset in keysets {
  302. self.get_keyset_keys(keyset.id).await?;
  303. }
  304. }
  305. }
  306. active_keysets.first().ok_or(Error::NoActiveKeyset).cloned()
  307. }
  308. /// Reclaim unspent proofs
  309. #[instrument(skip(self, proofs))]
  310. pub async fn reclaim_unspent(&self, proofs: Proofs) -> Result<(), Error> {
  311. let proof_ys = proofs
  312. .iter()
  313. // Find Y for the secret
  314. .flat_map(|p| hash_to_curve(p.secret.as_bytes()))
  315. .collect::<Vec<PublicKey>>();
  316. let spendable = self
  317. .client
  318. .post_check_state(self.mint_url.clone().try_into()?, proof_ys)
  319. .await?
  320. .states;
  321. let unspent: Proofs = proofs
  322. .into_iter()
  323. .zip(spendable)
  324. .filter_map(|(p, s)| (s.state == State::Unspent).then_some(p))
  325. .collect();
  326. self.swap(None, SplitTarget::default(), unspent, None, false)
  327. .await?;
  328. Ok(())
  329. }
  330. /// Check if a proof is spent
  331. #[instrument(skip(self, proofs))]
  332. pub async fn check_proofs_spent(&self, proofs: Proofs) -> Result<Vec<ProofState>, Error> {
  333. let spendable = self
  334. .client
  335. .post_check_state(
  336. self.mint_url.clone().try_into()?,
  337. proofs
  338. .iter()
  339. // Find Y for the secret
  340. .flat_map(|p| hash_to_curve(p.secret.as_bytes()))
  341. .collect::<Vec<PublicKey>>(),
  342. )
  343. .await?;
  344. Ok(spendable.states)
  345. }
  346. /// Checks pending proofs for spent status
  347. #[instrument(skip(self))]
  348. pub async fn check_all_pending_proofs(&self) -> Result<Amount, Error> {
  349. let mut balance = Amount::ZERO;
  350. if let Some(proofs) = self
  351. .localstore
  352. .get_proofs(
  353. Some(self.mint_url.clone()),
  354. Some(self.unit),
  355. Some(vec![State::Pending, State::Reserved]),
  356. None,
  357. )
  358. .await?
  359. {
  360. let states = self
  361. .check_proofs_spent(proofs.clone().into_iter().map(|p| p.proof).collect())
  362. .await?;
  363. // Both `State::Pending` and `State::Unspent` should be included in the pending table.
  364. // This is because a proof that has been crated to send will be stored in the pending table
  365. // in order to avoid accidentally double spending but to allow it to be explicitly reclaimed
  366. let pending_states: HashSet<PublicKey> = states
  367. .into_iter()
  368. .filter(|s| s.state.ne(&State::Spent))
  369. .map(|s| s.y)
  370. .collect();
  371. let (pending_proofs, non_pending_proofs): (Vec<ProofInfo>, Vec<ProofInfo>) = proofs
  372. .into_iter()
  373. .partition(|p| pending_states.contains(&p.y));
  374. let amount = pending_proofs.iter().map(|p| p.proof.amount).sum();
  375. self.localstore
  376. .remove_proofs(&non_pending_proofs.into_iter().map(|p| p.proof).collect())
  377. .await?;
  378. balance += amount;
  379. }
  380. Ok(balance)
  381. }
  382. /// Mint Quote
  383. #[instrument(skip(self))]
  384. pub async fn mint_quote(&self, amount: Amount) -> Result<MintQuote, Error> {
  385. let mint_url = self.mint_url.clone();
  386. let unit = self.unit;
  387. let quote_res = self
  388. .client
  389. .post_mint_quote(mint_url.clone().try_into()?, amount, unit)
  390. .await?;
  391. let quote = MintQuote {
  392. mint_url,
  393. id: quote_res.quote.clone(),
  394. amount,
  395. unit,
  396. request: quote_res.request,
  397. state: quote_res.state,
  398. expiry: quote_res.expiry.unwrap_or(0),
  399. };
  400. self.localstore.add_mint_quote(quote.clone()).await?;
  401. Ok(quote)
  402. }
  403. /// Mint quote status
  404. #[instrument(skip(self, quote_id))]
  405. pub async fn mint_quote_state(&self, quote_id: &str) -> Result<MintQuoteBolt11Response, Error> {
  406. let response = self
  407. .client
  408. .get_mint_quote_status(self.mint_url.clone().try_into()?, quote_id)
  409. .await?;
  410. match self.localstore.get_mint_quote(quote_id).await? {
  411. Some(quote) => {
  412. let mut quote = quote;
  413. quote.state = response.state;
  414. self.localstore.add_mint_quote(quote).await?;
  415. }
  416. None => {
  417. tracing::info!("Quote mint {} unknown", quote_id);
  418. }
  419. }
  420. Ok(response)
  421. }
  422. /// Check status of pending mint quotes
  423. #[instrument(skip(self))]
  424. pub async fn check_all_mint_quotes(&self) -> Result<Amount, Error> {
  425. let mint_quotes = self.localstore.get_mint_quotes().await?;
  426. let mut total_amount = Amount::ZERO;
  427. for mint_quote in mint_quotes {
  428. let mint_quote_response = self.mint_quote_state(&mint_quote.id).await?;
  429. if mint_quote_response.state == MintQuoteState::Paid {
  430. let amount = self
  431. .mint(&mint_quote.id, SplitTarget::default(), None)
  432. .await?;
  433. total_amount += amount;
  434. } else if mint_quote.expiry.le(&unix_time()) {
  435. self.localstore.remove_mint_quote(&mint_quote.id).await?;
  436. }
  437. }
  438. Ok(total_amount)
  439. }
  440. /// Mint
  441. #[instrument(skip(self))]
  442. pub async fn mint(
  443. &self,
  444. quote_id: &str,
  445. amount_split_target: SplitTarget,
  446. spending_conditions: Option<SpendingConditions>,
  447. ) -> Result<Amount, Error> {
  448. // Check that mint is in store of mints
  449. if self
  450. .localstore
  451. .get_mint(self.mint_url.clone())
  452. .await?
  453. .is_none()
  454. {
  455. self.get_mint_info().await?;
  456. }
  457. let quote_info = self.localstore.get_mint_quote(quote_id).await?;
  458. let quote_info = if let Some(quote) = quote_info {
  459. if quote.expiry.le(&unix_time()) && quote.expiry.ne(&0) {
  460. return Err(Error::QuoteExpired);
  461. }
  462. quote.clone()
  463. } else {
  464. return Err(Error::QuoteUnknown);
  465. };
  466. let active_keyset_id = self.get_active_mint_keyset().await?.id;
  467. let count = self
  468. .localstore
  469. .get_keyset_counter(&active_keyset_id)
  470. .await?;
  471. let count = count.map_or(0, |c| c + 1);
  472. let premint_secrets = match &spending_conditions {
  473. Some(spending_conditions) => PreMintSecrets::with_conditions(
  474. active_keyset_id,
  475. quote_info.amount,
  476. &amount_split_target,
  477. spending_conditions,
  478. )?,
  479. None => PreMintSecrets::from_xpriv(
  480. active_keyset_id,
  481. count,
  482. self.xpriv,
  483. quote_info.amount,
  484. &amount_split_target,
  485. )?,
  486. };
  487. let mint_res = self
  488. .client
  489. .post_mint(
  490. self.mint_url.clone().try_into()?,
  491. quote_id,
  492. premint_secrets.clone(),
  493. )
  494. .await?;
  495. let keys = self.get_keyset_keys(active_keyset_id).await?;
  496. // Verify the signature DLEQ is valid
  497. {
  498. for (sig, premint) in mint_res.signatures.iter().zip(&premint_secrets.secrets) {
  499. let keys = self.get_keyset_keys(sig.keyset_id).await?;
  500. let key = keys.amount_key(sig.amount).ok_or(Error::UnknownKey)?;
  501. match sig.verify_dleq(key, premint.blinded_message.blinded_secret) {
  502. Ok(_) | Err(nut12::Error::MissingDleqProof) => (),
  503. Err(_) => return Err(Error::CouldNotVerifyDleq),
  504. }
  505. }
  506. }
  507. let proofs = construct_proofs(
  508. mint_res.signatures,
  509. premint_secrets.rs(),
  510. premint_secrets.secrets(),
  511. &keys,
  512. )?;
  513. let minted_amount = proofs.iter().map(|p| p.amount).sum();
  514. // Remove filled quote from store
  515. self.localstore.remove_mint_quote(&quote_info.id).await?;
  516. if spending_conditions.is_none() {
  517. // Update counter for keyset
  518. self.localstore
  519. .increment_keyset_counter(&active_keyset_id, proofs.len() as u32)
  520. .await?;
  521. }
  522. let proofs = proofs
  523. .into_iter()
  524. .flat_map(|proof| {
  525. ProofInfo::new(
  526. proof,
  527. self.mint_url.clone(),
  528. State::Unspent,
  529. quote_info.unit,
  530. )
  531. })
  532. .collect();
  533. // Add new proofs to store
  534. self.localstore.add_proofs(proofs).await?;
  535. Ok(minted_amount)
  536. }
  537. /// Get amounts needed to refill proof state
  538. #[instrument(skip(self))]
  539. pub async fn amounts_needed_for_state_target(&self) -> Result<Vec<Amount>, Error> {
  540. let unspent_proofs = self.get_proofs().await?;
  541. let amounts_count: HashMap<usize, usize> =
  542. unspent_proofs
  543. .iter()
  544. .fold(HashMap::new(), |mut acc, proof| {
  545. let amount = proof.amount;
  546. let counter = acc.entry(u64::from(amount) as usize).or_insert(0);
  547. *counter += 1;
  548. acc
  549. });
  550. let all_possible_amounts: Vec<usize> = (0..32).map(|i| 2usize.pow(i as u32)).collect();
  551. let needed_amounts = all_possible_amounts
  552. .iter()
  553. .fold(Vec::new(), |mut acc, amount| {
  554. let count_needed: usize = self
  555. .target_proof_count
  556. .saturating_sub(*amounts_count.get(amount).unwrap_or(&0));
  557. for _i in 0..count_needed {
  558. acc.push(Amount::from(*amount as u64));
  559. }
  560. acc
  561. });
  562. Ok(needed_amounts)
  563. }
  564. /// Determine [`SplitTarget`] for amount based on state
  565. #[instrument(skip(self))]
  566. async fn determine_split_target_values(
  567. &self,
  568. change_amount: Amount,
  569. ) -> Result<SplitTarget, Error> {
  570. let mut amounts_needed_refill = self.amounts_needed_for_state_target().await?;
  571. amounts_needed_refill.sort();
  572. let mut values = Vec::new();
  573. for amount in amounts_needed_refill {
  574. let values_sum: Amount = values.clone().into_iter().sum();
  575. if values_sum + amount <= change_amount {
  576. values.push(amount);
  577. }
  578. }
  579. Ok(SplitTarget::Values(values))
  580. }
  581. /// Create Swap Payload
  582. #[instrument(skip(self, proofs))]
  583. pub async fn create_swap(
  584. &self,
  585. amount: Option<Amount>,
  586. amount_split_target: SplitTarget,
  587. proofs: Proofs,
  588. spending_conditions: Option<SpendingConditions>,
  589. include_fees: bool,
  590. ) -> Result<PreSwap, Error> {
  591. let active_keyset_id = self.get_active_mint_keyset().await?.id;
  592. // Desired amount is either amount passed or value of all proof
  593. let proofs_total: Amount = proofs.iter().map(|p| p.amount).sum();
  594. for proof in proofs.iter() {
  595. self.localstore
  596. .set_proof_state(proof.y()?, State::Pending)
  597. .await
  598. .ok();
  599. }
  600. let fee = self.get_proofs_fee(&proofs).await?;
  601. let change_amount: Amount = proofs_total - amount.unwrap_or(Amount::ZERO) - fee;
  602. let (send_amount, change_amount) = match include_fees {
  603. true => {
  604. let split_count = amount
  605. .unwrap_or(Amount::ZERO)
  606. .split_targeted(&SplitTarget::default())
  607. .unwrap()
  608. .len();
  609. let fee_to_redeam = self
  610. .get_keyset_count_fee(&active_keyset_id, split_count as u64)
  611. .await?;
  612. (
  613. amount.map(|a| a + fee_to_redeam),
  614. change_amount - fee_to_redeam,
  615. )
  616. }
  617. false => (amount, change_amount),
  618. };
  619. // If a non None split target is passed use that
  620. // else use state refill
  621. let change_split_target = match amount_split_target {
  622. SplitTarget::None => self.determine_split_target_values(change_amount).await?,
  623. s => s,
  624. };
  625. let derived_secret_count;
  626. let count = self
  627. .localstore
  628. .get_keyset_counter(&active_keyset_id)
  629. .await?;
  630. let mut count = count.map_or(0, |c| c + 1);
  631. let (mut desired_messages, change_messages) = match spending_conditions {
  632. Some(conditions) => {
  633. let change_premint_secrets = PreMintSecrets::from_xpriv(
  634. active_keyset_id,
  635. count,
  636. self.xpriv,
  637. change_amount,
  638. &change_split_target,
  639. )?;
  640. derived_secret_count = change_premint_secrets.len();
  641. (
  642. PreMintSecrets::with_conditions(
  643. active_keyset_id,
  644. send_amount.unwrap_or(Amount::ZERO),
  645. &SplitTarget::default(),
  646. &conditions,
  647. )?,
  648. change_premint_secrets,
  649. )
  650. }
  651. None => {
  652. let premint_secrets = PreMintSecrets::from_xpriv(
  653. active_keyset_id,
  654. count,
  655. self.xpriv,
  656. send_amount.unwrap_or(Amount::ZERO),
  657. &SplitTarget::default(),
  658. )?;
  659. count += premint_secrets.len() as u32;
  660. let change_premint_secrets = PreMintSecrets::from_xpriv(
  661. active_keyset_id,
  662. count,
  663. self.xpriv,
  664. change_amount,
  665. &change_split_target,
  666. )?;
  667. derived_secret_count = change_premint_secrets.len() + premint_secrets.len();
  668. (premint_secrets, change_premint_secrets)
  669. }
  670. };
  671. // Combine the BlindedMessages totaling the desired amount with change
  672. desired_messages.combine(change_messages);
  673. // Sort the premint secrets to avoid finger printing
  674. desired_messages.sort_secrets();
  675. let swap_request = SwapRequest::new(proofs, desired_messages.blinded_messages());
  676. Ok(PreSwap {
  677. pre_mint_secrets: desired_messages,
  678. swap_request,
  679. derived_secret_count: derived_secret_count as u32,
  680. fee,
  681. })
  682. }
  683. /// Swap
  684. #[instrument(skip(self, input_proofs))]
  685. pub async fn swap(
  686. &self,
  687. amount: Option<Amount>,
  688. amount_split_target: SplitTarget,
  689. input_proofs: Proofs,
  690. spending_conditions: Option<SpendingConditions>,
  691. include_fees: bool,
  692. ) -> Result<Option<Proofs>, Error> {
  693. let mint_url = &self.mint_url;
  694. let unit = &self.unit;
  695. let pre_swap = self
  696. .create_swap(
  697. amount,
  698. amount_split_target,
  699. input_proofs.clone(),
  700. spending_conditions.clone(),
  701. include_fees,
  702. )
  703. .await?;
  704. let swap_response = self
  705. .client
  706. .post_swap(mint_url.clone().try_into()?, pre_swap.swap_request)
  707. .await?;
  708. let active_keyset_id = pre_swap.pre_mint_secrets.keyset_id;
  709. let active_keys = self
  710. .localstore
  711. .get_keys(&active_keyset_id)
  712. .await?
  713. .ok_or(Error::NoActiveKeyset)?;
  714. let post_swap_proofs = construct_proofs(
  715. swap_response.signatures,
  716. pre_swap.pre_mint_secrets.rs(),
  717. pre_swap.pre_mint_secrets.secrets(),
  718. &active_keys,
  719. )?;
  720. self.localstore
  721. .increment_keyset_counter(&active_keyset_id, pre_swap.derived_secret_count)
  722. .await?;
  723. let change_proofs;
  724. let send_proofs;
  725. match amount {
  726. Some(amount) => {
  727. let (proofs_with_condition, proofs_without_condition): (Proofs, Proofs) =
  728. post_swap_proofs.into_iter().partition(|p| {
  729. let nut10_secret: Result<nut10::Secret, _> = p.secret.clone().try_into();
  730. nut10_secret.is_ok()
  731. });
  732. let (proofs_to_send, proofs_to_keep) = match spending_conditions {
  733. Some(_) => (proofs_with_condition, proofs_without_condition),
  734. None => {
  735. let mut all_proofs = proofs_without_condition;
  736. all_proofs.reverse();
  737. let mut proofs_to_send: Proofs = Vec::new();
  738. let mut proofs_to_keep = Vec::new();
  739. for proof in all_proofs {
  740. let proofs_to_send_amount =
  741. proofs_to_send.iter().map(|p| p.amount).sum::<Amount>();
  742. if proof.amount + proofs_to_send_amount <= amount + pre_swap.fee {
  743. proofs_to_send.push(proof);
  744. } else {
  745. proofs_to_keep.push(proof);
  746. }
  747. }
  748. (proofs_to_send, proofs_to_keep)
  749. }
  750. };
  751. let send_amount: Amount = proofs_to_send.iter().map(|p| p.amount).sum();
  752. if send_amount.ne(&(amount + pre_swap.fee)) {
  753. tracing::warn!(
  754. "Send amount proofs is {:?} expected {:?}",
  755. send_amount,
  756. amount
  757. );
  758. }
  759. let send_proofs_info = proofs_to_send
  760. .clone()
  761. .into_iter()
  762. .flat_map(|proof| {
  763. ProofInfo::new(proof, mint_url.clone(), State::Reserved, *unit)
  764. })
  765. .collect();
  766. self.localstore.add_proofs(send_proofs_info).await?;
  767. change_proofs = proofs_to_keep;
  768. send_proofs = Some(proofs_to_send);
  769. }
  770. None => {
  771. change_proofs = post_swap_proofs;
  772. send_proofs = None;
  773. }
  774. }
  775. let keep_proofs = change_proofs
  776. .into_iter()
  777. .flat_map(|proof| ProofInfo::new(proof, mint_url.clone(), State::Unspent, *unit))
  778. .collect();
  779. self.localstore.add_proofs(keep_proofs).await?;
  780. // Remove spent proofs used as inputs
  781. self.localstore.remove_proofs(&input_proofs).await?;
  782. Ok(send_proofs)
  783. }
  784. #[instrument(skip(self))]
  785. async fn swap_from_unspent(
  786. &self,
  787. amount: Amount,
  788. conditions: Option<SpendingConditions>,
  789. include_fees: bool,
  790. ) -> Result<Proofs, Error> {
  791. let available_proofs = self
  792. .localstore
  793. .get_proofs(
  794. Some(self.mint_url.clone()),
  795. Some(self.unit),
  796. Some(vec![State::Unspent]),
  797. None,
  798. )
  799. .await?
  800. .ok_or(Error::InsufficientFunds)?;
  801. let available_proofs = available_proofs.into_iter().map(|p| p.proof).collect();
  802. let proofs = self.select_proofs_to_swap(amount, available_proofs).await?;
  803. self.swap(
  804. Some(amount),
  805. SplitTarget::default(),
  806. proofs,
  807. conditions,
  808. include_fees,
  809. )
  810. .await?
  811. .ok_or(Error::InsufficientFunds)
  812. }
  813. /// Send specific proofs
  814. #[instrument(skip(self))]
  815. pub async fn send_proofs(&self, memo: Option<String>, proofs: Proofs) -> Result<String, Error> {
  816. for proof in proofs.iter() {
  817. self.localstore
  818. .set_proof_state(proof.y()?, State::Reserved)
  819. .await?;
  820. }
  821. Ok(Token::new(self.mint_url.clone(), proofs, memo, Some(self.unit)).to_string())
  822. }
  823. /// Send
  824. #[instrument(skip(self))]
  825. pub async fn send(
  826. &self,
  827. amount: Amount,
  828. memo: Option<String>,
  829. conditions: Option<SpendingConditions>,
  830. amount_split_target: &SplitTarget,
  831. send_kind: &SendKind,
  832. include_fees: bool,
  833. ) -> Result<String, Error> {
  834. // If online send check mint for current keysets fees
  835. if matches!(
  836. send_kind,
  837. SendKind::OnlineExact | SendKind::OnlineTolerance(_)
  838. ) {
  839. if let Err(e) = self.get_active_mint_keyset().await {
  840. tracing::error!(
  841. "Error fetching active mint keyset: {:?}. Using stored keysets",
  842. e
  843. );
  844. }
  845. }
  846. let mint_url = &self.mint_url;
  847. let unit = &self.unit;
  848. let available_proofs = self
  849. .localstore
  850. .get_proofs(
  851. Some(mint_url.clone()),
  852. Some(*unit),
  853. Some(vec![State::Unspent]),
  854. conditions.clone().map(|c| vec![c]),
  855. )
  856. .await?
  857. .unwrap_or_default();
  858. let available_proofs = available_proofs.into_iter().map(|p| p.proof).collect();
  859. let selected = self
  860. .select_proofs_to_send(amount, available_proofs, include_fees)
  861. .await;
  862. let send_proofs: Proofs = match (send_kind, selected, conditions.clone()) {
  863. // Handle exact matches offline
  864. (SendKind::OfflineExact, Ok(selected_proofs), _) => {
  865. let selected_proofs_amount =
  866. selected_proofs.iter().map(|p| p.amount).sum::<Amount>();
  867. let amount_to_send = match include_fees {
  868. true => amount + self.get_proofs_fee(&selected_proofs).await?,
  869. false => amount,
  870. };
  871. if selected_proofs_amount == amount_to_send {
  872. selected_proofs
  873. } else {
  874. return Err(Error::InsufficientFunds);
  875. }
  876. }
  877. // Handle exact matches
  878. (SendKind::OnlineExact, Ok(selected_proofs), _) => {
  879. let selected_proofs_amount =
  880. selected_proofs.iter().map(|p| p.amount).sum::<Amount>();
  881. let amount_to_send = match include_fees {
  882. true => amount + self.get_proofs_fee(&selected_proofs).await?,
  883. false => amount,
  884. };
  885. if selected_proofs_amount == amount_to_send {
  886. selected_proofs
  887. } else {
  888. tracing::info!("Could not select proofs exact while offline.");
  889. tracing::info!("Attempting to select proofs and swapping");
  890. self.swap_from_unspent(amount, conditions, include_fees)
  891. .await?
  892. }
  893. }
  894. // Handle offline tolerance
  895. (SendKind::OfflineTolerance(tolerance), Ok(selected_proofs), _) => {
  896. let selected_proofs_amount =
  897. selected_proofs.iter().map(|p| p.amount).sum::<Amount>();
  898. let amount_to_send = match include_fees {
  899. true => amount + self.get_proofs_fee(&selected_proofs).await?,
  900. false => amount,
  901. };
  902. if selected_proofs_amount - amount_to_send <= *tolerance {
  903. selected_proofs
  904. } else {
  905. tracing::info!("Selected proofs greater than tolerance. Must swap online");
  906. return Err(Error::InsufficientFunds);
  907. }
  908. }
  909. // Handle online tolerance when selection fails and conditions are present
  910. (SendKind::OnlineTolerance(_), Err(_), Some(_)) => {
  911. tracing::info!("Could not select proofs with conditions while offline.");
  912. tracing::info!("Attempting to select proofs without conditions and swapping");
  913. self.swap_from_unspent(amount, conditions, include_fees)
  914. .await?
  915. }
  916. // Handle online tolerance with successful selection
  917. (SendKind::OnlineTolerance(tolerance), Ok(selected_proofs), _) => {
  918. let selected_proofs_amount =
  919. selected_proofs.iter().map(|p| p.amount).sum::<Amount>();
  920. let amount_to_send = match include_fees {
  921. true => amount + self.get_proofs_fee(&selected_proofs).await?,
  922. false => amount,
  923. };
  924. if selected_proofs_amount - amount_to_send <= *tolerance {
  925. selected_proofs
  926. } else {
  927. tracing::info!("Could not select proofs while offline. Attempting swap");
  928. self.swap_from_unspent(amount, conditions, include_fees)
  929. .await?
  930. }
  931. }
  932. // Handle all other cases where selection fails
  933. (
  934. SendKind::OfflineExact
  935. | SendKind::OnlineExact
  936. | SendKind::OfflineTolerance(_)
  937. | SendKind::OnlineTolerance(_),
  938. Err(_),
  939. _,
  940. ) => {
  941. tracing::debug!("Could not select proofs");
  942. return Err(Error::InsufficientFunds);
  943. }
  944. };
  945. self.send_proofs(memo, send_proofs).await
  946. }
  947. /// Melt Quote
  948. #[instrument(skip(self))]
  949. pub async fn melt_quote(
  950. &self,
  951. request: String,
  952. mpp: Option<Amount>,
  953. ) -> Result<MeltQuote, Error> {
  954. let invoice = Bolt11Invoice::from_str(&request)?;
  955. let request_amount = invoice
  956. .amount_milli_satoshis()
  957. .ok_or(Error::InvoiceAmountUndefined)?;
  958. let amount = match self.unit {
  959. CurrencyUnit::Sat => Amount::from(request_amount / 1000),
  960. CurrencyUnit::Msat => Amount::from(request_amount),
  961. _ => return Err(Error::UnitNotSupported),
  962. };
  963. let quote_res = self
  964. .client
  965. .post_melt_quote(self.mint_url.clone().try_into()?, self.unit, invoice, mpp)
  966. .await?;
  967. if quote_res.amount != amount {
  968. return Err(Error::IncorrectQuoteAmount);
  969. }
  970. let quote = MeltQuote {
  971. id: quote_res.quote,
  972. amount,
  973. request,
  974. unit: self.unit,
  975. fee_reserve: quote_res.fee_reserve,
  976. state: quote_res.state,
  977. expiry: quote_res.expiry,
  978. payment_preimage: quote_res.payment_preimage,
  979. };
  980. self.localstore.add_melt_quote(quote.clone()).await?;
  981. Ok(quote)
  982. }
  983. /// Melt quote status
  984. #[instrument(skip(self, quote_id))]
  985. pub async fn melt_quote_status(
  986. &self,
  987. quote_id: &str,
  988. ) -> Result<MeltQuoteBolt11Response, Error> {
  989. let response = self
  990. .client
  991. .get_melt_quote_status(self.mint_url.clone().try_into()?, quote_id)
  992. .await?;
  993. match self.localstore.get_melt_quote(quote_id).await? {
  994. Some(quote) => {
  995. let mut quote = quote;
  996. quote.state = response.state;
  997. self.localstore.add_melt_quote(quote).await?;
  998. }
  999. None => {
  1000. tracing::info!("Quote melt {} unknown", quote_id);
  1001. }
  1002. }
  1003. Ok(response)
  1004. }
  1005. /// Melt specific proofs
  1006. #[instrument(skip(self))]
  1007. pub async fn melt_proofs(&self, quote_id: &str, proofs: Proofs) -> Result<Melted, Error> {
  1008. let quote_info = self.localstore.get_melt_quote(quote_id).await?;
  1009. let quote_info = if let Some(quote) = quote_info {
  1010. if quote.expiry.le(&unix_time()) {
  1011. return Err(Error::QuoteExpired);
  1012. }
  1013. quote.clone()
  1014. } else {
  1015. return Err(Error::QuoteUnknown);
  1016. };
  1017. for proof in proofs.iter() {
  1018. self.localstore
  1019. .set_proof_state(proof.y()?, State::Pending)
  1020. .await?;
  1021. }
  1022. let active_keyset_id = self.get_active_mint_keyset().await?.id;
  1023. let count = self
  1024. .localstore
  1025. .get_keyset_counter(&active_keyset_id)
  1026. .await?;
  1027. let count = count.map_or(0, |c| c + 1);
  1028. let premint_secrets = PreMintSecrets::from_xpriv_blank(
  1029. active_keyset_id,
  1030. count,
  1031. self.xpriv,
  1032. quote_info.fee_reserve,
  1033. )?;
  1034. let melt_response = self
  1035. .client
  1036. .post_melt(
  1037. self.mint_url.clone().try_into()?,
  1038. quote_id.to_string(),
  1039. proofs.clone(),
  1040. Some(premint_secrets.blinded_messages()),
  1041. )
  1042. .await;
  1043. let melt_response = match melt_response {
  1044. Ok(melt_response) => melt_response,
  1045. Err(err) => {
  1046. tracing::error!("Could not melt: {}", err);
  1047. tracing::info!("Checking status of input proofs.");
  1048. self.reclaim_unspent(proofs).await?;
  1049. return Err(err);
  1050. }
  1051. };
  1052. let active_keys = self
  1053. .localstore
  1054. .get_keys(&active_keyset_id)
  1055. .await?
  1056. .ok_or(Error::NoActiveKeyset)?;
  1057. let change_proofs = match melt_response.change {
  1058. Some(change) => Some(construct_proofs(
  1059. change,
  1060. premint_secrets.rs(),
  1061. premint_secrets.secrets(),
  1062. &active_keys,
  1063. )?),
  1064. None => None,
  1065. };
  1066. let state = match melt_response.paid {
  1067. true => MeltQuoteState::Paid,
  1068. false => MeltQuoteState::Unpaid,
  1069. };
  1070. let melted = Melted {
  1071. state,
  1072. preimage: melt_response.payment_preimage,
  1073. change: change_proofs.clone(),
  1074. };
  1075. if let Some(change_proofs) = change_proofs {
  1076. tracing::debug!(
  1077. "Change amount returned from melt: {}",
  1078. change_proofs.iter().map(|p| p.amount).sum::<Amount>()
  1079. );
  1080. // Update counter for keyset
  1081. self.localstore
  1082. .increment_keyset_counter(&active_keyset_id, change_proofs.len() as u32)
  1083. .await?;
  1084. let change_proofs_info = change_proofs
  1085. .into_iter()
  1086. .flat_map(|proof| {
  1087. ProofInfo::new(
  1088. proof,
  1089. self.mint_url.clone(),
  1090. State::Unspent,
  1091. quote_info.unit,
  1092. )
  1093. })
  1094. .collect();
  1095. self.localstore.add_proofs(change_proofs_info).await?;
  1096. }
  1097. self.localstore.remove_melt_quote(&quote_info.id).await?;
  1098. self.localstore.remove_proofs(&proofs).await?;
  1099. Ok(melted)
  1100. }
  1101. /// Melt
  1102. #[instrument(skip(self))]
  1103. pub async fn melt(&self, quote_id: &str) -> Result<Melted, Error> {
  1104. let quote_info = self.localstore.get_melt_quote(quote_id).await?;
  1105. let quote_info = if let Some(quote) = quote_info {
  1106. if quote.expiry.le(&unix_time()) {
  1107. return Err(Error::QuoteExpired);
  1108. }
  1109. quote.clone()
  1110. } else {
  1111. return Err(Error::QuoteUnknown);
  1112. };
  1113. let inputs_needed_amount = quote_info.amount + quote_info.fee_reserve;
  1114. let available_proofs = self.get_proofs().await?;
  1115. let input_proofs = self
  1116. .select_proofs_to_swap(inputs_needed_amount, available_proofs)
  1117. .await?;
  1118. self.melt_proofs(quote_id, input_proofs).await
  1119. }
  1120. /// Select proofs to send
  1121. #[instrument(skip_all)]
  1122. pub async fn select_proofs_to_send(
  1123. &self,
  1124. amount: Amount,
  1125. proofs: Proofs,
  1126. include_fees: bool,
  1127. ) -> Result<Proofs, Error> {
  1128. // TODO: Check all proofs are same unit
  1129. if proofs.iter().map(|p| p.amount).sum::<Amount>() < amount {
  1130. return Err(Error::InsufficientFunds);
  1131. }
  1132. let (mut proofs_larger, mut proofs_smaller): (Proofs, Proofs) =
  1133. proofs.into_iter().partition(|p| p.amount > amount);
  1134. let next_bigger_proof = proofs_larger.first().cloned();
  1135. let mut selected_proofs: Vec<Proof> = Vec::new();
  1136. let mut remaining_amount = amount;
  1137. while remaining_amount > Amount::ZERO {
  1138. proofs_larger.sort();
  1139. // Sort smaller proofs in descending order
  1140. proofs_smaller.sort_by(|a: &Proof, b: &Proof| b.cmp(a));
  1141. let selected_proof = if let Some(next_small) = proofs_smaller.clone().first() {
  1142. next_small.clone()
  1143. } else if let Some(next_bigger) = proofs_larger.first() {
  1144. next_bigger.clone()
  1145. } else {
  1146. break;
  1147. };
  1148. let proof_amount = selected_proof.amount;
  1149. selected_proofs.push(selected_proof);
  1150. let fees = match include_fees {
  1151. true => self.get_proofs_fee(&selected_proofs).await?,
  1152. false => Amount::ZERO,
  1153. };
  1154. if proof_amount >= remaining_amount + fees {
  1155. remaining_amount = Amount::ZERO;
  1156. break;
  1157. }
  1158. remaining_amount =
  1159. amount + fees - selected_proofs.iter().map(|p| p.amount).sum::<Amount>();
  1160. (proofs_larger, proofs_smaller) = proofs_smaller
  1161. .into_iter()
  1162. .skip(1)
  1163. .partition(|p| p.amount > remaining_amount);
  1164. }
  1165. if remaining_amount > Amount::ZERO {
  1166. if let Some(next_bigger) = next_bigger_proof {
  1167. return Ok(vec![next_bigger.clone()]);
  1168. }
  1169. return Err(Error::InsufficientFunds);
  1170. }
  1171. Ok(selected_proofs)
  1172. }
  1173. /// Select proofs to send
  1174. #[instrument(skip_all)]
  1175. pub async fn select_proofs_to_swap(
  1176. &self,
  1177. amount: Amount,
  1178. proofs: Proofs,
  1179. ) -> Result<Proofs, Error> {
  1180. let active_keyset_id = self.get_active_mint_keyset().await?.id;
  1181. let (mut active_proofs, mut inactive_proofs): (Proofs, Proofs) = proofs
  1182. .into_iter()
  1183. .partition(|p| p.keyset_id == active_keyset_id);
  1184. let mut selected_proofs: Proofs = Vec::new();
  1185. inactive_proofs.sort_by(|a: &Proof, b: &Proof| b.cmp(a));
  1186. for inactive_proof in inactive_proofs {
  1187. selected_proofs.push(inactive_proof);
  1188. let selected_total = selected_proofs.iter().map(|p| p.amount).sum::<Amount>();
  1189. let fees = self.get_proofs_fee(&selected_proofs).await?;
  1190. if selected_total >= amount + fees {
  1191. return Ok(selected_proofs);
  1192. }
  1193. }
  1194. active_proofs.sort_by(|a: &Proof, b: &Proof| b.cmp(a));
  1195. for active_proof in active_proofs {
  1196. selected_proofs.push(active_proof);
  1197. let selected_total = selected_proofs.iter().map(|p| p.amount).sum::<Amount>();
  1198. let fees = self.get_proofs_fee(&selected_proofs).await?;
  1199. if selected_total >= amount + fees {
  1200. return Ok(selected_proofs);
  1201. }
  1202. }
  1203. Err(Error::InsufficientFunds)
  1204. }
  1205. /// Receive proofs
  1206. #[instrument(skip_all)]
  1207. pub async fn receive_proofs(
  1208. &self,
  1209. proofs: Proofs,
  1210. amount_split_target: SplitTarget,
  1211. p2pk_signing_keys: &[SecretKey],
  1212. preimages: &[String],
  1213. ) -> Result<Amount, Error> {
  1214. let _ = self.get_active_mint_keyset().await?;
  1215. let mut received_proofs: HashMap<UncheckedUrl, Proofs> = HashMap::new();
  1216. let mint_url = &self.mint_url;
  1217. // Add mint if it does not exist in the store
  1218. if self
  1219. .localstore
  1220. .get_mint(self.mint_url.clone())
  1221. .await?
  1222. .is_none()
  1223. {
  1224. self.get_mint_info().await?;
  1225. }
  1226. let active_keyset_id = self.get_active_mint_keyset().await?.id;
  1227. let keys = self.get_keyset_keys(active_keyset_id).await?;
  1228. let mut proofs = proofs;
  1229. let mut sig_flag = SigFlag::SigInputs;
  1230. // Map hash of preimage to preimage
  1231. let hashed_to_preimage: HashMap<String, &String> = preimages
  1232. .iter()
  1233. .flat_map(|p| match hex::decode(p) {
  1234. Ok(hex_bytes) => Some((Sha256Hash::hash(&hex_bytes).to_string(), p)),
  1235. Err(_) => None,
  1236. })
  1237. .collect();
  1238. let p2pk_signing_keys: HashMap<XOnlyPublicKey, &SecretKey> = p2pk_signing_keys
  1239. .iter()
  1240. .map(|s| (s.x_only_public_key(&SECP256K1).0, s))
  1241. .collect();
  1242. for proof in &mut proofs {
  1243. // Verify that proof DLEQ is valid
  1244. if proof.dleq.is_some() {
  1245. let keys = self.get_keyset_keys(proof.keyset_id).await?;
  1246. let key = keys.amount_key(proof.amount).ok_or(Error::UnknownKey)?;
  1247. proof.verify_dleq(key)?;
  1248. }
  1249. if let Ok(secret) =
  1250. <crate::secret::Secret as TryInto<crate::nuts::nut10::Secret>>::try_into(
  1251. proof.secret.clone(),
  1252. )
  1253. {
  1254. let conditions: Result<Conditions, _> =
  1255. secret.secret_data.tags.unwrap_or_default().try_into();
  1256. if let Ok(conditions) = conditions {
  1257. let mut pubkeys = conditions.pubkeys.unwrap_or_default();
  1258. match secret.kind {
  1259. Kind::P2PK => {
  1260. let data_key = PublicKey::from_str(&secret.secret_data.data)?;
  1261. pubkeys.push(data_key);
  1262. }
  1263. Kind::HTLC => {
  1264. let hashed_preimage = &secret.secret_data.data;
  1265. let preimage = hashed_to_preimage
  1266. .get(hashed_preimage)
  1267. .ok_or(Error::PreimageNotProvided)?;
  1268. proof.add_preimage(preimage.to_string());
  1269. }
  1270. }
  1271. for pubkey in pubkeys {
  1272. if let Some(signing) = p2pk_signing_keys.get(&pubkey.x_only_public_key()) {
  1273. proof.sign_p2pk(signing.to_owned().clone())?;
  1274. }
  1275. }
  1276. if conditions.sig_flag.eq(&SigFlag::SigAll) {
  1277. sig_flag = SigFlag::SigAll;
  1278. }
  1279. }
  1280. }
  1281. }
  1282. let mut pre_swap = self
  1283. .create_swap(None, amount_split_target, proofs, None, false)
  1284. .await?;
  1285. if sig_flag.eq(&SigFlag::SigAll) {
  1286. for blinded_message in &mut pre_swap.swap_request.outputs {
  1287. for signing_key in p2pk_signing_keys.values() {
  1288. blinded_message.sign_p2pk(signing_key.to_owned().clone())?
  1289. }
  1290. }
  1291. }
  1292. let swap_response = self
  1293. .client
  1294. .post_swap(mint_url.clone().try_into()?, pre_swap.swap_request)
  1295. .await?;
  1296. // Proof to keep
  1297. let p = construct_proofs(
  1298. swap_response.signatures,
  1299. pre_swap.pre_mint_secrets.rs(),
  1300. pre_swap.pre_mint_secrets.secrets(),
  1301. &keys,
  1302. )?;
  1303. let mint_proofs = received_proofs.entry(mint_url.clone()).or_default();
  1304. self.localstore
  1305. .increment_keyset_counter(&active_keyset_id, p.len() as u32)
  1306. .await?;
  1307. mint_proofs.extend(p);
  1308. let mut total_amount = Amount::ZERO;
  1309. for (mint, proofs) in received_proofs {
  1310. total_amount += proofs.iter().map(|p| p.amount).sum();
  1311. let proofs = proofs
  1312. .into_iter()
  1313. .flat_map(|proof| ProofInfo::new(proof, mint.clone(), State::Unspent, self.unit))
  1314. .collect();
  1315. self.localstore.add_proofs(proofs).await?;
  1316. }
  1317. Ok(total_amount)
  1318. }
  1319. /// Receive
  1320. #[instrument(skip_all)]
  1321. pub async fn receive(
  1322. &self,
  1323. encoded_token: &str,
  1324. amount_split_target: SplitTarget,
  1325. p2pk_signing_keys: &[SecretKey],
  1326. preimages: &[String],
  1327. ) -> Result<Amount, Error> {
  1328. let token_data = Token::from_str(encoded_token)?;
  1329. let unit = token_data.unit().unwrap_or_default();
  1330. if unit != self.unit {
  1331. return Err(Error::UnitNotSupported);
  1332. }
  1333. let proofs = token_data.proofs();
  1334. if proofs.len() != 1 {
  1335. return Err(Error::MultiMintTokenNotSupported);
  1336. }
  1337. let (mint_url, proofs) = proofs.into_iter().next().expect("Token has proofs");
  1338. if self.mint_url != mint_url {
  1339. return Err(Error::IncorrectMint);
  1340. }
  1341. let amount = self
  1342. .receive_proofs(proofs, amount_split_target, p2pk_signing_keys, preimages)
  1343. .await?;
  1344. Ok(amount)
  1345. }
  1346. /// Restore
  1347. #[instrument(skip(self))]
  1348. pub async fn restore(&self) -> Result<Amount, Error> {
  1349. // Check that mint is in store of mints
  1350. if self
  1351. .localstore
  1352. .get_mint(self.mint_url.clone())
  1353. .await?
  1354. .is_none()
  1355. {
  1356. self.get_mint_info().await?;
  1357. }
  1358. let keysets = self.get_mint_keysets().await?;
  1359. let mut restored_value = Amount::ZERO;
  1360. for keyset in keysets {
  1361. let keys = self.get_keyset_keys(keyset.id).await?;
  1362. let mut empty_batch = 0;
  1363. let mut start_counter = 0;
  1364. while empty_batch.lt(&3) {
  1365. let premint_secrets = PreMintSecrets::restore_batch(
  1366. keyset.id,
  1367. self.xpriv,
  1368. start_counter,
  1369. start_counter + 100,
  1370. )?;
  1371. tracing::debug!(
  1372. "Attempting to restore counter {}-{} for mint {} keyset {}",
  1373. start_counter,
  1374. start_counter + 100,
  1375. self.mint_url,
  1376. keyset.id
  1377. );
  1378. let restore_request = RestoreRequest {
  1379. outputs: premint_secrets.blinded_messages(),
  1380. };
  1381. let response = self
  1382. .client
  1383. .post_restore(self.mint_url.clone().try_into()?, restore_request)
  1384. .await?;
  1385. if response.signatures.is_empty() {
  1386. empty_batch += 1;
  1387. start_counter += 100;
  1388. continue;
  1389. }
  1390. let premint_secrets: Vec<_> = premint_secrets
  1391. .secrets
  1392. .iter()
  1393. .filter(|p| response.outputs.contains(&p.blinded_message))
  1394. .collect();
  1395. let premint_secrets: Vec<_> = premint_secrets
  1396. .iter()
  1397. .filter(|p| response.outputs.contains(&p.blinded_message))
  1398. .collect();
  1399. // the response outputs and premint secrets should be the same after filtering
  1400. // blinded messages the mint did not have signatures for
  1401. assert_eq!(response.outputs.len(), premint_secrets.len());
  1402. let proofs = construct_proofs(
  1403. response.signatures,
  1404. premint_secrets.iter().map(|p| p.r.clone()).collect(),
  1405. premint_secrets.iter().map(|p| p.secret.clone()).collect(),
  1406. &keys,
  1407. )?;
  1408. tracing::debug!("Restored {} proofs", proofs.len());
  1409. self.localstore
  1410. .increment_keyset_counter(&keyset.id, proofs.len() as u32)
  1411. .await?;
  1412. let states = self.check_proofs_spent(proofs.clone()).await?;
  1413. let unspent_proofs: Vec<Proof> = proofs
  1414. .iter()
  1415. .zip(states)
  1416. .filter(|(_, state)| !state.state.eq(&State::Spent))
  1417. .map(|(p, _)| p)
  1418. .cloned()
  1419. .collect();
  1420. restored_value += unspent_proofs.iter().map(|p| p.amount).sum();
  1421. let unspent_proofs = unspent_proofs
  1422. .into_iter()
  1423. .flat_map(|proof| {
  1424. ProofInfo::new(proof, self.mint_url.clone(), State::Unspent, keyset.unit)
  1425. })
  1426. .collect();
  1427. self.localstore.add_proofs(unspent_proofs).await?;
  1428. empty_batch = 0;
  1429. start_counter += 100;
  1430. }
  1431. }
  1432. Ok(restored_value)
  1433. }
  1434. /// Verify all proofs in token have meet the required spend
  1435. /// Can be used to allow a wallet to accept payments offline while reducing
  1436. /// the risk of claiming back to the limits let by the spending_conditions
  1437. #[instrument(skip(self, token))]
  1438. pub fn verify_token_p2pk(
  1439. &self,
  1440. token: &Token,
  1441. spending_conditions: SpendingConditions,
  1442. ) -> Result<(), Error> {
  1443. let (refund_keys, pubkeys, locktime, num_sigs) = match spending_conditions {
  1444. SpendingConditions::P2PKConditions { data, conditions } => {
  1445. let mut pubkeys = vec![data];
  1446. match conditions {
  1447. Some(conditions) => {
  1448. pubkeys.extend(conditions.pubkeys.unwrap_or_default());
  1449. (
  1450. conditions.refund_keys,
  1451. Some(pubkeys),
  1452. conditions.locktime,
  1453. conditions.num_sigs,
  1454. )
  1455. }
  1456. None => (None, Some(pubkeys), None, None),
  1457. }
  1458. }
  1459. SpendingConditions::HTLCConditions {
  1460. conditions,
  1461. data: _,
  1462. } => match conditions {
  1463. Some(conditions) => (
  1464. conditions.refund_keys,
  1465. conditions.pubkeys,
  1466. conditions.locktime,
  1467. conditions.num_sigs,
  1468. ),
  1469. None => (None, None, None, None),
  1470. },
  1471. };
  1472. if refund_keys.is_some() && locktime.is_none() {
  1473. tracing::warn!(
  1474. "Invalid spending conditions set: Locktime must be set if refund keys are allowed"
  1475. );
  1476. return Err(Error::InvalidSpendConditions(
  1477. "Must set locktime".to_string(),
  1478. ));
  1479. }
  1480. for (mint_url, proofs) in &token.proofs() {
  1481. if mint_url != &self.mint_url {
  1482. return Err(Error::IncorrectWallet(format!(
  1483. "Should be {} not {}",
  1484. self.mint_url, mint_url
  1485. )));
  1486. }
  1487. for proof in proofs {
  1488. let secret: nut10::Secret = (&proof.secret).try_into()?;
  1489. let proof_conditions: SpendingConditions = secret.try_into()?;
  1490. if num_sigs.ne(&proof_conditions.num_sigs()) {
  1491. tracing::debug!(
  1492. "Spending condition requires: {:?} sigs proof secret specifies: {:?}",
  1493. num_sigs,
  1494. proof_conditions.num_sigs()
  1495. );
  1496. return Err(Error::P2PKConditionsNotMet(
  1497. "Num sigs did not match spending condition".to_string(),
  1498. ));
  1499. }
  1500. let spending_condition_pubkeys = pubkeys.clone().unwrap_or_default();
  1501. let proof_pubkeys = proof_conditions.pubkeys().unwrap_or_default();
  1502. // Check the Proof has the required pubkeys
  1503. if proof_pubkeys.len().ne(&spending_condition_pubkeys.len())
  1504. || !proof_pubkeys
  1505. .iter()
  1506. .all(|pubkey| spending_condition_pubkeys.contains(pubkey))
  1507. {
  1508. tracing::debug!("Proof did not included Publickeys meeting condition");
  1509. tracing::debug!("{:?}", proof_pubkeys);
  1510. tracing::debug!("{:?}", spending_condition_pubkeys);
  1511. return Err(Error::P2PKConditionsNotMet(
  1512. "Pubkeys in proof not allowed by spending condition".to_string(),
  1513. ));
  1514. }
  1515. // If spending condition refund keys is allowed (Some(Empty Vec))
  1516. // If spending conition refund keys is allowed to restricted set of keys check
  1517. // it is one of them Check that proof locktime is > condition
  1518. // locktime
  1519. if let Some(proof_refund_keys) = proof_conditions.refund_keys() {
  1520. let proof_locktime = proof_conditions
  1521. .locktime()
  1522. .ok_or(Error::LocktimeNotProvided)?;
  1523. if let (Some(condition_refund_keys), Some(condition_locktime)) =
  1524. (&refund_keys, locktime)
  1525. {
  1526. // Proof locktime must be greater then condition locktime to ensure it
  1527. // cannot be claimed back
  1528. if proof_locktime.lt(&condition_locktime) {
  1529. return Err(Error::P2PKConditionsNotMet(
  1530. "Proof locktime less then required".to_string(),
  1531. ));
  1532. }
  1533. // A non empty condition refund key list is used as a restricted set of keys
  1534. // returns are allowed to An empty list means the
  1535. // proof can be refunded to anykey set in the secret
  1536. if !condition_refund_keys.is_empty()
  1537. && !proof_refund_keys
  1538. .iter()
  1539. .all(|refund_key| condition_refund_keys.contains(refund_key))
  1540. {
  1541. return Err(Error::P2PKConditionsNotMet(
  1542. "Refund Key not allowed".to_string(),
  1543. ));
  1544. }
  1545. } else {
  1546. // Spending conditions does not allow refund keys
  1547. return Err(Error::P2PKConditionsNotMet(
  1548. "Spending condition does not allow refund keys".to_string(),
  1549. ));
  1550. }
  1551. }
  1552. }
  1553. }
  1554. Ok(())
  1555. }
  1556. /// Verify all proofs in token have a valid DLEQ proof
  1557. #[instrument(skip(self, token))]
  1558. pub async fn verify_token_dleq(&self, token: &Token) -> Result<(), Error> {
  1559. let mut keys_cache: HashMap<Id, Keys> = HashMap::new();
  1560. for (mint_url, proofs) in &token.proofs() {
  1561. if mint_url != &self.mint_url {
  1562. return Err(Error::IncorrectWallet(format!(
  1563. "Should be {} not {}",
  1564. self.mint_url, mint_url
  1565. )));
  1566. }
  1567. for proof in proofs {
  1568. let mint_pubkey = match keys_cache.get(&proof.keyset_id) {
  1569. Some(keys) => keys.amount_key(proof.amount),
  1570. None => {
  1571. let keys = self.get_keyset_keys(proof.keyset_id).await?;
  1572. let key = keys.amount_key(proof.amount);
  1573. keys_cache.insert(proof.keyset_id, keys);
  1574. key
  1575. }
  1576. }
  1577. .ok_or(Error::UnknownKey)?;
  1578. proof
  1579. .verify_dleq(mint_pubkey)
  1580. .map_err(|_| Error::CouldNotVerifyDleq)?;
  1581. }
  1582. }
  1583. Ok(())
  1584. }
  1585. }