Time time series (#708)

* feat: Add created_time and paid_time fields to MintQuote struct

* feat: Add serde default of 0 for created_time in MintQuote

* feat: Add created_time and paid_time to MintQuote and MeltQuote structs

* feat: Add paid_time update when setting melt quote state to Paid

* fix: Update melt quote state with current Unix timestamp

* feat: Add paid_time update for mint quote when state is set to Paid

* feat: Add issued_time field to MintQuote conversion from SQLite row

* feat: Add issued_time tracking for MintQuoteState::Issued state

* feat: Add migration script for mint time of quotes

* feat: Add timestamp columns to mint_quote and melt_quote tables

* feat: Add timestamp columns to `add_mint_quote` method

* refactor: Improve code formatting and readability in mint quote state update logic

* feat: Add created_time and paid_time columns to melt_quote query

* feat: time on mint and melt quotes

* feat: Add migration script for mint created time signature

feat: Add created_time column to blind_signature table

feat: Add created_time to blind_signature insertion

feat: Add created_time column to proof table and update insert query

feat: time on mint and melt quotes

* feat: Add new table to track blind signature creation time

* feat: Add timestamp tracking for proofs in ReDB database

* feat: redb proof time

* chore: fmt
This commit is contained in:
thesimplekid
2025-04-07 12:51:14 +01:00
committed by GitHub
parent 43ab1fdde1
commit 0b9ca1a474
8 changed files with 177 additions and 30 deletions

View File

@@ -1,6 +1,7 @@
//! Mint types //! Mint types
use bitcoin::bip32::DerivationPath; use bitcoin::bip32::DerivationPath;
use cashu::util::unix_time;
use cashu::{MeltQuoteBolt11Response, MintQuoteBolt11Response}; use cashu::{MeltQuoteBolt11Response, MintQuoteBolt11Response};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use uuid::Uuid; use uuid::Uuid;
@@ -27,6 +28,13 @@ pub struct MintQuote {
pub request_lookup_id: String, pub request_lookup_id: String,
/// Pubkey /// Pubkey
pub pubkey: Option<PublicKey>, pub pubkey: Option<PublicKey>,
/// Unix time quote was created
#[serde(default)]
pub created_time: u64,
/// Unix time quote was paid
pub paid_time: Option<u64>,
/// Unix time quote was issued
pub issued_time: Option<u64>,
} }
impl MintQuote { impl MintQuote {
@@ -50,6 +58,9 @@ impl MintQuote {
expiry, expiry,
request_lookup_id, request_lookup_id,
pubkey, pubkey,
created_time: unix_time(),
paid_time: None,
issued_time: None,
} }
} }
} }
@@ -79,6 +90,11 @@ pub struct MeltQuote {
/// ///
/// Used for an amountless invoice /// Used for an amountless invoice
pub msat_to_pay: Option<Amount>, pub msat_to_pay: Option<Amount>,
/// Unix time quote was created
#[serde(default)]
pub created_time: u64,
/// Unix time quote was paid
pub paid_time: Option<u64>,
} }
impl MeltQuote { impl MeltQuote {
@@ -105,6 +121,8 @@ impl MeltQuote {
payment_preimage: None, payment_preimage: None,
request_lookup_id, request_lookup_id,
msat_to_pay, msat_to_pay,
created_time: unix_time(),
paid_time: None,
} }
} }
} }

View File

@@ -176,6 +176,8 @@ impl From<cdk_common::mint::MeltQuote> for MeltQuote {
payment_preimage: value.payment_preimage, payment_preimage: value.payment_preimage,
request_lookup_id: value.request_lookup_id, request_lookup_id: value.request_lookup_id,
msat_to_pay: value.msat_to_pay.map(|a| a.into()), msat_to_pay: value.msat_to_pay.map(|a| a.into()),
created_time: value.created_time,
paid_time: value.paid_time,
} }
} }
} }
@@ -198,6 +200,8 @@ impl TryFrom<MeltQuote> for cdk_common::mint::MeltQuote {
payment_preimage: value.payment_preimage, payment_preimage: value.payment_preimage,
request_lookup_id: value.request_lookup_id, request_lookup_id: value.request_lookup_id,
msat_to_pay: value.msat_to_pay.map(|a| a.into()), msat_to_pay: value.msat_to_pay.map(|a| a.into()),
created_time: value.created_time,
paid_time: value.paid_time,
}) })
} }
} }

