feat: store melt_quote_id for proof

This commit is contained in:
thesimplekid
2024-09-19 13:19:11 +02:00
parent 3413c24936
commit 117443d126
8 changed files with 72 additions and 23 deletions

View File

@@ -8,7 +8,7 @@ use cdk::mint_url::MintUrl;
use cdk::nuts::{CurrencyUnit, MintQuoteState, Proof, State}; use cdk::nuts::{CurrencyUnit, MintQuoteState, Proof, State};
use cdk::Amount; use cdk::Amount;
use lightning_invoice::Bolt11Invoice; use lightning_invoice::Bolt11Invoice;
use redb::{Database, ReadableTable, TableDefinition}; use redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::{Error, PROOFS_STATE_TABLE, PROOFS_TABLE}; use super::{Error, PROOFS_STATE_TABLE, PROOFS_TABLE};
@@ -17,6 +17,8 @@ const MINT_QUOTES_TABLE: TableDefinition<&str, &str> = TableDefinition::new("min
const PENDING_PROOFS_TABLE: TableDefinition<[u8; 33], &str> = const PENDING_PROOFS_TABLE: TableDefinition<[u8; 33], &str> =
TableDefinition::new("pending_proofs"); TableDefinition::new("pending_proofs");
const SPENT_PROOFS_TABLE: TableDefinition<[u8; 33], &str> = TableDefinition::new("spent_proofs"); const SPENT_PROOFS_TABLE: TableDefinition<[u8; 33], &str> = TableDefinition::new("spent_proofs");
const QUOTE_PROOFS_TABLE: MultimapTableDefinition<&str, [u8; 33]> =
MultimapTableDefinition::new("quote_proofs");
pub fn migrate_01_to_02(db: Arc<Database>) -> Result<u32, Error> { pub fn migrate_01_to_02(db: Arc<Database>) -> Result<u32, Error> {
migrate_mint_quotes_01_to_02(db)?; migrate_mint_quotes_01_to_02(db)?;
@@ -27,6 +29,11 @@ pub fn migrate_02_to_03(db: Arc<Database>) -> Result<u32, Error> {
migrate_mint_proofs_02_to_03(db)?; migrate_mint_proofs_02_to_03(db)?;
Ok(3) Ok(3)
} }
pub fn migrate_03_to_04(db: Arc<Database>) -> Result<u32, Error> {
let write_txn = db.begin_write()?;
let _ = write_txn.open_multimap_table(QUOTE_PROOFS_TABLE)?;
Ok(4)
}
/// Mint Quote Info /// Mint Quote Info
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]

View File

@@ -16,11 +16,11 @@ use cdk::nuts::{
}; };
use cdk::{cdk_database, mint}; use cdk::{cdk_database, mint};
use migrations::migrate_01_to_02; use migrations::migrate_01_to_02;
use redb::{Database, ReadableTable, TableDefinition}; use redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition};
use super::error::Error; use super::error::Error;
use crate::migrations::migrate_00_to_01; use crate::migrations::migrate_00_to_01;
use crate::mint::migrations::migrate_02_to_03; use crate::mint::migrations::{migrate_02_to_03, migrate_03_to_04};
mod migrations; mod migrations;
@@ -34,8 +34,10 @@ const CONFIG_TABLE: TableDefinition<&str, &str> = TableDefinition::new("config")
// Key is hex blinded_message B_ value is blinded_signature // Key is hex blinded_message B_ value is blinded_signature
const BLINDED_SIGNATURES: TableDefinition<[u8; 33], &str> = const BLINDED_SIGNATURES: TableDefinition<[u8; 33], &str> =
TableDefinition::new("blinded_signatures"); TableDefinition::new("blinded_signatures");
const QUOTE_PROOFS_TABLE: MultimapTableDefinition<&str, [u8; 33]> =
MultimapTableDefinition::new("quote_proofs");
const DATABASE_VERSION: u32 = 3; const DATABASE_VERSION: u32 = 4;
/// Mint Redbdatabase /// Mint Redbdatabase
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -81,6 +83,10 @@ impl MintRedbDatabase {
current_file_version = migrate_02_to_03(Arc::clone(&db))?; current_file_version = migrate_02_to_03(Arc::clone(&db))?;
} }
if current_file_version == 3 {
current_file_version = migrate_03_to_04(Arc::clone(&db))?;
}
if current_file_version != DATABASE_VERSION { if current_file_version != DATABASE_VERSION {
tracing::warn!( tracing::warn!(
"Database upgrade did not complete at {} current is {}", "Database upgrade did not complete at {} current is {}",
@@ -125,6 +131,7 @@ impl MintRedbDatabase {
let _ = write_txn.open_table(PROOFS_TABLE)?; let _ = write_txn.open_table(PROOFS_TABLE)?;
let _ = write_txn.open_table(PROOFS_STATE_TABLE)?; let _ = write_txn.open_table(PROOFS_STATE_TABLE)?;
let _ = write_txn.open_table(BLINDED_SIGNATURES)?; let _ = write_txn.open_table(BLINDED_SIGNATURES)?;
let _ = write_txn.open_multimap_table(QUOTE_PROOFS_TABLE)?;
table.insert("db_version", DATABASE_VERSION.to_string().as_str())?; table.insert("db_version", DATABASE_VERSION.to_string().as_str())?;
} }
@@ -483,21 +490,31 @@ impl MintDatabase for MintRedbDatabase {
Ok(()) Ok(())
} }
async fn add_proofs(&self, proofs: Proofs) -> Result<(), Self::Err> { async fn add_proofs(&self, proofs: Proofs, quote_id: Option<String>) -> Result<(), Self::Err> {
let write_txn = self.db.begin_write().map_err(Error::from)?; let write_txn = self.db.begin_write().map_err(Error::from)?;
{ {
let mut table = write_txn.open_table(PROOFS_TABLE).map_err(Error::from)?; let mut table = write_txn.open_table(PROOFS_TABLE).map_err(Error::from)?;
let mut quote_proofs_table = write_txn
.open_multimap_table(QUOTE_PROOFS_TABLE)
.map_err(Error::from)?;
for proof in proofs { for proof in proofs {
let y: PublicKey = hash_to_curve(&proof.secret.to_bytes()).map_err(Error::from)?; let y: PublicKey = hash_to_curve(&proof.secret.to_bytes()).map_err(Error::from)?;
if table.get(y.to_bytes()).map_err(Error::from)?.is_none() { let y = y.to_bytes();
if table.get(y).map_err(Error::from)?.is_none() {
table table
.insert( .insert(
y.to_bytes(), y,
serde_json::to_string(&proof).map_err(Error::from)?.as_str(), serde_json::to_string(&proof).map_err(Error::from)?.as_str(),
) )
.map_err(Error::from)?; .map_err(Error::from)?;
} }
if let Some(quote_id) = &quote_id {
quote_proofs_table
.insert(quote_id.as_str(), y)
.map_err(Error::from)?;
}
} }
} }
write_txn.commit().map_err(Error::from)?; write_txn.commit().map_err(Error::from)?;

View File

@@ -0,0 +1 @@
ALTER TABLE proof ADD COLUMN quote_id TEXT;

View File

@@ -746,14 +746,14 @@ FROM keyset;
} }
} }
async fn add_proofs(&self, proofs: Proofs) -> Result<(), Self::Err> { async fn add_proofs(&self, proofs: Proofs, quote_id: Option<String>) -> Result<(), Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?; let mut transaction = self.pool.begin().await.map_err(Error::from)?;
for proof in proofs { for proof in proofs {
if let Err(err) = sqlx::query( if let Err(err) = sqlx::query(
r#" r#"
INSERT INTO proof INSERT INTO proof
(y, amount, keyset_id, secret, c, witness, state) (y, amount, keyset_id, secret, c, witness, state, quote_id)
VALUES (?, ?, ?, ?, ?, ?, ?); VALUES (?, ?, ?, ?, ?, ?, ?, ?);
"#, "#,
) )
.bind(proof.y()?.to_bytes().to_vec()) .bind(proof.y()?.to_bytes().to_vec())
@@ -763,6 +763,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?);
.bind(proof.c.to_bytes().to_vec()) .bind(proof.c.to_bytes().to_vec())
.bind(proof.witness.map(|w| serde_json::to_string(&w).unwrap())) .bind(proof.witness.map(|w| serde_json::to_string(&w).unwrap()))
.bind("UNSPENT") .bind("UNSPENT")
.bind(quote_id.clone())
.execute(&mut transaction) .execute(&mut transaction)
.await .await
.map_err(Error::from) .map_err(Error::from)
@@ -774,6 +775,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?);
Ok(()) Ok(())
} }
async fn get_proofs_by_ys(&self, ys: &[PublicKey]) -> Result<Vec<Option<Proof>>, Self::Err> { async fn get_proofs_by_ys(&self, ys: &[PublicKey]) -> Result<Vec<Option<Proof>>, Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?; let mut transaction = self.pool.begin().await.map_err(Error::from)?;

View File

@@ -24,6 +24,7 @@ pub struct MintMemoryDatabase {
melt_quotes: Arc<RwLock<HashMap<String, mint::MeltQuote>>>, melt_quotes: Arc<RwLock<HashMap<String, mint::MeltQuote>>>,
proofs: Arc<RwLock<HashMap<[u8; 33], Proof>>>, proofs: Arc<RwLock<HashMap<[u8; 33], Proof>>>,
proof_state: Arc<Mutex<HashMap<[u8; 33], nut07::State>>>, proof_state: Arc<Mutex<HashMap<[u8; 33], nut07::State>>>,
quote_proofs: Arc<Mutex<HashMap<String, Vec<PublicKey>>>>,
blinded_signatures: Arc<RwLock<HashMap<[u8; 33], BlindSignature>>>, blinded_signatures: Arc<RwLock<HashMap<[u8; 33], BlindSignature>>>,
} }
@@ -37,6 +38,7 @@ impl MintMemoryDatabase {
melt_quotes: Vec<mint::MeltQuote>, melt_quotes: Vec<mint::MeltQuote>,
pending_proofs: Proofs, pending_proofs: Proofs,
spent_proofs: Proofs, spent_proofs: Proofs,
quote_proofs: HashMap<String, Vec<PublicKey>>,
blinded_signatures: HashMap<[u8; 33], BlindSignature>, blinded_signatures: HashMap<[u8; 33], BlindSignature>,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
let mut proofs = HashMap::new(); let mut proofs = HashMap::new();
@@ -68,6 +70,7 @@ impl MintMemoryDatabase {
proofs: Arc::new(RwLock::new(proofs)), proofs: Arc::new(RwLock::new(proofs)),
proof_state: Arc::new(Mutex::new(proof_states)), proof_state: Arc::new(Mutex::new(proof_states)),
blinded_signatures: Arc::new(RwLock::new(blinded_signatures)), blinded_signatures: Arc::new(RwLock::new(blinded_signatures)),
quote_proofs: Arc::new(Mutex::new(quote_proofs)),
}) })
} }
} }
@@ -219,13 +222,26 @@ impl MintDatabase for MintMemoryDatabase {
Ok(()) Ok(())
} }
async fn add_proofs(&self, proofs: Proofs) -> Result<(), Self::Err> { async fn add_proofs(&self, proofs: Proofs, quote_id: Option<String>) -> Result<(), Self::Err> {
let mut db_proofs = self.proofs.write().await; let mut db_proofs = self.proofs.write().await;
let mut ys = Vec::with_capacity(proofs.capacity());
for proof in proofs { for proof in proofs {
let secret_point = hash_to_curve(&proof.secret.to_bytes())?; let y = hash_to_curve(&proof.secret.to_bytes())?;
db_proofs.insert(secret_point.to_bytes(), proof); ys.push(y);
let y = y.to_bytes();
db_proofs.insert(y, proof);
} }
if let Some(quote_id) = quote_id {
let mut db_quote_proofs = self.quote_proofs.lock().await;
db_quote_proofs.insert(quote_id, ys);
}
Ok(()) Ok(())
} }

