refactor: move mint_info to database

This commit is contained in:
thesimplekid
2025-01-29 23:29:25 +00:00
parent 0674144001
commit 5481286ec9
15 changed files with 174 additions and 72 deletions

View File

@@ -342,7 +342,18 @@ pub async fn post_check(
))]
/// Mint information, operator contact information, and other info
pub async fn get_mint_info(State(state): State<MintState>) -> Result<Json<MintInfo>, Response> {
Ok(Json(state.mint.mint_info().clone().time(unix_time())))
Ok(Json(
state
.mint
.mint_info()
.await
.map_err(|err| {
tracing::error!("Could not get mint info: {}", err);
into_response(err)
})?
.clone()
.time(unix_time()),
))
}
#[cfg_attr(feature = "swagger", utoipa::path(

View File

@@ -3,6 +3,7 @@
use std::collections::HashMap;
use async_trait::async_trait;
use cashu::MintInfo;
use uuid::Uuid;
use super::Error;
@@ -127,4 +128,9 @@ pub trait Database {
&self,
quote_id: &Uuid,
) -> Result<Vec<BlindSignature>, Self::Err>;
/// Set [`MintInfo`]
async fn set_mint_info(&self, mint_info: MintInfo) -> Result<(), Self::Err>;
/// Get [`MintInfo`]
async fn get_mint_info(&self) -> Result<MintInfo, Self::Err>;
}

View File

