mirror of
https://github.com/aljazceru/cdk.git
synced 2026-01-22 22:36:05 +01:00
Merge pull request #569 from thesimplekid/move_config_to_db
refactor: Move mint info and quote ttl to database
This commit is contained in:
@@ -4,7 +4,6 @@ use bitcoin::bip32::DerivationPath;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::mint_url::MintUrl;
|
||||
use crate::nuts::{MeltQuoteState, MintQuoteState};
|
||||
use crate::{Amount, CurrencyUnit, Id, KeySetInfo, PublicKey};
|
||||
|
||||
@@ -13,8 +12,6 @@ use crate::{Amount, CurrencyUnit, Id, KeySetInfo, PublicKey};
|
||||
pub struct MintQuote {
|
||||
/// Quote id
|
||||
pub id: Uuid,
|
||||
/// Mint Url
|
||||
pub mint_url: MintUrl,
|
||||
/// Amount of quote
|
||||
pub amount: Amount,
|
||||
/// Unit of quote
|
||||
@@ -34,7 +31,6 @@ pub struct MintQuote {
|
||||
impl MintQuote {
|
||||
/// Create new [`MintQuote`]
|
||||
pub fn new(
|
||||
mint_url: MintUrl,
|
||||
request: String,
|
||||
unit: CurrencyUnit,
|
||||
amount: Amount,
|
||||
@@ -45,7 +41,6 @@ impl MintQuote {
|
||||
let id = Uuid::new_v4();
|
||||
|
||||
Self {
|
||||
mint_url,
|
||||
id,
|
||||
amount,
|
||||
unit,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use cashu::MintInfo;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::Error;
|
||||
use crate::common::LnKey;
|
||||
use crate::common::{LnKey, QuoteTTL};
|
||||
use crate::mint::{self, MintKeySetInfo, MintQuote as MintMintQuote};
|
||||
use crate::nuts::{
|
||||
BlindSignature, CurrencyUnit, Id, MeltBolt11Request, MeltQuoteState, MintQuoteState, Proof,
|
||||
@@ -127,4 +128,14 @@ 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>;
|
||||
|
||||
/// Set [`QuoteTTL`]
|
||||
async fn set_quote_ttl(&self, quote_ttl: QuoteTTL) -> Result<(), Self::Err>;
|
||||
/// Get [`QuoteTTL`]
|
||||
async fn get_quote_ttl(&self) -> Result<QuoteTTL, Self::Err>;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@ where
|
||||
|
||||
mint_builder = mint_builder
|
||||
.with_name("fake test mint".to_string())
|
||||
.with_mint_url(format!("http://{addr}:{port}"))
|
||||
.with_description("fake test mint".to_string())
|
||||
.with_quote_ttl(10000, 10000)
|
||||
.with_seed(mnemonic.to_seed_normalized("").to_vec());
|
||||
|
||||
@@ -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(
|
||||
@@ -175,7 +171,6 @@ pub async fn create_and_start_test_mint() -> anyhow::Result<Arc<Mint>> {
|
||||
|
||||
mint_builder = mint_builder
|
||||
.with_name("pure test mint".to_string())
|
||||
.with_mint_url("http://aa".to_string())
|
||||
.with_description("pure test mint".to_string())
|
||||
.with_quote_ttl(10000, 10000)
|
||||
.with_seed(mnemonic.to_seed_normalized("").to_vec());
|
||||
@@ -198,7 +193,7 @@ pub fn create_test_wallet_for_mint(mint: Arc<Mint>) -> anyhow::Result<Arc<Wallet
|
||||
let connector = DirectMintConnection::new(mint);
|
||||
|
||||
let seed = Mnemonic::generate(12)?.to_seed_normalized("");
|
||||
let mint_url = connector.mint.config.mint_url().to_string();
|
||||
let mint_url = "http://aa".to_string();
|
||||
let unit = CurrencyUnit::Sat;
|
||||
let localstore = WalletMemoryDatabase::default();
|
||||
let mut wallet = Wallet::new(&mint_url, unit, Arc::new(localstore), &seed, None)?;
|
||||
|
||||
@@ -169,7 +169,6 @@ where
|
||||
|
||||
mint_builder = mint_builder
|
||||
.with_name("regtest mint".to_string())
|
||||
.with_mint_url(format!("http://{addr}:{port}"))
|
||||
.with_description("regtest mint".to_string())
|
||||
.with_quote_ttl(10000, 10000)
|
||||
.with_seed(mnemonic.to_seed_normalized("").to_vec());
|
||||
|
||||
@@ -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;
|
||||
@@ -16,7 +17,6 @@ use cdk::nuts::{
|
||||
ProofState, Proofs, SecretKey, SpendingConditions, State, SwapRequest,
|
||||
};
|
||||
use cdk::subscription::{IndexableParams, Params};
|
||||
use cdk::types::QuoteTTL;
|
||||
use cdk::util::unix_time;
|
||||
use cdk::Mint;
|
||||
use tokio::sync::OnceCell;
|
||||
@@ -41,16 +41,17 @@ 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(
|
||||
MINT_URL,
|
||||
&mnemonic.to_seed_normalized(""),
|
||||
mint_info,
|
||||
quote_ttl,
|
||||
Arc::new(MintMemoryDatabase::default()),
|
||||
Arc::new(localstore),
|
||||
HashMap::new(),
|
||||
supported_units,
|
||||
HashMap::new(),
|
||||
@@ -72,7 +73,6 @@ async fn mint_proofs(
|
||||
let request_lookup = uuid::Uuid::new_v4().to_string();
|
||||
|
||||
let quote = MintQuote::new(
|
||||
mint.config.mint_url(),
|
||||
"".to_string(),
|
||||
CurrencyUnit::Sat,
|
||||
amount,
|
||||
|
||||
@@ -301,7 +301,6 @@ async fn main() -> anyhow::Result<()> {
|
||||
|
||||
mint_builder = mint_builder
|
||||
.with_name(settings.mint_info.name)
|
||||
.with_mint_url(settings.info.url)
|
||||
.with_version(mint_version)
|
||||
.with_description(settings.mint_info.description)
|
||||
.with_quote_ttl(10000, 10000)
|
||||
|
||||
@@ -52,6 +52,9 @@ pub enum Error {
|
||||
/// Unknown Mint Info
|
||||
#[error("Unknown mint info")]
|
||||
UnknownMintInfo,
|
||||
/// Unknown quote ttl
|
||||
#[error("Unknown quote ttl")]
|
||||
UnknownQuoteTTL,
|
||||
/// Unknown Proof Y
|
||||
#[error("Unknown proof Y")]
|
||||
UnknownY,
|
||||
|
||||
@@ -202,7 +202,6 @@ impl From<V1MintQuote> for MintQuote {
|
||||
fn from(quote: V1MintQuote) -> MintQuote {
|
||||
MintQuote {
|
||||
id: quote.id,
|
||||
mint_url: quote.mint_url,
|
||||
amount: quote.amount,
|
||||
unit: quote.unit,
|
||||
request: quote.request.clone(),
|
||||
|
||||
@@ -7,14 +7,14 @@ use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use cdk_common::common::LnKey;
|
||||
use cdk_common::common::{LnKey, QuoteTTL};
|
||||
use cdk_common::database::{self, MintDatabase};
|
||||
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,56 @@ 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())
|
||||
}
|
||||
|
||||
async fn set_quote_ttl(&self, quote_ttl: QuoteTTL) -> 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("quote_ttl", serde_json::to_string("e_ttl)?.as_str())
|
||||
.map_err(Error::from)?;
|
||||
}
|
||||
write_txn.commit().map_err(Error::from)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
async fn get_quote_ttl(&self) -> Result<QuoteTTL, 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(quote_ttl) = table.get("quote_ttl").map_err(Error::from)? {
|
||||
let quote_ttl = serde_json::from_str(quote_ttl.value())?;
|
||||
|
||||
return Ok(quote_ttl);
|
||||
}
|
||||
|
||||
Err(Error::UnknownQuoteTTL.into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,12 @@ pub enum Error {
|
||||
/// Serde Error
|
||||
#[error(transparent)]
|
||||
Serde(#[from] serde_json::Error),
|
||||
/// Unknown Mint Info
|
||||
#[error("Unknown mint info")]
|
||||
UnknownMintInfo,
|
||||
/// Unknown quote TTL
|
||||
#[error("Unknown quote TTL")]
|
||||
UnknownQuoteTTL,
|
||||
}
|
||||
|
||||
impl From<Error> for cdk_common::database::Error {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE mint_quote DROP COLUMN mint_url;
|
||||
@@ -0,0 +1,4 @@
|
||||
CREATE TABLE IF NOT EXISTS config (
|
||||
id TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL
|
||||
);
|
||||
@@ -7,16 +7,16 @@ use std::time::Duration;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use bitcoin::bip32::DerivationPath;
|
||||
use cdk_common::common::LnKey;
|
||||
use cdk_common::common::{LnKey, QuoteTTL};
|
||||
use cdk_common::database::{self, MintDatabase};
|
||||
use cdk_common::mint::{self, MintKeySetInfo, MintQuote};
|
||||
use cdk_common::mint_url::MintUrl;
|
||||
use cdk_common::nut00::ProofsMethods;
|
||||
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;
|
||||
@@ -206,12 +206,11 @@ WHERE active = 1
|
||||
let res = sqlx::query(
|
||||
r#"
|
||||
INSERT OR REPLACE INTO mint_quote
|
||||
(id, mint_url, amount, unit, request, state, expiry, request_lookup_id, pubkey)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);
|
||||
(id, amount, unit, request, state, expiry, request_lookup_id, pubkey)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?);
|
||||
"#,
|
||||
)
|
||||
.bind(quote.id.to_string())
|
||||
.bind(quote.mint_url.to_string())
|
||||
.bind(u64::from(quote.amount) as i64)
|
||||
.bind(quote.unit.to_string())
|
||||
.bind(quote.request)
|
||||
@@ -1221,6 +1220,148 @@ 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())
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async fn set_quote_ttl(&self, quote_ttl: QuoteTTL) -> 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("quote_ttl")
|
||||
.bind(serde_json::to_string("e_ttl)?)
|
||||
.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_quote_ttl(&self) -> Result<QuoteTTL, Self::Err> {
|
||||
let mut transaction = self.pool.begin().await.map_err(Error::from)?;
|
||||
|
||||
let rec = sqlx::query(
|
||||
r#"
|
||||
SELECT *
|
||||
FROM config
|
||||
WHERE id=?;
|
||||
"#,
|
||||
)
|
||||
.bind("quote_ttl")
|
||||
.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 quote_ttl = serde_json::from_str(&value)?;
|
||||
|
||||
Ok(quote_ttl)
|
||||
}
|
||||
Err(err) => match err {
|
||||
sqlx::Error::RowNotFound => {
|
||||
transaction.commit().await.map_err(Error::from)?;
|
||||
return Err(Error::UnknownQuoteTTL.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> {
|
||||
@@ -1250,7 +1391,6 @@ fn sqlite_row_to_keyset_info(row: SqliteRow) -> Result<MintKeySetInfo, Error> {
|
||||
|
||||
fn sqlite_row_to_mint_quote(row: SqliteRow) -> Result<MintQuote, Error> {
|
||||
let row_id: Hyphenated = row.try_get("id").map_err(Error::from)?;
|
||||
let row_mint_url: String = row.try_get("mint_url").map_err(Error::from)?;
|
||||
let row_amount: i64 = row.try_get("amount").map_err(Error::from)?;
|
||||
let row_unit: String = row.try_get("unit").map_err(Error::from)?;
|
||||
let row_request: String = row.try_get("request").map_err(Error::from)?;
|
||||
@@ -1274,7 +1414,6 @@ fn sqlite_row_to_mint_quote(row: SqliteRow) -> Result<MintQuote, Error> {
|
||||
|
||||
Ok(MintQuote {
|
||||
id: row_id.into_uuid(),
|
||||
mint_url: MintUrl::from_str(&row_mint_url)?,
|
||||
amount: Amount::from(row_amount as u64),
|
||||
unit: CurrencyUnit::from_str(&row_unit).map_err(Error::from)?,
|
||||
request: row_request,
|
||||
|
||||
@@ -23,7 +23,6 @@ http_subscription = []
|
||||
[dependencies]
|
||||
cdk-common = { path = "../cdk-common", version = "0.6.0" }
|
||||
cbor-diag = "0.1.12"
|
||||
arc-swap = "1.7.1"
|
||||
async-trait = "0.1"
|
||||
anyhow = { version = "1.0.43", features = ["backtrace"] }
|
||||
bitcoin = { version = "0.32.2", features = [
|
||||
|
||||
@@ -4,9 +4,11 @@ use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use cdk_common::common::QuoteTTL;
|
||||
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 +35,8 @@ 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>>,
|
||||
quote_ttl: Arc<RwLock<QuoteTTL>>,
|
||||
}
|
||||
|
||||
impl MintMemoryDatabase {
|
||||
@@ -49,6 +53,8 @@ impl MintMemoryDatabase {
|
||||
blinded_signatures: HashMap<[u8; 33], BlindSignature>,
|
||||
quote_signatures: HashMap<Uuid, Vec<BlindSignature>>,
|
||||
melt_request: Vec<(MeltBolt11Request<Uuid>, LnKey)>,
|
||||
mint_info: MintInfo,
|
||||
quote_ttl: QuoteTTL,
|
||||
) -> Result<Self, Error> {
|
||||
let mut proofs = HashMap::new();
|
||||
let mut proof_states = HashMap::new();
|
||||
@@ -87,6 +93,8 @@ 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)),
|
||||
quote_ttl: Arc::new(RwLock::new(quote_ttl)),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -412,4 +420,30 @@ 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())
|
||||
}
|
||||
|
||||
async fn set_quote_ttl(&self, quote_ttl: QuoteTTL) -> Result<(), Self::Err> {
|
||||
let mut current_quote_ttl = self.quote_ttl.write().await;
|
||||
|
||||
*current_quote_ttl = quote_ttl;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
async fn get_quote_ttl(&self) -> Result<QuoteTTL, Self::Err> {
|
||||
let quote_ttl = self.quote_ttl.read().await;
|
||||
|
||||
Ok(*quote_ttl)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,6 @@ use crate::types::{LnKey, QuoteTTL};
|
||||
/// Cashu Mint
|
||||
#[derive(Default)]
|
||||
pub struct MintBuilder {
|
||||
/// Mint Url
|
||||
mint_url: Option<String>,
|
||||
/// Mint Info
|
||||
mint_info: MintInfo,
|
||||
/// Mint Storage backend
|
||||
@@ -63,12 +61,6 @@ impl MintBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set mint url
|
||||
pub fn with_mint_url(mut self, mint_url: String) -> Self {
|
||||
self.mint_url = Some(mint_url);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set seed
|
||||
pub fn with_seed(mut self, seed: Vec<u8>) -> Self {
|
||||
self.seed = Some(seed);
|
||||
@@ -226,14 +218,19 @@ 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?;
|
||||
|
||||
localstore
|
||||
.set_quote_ttl(self.quote_ttl.ok_or(anyhow!("Quote ttl not set"))?)
|
||||
.await?;
|
||||
|
||||
Ok(Mint::new(
|
||||
self.mint_url.as_ref().ok_or(anyhow!("Mint url not set"))?,
|
||||
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(),
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
//! Active mint configuration
|
||||
//!
|
||||
//! This is the active configuration that can be updated at runtime.
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use arc_swap::ArcSwap;
|
||||
|
||||
use super::{Id, MintInfo, MintKeySet};
|
||||
use crate::mint_url::MintUrl;
|
||||
use crate::types::QuoteTTL;
|
||||
|
||||
/// Mint Inner configuration
|
||||
pub struct Config {
|
||||
/// Active Mint Keysets
|
||||
pub keysets: HashMap<Id, MintKeySet>,
|
||||
/// Mint url
|
||||
pub mint_info: MintInfo,
|
||||
/// Mint config
|
||||
pub mint_url: MintUrl,
|
||||
/// Quotes ttl
|
||||
pub quote_ttl: QuoteTTL,
|
||||
}
|
||||
|
||||
/// Mint configuration
|
||||
///
|
||||
/// This struct is used to configure the mint, and it is wrapped inside a ArcSwap, so it can be
|
||||
/// updated at runtime without locking the shared config nor without requiriming a mutable reference
|
||||
/// to the config
|
||||
///
|
||||
/// ArcSwap is used instead of a RwLock since the updates should be less frequent than the reads
|
||||
#[derive(Clone)]
|
||||
pub struct SwappableConfig {
|
||||
config: Arc<ArcSwap<Config>>,
|
||||
}
|
||||
|
||||
impl SwappableConfig {
|
||||
/// Creates a new configuration instance
|
||||
pub fn new(
|
||||
mint_url: MintUrl,
|
||||
quote_ttl: QuoteTTL,
|
||||
mint_info: MintInfo,
|
||||
keysets: HashMap<Id, MintKeySet>,
|
||||
) -> Self {
|
||||
let inner = Config {
|
||||
keysets,
|
||||
quote_ttl,
|
||||
mint_info,
|
||||
mint_url,
|
||||
};
|
||||
|
||||
Self {
|
||||
config: Arc::new(ArcSwap::from_pointee(inner)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets an Arc of the current configuration
|
||||
pub fn load(&self) -> Arc<Config> {
|
||||
self.config.load().clone()
|
||||
}
|
||||
|
||||
/// Gets a copy of the mint url
|
||||
pub fn mint_url(&self) -> MintUrl {
|
||||
self.load().mint_url.clone()
|
||||
}
|
||||
|
||||
/// Replace the current mint url with a new one
|
||||
pub fn set_mint_url(&self, mint_url: MintUrl) {
|
||||
let current_inner = self.load();
|
||||
let new_inner = Config {
|
||||
mint_url,
|
||||
quote_ttl: current_inner.quote_ttl,
|
||||
mint_info: current_inner.mint_info.clone(),
|
||||
keysets: current_inner.keysets.clone(),
|
||||
};
|
||||
|
||||
self.config.store(Arc::new(new_inner));
|
||||
}
|
||||
|
||||
/// Gets a copy of the quote ttl
|
||||
pub fn quote_ttl(&self) -> QuoteTTL {
|
||||
self.load().quote_ttl
|
||||
}
|
||||
|
||||
/// Replaces the current quote ttl with a new one
|
||||
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(),
|
||||
mint_url: current_inner.mint_url.clone(),
|
||||
quote_ttl,
|
||||
keysets: current_inner.keysets.clone(),
|
||||
};
|
||||
|
||||
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,
|
||||
mint_url: current_inner.mint_url.clone(),
|
||||
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,
|
||||
mint_url: current_inner.mint_url.clone(),
|
||||
keysets,
|
||||
};
|
||||
|
||||
self.config.store(Arc::new(new_inner));
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
use tracing::instrument;
|
||||
|
||||
use super::{Mint, MintInfo};
|
||||
use crate::mint_url::MintUrl;
|
||||
|
||||
impl Mint {
|
||||
/// Set Mint Url
|
||||
#[instrument(skip_all)]
|
||||
pub fn set_mint_url(&self, mint_url: MintUrl) {
|
||||
self.config.set_mint_url(mint_url);
|
||||
}
|
||||
|
||||
/// Get Mint Url
|
||||
#[instrument(skip_all)]
|
||||
pub fn get_mint_url(&self) -> MintUrl {
|
||||
self.config.mint_url()
|
||||
}
|
||||
|
||||
/// 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()
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,9 @@ impl Mint {
|
||||
pub async fn keyset_pubkeys(&self, keyset_id: &Id) -> Result<KeysResponse, Error> {
|
||||
self.ensure_keyset_loaded(keyset_id).await?;
|
||||
let keyset = self
|
||||
.config
|
||||
.load()
|
||||
.keysets
|
||||
.read()
|
||||
.await
|
||||
.get(keyset_id)
|
||||
.ok_or(Error::UnknownKeySet)?
|
||||
.clone();
|
||||
@@ -41,9 +41,9 @@ impl Mint {
|
||||
|
||||
Ok(KeysResponse {
|
||||
keysets: self
|
||||
.config
|
||||
.load()
|
||||
.keysets
|
||||
.read()
|
||||
.await
|
||||
.values()
|
||||
.filter_map(|k| match active_keysets.contains(&k.id) {
|
||||
true => Some(k.clone().into()),
|
||||
@@ -82,8 +82,7 @@ impl Mint {
|
||||
#[instrument(skip(self))]
|
||||
pub async fn keyset(&self, id: &Id) -> Result<Option<KeySet>, Error> {
|
||||
self.ensure_keyset_loaded(id).await?;
|
||||
let config = self.config.load();
|
||||
let keysets = &config.keysets;
|
||||
let keysets = self.keysets.read().await;
|
||||
let keyset = keysets.get(id).map(|k| k.clone().into());
|
||||
Ok(keyset)
|
||||
}
|
||||
@@ -118,9 +117,8 @@ impl Mint {
|
||||
self.localstore.add_keyset_info(keyset_info).await?;
|
||||
self.localstore.set_active_keyset(unit, id).await?;
|
||||
|
||||
let mut keysets = self.config.load().keysets.clone();
|
||||
let mut keysets = self.keysets.write().await;
|
||||
keysets.insert(id, keyset);
|
||||
self.config.set_keysets(keysets);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -128,11 +126,14 @@ impl Mint {
|
||||
/// Ensure Keyset is loaded in mint
|
||||
#[instrument(skip(self))]
|
||||
pub async fn ensure_keyset_loaded(&self, id: &Id) -> Result<(), Error> {
|
||||
if self.config.load().keysets.contains_key(id) {
|
||||
return Ok(());
|
||||
{
|
||||
let keysets = self.keysets.read().await;
|
||||
if keysets.contains_key(id) {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let mut keysets = self.config.load().keysets.clone();
|
||||
let mut keysets = self.keysets.write().await;
|
||||
let keyset_info = self
|
||||
.localstore
|
||||
.get_keyset_info(id)
|
||||
@@ -140,7 +141,6 @@ impl Mint {
|
||||
.ok_or(Error::UnknownKeySet)?;
|
||||
let id = keyset_info.id;
|
||||
keysets.insert(id, self.generate_keyset(keyset_info));
|
||||
self.config.set_keysets(keysets);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -120,12 +120,14 @@ impl Mint {
|
||||
// or we want to ignore the amount and do an mpp payment
|
||||
let msats_to_pay = options.map(|opt| opt.amount_msat());
|
||||
|
||||
let melt_ttl = self.localstore.get_quote_ttl().await?.melt_ttl;
|
||||
|
||||
let quote = MeltQuote::new(
|
||||
request.to_string(),
|
||||
unit.clone(),
|
||||
payment_quote.amount,
|
||||
payment_quote.fee,
|
||||
unix_time() + self.config.quote_ttl().melt_ttl,
|
||||
unix_time() + melt_ttl,
|
||||
payment_quote.request_lookup_id.clone(),
|
||||
msats_to_pay,
|
||||
);
|
||||
|
||||
@@ -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
|
||||
@@ -80,7 +80,9 @@ impl Mint {
|
||||
Error::UnitUnsupported
|
||||
})?;
|
||||
|
||||
let quote_expiry = unix_time() + self.config.quote_ttl().mint_ttl;
|
||||
let mint_ttl = self.localstore.get_quote_ttl().await?.mint_ttl;
|
||||
|
||||
let quote_expiry = unix_time() + mint_ttl;
|
||||
|
||||
if description.is_some() && !ln.get_settings().invoice_description {
|
||||
tracing::error!("Backend does not support invoice description");
|
||||
@@ -101,7 +103,6 @@ impl Mint {
|
||||
})?;
|
||||
|
||||
let quote = MintQuote::new(
|
||||
self.config.mint_url(),
|
||||
create_invoice_response.request.to_string(),
|
||||
unit.clone(),
|
||||
amount,
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user