View File

@@ -81,6 +81,8 @@ message MeltQuote {
optional string payment_preimage = 8; optional string payment_preimage = 8;
string request_lookup_id = 9; string request_lookup_id = 9;
optional uint64 msat_to_pay = 10; optional uint64 msat_to_pay = 10;
uint64 created_time = 11;
optional uint64 paid_time = 12;
} }
message MakePaymentRequest { message MakePaymentRequest {

View File

@@ -5,6 +5,7 @@ use std::sync::Arc;
use cdk_common::mint::MintQuote; use cdk_common::mint::MintQuote;
use cdk_common::mint_url::MintUrl; use cdk_common::mint_url::MintUrl;
use cdk_common::util::unix_time;
use cdk_common::{Amount, CurrencyUnit, MintQuoteState, Proof, State}; use cdk_common::{Amount, CurrencyUnit, MintQuoteState, Proof, State};
use lightning_invoice::Bolt11Invoice; use lightning_invoice::Bolt11Invoice;
use redb::{ use redb::{
@@ -209,6 +210,9 @@ impl From<V1MintQuote> for MintQuote {
expiry: quote.expiry, expiry: quote.expiry,
request_lookup_id: Bolt11Invoice::from_str(&quote.request).unwrap().to_string(), request_lookup_id: Bolt11Invoice::from_str(&quote.request).unwrap().to_string(),
pubkey: None, pubkey: None,
created_time: unix_time(),
paid_time: None,
issued_time: None,
} }
} }
} }

View File