@@ -38,11 +38,7 @@ impl DirectMintConnection {
impl Debug for DirectMintConnection {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"DirectMintConnection {{ mint_info: {:?} }}",
self.mint.config.mint_info()
)
write!(f, "DirectMintConnection",)
}
}
@@ -130,7 +126,7 @@ impl MintConnector for DirectMintConnection {
}
async fn get_mint_info(&self) -> Result<MintInfo, Error> {
Ok(self.mint.mint_info().clone().time(unix_time()))
Ok(self.mint.mint_info().await?.clone().time(unix_time()))
}
async fn post_check_state(

View File

@@ -8,6 +8,7 @@ use anyhow::{bail, Result};
use bip39::Mnemonic;
use cdk::amount::{Amount, SplitTarget};
use cdk::cdk_database::mint_memory::MintMemoryDatabase;
use cdk::cdk_database::MintDatabase;
use cdk::dhke::construct_proofs;
use cdk::mint::MintQuote;
use cdk::nuts::nut00::ProofsMethods;
@@ -41,15 +42,20 @@ async fn new_mint(fee: u64) -> Mint {
let mint_info = MintInfo::new().nuts(nuts);
let localstore = MintMemoryDatabase::default();
localstore
.set_mint_info(mint_info)
.await
.expect("Could not set mint info");
let mnemonic = Mnemonic::generate(12).unwrap();
let quote_ttl = QuoteTTL::new(10000, 10000);
Mint::new(
&mnemonic.to_seed_normalized(""),
mint_info,
quote_ttl,
Arc::new(MintMemoryDatabase::default()),
Arc::new(localstore),
HashMap::new(),
supported_units,
HashMap::new(),

View File

@@ -13,8 +13,8 @@ use cdk_common::dhke::hash_to_curve;
use cdk_common::mint::{self, MintKeySetInfo, MintQuote};
use cdk_common::nut00::ProofsMethods;
use cdk_common::{
BlindSignature, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState, MintQuoteState, Proof,
Proofs, PublicKey, State,
BlindSignature, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState, MintInfo, MintQuoteState,
Proof, Proofs, PublicKey, State,
};
use migrations::{migrate_01_to_02, migrate_04_to_05};
use redb::{Database, MultimapTableDefinition, ReadableTable, TableDefinition};
@@ -812,4 +812,30 @@ impl MintDatabase for MintRedbDatabase {
Ok(signatures)
}
async fn set_mint_info(&self, mint_info: MintInfo) -> Result<(), Self::Err> {
let write_txn = self.db.begin_write().map_err(Error::from)?;
{
let mut table = write_txn.open_table(CONFIG_TABLE).map_err(Error::from)?;
table
.insert("mint_info", serde_json::to_string(&mint_info)?.as_str())
.map_err(Error::from)?;
}
write_txn.commit().map_err(Error::from)?;
Ok(())
}
async fn get_mint_info(&self) -> Result<MintInfo, Self::Err> {
let read_txn = self.db.begin_read().map_err(Error::from)?;
let table = read_txn.open_table(CONFIG_TABLE).map_err(Error::from)?;
if let Some(mint_info) = table.get("mint_info").map_err(Error::from)? {
let mint_info = serde_json::from_str(mint_info.value())?;
return Ok(mint_info);
}
Err(Error::UnknownMintInfo.into())
}
}

View File

@@ -44,6 +44,9 @@ pub enum Error {
/// Serde Error
#[error(transparent)]
Serde(#[from] serde_json::Error),
/// Unknown Mint Info
#[error("Unknown mint info")]
UnknownMintInfo,
}
impl From<Error> for cdk_common::database::Error {

View File

@@ -0,0 +1,4 @@
CREATE TABLE IF NOT EXISTS config (
id TEXT PRIMARY KEY,
value TEXT NOT NULL
);

View File

@@ -15,7 +15,8 @@ use cdk_common::nut05::QuoteState;
use cdk_common::secret::Secret;
use cdk_common::{
Amount, BlindSignature, BlindSignatureDleq, CurrencyUnit, Id, MeltBolt11Request,
MeltQuoteState, MintQuoteState, PaymentMethod, Proof, Proofs, PublicKey, SecretKey, State,
MeltQuoteState, MintInfo, MintQuoteState, PaymentMethod, Proof, Proofs, PublicKey, SecretKey,
State,
};
use error::Error;
use lightning_invoice::Bolt11Invoice;
@@ -1219,6 +1220,77 @@ WHERE quote_id=?;
}
}
}
async fn set_mint_info(&self, mint_info: MintInfo) -> Result<(), Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?;
let res = sqlx::query(
r#"
INSERT OR REPLACE INTO config
(id, value)
VALUES (?, ?);
"#,
)
.bind("mint_info")
.bind(serde_json::to_string(&mint_info)?)
.execute(&mut transaction)
.await;
match res {
Ok(_) => {
transaction.commit().await.map_err(Error::from)?;
Ok(())
}
Err(err) => {
tracing::error!("SQLite Could not update mint info");
if let Err(err) = transaction.rollback().await {
tracing::error!("Could not rollback sql transaction: {}", err);
}
Err(Error::from(err).into())
}
}
}
async fn get_mint_info(&self) -> Result<MintInfo, Self::Err> {
let mut transaction = self.pool.begin().await.map_err(Error::from)?;
let rec = sqlx::query(
r#"
SELECT *
FROM config
WHERE id=?;
"#,
)
.bind("mint_info")
.fetch_one(&mut transaction)
.await;
match rec {
Ok(rec) => {
transaction.commit().await.map_err(Error::from)?;
let value: String = rec.try_get("value").map_err(Error::from)?;
let mint_info = serde_json::from_str(&value)?;
Ok(mint_info)
}
Err(err) => match err {
sqlx::Error::RowNotFound => {
transaction.commit().await.map_err(Error::from)?;
return Err(Error::UnknownMintInfo.into());
}
_ => {
return {
if let Err(err) = transaction.rollback().await {
tracing::error!("Could not rollback sql transaction: {}", err);
}
Err(Error::SQLX(err).into())
}
}
},
}
}
}
fn sqlite_row_to_keyset_info(row: SqliteRow) -> Result<MintKeySetInfo, Error> {

View File

@@ -7,6 +7,7 @@ use async_trait::async_trait;
use cdk_common::database::{Error, MintDatabase};
use cdk_common::mint::MintKeySetInfo;
use cdk_common::nut00::ProofsMethods;
use cdk_common::MintInfo;
use tokio::sync::{Mutex, RwLock};
use uuid::Uuid;
@@ -33,6 +34,7 @@ pub struct MintMemoryDatabase {
blinded_signatures: Arc<RwLock<HashMap<[u8; 33], BlindSignature>>>,
quote_signatures: Arc<RwLock<HashMap<Uuid, Vec<BlindSignature>>>>,
melt_requests: Arc<RwLock<HashMap<Uuid, (MeltBolt11Request<Uuid>, LnKey)>>>,
mint_info: Arc<RwLock<MintInfo>>,
}
impl MintMemoryDatabase {
@@ -49,6 +51,7 @@ impl MintMemoryDatabase {
blinded_signatures: HashMap<[u8; 33], BlindSignature>,
quote_signatures: HashMap<Uuid, Vec<BlindSignature>>,
melt_request: Vec<(MeltBolt11Request<Uuid>, LnKey)>,
mint_info: MintInfo,
) -> Result<Self, Error> {
let mut proofs = HashMap::new();
let mut proof_states = HashMap::new();
@@ -87,6 +90,7 @@ impl MintMemoryDatabase {
quote_proofs: Arc::new(Mutex::new(quote_proofs)),
quote_signatures: Arc::new(RwLock::new(quote_signatures)),
melt_requests: Arc::new(RwLock::new(melt_requests)),
mint_info: Arc::new(RwLock::new(mint_info)),
})
}
}
@@ -412,4 +416,17 @@ impl MintDatabase for MintMemoryDatabase {
Ok(ys.get(quote_id).cloned().unwrap_or_default())
}
async fn set_mint_info(&self, mint_info: MintInfo) -> Result<(), Self::Err> {
let mut current_mint_info = self.mint_info.write().await;
*current_mint_info = mint_info;
Ok(())
}
async fn get_mint_info(&self) -> Result<MintInfo, Self::Err> {
let mint_info = self.mint_info.read().await;
Ok(mint_info.clone())
}
}

