|  | @@ -1004,91 +1004,89 @@ impl Mint {
 | 
	
		
			
				|  |  |          total_spent: Amount,
 | 
	
		
			
				|  |  |      ) -> Result<MeltQuoteBolt11Response, Error> {
 | 
	
		
			
				|  |  |          tracing::debug!("Processing melt quote: {}", melt_request.quote);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          let quote = self
 | 
	
		
			
				|  |  |              .localstore
 | 
	
		
			
				|  |  |              .get_melt_quote(&melt_request.quote)
 | 
	
		
			
				|  |  |              .await?
 | 
	
		
			
				|  |  |              .ok_or(Error::UnknownQuote)?;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if let Some(outputs) = &melt_request.outputs {
 | 
	
		
			
				|  |  | -            let blinded_messages: Vec<PublicKey> =
 | 
	
		
			
				|  |  | -                outputs.iter().map(|b| b.blinded_secret).collect();
 | 
	
		
			
				|  |  | +        let input_ys = melt_request
 | 
	
		
			
				|  |  | +            .inputs
 | 
	
		
			
				|  |  | +            .iter()
 | 
	
		
			
				|  |  | +            .map(|p| hash_to_curve(&p.secret.to_bytes()))
 | 
	
		
			
				|  |  | +            .collect::<Result<Vec<PublicKey>, _>>()?;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            if self
 | 
	
		
			
				|  |  | -                .localstore
 | 
	
		
			
				|  |  | -                .get_blind_signatures(&blinded_messages)
 | 
	
		
			
				|  |  | -                .await?
 | 
	
		
			
				|  |  | -                .iter()
 | 
	
		
			
				|  |  | -                .flatten()
 | 
	
		
			
				|  |  | -                .next()
 | 
	
		
			
				|  |  | -                .is_some()
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                tracing::info!("Output has already been signed",);
 | 
	
		
			
				|  |  | +        self.localstore
 | 
	
		
			
				|  |  | +            .update_proofs_states(&input_ys, State::Spent)
 | 
	
		
			
				|  |  | +            .await?;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                return Err(Error::BlindedMessageAlreadySigned);
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | +        self.localstore
 | 
	
		
			
				|  |  | +            .update_melt_quote_state(&melt_request.quote, MeltQuoteState::Paid)
 | 
	
		
			
				|  |  | +            .await?;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          let mut change = None;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if let Some(outputs) = melt_request.outputs.clone() {
 | 
	
		
			
				|  |  | -            let change_target = melt_request.proofs_amount() - total_spent;
 | 
	
		
			
				|  |  | -            let mut amounts = change_target.split();
 | 
	
		
			
				|  |  | -            let mut change_sigs = Vec::with_capacity(amounts.len());
 | 
	
		
			
				|  |  | +        // Check if there is change to return
 | 
	
		
			
				|  |  | +        if melt_request.proofs_amount() > total_spent {
 | 
	
		
			
				|  |  | +            // Check if wallet provided change outputs
 | 
	
		
			
				|  |  | +            if let Some(outputs) = melt_request.outputs.clone() {
 | 
	
		
			
				|  |  | +                let blinded_messages: Vec<PublicKey> =
 | 
	
		
			
				|  |  | +                    outputs.iter().map(|b| b.blinded_secret).collect();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            if outputs.len().lt(&amounts.len()) {
 | 
	
		
			
				|  |  | -                tracing::debug!(
 | 
	
		
			
				|  |  | -                    "Providing change requires {} blinded messages, but only {} provided",
 | 
	
		
			
				|  |  | -                    amounts.len(),
 | 
	
		
			
				|  |  | -                    outputs.len()
 | 
	
		
			
				|  |  | -                );
 | 
	
		
			
				|  |  | +                if self
 | 
	
		
			
				|  |  | +                    .localstore
 | 
	
		
			
				|  |  | +                    .get_blind_signatures(&blinded_messages)
 | 
	
		
			
				|  |  | +                    .await?
 | 
	
		
			
				|  |  | +                    .iter()
 | 
	
		
			
				|  |  | +                    .flatten()
 | 
	
		
			
				|  |  | +                    .next()
 | 
	
		
			
				|  |  | +                    .is_some()
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    tracing::info!("Output has already been signed");
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                // In the case that not enough outputs are provided to return all change
 | 
	
		
			
				|  |  | -                // Reverse sort the amounts so that the most amount of change possible is
 | 
	
		
			
				|  |  | -                // returned. The rest is burnt
 | 
	
		
			
				|  |  | -                amounts.sort_by(|a, b| b.cmp(a));
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | +                    return Err(Error::BlindedMessageAlreadySigned);
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            let mut outputs = outputs;
 | 
	
		
			
				|  |  | +                let change_target = melt_request.proofs_amount() - total_spent;
 | 
	
		
			
				|  |  | +                let mut amounts = change_target.split();
 | 
	
		
			
				|  |  | +                let mut change_sigs = Vec::with_capacity(amounts.len());
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            for (amount, blinded_message) in amounts.iter().zip(&mut outputs) {
 | 
	
		
			
				|  |  | -                blinded_message.amount = *amount;
 | 
	
		
			
				|  |  | +                if outputs.len().lt(&amounts.len()) {
 | 
	
		
			
				|  |  | +                    tracing::debug!(
 | 
	
		
			
				|  |  | +                        "Providing change requires {} blinded messages, but only {} provided",
 | 
	
		
			
				|  |  | +                        amounts.len(),
 | 
	
		
			
				|  |  | +                        outputs.len()
 | 
	
		
			
				|  |  | +                    );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                let blinded_signature = self.blind_sign(blinded_message).await?;
 | 
	
		
			
				|  |  | -                change_sigs.push(blinded_signature)
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | +                    // In the case that not enough outputs are provided to return all change
 | 
	
		
			
				|  |  | +                    // Reverse sort the amounts so that the most amount of change possible is
 | 
	
		
			
				|  |  | +                    // returned. The rest is burnt
 | 
	
		
			
				|  |  | +                    amounts.sort_by(|a, b| b.cmp(a));
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            self.localstore
 | 
	
		
			
				|  |  | -                .add_blind_signatures(
 | 
	
		
			
				|  |  | -                    &outputs[0..change_sigs.len()]
 | 
	
		
			
				|  |  | -                        .iter()
 | 
	
		
			
				|  |  | -                        .map(|o| o.blinded_secret)
 | 
	
		
			
				|  |  | -                        .collect::<Vec<PublicKey>>(),
 | 
	
		
			
				|  |  | -                    &change_sigs,
 | 
	
		
			
				|  |  | -                )
 | 
	
		
			
				|  |  | -                .await?;
 | 
	
		
			
				|  |  | +                let mut outputs = outputs;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            change = Some(change_sigs);
 | 
	
		
			
				|  |  | -        } else {
 | 
	
		
			
				|  |  | -            tracing::info!(
 | 
	
		
			
				|  |  | -                "No change outputs provided. Burnt: {:?} sats",
 | 
	
		
			
				|  |  | -                (melt_request.proofs_amount() - total_spent)
 | 
	
		
			
				|  |  | -            );
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | +                for (amount, blinded_message) in amounts.iter().zip(&mut outputs) {
 | 
	
		
			
				|  |  | +                    blinded_message.amount = *amount;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        let input_ys: Vec<PublicKey> = melt_request
 | 
	
		
			
				|  |  | -            .inputs
 | 
	
		
			
				|  |  | -            .iter()
 | 
	
		
			
				|  |  | -            .flat_map(|p| hash_to_curve(&p.secret.to_bytes()))
 | 
	
		
			
				|  |  | -            .collect();
 | 
	
		
			
				|  |  | +                    let blinded_signature = self.blind_sign(blinded_message).await?;
 | 
	
		
			
				|  |  | +                    change_sigs.push(blinded_signature)
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        self.localstore
 | 
	
		
			
				|  |  | -            .update_proofs_states(&input_ys, State::Spent)
 | 
	
		
			
				|  |  | -            .await?;
 | 
	
		
			
				|  |  | +                self.localstore
 | 
	
		
			
				|  |  | +                    .add_blind_signatures(
 | 
	
		
			
				|  |  | +                        &outputs[0..change_sigs.len()]
 | 
	
		
			
				|  |  | +                            .iter()
 | 
	
		
			
				|  |  | +                            .map(|o| o.blinded_secret)
 | 
	
		
			
				|  |  | +                            .collect::<Vec<PublicKey>>(),
 | 
	
		
			
				|  |  | +                        &change_sigs,
 | 
	
		
			
				|  |  | +                    )
 | 
	
		
			
				|  |  | +                    .await?;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        self.localstore
 | 
	
		
			
				|  |  | -            .update_melt_quote_state(&melt_request.quote, MeltQuoteState::Paid)
 | 
	
		
			
				|  |  | -            .await?;
 | 
	
		
			
				|  |  | +                change = Some(change_sigs);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          Ok(MeltQuoteBolt11Response {
 | 
	
		
			
				|  |  |              amount: quote.amount,
 |