@@ -15,6 +15,7 @@ use cdk_common::database::{
use cdk_common::dhke::hash_to_curve; use cdk_common::dhke::hash_to_curve;
use cdk_common::mint::{self, MintKeySetInfo, MintQuote}; use cdk_common::mint::{self, MintKeySetInfo, MintQuote};
use cdk_common::nut00::ProofsMethods; use cdk_common::nut00::ProofsMethods;
use cdk_common::util::unix_time;
use cdk_common::{ use cdk_common::{
BlindSignature, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState, MintInfo, MintQuoteState, BlindSignature, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState, MintInfo, MintQuoteState,
Proof, Proofs, PublicKey, State, Proof, Proofs, PublicKey, State,
@@ -40,10 +41,14 @@ const MINT_QUOTES_TABLE: TableDefinition<[u8; 16], &str> = TableDefinition::new(
const MELT_QUOTES_TABLE: TableDefinition<[u8; 16], &str> = TableDefinition::new("melt_quotes"); const MELT_QUOTES_TABLE: TableDefinition<[u8; 16], &str> = TableDefinition::new("melt_quotes");
const PROOFS_TABLE: TableDefinition<[u8; 33], &str> = TableDefinition::new("proofs"); const PROOFS_TABLE: TableDefinition<[u8; 33], &str> = TableDefinition::new("proofs");
const PROOFS_STATE_TABLE: TableDefinition<[u8; 33], &str> = TableDefinition::new("proofs_state"); const PROOFS_STATE_TABLE: TableDefinition<[u8; 33], &str> = TableDefinition::new("proofs_state");
const PROOF_CREATED_TIME: TableDefinition<[u8; 33], u64> =
TableDefinition::new("proof_created_time");
const CONFIG_TABLE: TableDefinition<&str, &str> = TableDefinition::new("config"); 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 BLIND_SIGNATURE_CREATED_TIME: TableDefinition<[u8; 33], u64> =
TableDefinition::new("blind_signature_created_time");
const QUOTE_PROOFS_TABLE: MultimapTableDefinition<[u8; 16], [u8; 33]> = const QUOTE_PROOFS_TABLE: MultimapTableDefinition<[u8; 16], [u8; 33]> =
MultimapTableDefinition::new("quote_proofs"); MultimapTableDefinition::new("quote_proofs");
const QUOTE_SIGNATURES_TABLE: MultimapTableDefinition<[u8; 16], [u8; 33]> = const QUOTE_SIGNATURES_TABLE: MultimapTableDefinition<[u8; 16], [u8; 33]> =
@@ -149,7 +154,9 @@ impl MintRedbDatabase {
let _ = write_txn.open_table(MELT_QUOTES_TABLE)?; let _ = write_txn.open_table(MELT_QUOTES_TABLE)?;
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(PROOF_CREATED_TIME)?;
let _ = write_txn.open_table(BLINDED_SIGNATURES)?; let _ = write_txn.open_table(BLINDED_SIGNATURES)?;
let _ = write_txn.open_table(BLIND_SIGNATURE_CREATED_TIME)?;
let _ = write_txn.open_multimap_table(QUOTE_PROOFS_TABLE)?; let _ = write_txn.open_multimap_table(QUOTE_PROOFS_TABLE)?;
let _ = write_txn.open_multimap_table(QUOTE_SIGNATURES_TABLE)?; let _ = write_txn.open_multimap_table(QUOTE_SIGNATURES_TABLE)?;
@@ -583,9 +590,16 @@ impl MintProofsDatabase for MintRedbDatabase {
{ {
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 time_table = write_txn
.open_table(PROOF_CREATED_TIME)
.map_err(Error::from)?;
let mut quote_proofs_table = write_txn let mut quote_proofs_table = write_txn
.open_multimap_table(QUOTE_PROOFS_TABLE) .open_multimap_table(QUOTE_PROOFS_TABLE)
.map_err(Error::from)?; .map_err(Error::from)?;
// Get current timestamp in seconds
let current_time = unix_time();
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)?;
let y = y.to_bytes(); let y = y.to_bytes();
@@ -596,6 +610,9 @@ impl MintProofsDatabase for MintRedbDatabase {
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)?;
// Store creation time
time_table.insert(y, current_time).map_err(Error::from)?;
} }
if let Some(quote_id) = &quote_id { if let Some(quote_id) = &quote_id {
@@ -644,9 +661,13 @@ impl MintProofsDatabase for MintRedbDatabase {
{ {
let mut proofs_table = write_txn.open_table(PROOFS_TABLE).map_err(Error::from)?; let mut proofs_table = write_txn.open_table(PROOFS_TABLE).map_err(Error::from)?;
let mut time_table = write_txn
.open_table(PROOF_CREATED_TIME)
.map_err(Error::from)?;
for y in ys { for y in ys {
proofs_table.remove(&y.to_bytes()).map_err(Error::from)?; proofs_table.remove(&y.to_bytes()).map_err(Error::from)?;
time_table.remove(&y.to_bytes()).map_err(Error::from)?;
} }
} }
@@ -817,10 +838,16 @@ impl MintSignaturesDatabase for MintRedbDatabase {
let mut table = write_txn let mut table = write_txn
.open_table(BLINDED_SIGNATURES) .open_table(BLINDED_SIGNATURES)
.map_err(Error::from)?; .map_err(Error::from)?;
let mut time_table = write_txn
.open_table(BLIND_SIGNATURE_CREATED_TIME)
.map_err(Error::from)?;
let mut quote_sigs_table = write_txn let mut quote_sigs_table = write_txn
.open_multimap_table(QUOTE_SIGNATURES_TABLE) .open_multimap_table(QUOTE_SIGNATURES_TABLE)
.map_err(Error::from)?; .map_err(Error::from)?;
// Get current timestamp in seconds
let current_time = unix_time();
for (blinded_message, blind_signature) in blinded_messages.iter().zip(blind_signatures) for (blinded_message, blind_signature) in blinded_messages.iter().zip(blind_signatures)
{ {
let blind_sig = serde_json::to_string(&blind_signature).map_err(Error::from)?; let blind_sig = serde_json::to_string(&blind_signature).map_err(Error::from)?;
@@ -828,6 +855,11 @@ impl MintSignaturesDatabase for MintRedbDatabase {
.insert(blinded_message.to_bytes(), blind_sig.as_str()) .insert(blinded_message.to_bytes(), blind_sig.as_str())
.map_err(Error::from)?; .map_err(Error::from)?;
// Store creation time
time_table
.insert(blinded_message.to_bytes(), current_time)
.map_err(Error::from)?;
if let Some(quote_id) = &quote_id { if let Some(quote_id) = &quote_id {
quote_sigs_table quote_sigs_table
.insert(quote_id.as_bytes(), blinded_message.to_bytes()) .insert(quote_id.as_bytes(), blinded_message.to_bytes())

View File

@@ -0,0 +1,8 @@
-- Add timestamp columns to mint_quote table
ALTER TABLE mint_quote ADD COLUMN created_time INTEGER NOT NULL DEFAULT 0;
ALTER TABLE mint_quote ADD COLUMN paid_time INTEGER;
ALTER TABLE mint_quote ADD COLUMN issued_time INTEGER;
-- Add timestamp columns to melt_quote table
ALTER TABLE melt_quote ADD COLUMN created_time INTEGER NOT NULL DEFAULT 0;
ALTER TABLE melt_quote ADD COLUMN paid_time INTEGER;

View File

@@ -0,0 +1,4 @@
-- Add created_time column to blind_signature table
ALTER TABLE blind_signature ADD COLUMN created_time INTEGER NOT NULL DEFAULT 0;
-- Add created_time column to proof table
ALTER TABLE proof ADD COLUMN created_time INTEGER NOT NULL DEFAULT 0;

View File

@@ -15,6 +15,7 @@ use cdk_common::mint::{self, MintKeySetInfo, MintQuote};
use cdk_common::nut00::ProofsMethods; use cdk_common::nut00::ProofsMethods;
use cdk_common::nut05::QuoteState; use cdk_common::nut05::QuoteState;
use cdk_common::secret::Secret; use cdk_common::secret::Secret;
use cdk_common::util::unix_time;
use cdk_common::{ use cdk_common::{
Amount, BlindSignature, BlindSignatureDleq, CurrencyUnit, Id, MeltBolt11Request, Amount, BlindSignature, BlindSignatureDleq, CurrencyUnit, Id, MeltBolt11Request,
MeltQuoteState, MintInfo, MintQuoteState, PaymentMethod, Proof, Proofs, PublicKey, SecretKey, MeltQuoteState, MintInfo, MintQuoteState, PaymentMethod, Proof, Proofs, PublicKey, SecretKey,
@@ -373,22 +374,28 @@ impl MintQuotesDatabase for MintSqliteDatabase {
let res = sqlx::query( let res = sqlx::query(
r#" r#"
INSERT INTO mint_quote INSERT INTO mint_quote
(id, amount, unit, request, state, expiry, request_lookup_id, pubkey) (id, amount, unit, request, state, expiry, request_lookup_id, pubkey, created_time, paid_time, issued_time)
VALUES (?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE SET ON CONFLICT(id) DO UPDATE SET
amount = excluded.amount, amount = excluded.amount,
unit = excluded.unit, unit = excluded.unit,
request = excluded.request, request = excluded.request,
state = excluded.state, state = excluded.state,
expiry = excluded.expiry, expiry = excluded.expiry,
request_lookup_id = excluded.request_lookup_id request_lookup_id = excluded.request_lookup_id,
created_time = excluded.created_time,
paid_time = excluded.paid_time,
issued_time = excluded.issued_time
ON CONFLICT(request_lookup_id) DO UPDATE SET ON CONFLICT(request_lookup_id) DO UPDATE SET
amount = excluded.amount, amount = excluded.amount,
unit = excluded.unit, unit = excluded.unit,
request = excluded.request, request = excluded.request,
state = excluded.state, state = excluded.state,
expiry = excluded.expiry, expiry = excluded.expiry,
id = excluded.id id = excluded.id,
created_time = excluded.created_time,
paid_time = excluded.paid_time,
issued_time = excluded.issued_time
"#, "#,
) )
.bind(quote.id.to_string()) .bind(quote.id.to_string())
@@ -399,6 +406,9 @@ ON CONFLICT(request_lookup_id) DO UPDATE SET
.bind(quote.expiry as i64) .bind(quote.expiry as i64)
.bind(quote.request_lookup_id) .bind(quote.request_lookup_id)
.bind(quote.pubkey.map(|p| p.to_string())) .bind(quote.pubkey.map(|p| p.to_string()))
.bind(quote.created_time as i64)
.bind(quote.paid_time.map(|t| t as i64))
.bind(quote.issued_time.map(|t| t as i64))
.execute(&mut *transaction) .execute(&mut *transaction)
.await; .await;
@@ -554,15 +564,43 @@ WHERE id=?;
} }
}; };
let update = sqlx::query( let update_query = match state {
r#" MintQuoteState::Paid => {
UPDATE mint_quote SET state = ? WHERE id = ? r#"UPDATE mint_quote SET state = ?, paid_time = ? WHERE id = ?"#
"#, }
) MintQuoteState::Issued => {
.bind(state.to_string()) r#"UPDATE mint_quote SET state = ?, issued_time = ? WHERE id = ?"#
.bind(quote_id.as_hyphenated()) }
.execute(&mut *transaction) _ => r#"UPDATE mint_quote SET state = ? WHERE id = ?"#,
.await; };
let current_time = unix_time();
let update = match state {
MintQuoteState::Paid => {
sqlx::query(update_query)
.bind(state.to_string())
.bind(current_time as i64)
.bind(quote_id.as_hyphenated())
.execute(&mut *transaction)
.await
}
MintQuoteState::Issued => {
sqlx::query(update_query)
.bind(state.to_string())
.bind(current_time as i64)
.bind(quote_id.as_hyphenated())
.execute(&mut *transaction)
.await
}
_ => {
sqlx::query(update_query)
.bind(state.to_string())
.bind(quote_id.as_hyphenated())
.execute(&mut *transaction)
.await
}
};
match update { match update {
Ok(_) => { Ok(_) => {
@@ -684,8 +722,8 @@ WHERE id=?
let res = sqlx::query( let res = sqlx::query(
r#" r#"
INSERT INTO melt_quote INSERT INTO melt_quote
(id, unit, amount, request, fee_reserve, state, expiry, payment_preimage, request_lookup_id, msat_to_pay) (id, unit, amount, request, fee_reserve, state, expiry, payment_preimage, request_lookup_id, msat_to_pay, created_time, paid_time)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE SET ON CONFLICT(id) DO UPDATE SET
unit = excluded.unit, unit = excluded.unit,
amount = excluded.amount, amount = excluded.amount,
@@ -695,7 +733,9 @@ ON CONFLICT(id) DO UPDATE SET
expiry = excluded.expiry, expiry = excluded.expiry,
payment_preimage = excluded.payment_preimage, payment_preimage = excluded.payment_preimage,
request_lookup_id = excluded.request_lookup_id, request_lookup_id = excluded.request_lookup_id,
msat_to_pay = excluded.msat_to_pay msat_to_pay = excluded.msat_to_pay,
created_time = excluded.created_time,
paid_time = excluded.paid_time
ON CONFLICT(request_lookup_id) DO UPDATE SET ON CONFLICT(request_lookup_id) DO UPDATE SET
unit = excluded.unit, unit = excluded.unit,
amount = excluded.amount, amount = excluded.amount,
@@ -704,7 +744,9 @@ ON CONFLICT(request_lookup_id) DO UPDATE SET
state = excluded.state, state = excluded.state,
expiry = excluded.expiry, expiry = excluded.expiry,
payment_preimage = excluded.payment_preimage, payment_preimage = excluded.payment_preimage,
id = excluded.id; id = excluded.id,
created_time = excluded.created_time,
paid_time = excluded.paid_time;
"#, "#,
) )
.bind(quote.id.to_string()) .bind(quote.id.to_string())
@@ -717,6 +759,8 @@ ON CONFLICT(request_lookup_id) DO UPDATE SET
.bind(quote.payment_preimage) .bind(quote.payment_preimage)
.bind(quote.request_lookup_id) .bind(quote.request_lookup_id)
.bind(quote.msat_to_pay.map(|a| u64::from(a) as i64)) .bind(quote.msat_to_pay.map(|a| u64::from(a) as i64))
.bind(quote.created_time as i64)
.bind(quote.paid_time.map(|t| t as i64))
.execute(&mut *transaction) .execute(&mut *transaction)
.await; .await;
@@ -831,15 +875,28 @@ WHERE id=?;
} }
}; };
let rec = sqlx::query( let update_query = if state == MeltQuoteState::Paid {
r#" r#"UPDATE melt_quote SET state = ?, paid_time = ? WHERE id = ?"#
UPDATE melt_quote SET state = ? WHERE id = ? } else {
"#, r#"UPDATE melt_quote SET state = ? WHERE id = ?"#
) };
.bind(state.to_string())
.bind(quote_id.as_hyphenated()) let current_time = unix_time();
.execute(&mut *transaction)
.await; let rec = if state == MeltQuoteState::Paid {
sqlx::query(update_query)
.bind(state.to_string())
.bind(current_time as i64)
.bind(quote_id.as_hyphenated())
.execute(&mut *transaction)
.await
} else {
sqlx::query(update_query)
.bind(state.to_string())
.bind(quote_id.as_hyphenated())
.execute(&mut *transaction)
.await
};
match rec { match rec {
Ok(_) => { Ok(_) => {
@@ -978,12 +1035,14 @@ impl MintProofsDatabase for MintSqliteDatabase {
async fn add_proofs(&self, proofs: Proofs, quote_id: Option<Uuid>) -> Result<(), Self::Err> { async fn add_proofs(&self, proofs: Proofs, quote_id: Option<Uuid>) -> 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)?;
let current_time = unix_time();
for proof in proofs { for proof in proofs {
let result = sqlx::query( let result = sqlx::query(
r#" r#"
INSERT OR IGNORE INTO proof INSERT OR IGNORE INTO proof
(y, amount, keyset_id, secret, c, witness, state, quote_id) (y, amount, keyset_id, secret, c, witness, state, quote_id, created_time)
VALUES (?, ?, ?, ?, ?, ?, ?, ?); VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);
"#, "#,
) )
.bind(proof.y()?.to_bytes().to_vec()) .bind(proof.y()?.to_bytes().to_vec())
@@ -994,6 +1053,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?, ?);
.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.map(|q| q.hyphenated())) .bind(quote_id.map(|q| q.hyphenated()))
.bind(current_time as i64)
.execute(&mut *transaction) .execute(&mut *transaction)
.await; .await;
@@ -1292,12 +1352,14 @@ impl MintSignaturesDatabase for MintSqliteDatabase {
quote_id: Option<Uuid>, quote_id: Option<Uuid>,
) -> Result<(), Self::Err> { ) -> 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)?;
let current_time = unix_time();
for (message, signature) in blinded_messages.iter().zip(blinded_signatures) { for (message, signature) in blinded_messages.iter().zip(blinded_signatures) {
let res = sqlx::query( let res = sqlx::query(
r#" r#"
INSERT INTO blind_signature INSERT INTO blind_signature
(y, amount, keyset_id, c, quote_id, dleq_e, dleq_s) (y, amount, keyset_id, c, quote_id, dleq_e, dleq_s, created_time)
VALUES (?, ?, ?, ?, ?, ?, ?); VALUES (?, ?, ?, ?, ?, ?, ?, ?);
"#, "#,
) )
.bind(message.to_bytes().to_vec()) .bind(message.to_bytes().to_vec())
@@ -1307,6 +1369,7 @@ VALUES (?, ?, ?, ?, ?, ?, ?);
.bind(quote_id.map(|q| q.hyphenated())) .bind(quote_id.map(|q| q.hyphenated()))
.bind(signature.dleq.as_ref().map(|dleq| dleq.e.to_secret_hex())) .bind(signature.dleq.as_ref().map(|dleq| dleq.e.to_secret_hex()))
.bind(signature.dleq.as_ref().map(|dleq| dleq.s.to_secret_hex())) .bind(signature.dleq.as_ref().map(|dleq| dleq.s.to_secret_hex()))
.bind(current_time as i64)
.execute(&mut *transaction) .execute(&mut *transaction)
.await; .await;
@@ -1624,6 +1687,10 @@ fn sqlite_row_to_mint_quote(row: SqliteRow) -> Result<MintQuote, Error> {
row.try_get("request_lookup_id").map_err(Error::from)?; row.try_get("request_lookup_id").map_err(Error::from)?;
let row_pubkey: Option<String> = row.try_get("pubkey").map_err(Error::from)?; let row_pubkey: Option<String> = row.try_get("pubkey").map_err(Error::from)?;
let row_created_time: i64 = row.try_get("created_time").map_err(Error::from)?;
let row_paid_time: Option<i64> = row.try_get("paid_time").map_err(Error::from)?;
let row_issued_time: Option<i64> = row.try_get("issued_time").map_err(Error::from)?;
let request_lookup_id = match row_request_lookup_id { let request_lookup_id = match row_request_lookup_id {
Some(id) => id, Some(id) => id,
None => match Bolt11Invoice::from_str(&row_request) { None => match Bolt11Invoice::from_str(&row_request) {
@@ -1645,6 +1712,9 @@ fn sqlite_row_to_mint_quote(row: SqliteRow) -> Result<MintQuote, Error> {
expiry: row_expiry as u64, expiry: row_expiry as u64,
request_lookup_id, request_lookup_id,
pubkey, pubkey,
created_time: row_created_time as u64,
paid_time: row_paid_time.map(|p| p as u64),
issued_time: row_issued_time.map(|p| p as u64),
}) })
} }
@@ -1664,6 +1734,9 @@ fn sqlite_row_to_melt_quote(row: SqliteRow) -> Result<mint::MeltQuote, Error> {
let row_msat_to_pay: Option<i64> = row.try_get("msat_to_pay").map_err(Error::from)?; let row_msat_to_pay: Option<i64> = row.try_get("msat_to_pay").map_err(Error::from)?;
let row_created_time: i64 = row.try_get("created_time").map_err(Error::from)?;
let row_paid_time: Option<i64> = row.try_get("paid_time").map_err(Error::from)?;
Ok(mint::MeltQuote { Ok(mint::MeltQuote {
id: row_id.into_uuid(), id: row_id.into_uuid(),
amount: Amount::from(row_amount as u64), amount: Amount::from(row_amount as u64),
@@ -1675,6 +1748,8 @@ fn sqlite_row_to_melt_quote(row: SqliteRow) -> Result<mint::MeltQuote, Error> {
payment_preimage: row_preimage, payment_preimage: row_preimage,
request_lookup_id, request_lookup_id,
msat_to_pay: row_msat_to_pay.map(|a| Amount::from(a as u64)), msat_to_pay: row_msat_to_pay.map(|a| Amount::from(a as u64)),
created_time: row_created_time as u64,
paid_time: row_paid_time.map(|p| p as u64),
}) })
} }