View File

@@ -218,13 +218,16 @@ impl MintBuilder {
/// Build mint
pub async fn build(&self) -> anyhow::Result<Mint> {
let localstore = self
.localstore
.clone()
.ok_or(anyhow!("Localstore not set"))?;
localstore.set_mint_info(self.mint_info.clone()).await?;
Ok(Mint::new(
self.seed.as_ref().ok_or(anyhow!("Mint seed not set"))?,
self.mint_info.clone(),
self.quote_ttl.ok_or(anyhow!("Quote ttl not set"))?,
self.localstore
.clone()
.ok_or(anyhow!("Localstore not set"))?,
localstore,
self.ln.clone().ok_or(anyhow!("Ln backends not set"))?,
self.supported_units.clone(),
HashMap::new(),

View File

@@ -6,15 +6,13 @@ use std::sync::Arc;
use arc_swap::ArcSwap;
use super::{Id, MintInfo, MintKeySet};
use super::{Id, MintKeySet};
use crate::types::QuoteTTL;
/// Mint Inner configuration
pub struct Config {
/// Active Mint Keysets
pub keysets: HashMap<Id, MintKeySet>,
/// Mint url
pub mint_info: MintInfo,
/// Quotes ttl
pub quote_ttl: QuoteTTL,
}
@@ -33,12 +31,8 @@ pub struct SwappableConfig {
impl SwappableConfig {
/// Creates a new configuration instance
pub fn new(quote_ttl: QuoteTTL, mint_info: MintInfo, keysets: HashMap<Id, MintKeySet>) -> Self {
let inner = Config {
keysets,
quote_ttl,
mint_info,
};
pub fn new(quote_ttl: QuoteTTL, keysets: HashMap<Id, MintKeySet>) -> Self {
let inner = Config { keysets, quote_ttl };
Self {
config: Arc::new(ArcSwap::from_pointee(inner)),
@@ -59,7 +53,6 @@ impl SwappableConfig {
pub fn set_quote_ttl(&self, quote_ttl: QuoteTTL) {
let current_inner = self.load();
let new_inner = Config {
mint_info: current_inner.mint_info.clone(),
quote_ttl,
keysets: current_inner.keysets.clone(),
};
@@ -67,28 +60,10 @@ impl SwappableConfig {
self.config.store(Arc::new(new_inner));
}
/// Gets a copy of the mint info
pub fn mint_info(&self) -> MintInfo {
self.load().mint_info.clone()
}
/// Replaces the current mint info with a new one
pub fn set_mint_info(&self, mint_info: MintInfo) {
let current_inner = self.load();
let new_inner = Config {
mint_info,
quote_ttl: current_inner.quote_ttl,
keysets: current_inner.keysets.clone(),
};
self.config.store(Arc::new(new_inner));
}
/// Replaces the current keysets with a new one
pub fn set_keysets(&self, keysets: HashMap<Id, MintKeySet>) {
let current_inner = self.load();
let new_inner = Config {
mint_info: current_inner.mint_info.clone(),
quote_ttl: current_inner.quote_ttl,
keysets,
};

View File

@@ -1,17 +0,0 @@
use tracing::instrument;
use super::{Mint, MintInfo};
impl Mint {
/// Set Mint Info
#[instrument(skip_all)]
pub fn set_mint_info(&self, mint_info: MintInfo) {
self.config.set_mint_info(mint_info);
}
/// Get Mint Info
#[instrument(skip_all)]
pub fn mint_info(&self) -> MintInfo {
self.config.mint_info()
}
}

View File

@@ -31,7 +31,7 @@ impl Mint {
request: String,
options: Option<MeltOptions>,
) -> Result<(), Error> {
let mint_info = self.mint_info();
let mint_info = self.localstore.get_mint_info().await?;
let nut05 = mint_info.nuts.nut05;
let nut15 = mint_info.nuts.nut15;

View File

@@ -12,12 +12,12 @@ use crate::{Amount, Error};
impl Mint {
/// Checks that minting is enabled, request is supported unit and within range
fn check_mint_request_acceptable(
async fn check_mint_request_acceptable(
&self,
amount: Amount,
unit: &CurrencyUnit,
) -> Result<(), Error> {
let mint_info = self.mint_info();
let mint_info = self.localstore.get_mint_info().await?;
let nut04 = &mint_info.nuts.nut04;
if nut04.disabled {
@@ -69,7 +69,7 @@ impl Mint {
pubkey,
} = mint_quote_request;
self.check_mint_request_acceptable(amount, &unit)?;
self.check_mint_request_acceptable(amount, &unit).await?;
let ln = self
.ln

View File

@@ -28,7 +28,6 @@ use crate::Amount;
mod builder;
mod check_spendable;
pub mod config;
mod info;
mod keysets;
mod melt;
mod mint_nut04;
@@ -59,7 +58,6 @@ impl Mint {
#[allow(clippy::too_many_arguments)]
pub async fn new(
seed: &[u8],
mint_info: MintInfo,
quote_ttl: QuoteTTL,
localstore: Arc<dyn MintDatabase<Err = database::Error> + Send + Sync>,
ln: HashMap<LnKey, Arc<dyn MintLightning<Err = cdk_lightning::Error> + Send + Sync>>,
@@ -180,7 +178,7 @@ impl Mint {
}
Ok(Self {
config: SwappableConfig::new(quote_ttl, mint_info, active_keysets),
config: SwappableConfig::new(quote_ttl, active_keysets),
pubsub_manager: Arc::new(localstore.clone().into()),
secp_ctx,
xpriv,
@@ -189,6 +187,11 @@ impl Mint {
})
}
/// Get mint info
pub async fn mint_info(&self) -> Result<MintInfo, Error> {
Ok(self.localstore.get_mint_info().await?)
}
/// Wait for any invoice to be paid
/// For each backend starts a task that waits for any invoice to be paid
/// Once invoice is paid mint quote status is updated
@@ -683,13 +686,13 @@ mod tests {
config.blinded_signatures,
config.quote_signatures,
config.melt_requests,
config.mint_info,
)
.unwrap(),
);
Mint::new(
config.seed,
config.mint_info,
config.quote_ttl,
localstore,
HashMap::new(),
@@ -706,9 +709,6 @@ mod tests {
};
let mint = create_mint(config).await?;
let info = mint.mint_info();
assert!(info.name.is_none());
assert!(info.pubkey.is_none());
assert_eq!(
mint.pubkeys().await.unwrap(),
KeysResponse {