View File

@@ -228,7 +228,7 @@ pub trait MintDatabase {
async fn get_keyset_infos(&self) -> Result<Vec<MintKeySetInfo>, Self::Err>; async fn get_keyset_infos(&self) -> Result<Vec<MintKeySetInfo>, Self::Err>;
/// Add spent [`Proofs`] /// Add spent [`Proofs`]
async fn add_proofs(&self, proof: Proofs) -> Result<(), Self::Err>; async fn add_proofs(&self, proof: Proofs, quote_id: Option<String>) -> Result<(), Self::Err>;
/// Get [`Proofs`] by ys /// Get [`Proofs`] by ys
async fn get_proofs_by_ys(&self, ys: &[PublicKey]) -> Result<Vec<Option<Proof>>, Self::Err>; async fn get_proofs_by_ys(&self, ys: &[PublicKey]) -> Result<Vec<Option<Proof>>, Self::Err>;
/// Get [`Proofs`] state /// Get [`Proofs`] state

View File

@@ -783,7 +783,7 @@ impl Mint {
.collect::<Result<Vec<PublicKey>, _>>()?; .collect::<Result<Vec<PublicKey>, _>>()?;
self.localstore self.localstore
.add_proofs(swap_request.inputs.clone()) .add_proofs(swap_request.inputs.clone(), None)
.await?; .await?;
self.check_ys_spendable(&input_ys, State::Pending).await?; self.check_ys_spendable(&input_ys, State::Pending).await?;
@@ -1024,7 +1024,10 @@ impl Mint {
} }
self.localstore self.localstore
.add_proofs(melt_request.inputs.clone()) .add_proofs(
melt_request.inputs.clone(),
Some(melt_request.quote.clone()),
)
.await?; .await?;
self.check_ys_spendable(&ys, State::Pending).await?; self.check_ys_spendable(&ys, State::Pending).await?;
@@ -1549,6 +1552,7 @@ mod tests {
pending_proofs: Proofs, pending_proofs: Proofs,
spent_proofs: Proofs, spent_proofs: Proofs,
blinded_signatures: HashMap<[u8; 33], BlindSignature>, blinded_signatures: HashMap<[u8; 33], BlindSignature>,
quote_proofs: HashMap<String, Vec<PublicKey>>,
mint_url: &'a str, mint_url: &'a str,
seed: &'a [u8], seed: &'a [u8],
mint_info: MintInfo, mint_info: MintInfo,
@@ -1564,6 +1568,7 @@ mod tests {
config.melt_quotes, config.melt_quotes,
config.pending_proofs, config.pending_proofs,
config.spent_proofs, config.spent_proofs,
config.quote_proofs,
config.blinded_signatures, config.blinded_signatures,
) )
.unwrap(), .unwrap(),

View File

@@ -70,17 +70,18 @@
lnd lnd
clightning clightning
bitcoind bitcoind
sqlx-cli
] ++ libsDarwin; ] ++ libsDarwin;
# WASM deps # WASM deps
WASMInputs = with pkgs; [ # WASMInputs = with pkgs; [
]; # ];
nativeBuildInputs = with pkgs; [ # nativeBuildInputs = with pkgs; [
# Add additional build inputs here # Add additional build inputs here
] ++ lib.optionals isDarwin [ #] ++ lib.optionals isDarwin [
# Additional darwin specific native inputs can be set here # Additional darwin specific native inputs can be set here
]; # ];
in in
{ {
checks = { checks = {