| 
					
				 | 
			
			
				@@ -32,8 +32,8 @@ use cdk_common::secret::Secret; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use cdk_common::state::check_state_transition; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use cdk_common::util::unix_time; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use cdk_common::{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    Amount, BlindSignature, BlindSignatureDleq, CurrencyUnit, Id, MeltQuoteState, MintInfo, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    PaymentMethod, Proof, Proofs, PublicKey, SecretKey, State, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    Amount, BlindSignature, BlindSignatureDleq, BlindedMessage, CurrencyUnit, Id, MeltQuoteState, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    MintInfo, PaymentMethod, Proof, Proofs, PublicKey, SecretKey, State, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use lightning_invoice::Bolt11Invoice; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 use migrations::MIGRATIONS; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -277,6 +277,33 @@ where 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         Ok(()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async fn get_proof_ys_by_quote_id( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        &self, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        quote_id: &QuoteId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) -> Result<Vec<PublicKey>, Self::Err> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Ok(query( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            r#" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            SELECT 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                amount, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                keyset_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                secret, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                c, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                witness 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            FROM 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                proof 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            WHERE 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                quote_id = :quote_id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "#, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        )? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .bind("quote_id", quote_id.to_string()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .fetch_all(&self.inner) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .await? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .into_iter() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .map(sql_row_to_proof) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .collect::<Result<Vec<Proof>, _>>()? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .ys()?) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 #[async_trait] 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -564,6 +591,123 @@ where 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     type Err = Error; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async fn add_melt_request_and_blinded_messages( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        &mut self, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        quote_id: &QuoteId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        inputs_amount: Amount, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        inputs_fee: Amount, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        blinded_messages: &[BlindedMessage], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) -> Result<(), Self::Err> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        query( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            r#" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            INSERT INTO melt_request 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            (quote_id, inputs_amount, inputs_fee) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            VALUES 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            (:quote_id, :inputs_amount, :inputs_fee) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "#, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        )? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .bind("quote_id", quote_id.to_string()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .bind("inputs_amount", inputs_amount.to_i64()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .bind("inputs_fee", inputs_fee.to_i64()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .execute(&self.inner) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for message in blinded_messages { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            query( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                r#" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                INSERT INTO blinded_messages 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                (quote_id, blinded_message, keyset_id, amount) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                VALUES 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                (:quote_id, :blinded_message, :keyset_id, :amount) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                "#, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            )? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .bind("quote_id", quote_id.to_string()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .bind( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                "blinded_message", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                message.blinded_secret.to_bytes().to_vec(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .bind("keyset_id", message.keyset_id.to_string()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .bind("amount", message.amount.to_i64()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .execute(&self.inner) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Ok(()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async fn get_melt_request_and_blinded_messages( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        &mut self, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        quote_id: &QuoteId, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) -> Result<Option<database::mint::MeltRequestInfo>, Self::Err> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let melt_request_row = query( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            r#" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            SELECT inputs_amount, inputs_fee 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            FROM melt_request 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            WHERE quote_id = :quote_id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            FOR UPDATE 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "#, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        )? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .bind("quote_id", quote_id.to_string()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .fetch_one(&self.inner) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if let Some(row) = melt_request_row { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let inputs_amount: u64 = column_as_number!(row[0].clone()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let inputs_fee: u64 = column_as_number!(row[1].clone()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let blinded_messages_rows = query( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                r#" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                SELECT blinded_message, keyset_id, amount 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                FROM blinded_messages 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                WHERE quote_id = :quote_id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                "#, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            )? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .bind("quote_id", quote_id.to_string()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .fetch_all(&self.inner) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            .await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let blinded_messages: Result<Vec<BlindedMessage>, Error> = blinded_messages_rows 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .into_iter() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .map(|row| -> Result<BlindedMessage, Error> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    let blinded_message_key = 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        column_as_string!(&row[0], PublicKey::from_hex, PublicKey::from_slice); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    let keyset_id = column_as_string!(&row[1], Id::from_str, Id::from_bytes); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    let amount: u64 = column_as_number!(row[2].clone()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    Ok(BlindedMessage { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        blinded_secret: blinded_message_key, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        keyset_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        amount: Amount::from(amount), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        witness: None, // Not storing witness in database currently 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                .collect(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let blinded_messages = blinded_messages?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Ok(Some(database::mint::MeltRequestInfo { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                inputs_amount: Amount::from(inputs_amount), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                inputs_fee: Amount::from(inputs_fee), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                change_outputs: blinded_messages, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            })) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Ok(None) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async fn delete_melt_request(&mut self, quote_id: &QuoteId) -> Result<(), Self::Err> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        query( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            r#" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            DELETE FROM melt_request 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            WHERE quote_id = :quote_id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "#, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        )? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .bind("quote_id", quote_id.to_string()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .execute(&self.inner) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        .await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Ok(()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     #[instrument(skip(self))] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     async fn increment_mint_quote_amount_paid( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         &mut self, 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -788,8 +932,6 @@ VALUES (:quote_id, :amount, :timestamp); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     async fn add_melt_quote(&mut self, quote: mint::MeltQuote) -> Result<(), Self::Err> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        // First try to find and replace any expired UNPAID quotes with the same request_lookup_id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         // Now insert the new quote 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         query( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             r#" 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -916,6 +1058,10 @@ VALUES (:quote_id, :amount, :timestamp); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         let old_state = quote.state; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         quote.state = state; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if state == MeltQuoteState::Unpaid || state == MeltQuoteState::Failed { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            self.delete_melt_request(quote_id).await?; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         Ok((old_state, quote)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 |