mirror of
https://github.com/aljazceru/cdk.git
synced 2025-12-22 23:25:22 +01:00
Refactor MintBuilder (#887)
* Refactor MintBuilder * Validate azp instead of aud for client id
This commit is contained in:
@@ -20,7 +20,7 @@ pub(crate) async fn handle(
|
||||
let mut subscription = context
|
||||
.state
|
||||
.mint
|
||||
.pubsub_manager
|
||||
.pubsub_manager()
|
||||
.try_subscribe(params)
|
||||
.await
|
||||
.map_err(|_| WsError::ParseError)?;
|
||||
|
||||
@@ -82,7 +82,7 @@ async fn get_device_code_token(mint_info: &MintInfo, client_id: &str) -> (String
|
||||
.expect("Nut21 defined")
|
||||
.openid_discovery;
|
||||
|
||||
let oidc_client = OidcClient::new(openid_discovery);
|
||||
let oidc_client = OidcClient::new(openid_discovery, None);
|
||||
|
||||
// Get the OIDC configuration
|
||||
let oidc_config = oidc_client
|
||||
|
||||
@@ -93,7 +93,7 @@ async fn get_access_token(
|
||||
.expect("Nut21 defined")
|
||||
.openid_discovery;
|
||||
|
||||
let oidc_client = OidcClient::new(openid_discovery);
|
||||
let oidc_client = OidcClient::new(openid_discovery, None);
|
||||
|
||||
// Get the token endpoint from the OIDC configuration
|
||||
let token_url = oidc_client
|
||||
|
||||
@@ -164,7 +164,7 @@ async fn refresh_access_token(
|
||||
.ok_or_else(|| anyhow::anyhow!("OIDC discovery information not available"))?
|
||||
.openid_discovery;
|
||||
|
||||
let oidc_client = OidcClient::new(openid_discovery);
|
||||
let oidc_client = OidcClient::new(openid_discovery, None);
|
||||
|
||||
// Get the token endpoint from the OIDC configuration
|
||||
let token_url = oidc_client.get_oidc_config().await?.token_endpoint;
|
||||
|
||||
@@ -162,8 +162,8 @@ impl PaymentProcessorKey {
|
||||
}
|
||||
}
|
||||
|
||||
/// Secs wuotes are valid
|
||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
/// Seconds quotes are valid
|
||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct QuoteTTL {
|
||||
/// Seconds mint quote is valid
|
||||
pub mint_ttl: u64,
|
||||
@@ -178,6 +178,15 @@ impl QuoteTTL {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for QuoteTTL {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
mint_ttl: 60 * 60, // 1 hour
|
||||
melt_ttl: 60, // 1 minute
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
|
||||
@@ -31,14 +31,10 @@ where
|
||||
|
||||
let fake_wallet = FakeWallet::new(fee_reserve, HashMap::default(), HashSet::default(), 0);
|
||||
|
||||
let mut mint_builder = MintBuilder::new();
|
||||
let mut mint_builder = MintBuilder::new(Arc::new(database));
|
||||
|
||||
mint_builder = mint_builder
|
||||
.with_localstore(Arc::new(database))
|
||||
.with_keystore(Arc::new(key_store));
|
||||
|
||||
mint_builder = mint_builder
|
||||
.add_ln_backend(
|
||||
mint_builder
|
||||
.add_payment_processor(
|
||||
CurrencyUnit::Sat,
|
||||
PaymentMethod::Bolt11,
|
||||
MintMeltLimits::new(1, 300),
|
||||
@@ -46,10 +42,14 @@ where
|
||||
)
|
||||
.await?;
|
||||
|
||||
mint_builder =
|
||||
mint_builder.set_clear_auth_settings(openid_discovery, "cashu-client".to_string());
|
||||
let auth_database = Arc::new(auth_database);
|
||||
|
||||
mint_builder = mint_builder.set_blind_auth_settings(50);
|
||||
mint_builder = mint_builder.with_auth(
|
||||
auth_database.clone(),
|
||||
openid_discovery,
|
||||
"cashu-client".to_string(),
|
||||
vec![],
|
||||
);
|
||||
|
||||
let blind_auth_endpoints = vec![
|
||||
ProtectedEndpoint::new(Method::Post, RoutePath::MintQuoteBolt11),
|
||||
@@ -71,6 +71,8 @@ where
|
||||
acc
|
||||
});
|
||||
|
||||
mint_builder = mint_builder.with_blind_auth(50, blind_auth_endpoints.keys().cloned().collect());
|
||||
|
||||
let mut tx = auth_database.begin_transaction().await?;
|
||||
|
||||
tx.add_protected_endpoints(blind_auth_endpoints).await?;
|
||||
@@ -85,15 +87,13 @@ where
|
||||
|
||||
tx.commit().await?;
|
||||
|
||||
mint_builder = mint_builder.with_auth_localstore(Arc::new(auth_database));
|
||||
|
||||
let mnemonic = Mnemonic::generate(12)?;
|
||||
|
||||
mint_builder = mint_builder
|
||||
.with_description("fake test mint".to_string())
|
||||
.with_seed(mnemonic.to_seed_normalized("").to_vec());
|
||||
mint_builder = mint_builder.with_description("fake test mint".to_string());
|
||||
|
||||
let _mint = mint_builder.build().await?;
|
||||
let _mint = mint_builder
|
||||
.build_with_seed(Arc::new(key_store), &mnemonic.to_seed_normalized(""))
|
||||
.await?;
|
||||
|
||||
todo!("Need to start this a cdk mintd keeping as ref for now");
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use async_trait::async_trait;
|
||||
use bip39::Mnemonic;
|
||||
use cashu::{MeltQuoteBolt12Request, MintQuoteBolt12Request, MintQuoteBolt12Response};
|
||||
use cdk::amount::SplitTarget;
|
||||
use cdk::cdk_database::{self, WalletDatabase};
|
||||
use cdk::cdk_database::{self, MintDatabase, WalletDatabase};
|
||||
use cdk::mint::{MintBuilder, MintMeltLimits};
|
||||
use cdk::nuts::nut00::ProofsMethods;
|
||||
use cdk::nuts::{
|
||||
@@ -227,25 +227,22 @@ pub async fn create_and_start_test_mint() -> Result<Mint> {
|
||||
// Read environment variable to determine database type
|
||||
let db_type = env::var("CDK_TEST_DB_TYPE").expect("Database type set");
|
||||
|
||||
let mut mint_builder = match db_type.to_lowercase().as_str() {
|
||||
"memory" => MintBuilder::new()
|
||||
.with_localstore(Arc::new(cdk_sqlite::mint::memory::empty().await?))
|
||||
.with_keystore(Arc::new(cdk_sqlite::mint::memory::empty().await?)),
|
||||
let localstore = match db_type.to_lowercase().as_str() {
|
||||
"memory" => Arc::new(cdk_sqlite::mint::memory::empty().await?),
|
||||
_ => {
|
||||
// Create a temporary directory for SQLite database
|
||||
let temp_dir = create_temp_dir("cdk-test-sqlite-mint")?;
|
||||
let path = temp_dir.join("mint.db").to_str().unwrap().to_string();
|
||||
let database = Arc::new(
|
||||
Arc::new(
|
||||
cdk_sqlite::MintSqliteDatabase::new(&path)
|
||||
.await
|
||||
.expect("Could not create sqlite db"),
|
||||
);
|
||||
MintBuilder::new()
|
||||
.with_localstore(database.clone())
|
||||
.with_keystore(database)
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let mut mint_builder = MintBuilder::new(localstore.clone());
|
||||
|
||||
let fee_reserve = FeeReserve {
|
||||
min_fee_reserve: 1.into(),
|
||||
percent_fee_reserve: 1.0,
|
||||
@@ -258,8 +255,8 @@ pub async fn create_and_start_test_mint() -> Result<Mint> {
|
||||
0,
|
||||
);
|
||||
|
||||
mint_builder = mint_builder
|
||||
.add_ln_backend(
|
||||
mint_builder
|
||||
.add_payment_processor(
|
||||
CurrencyUnit::Sat,
|
||||
PaymentMethod::Bolt11,
|
||||
MintMeltLimits::new(1, 10_000),
|
||||
@@ -272,23 +269,18 @@ pub async fn create_and_start_test_mint() -> Result<Mint> {
|
||||
mint_builder = mint_builder
|
||||
.with_name("pure test mint".to_string())
|
||||
.with_description("pure test mint".to_string())
|
||||
.with_urls(vec!["https://aaa".to_string()])
|
||||
.with_seed(mnemonic.to_seed_normalized("").to_vec());
|
||||
.with_urls(vec!["https://aaa".to_string()]);
|
||||
|
||||
let localstore = mint_builder
|
||||
.localstore
|
||||
.as_ref()
|
||||
.map(|x| x.clone())
|
||||
.expect("localstore");
|
||||
|
||||
let mut tx = localstore.begin_transaction().await?;
|
||||
tx.set_mint_info(mint_builder.mint_info.clone()).await?;
|
||||
let tx_localstore = localstore.clone();
|
||||
let mut tx = tx_localstore.begin_transaction().await?;
|
||||
|
||||
let quote_ttl = QuoteTTL::new(10000, 10000);
|
||||
tx.set_quote_ttl(quote_ttl).await?;
|
||||
tx.commit().await?;
|
||||
|
||||
let mint = mint_builder.build().await?;
|
||||
let mint = mint_builder
|
||||
.build_with_seed(localstore.clone(), &mnemonic.to_seed_normalized(""))
|
||||
.await?;
|
||||
|
||||
let mint_clone = mint.clone();
|
||||
let shutdown = Arc::new(Notify::new());
|
||||
|
||||
@@ -753,7 +753,7 @@ async fn get_access_token(mint_info: &MintInfo) -> (String, String) {
|
||||
.expect("Nutxx defined")
|
||||
.openid_discovery;
|
||||
|
||||
let oidc_client = OidcClient::new(openid_discovery);
|
||||
let oidc_client = OidcClient::new(openid_discovery, None);
|
||||
|
||||
// Get the token endpoint from the OIDC configuration
|
||||
let token_url = oidc_client
|
||||
@@ -811,7 +811,7 @@ async fn get_custom_access_token(
|
||||
.expect("Nutxx defined")
|
||||
.openid_discovery;
|
||||
|
||||
let oidc_client = OidcClient::new(openid_discovery);
|
||||
let oidc_client = OidcClient::new(openid_discovery, None);
|
||||
|
||||
// Get the token endpoint from the OIDC configuration
|
||||
let token_url = oidc_client
|
||||
|
||||
@@ -443,7 +443,7 @@ pub async fn test_p2pk_swap() {
|
||||
.collect();
|
||||
|
||||
let mut listener = mint_bob
|
||||
.pubsub_manager
|
||||
.pubsub_manager()
|
||||
.try_subscribe::<IndexableParams>(
|
||||
Params {
|
||||
kind: cdk::nuts::nut17::Kind::ProofState,
|
||||
@@ -773,7 +773,7 @@ async fn test_concurrent_double_spend_swap() {
|
||||
|
||||
// Verify that all proofs are marked as spent in the mint
|
||||
let states = mint_bob
|
||||
.localstore
|
||||
.localstore()
|
||||
.get_proofs_states(&proofs.iter().map(|p| p.y().unwrap()).collect::<Vec<_>>())
|
||||
.await
|
||||
.expect("Failed to get proof state");
|
||||
@@ -870,7 +870,7 @@ async fn test_concurrent_double_spend_melt() {
|
||||
|
||||
// Verify that all proofs are marked as spent in the mint
|
||||
let states = mint_bob
|
||||
.localstore
|
||||
.localstore()
|
||||
.get_proofs_states(&proofs.iter().map(|p| p.y().unwrap()).collect::<Vec<_>>())
|
||||
.await
|
||||
.expect("Failed to get proof state");
|
||||
|
||||
@@ -29,14 +29,15 @@ async fn test_correct_keyset() {
|
||||
|
||||
let fake_wallet = FakeWallet::new(fee_reserve, HashMap::default(), HashSet::default(), 0);
|
||||
|
||||
let mut mint_builder = MintBuilder::new();
|
||||
let localstore = Arc::new(database);
|
||||
mint_builder = mint_builder
|
||||
.with_localstore(localstore.clone())
|
||||
.with_keystore(localstore.clone());
|
||||
let mut mint_builder = MintBuilder::new(localstore.clone());
|
||||
|
||||
mint_builder = mint_builder
|
||||
.add_ln_backend(
|
||||
.with_name("regtest mint".to_string())
|
||||
.with_description("regtest mint".to_string());
|
||||
|
||||
mint_builder
|
||||
.add_payment_processor(
|
||||
CurrencyUnit::Sat,
|
||||
PaymentMethod::Bolt11,
|
||||
MintMeltLimits::new(1, 5_000),
|
||||
@@ -44,18 +45,14 @@ async fn test_correct_keyset() {
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
// .with_seed(mnemonic.to_seed_normalized("").to_vec());
|
||||
|
||||
mint_builder = mint_builder
|
||||
.with_name("regtest mint".to_string())
|
||||
.with_description("regtest mint".to_string())
|
||||
.with_seed(mnemonic.to_seed_normalized("").to_vec());
|
||||
|
||||
let mint = mint_builder.build().await.unwrap();
|
||||
let mut tx = localstore.begin_transaction().await.unwrap();
|
||||
|
||||
tx.set_mint_info(mint_builder.mint_info.clone())
|
||||
let mint = mint_builder
|
||||
.build_with_seed(localstore.clone(), &mnemonic.to_seed_normalized(""))
|
||||
.await
|
||||
.unwrap();
|
||||
let mut tx = localstore.begin_transaction().await.unwrap();
|
||||
|
||||
let quote_ttl = QuoteTTL::new(10000, 10000);
|
||||
tx.set_quote_ttl(quote_ttl).await.unwrap();
|
||||
|
||||
@@ -81,7 +78,6 @@ async fn test_correct_keyset() {
|
||||
assert_ne!(keyset_info.id, old_keyset_info.id);
|
||||
|
||||
mint.rotate_keyset(CurrencyUnit::Sat, 32, 0).await.unwrap();
|
||||
let mint = mint_builder.build().await.unwrap();
|
||||
|
||||
let active = mint.get_active_keysets();
|
||||
|
||||
|
||||
@@ -643,7 +643,7 @@ impl CdkMint for MintRPCServer {
|
||||
|
||||
let mint_quote = self
|
||||
.mint
|
||||
.localstore
|
||||
.localstore()
|
||||
.get_mint_quote("e_id)
|
||||
.await
|
||||
.map_err(|_| Status::invalid_argument("Could not find quote".to_string()))?
|
||||
@@ -659,9 +659,8 @@ impl CdkMint for MintRPCServer {
|
||||
payment_identifier: mint_quote.request_lookup_id.clone(),
|
||||
};
|
||||
|
||||
let mut tx = self
|
||||
.mint
|
||||
.localstore
|
||||
let localstore = self.mint.localstore();
|
||||
let mut tx = localstore
|
||||
.begin_transaction()
|
||||
.await
|
||||
.map_err(|_| Status::internal("Could not start db transaction".to_string()))?;
|
||||
@@ -693,9 +692,8 @@ impl CdkMint for MintRPCServer {
|
||||
vec![], // payment_ids
|
||||
);
|
||||
|
||||
let mut tx = self
|
||||
.mint
|
||||
.localstore
|
||||
let mint_store = self.mint.localstore();
|
||||
let mut tx = mint_store
|
||||
.begin_transaction()
|
||||
.await
|
||||
.map_err(|_| Status::internal("Could not update quote".to_string()))?;
|
||||
@@ -710,7 +708,7 @@ impl CdkMint for MintRPCServer {
|
||||
|
||||
let mint_quote = self
|
||||
.mint
|
||||
.localstore
|
||||
.localstore()
|
||||
.get_mint_quote("e_id)
|
||||
.await
|
||||
.map_err(|_| Status::invalid_argument("Could not find quote".to_string()))?
|
||||
|
||||
@@ -19,7 +19,7 @@ use bip39::Mnemonic;
|
||||
use cdk::cdk_database::{self, MintDatabase, MintKeysDatabase};
|
||||
use cdk::cdk_payment;
|
||||
use cdk::cdk_payment::MintPayment;
|
||||
use cdk::mint::{MintBuilder, MintMeltLimits};
|
||||
use cdk::mint::{Mint, MintBuilder, MintMeltLimits};
|
||||
#[cfg(any(
|
||||
feature = "cln",
|
||||
feature = "lnbits",
|
||||
@@ -84,16 +84,13 @@ compile_error!(
|
||||
async fn main() -> Result<()> {
|
||||
let (work_dir, settings, localstore, keystore) = initial_setup().await?;
|
||||
|
||||
let mint_builder = MintBuilder::new()
|
||||
.with_localstore(localstore)
|
||||
.with_keystore(keystore);
|
||||
let mint_builder = MintBuilder::new(localstore);
|
||||
|
||||
let (mint_builder, ln_routers) = configure_mint_builder(&settings, mint_builder).await?;
|
||||
#[cfg(feature = "auth")]
|
||||
let mint_builder = setup_authentication(&settings, &work_dir, mint_builder).await?;
|
||||
let mint_builder_info = mint_builder.mint_info.clone();
|
||||
|
||||
let mint = mint_builder.build().await?;
|
||||
let mint = build_mint(&settings, keystore, mint_builder).await?;
|
||||
|
||||
tracing::debug!("Mint built from builder.");
|
||||
|
||||
@@ -109,7 +106,7 @@ async fn main() -> Result<()> {
|
||||
&settings,
|
||||
ln_routers,
|
||||
&work_dir,
|
||||
mint_builder_info,
|
||||
mint.mint_info().await?,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -236,9 +233,6 @@ async fn configure_mint_builder(
|
||||
// Configure lightning backend
|
||||
let mint_builder = configure_lightning_backend(settings, mint_builder, &mut ln_routers).await?;
|
||||
|
||||
// Configure signatory or seed
|
||||
let mint_builder = configure_signing_method(settings, mint_builder).await?;
|
||||
|
||||
// Configure caching
|
||||
let mint_builder = configure_cache(settings, mint_builder);
|
||||
|
||||
@@ -274,7 +268,7 @@ fn configure_basic_info(settings: &config::Settings, mint_builder: MintBuilder)
|
||||
}
|
||||
|
||||
for contact in contacts {
|
||||
builder = builder.add_contact_info(contact);
|
||||
builder = builder.with_contact_info(contact);
|
||||
}
|
||||
|
||||
if let Some(pubkey) = settings.mint_info.pubkey {
|
||||
@@ -435,8 +429,8 @@ async fn configure_backend_for_unit(
|
||||
|
||||
if let Some(bolt12) = payment_settings.get("bolt12") {
|
||||
if bolt12.as_bool().unwrap_or_default() {
|
||||
mint_builder = mint_builder
|
||||
.add_ln_backend(
|
||||
mint_builder
|
||||
.add_payment_processor(
|
||||
unit.clone(),
|
||||
PaymentMethod::Bolt12,
|
||||
mint_melt_limits,
|
||||
@@ -446,8 +440,8 @@ async fn configure_backend_for_unit(
|
||||
}
|
||||
}
|
||||
|
||||
mint_builder = mint_builder
|
||||
.add_ln_backend(
|
||||
mint_builder
|
||||
.add_payment_processor(
|
||||
unit.clone(),
|
||||
PaymentMethod::Bolt11,
|
||||
mint_melt_limits,
|
||||
@@ -456,47 +450,15 @@ async fn configure_backend_for_unit(
|
||||
.await?;
|
||||
|
||||
if let Some(input_fee) = settings.info.input_fee_ppk {
|
||||
mint_builder = mint_builder.set_unit_fee(&unit, input_fee)?;
|
||||
mint_builder.set_unit_fee(&unit, input_fee)?;
|
||||
}
|
||||
|
||||
let nut17_supported = SupportedMethods::default_bolt11(unit);
|
||||
mint_builder = mint_builder.add_supported_websockets(nut17_supported);
|
||||
mint_builder = mint_builder.with_supported_websockets(nut17_supported);
|
||||
|
||||
Ok(mint_builder)
|
||||
}
|
||||
|
||||
/// Configures the signing method (remote signatory or local seed)
|
||||
async fn configure_signing_method(
|
||||
settings: &config::Settings,
|
||||
mint_builder: MintBuilder,
|
||||
) -> Result<MintBuilder> {
|
||||
if let Some(signatory_url) = settings.info.signatory_url.clone() {
|
||||
tracing::info!(
|
||||
"Connecting to remote signatory to {} with certs {:?}",
|
||||
signatory_url,
|
||||
settings.info.signatory_certs.clone()
|
||||
);
|
||||
|
||||
Ok(mint_builder.with_signatory(Arc::new(
|
||||
cdk_signatory::SignatoryRpcClient::new(
|
||||
signatory_url,
|
||||
settings.info.signatory_certs.clone(),
|
||||
)
|
||||
.await?,
|
||||
)))
|
||||
} else if let Some(mnemonic) = settings
|
||||
.info
|
||||
.mnemonic
|
||||
.clone()
|
||||
.map(|s| Mnemonic::from_str(&s))
|
||||
.transpose()?
|
||||
{
|
||||
Ok(mint_builder.with_seed(mnemonic.to_seed_normalized("").to_vec()))
|
||||
} else {
|
||||
bail!("No seed nor remote signatory set");
|
||||
}
|
||||
}
|
||||
|
||||
/// Configures cache settings
|
||||
fn configure_cache(settings: &config::Settings, mint_builder: MintBuilder) -> MintBuilder {
|
||||
let cached_endpoints = vec![
|
||||
@@ -506,7 +468,7 @@ fn configure_cache(settings: &config::Settings, mint_builder: MintBuilder) -> Mi
|
||||
];
|
||||
|
||||
let cache: HttpCache = settings.info.http_cache.clone().into();
|
||||
mint_builder.add_cache(Some(cache.ttl.as_secs()), cached_endpoints)
|
||||
mint_builder.with_cache(Some(cache.ttl.as_secs()), cached_endpoints)
|
||||
}
|
||||
|
||||
#[cfg(feature = "auth")]
|
||||
@@ -532,16 +494,9 @@ async fn setup_authentication(
|
||||
}
|
||||
};
|
||||
|
||||
mint_builder = mint_builder.with_auth_localstore(auth_localstore.clone());
|
||||
|
||||
let mint_blind_auth_endpoint =
|
||||
ProtectedEndpoint::new(Method::Post, RoutePath::MintBlindAuth);
|
||||
|
||||
mint_builder = mint_builder.set_clear_auth_settings(
|
||||
auth_settings.openid_discovery,
|
||||
auth_settings.openid_client_id,
|
||||
);
|
||||
|
||||
let mut protected_endpoints = HashMap::new();
|
||||
|
||||
protected_endpoints.insert(mint_blind_auth_endpoint, AuthRequired::Clear);
|
||||
@@ -644,7 +599,14 @@ async fn setup_authentication(
|
||||
}
|
||||
}
|
||||
|
||||
mint_builder = mint_builder.set_blind_auth_settings(auth_settings.mint_max_bat);
|
||||
mint_builder = mint_builder.with_auth(
|
||||
auth_localstore.clone(),
|
||||
auth_settings.openid_discovery,
|
||||
auth_settings.openid_client_id,
|
||||
vec![mint_blind_auth_endpoint],
|
||||
);
|
||||
mint_builder =
|
||||
mint_builder.with_blind_auth(auth_settings.mint_max_bat, blind_auth_endpoints);
|
||||
|
||||
let mut tx = auth_localstore.begin_transaction().await?;
|
||||
|
||||
@@ -655,6 +617,43 @@ async fn setup_authentication(
|
||||
Ok(mint_builder)
|
||||
}
|
||||
|
||||
/// Build mints with the configured the signing method (remote signatory or local seed)
|
||||
async fn build_mint(
|
||||
settings: &config::Settings,
|
||||
keystore: Arc<dyn MintKeysDatabase<Err = cdk_database::Error> + Send + Sync>,
|
||||
mint_builder: MintBuilder,
|
||||
) -> Result<Mint> {
|
||||
if let Some(signatory_url) = settings.info.signatory_url.clone() {
|
||||
tracing::info!(
|
||||
"Connecting to remote signatory to {} with certs {:?}",
|
||||
signatory_url,
|
||||
settings.info.signatory_certs.clone()
|
||||
);
|
||||
|
||||
Ok(mint_builder
|
||||
.build_with_signatory(Arc::new(
|
||||
cdk_signatory::SignatoryRpcClient::new(
|
||||
signatory_url,
|
||||
settings.info.signatory_certs.clone(),
|
||||
)
|
||||
.await?,
|
||||
))
|
||||
.await?)
|
||||
} else if let Some(mnemonic) = settings
|
||||
.info
|
||||
.mnemonic
|
||||
.clone()
|
||||
.map(|s| Mnemonic::from_str(&s))
|
||||
.transpose()?
|
||||
{
|
||||
Ok(mint_builder
|
||||
.build_with_seed(keystore, &mnemonic.to_seed_normalized(""))
|
||||
.await?)
|
||||
} else {
|
||||
bail!("No seed nor remote signatory set");
|
||||
}
|
||||
}
|
||||
|
||||
async fn start_services(
|
||||
mint: Arc<cdk::mint::Mint>,
|
||||
settings: &config::Settings,
|
||||
@@ -704,7 +703,7 @@ async fn start_services(
|
||||
mint.set_mint_info(mint_builder_info).await?;
|
||||
mint.set_quote_ttl(QuoteTTL::new(10_000, 10_000)).await?;
|
||||
} else {
|
||||
if mint.localstore.get_quote_ttl().await.is_err() {
|
||||
if mint.localstore().get_quote_ttl().await.is_err() {
|
||||
mint.set_quote_ttl(QuoteTTL::new(10_000, 10_000)).await?;
|
||||
}
|
||||
// Add version information
|
||||
|
||||
@@ -112,7 +112,7 @@ async fn get_access_token(mint_info: &MintInfo) -> String {
|
||||
.expect("Nut21 defined")
|
||||
.openid_discovery;
|
||||
|
||||
let oidc_client = OidcClient::new(openid_discovery);
|
||||
let oidc_client = OidcClient::new(openid_discovery, None);
|
||||
|
||||
// Get the token endpoint from the OIDC configuration
|
||||
let token_url = oidc_client
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use bitcoin::bip32::DerivationPath;
|
||||
use cdk_common::database::{self, MintDatabase, MintKeysDatabase};
|
||||
use cdk_common::error::Error;
|
||||
use cdk_common::nut04::MintMethodOptions;
|
||||
use cdk_common::nut05::MeltMethodOptions;
|
||||
use cdk_common::payment::Bolt11Settings;
|
||||
#[cfg(feature = "auth")]
|
||||
use cdk_common::{nut21, nut22};
|
||||
use cdk_signatory::signatory::Signatory;
|
||||
|
||||
@@ -19,103 +19,91 @@ use super::nut19::{self, CachedEndpoint};
|
||||
use super::MintAuthDatabase;
|
||||
use super::Nuts;
|
||||
use crate::amount::Amount;
|
||||
#[cfg(feature = "auth")]
|
||||
use crate::cdk_database;
|
||||
use crate::cdk_payment::{self, MintPayment};
|
||||
use crate::mint::Mint;
|
||||
#[cfg(feature = "auth")]
|
||||
use crate::nuts::ProtectedEndpoint;
|
||||
use crate::nuts::{
|
||||
ContactInfo, CurrencyUnit, MeltMethodSettings, MintInfo, MintMethodSettings, MintVersion,
|
||||
MppMethodSettings, PaymentMethod,
|
||||
};
|
||||
use crate::types::PaymentProcessorKey;
|
||||
|
||||
/// Cashu Mint
|
||||
#[derive(Default)]
|
||||
/// Cashu Mint Builder
|
||||
pub struct MintBuilder {
|
||||
/// Mint Info
|
||||
pub mint_info: MintInfo,
|
||||
/// Mint Storage backend
|
||||
pub localstore: Option<Arc<dyn MintDatabase<database::Error> + Send + Sync>>,
|
||||
/// Database for the Signatory
|
||||
keystore: Option<Arc<dyn MintKeysDatabase<Err = database::Error> + Send + Sync>>,
|
||||
/// Mint Storage backend
|
||||
mint_info: MintInfo,
|
||||
localstore: Arc<dyn MintDatabase<database::Error> + Send + Sync>,
|
||||
#[cfg(feature = "auth")]
|
||||
auth_localstore: Option<Arc<dyn MintAuthDatabase<Err = cdk_database::Error> + Send + Sync>>,
|
||||
/// Ln backends for mint
|
||||
ln: Option<
|
||||
payment_processors:
|
||||
HashMap<PaymentProcessorKey, Arc<dyn MintPayment<Err = cdk_payment::Error> + Send + Sync>>,
|
||||
>,
|
||||
seed: Option<Vec<u8>>,
|
||||
supported_units: HashMap<CurrencyUnit, (u64, u8)>,
|
||||
custom_paths: HashMap<CurrencyUnit, DerivationPath>,
|
||||
// protected_endpoints: HashMap<ProtectedEndpoint, AuthRequired>,
|
||||
openid_discovery: Option<String>,
|
||||
signatory: Option<Arc<dyn Signatory + Sync + Send + 'static>>,
|
||||
}
|
||||
|
||||
impl MintBuilder {
|
||||
/// New mint builder
|
||||
pub fn new() -> MintBuilder {
|
||||
let mut builder = MintBuilder::default();
|
||||
|
||||
let nuts = Nuts::new()
|
||||
/// New [`MintBuilder`]
|
||||
pub fn new(localstore: Arc<dyn MintDatabase<database::Error> + Send + Sync>) -> MintBuilder {
|
||||
let mint_info = MintInfo {
|
||||
nuts: Nuts::new()
|
||||
.nut07(true)
|
||||
.nut08(true)
|
||||
.nut09(true)
|
||||
.nut10(true)
|
||||
.nut11(true)
|
||||
.nut12(true)
|
||||
.nut14(true)
|
||||
.nut20(true);
|
||||
.nut12(true),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
builder.mint_info.nuts = nuts;
|
||||
|
||||
builder
|
||||
}
|
||||
|
||||
/// Set signatory service
|
||||
pub fn with_signatory(mut self, signatory: Arc<dyn Signatory + Sync + Send + 'static>) -> Self {
|
||||
self.signatory = Some(signatory);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set seed
|
||||
pub fn with_seed(mut self, seed: Vec<u8>) -> Self {
|
||||
self.seed = Some(seed);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set localstore
|
||||
pub fn with_localstore(
|
||||
mut self,
|
||||
localstore: Arc<dyn MintDatabase<database::Error> + Send + Sync>,
|
||||
) -> MintBuilder {
|
||||
self.localstore = Some(localstore);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set keystore database
|
||||
pub fn with_keystore(
|
||||
mut self,
|
||||
keystore: Arc<dyn MintKeysDatabase<Err = database::Error> + Send + Sync>,
|
||||
) -> MintBuilder {
|
||||
self.keystore = Some(keystore);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set auth localstore
|
||||
MintBuilder {
|
||||
mint_info,
|
||||
localstore,
|
||||
#[cfg(feature = "auth")]
|
||||
pub fn with_auth_localstore(
|
||||
auth_localstore: None,
|
||||
payment_processors: HashMap::new(),
|
||||
supported_units: HashMap::new(),
|
||||
custom_paths: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set clear auth settings
|
||||
#[cfg(feature = "auth")]
|
||||
pub fn with_auth(
|
||||
mut self,
|
||||
localstore: Arc<dyn MintAuthDatabase<Err = cdk_database::Error> + Send + Sync>,
|
||||
) -> MintBuilder {
|
||||
self.auth_localstore = Some(localstore);
|
||||
auth_localstore: Arc<dyn MintAuthDatabase<Err = cdk_database::Error> + Send + Sync>,
|
||||
openid_discovery: String,
|
||||
client_id: String,
|
||||
protected_endpoints: Vec<ProtectedEndpoint>,
|
||||
) -> Self {
|
||||
self.auth_localstore = Some(auth_localstore);
|
||||
self.mint_info.nuts.nut21 = Some(nut21::Settings::new(
|
||||
openid_discovery,
|
||||
client_id,
|
||||
protected_endpoints,
|
||||
));
|
||||
self
|
||||
}
|
||||
|
||||
/// Set Openid discovery url
|
||||
pub fn with_openid_discovery(mut self, openid_discovery: String) -> Self {
|
||||
self.openid_discovery = Some(openid_discovery);
|
||||
/// Set blind auth settings
|
||||
#[cfg(feature = "auth")]
|
||||
pub fn with_blind_auth(
|
||||
mut self,
|
||||
bat_max_mint: u64,
|
||||
protected_endpoints: Vec<ProtectedEndpoint>,
|
||||
) -> Self {
|
||||
let mut nuts = self.mint_info.nuts;
|
||||
|
||||
nuts.nut22 = Some(nut22::Settings::new(bat_max_mint, protected_endpoints));
|
||||
|
||||
self.mint_info.nuts = nuts;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Set mint info
|
||||
pub fn with_mint_info(mut self, mint_info: MintInfo) -> Self {
|
||||
self.mint_info = mint_info;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -168,32 +156,68 @@ impl MintBuilder {
|
||||
}
|
||||
|
||||
/// Set contact info
|
||||
pub fn add_contact_info(mut self, contact_info: ContactInfo) -> Self {
|
||||
pub fn with_contact_info(mut self, contact_info: ContactInfo) -> Self {
|
||||
let mut contacts = self.mint_info.contact.clone().unwrap_or_default();
|
||||
contacts.push(contact_info);
|
||||
self.mint_info.contact = Some(contacts);
|
||||
self
|
||||
}
|
||||
|
||||
/// Add ln backend
|
||||
pub async fn add_ln_backend(
|
||||
/// Set pubkey
|
||||
pub fn with_pubkey(mut self, pubkey: crate::nuts::PublicKey) -> Self {
|
||||
self.mint_info.pubkey = Some(pubkey);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Support websockets
|
||||
pub fn with_supported_websockets(mut self, supported_method: SupportedMethods) -> Self {
|
||||
let mut supported_settings = self.mint_info.nuts.nut17.supported.clone();
|
||||
|
||||
if !supported_settings.contains(&supported_method) {
|
||||
supported_settings.push(supported_method);
|
||||
|
||||
self.mint_info.nuts = self.mint_info.nuts.nut17(supported_settings);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Add support for NUT19
|
||||
pub fn with_cache(mut self, ttl: Option<u64>, cached_endpoints: Vec<CachedEndpoint>) -> Self {
|
||||
let nut19_settings = nut19::Settings {
|
||||
ttl,
|
||||
cached_endpoints,
|
||||
};
|
||||
|
||||
self.mint_info.nuts.nut19 = nut19_settings;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Set custom derivation paths for mint units
|
||||
pub fn with_custom_derivation_paths(
|
||||
mut self,
|
||||
custom_paths: HashMap<CurrencyUnit, DerivationPath>,
|
||||
) -> Self {
|
||||
self.custom_paths = custom_paths;
|
||||
self
|
||||
}
|
||||
|
||||
/// Add payment processor
|
||||
pub async fn add_payment_processor(
|
||||
&mut self,
|
||||
unit: CurrencyUnit,
|
||||
method: PaymentMethod,
|
||||
limits: MintMeltLimits,
|
||||
ln_backend: Arc<dyn MintPayment<Err = cdk_payment::Error> + Send + Sync>,
|
||||
) -> Result<Self, Error> {
|
||||
let ln_key = PaymentProcessorKey {
|
||||
payment_processor: Arc<dyn MintPayment<Err = cdk_payment::Error> + Send + Sync>,
|
||||
) -> Result<(), Error> {
|
||||
let key = PaymentProcessorKey {
|
||||
unit: unit.clone(),
|
||||
method: method.clone(),
|
||||
};
|
||||
|
||||
tracing::debug!("Adding ln backed for {}, {}", unit, method);
|
||||
tracing::debug!("with limits {:?}", limits);
|
||||
|
||||
let mut ln = self.ln.unwrap_or_default();
|
||||
|
||||
let settings = ln_backend.get_settings().await?;
|
||||
let settings = payment_processor.get_settings().await?;
|
||||
|
||||
let settings: Bolt11Settings = settings.try_into()?;
|
||||
|
||||
@@ -235,91 +259,19 @@ impl MintBuilder {
|
||||
self.mint_info.nuts.nut05.methods.push(melt_method_settings);
|
||||
self.mint_info.nuts.nut05.disabled = false;
|
||||
|
||||
ln.insert(ln_key.clone(), ln_backend);
|
||||
|
||||
let mut supported_units = self.supported_units.clone();
|
||||
|
||||
supported_units.insert(ln_key.unit, (0, 32));
|
||||
supported_units.insert(key.unit.clone(), (0, 32));
|
||||
self.supported_units = supported_units;
|
||||
|
||||
self.ln = Some(ln);
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Set pubkey
|
||||
pub fn with_pubkey(mut self, pubkey: crate::nuts::PublicKey) -> Self {
|
||||
self.mint_info.pubkey = Some(pubkey);
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Support websockets
|
||||
pub fn add_supported_websockets(mut self, supported_method: SupportedMethods) -> Self {
|
||||
let mut supported_settings = self.mint_info.nuts.nut17.supported.clone();
|
||||
|
||||
if !supported_settings.contains(&supported_method) {
|
||||
supported_settings.push(supported_method);
|
||||
|
||||
self.mint_info.nuts = self.mint_info.nuts.nut17(supported_settings);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Add support for NUT19
|
||||
pub fn add_cache(mut self, ttl: Option<u64>, cached_endpoints: Vec<CachedEndpoint>) -> Self {
|
||||
let nut19_settings = nut19::Settings {
|
||||
ttl,
|
||||
cached_endpoints,
|
||||
};
|
||||
|
||||
self.mint_info.nuts.nut19 = nut19_settings;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Set custom derivation paths for mint units
|
||||
pub fn add_custom_derivation_paths(
|
||||
mut self,
|
||||
custom_paths: HashMap<CurrencyUnit, DerivationPath>,
|
||||
) -> Self {
|
||||
self.custom_paths = custom_paths;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set clear auth settings
|
||||
pub fn set_clear_auth_settings(mut self, openid_discovery: String, client_id: String) -> Self {
|
||||
let mut nuts = self.mint_info.nuts;
|
||||
|
||||
nuts.nut21 = Some(nut21::Settings::new(
|
||||
openid_discovery.clone(),
|
||||
client_id,
|
||||
vec![],
|
||||
));
|
||||
|
||||
self.openid_discovery = Some(openid_discovery);
|
||||
|
||||
self.mint_info.nuts = nuts;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Set blind auth settings
|
||||
pub fn set_blind_auth_settings(mut self, bat_max_mint: u64) -> Self {
|
||||
let mut nuts = self.mint_info.nuts;
|
||||
|
||||
nuts.nut22 = Some(nut22::Settings::new(bat_max_mint, vec![]));
|
||||
|
||||
self.mint_info.nuts = nuts;
|
||||
|
||||
self
|
||||
self.payment_processors.insert(key, payment_processor);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets the input fee ppk for a given unit
|
||||
///
|
||||
/// The unit **MUST** already have been added with a ln backend
|
||||
pub fn set_unit_fee(mut self, unit: &CurrencyUnit, input_fee_ppk: u64) -> Result<Self, Error> {
|
||||
pub fn set_unit_fee(&mut self, unit: &CurrencyUnit, input_fee_ppk: u64) -> Result<(), Error> {
|
||||
let (input_fee, _max_order) = self
|
||||
.supported_units
|
||||
.get_mut(unit)
|
||||
@@ -327,59 +279,53 @@ impl MintBuilder {
|
||||
|
||||
*input_fee = input_fee_ppk;
|
||||
|
||||
Ok(self)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Build mint
|
||||
pub async fn build(&self) -> anyhow::Result<Mint> {
|
||||
let localstore = self
|
||||
.localstore
|
||||
.clone()
|
||||
.ok_or(anyhow!("Localstore not set"))?;
|
||||
let ln = self.ln.clone().ok_or(anyhow!("Ln backends not set"))?;
|
||||
/// Build the mint with the provided signatory
|
||||
pub async fn build_with_signatory(
|
||||
self,
|
||||
signatory: Arc<dyn Signatory + Send + Sync>,
|
||||
) -> Result<Mint, Error> {
|
||||
#[cfg(feature = "auth")]
|
||||
if let Some(auth_localstore) = self.auth_localstore {
|
||||
return Mint::new_with_auth(
|
||||
self.mint_info,
|
||||
signatory,
|
||||
self.localstore,
|
||||
auth_localstore,
|
||||
self.payment_processors,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
Mint::new(
|
||||
self.mint_info,
|
||||
signatory,
|
||||
self.localstore,
|
||||
self.payment_processors,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
let signatory = if let Some(signatory) = self.signatory.as_ref() {
|
||||
signatory.clone()
|
||||
} else {
|
||||
let seed = self.seed.as_ref().ok_or(anyhow!("Mint seed not set"))?;
|
||||
/// Build the mint with the provided keystore and seed
|
||||
pub async fn build_with_seed(
|
||||
self,
|
||||
keystore: Arc<dyn MintKeysDatabase<Err = cdk_database::Error> + Send + Sync>,
|
||||
seed: &[u8],
|
||||
) -> Result<Mint, Error> {
|
||||
let in_memory_signatory = cdk_signatory::db_signatory::DbSignatory::new(
|
||||
self.keystore.clone().ok_or(anyhow!("keystore not set"))?,
|
||||
keystore,
|
||||
seed,
|
||||
self.supported_units.clone(),
|
||||
HashMap::new(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Arc::new(cdk_signatory::embedded::Service::new(Arc::new(
|
||||
let signatory = Arc::new(cdk_signatory::embedded::Service::new(Arc::new(
|
||||
in_memory_signatory,
|
||||
)))
|
||||
};
|
||||
)));
|
||||
|
||||
#[cfg(feature = "auth")]
|
||||
if let Some(openid_discovery) = &self.openid_discovery {
|
||||
let auth_localstore = self
|
||||
.auth_localstore
|
||||
.clone()
|
||||
.ok_or(anyhow!("Auth localstore not set"))?;
|
||||
|
||||
return Ok(Mint::new_with_auth(
|
||||
signatory,
|
||||
localstore,
|
||||
auth_localstore,
|
||||
ln,
|
||||
openid_discovery.clone(),
|
||||
)
|
||||
.await?);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "auth"))]
|
||||
if self.openid_discovery.is_some() {
|
||||
return Err(anyhow!(
|
||||
"OpenID discovery URL provided but auth feature is not enabled"
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Mint::new(signatory, localstore, ln).await?)
|
||||
self.build_with_signatory(signatory).await
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ impl Mint {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let ln = match self.ln.get(&PaymentProcessorKey::new(
|
||||
let ln = match self.payment_processors.get(&PaymentProcessorKey::new(
|
||||
quote.unit.clone(),
|
||||
quote.payment_method.clone(),
|
||||
)) {
|
||||
|
||||
@@ -152,7 +152,7 @@ impl Mint {
|
||||
.await?;
|
||||
|
||||
let ln = self
|
||||
.ln
|
||||
.payment_processors
|
||||
.get(&PaymentProcessorKey::new(
|
||||
unit.clone(),
|
||||
PaymentMethod::Bolt11,
|
||||
@@ -250,7 +250,7 @@ impl Mint {
|
||||
.await?;
|
||||
|
||||
let ln = self
|
||||
.ln
|
||||
.payment_processors
|
||||
.get(&PaymentProcessorKey::new(
|
||||
unit.clone(),
|
||||
PaymentMethod::Bolt12,
|
||||
@@ -586,7 +586,7 @@ impl Mint {
|
||||
_ => None,
|
||||
};
|
||||
tracing::debug!("partial_amount: {:?}", partial_amount);
|
||||
let ln = match self.ln.get(&PaymentProcessorKey::new(
|
||||
let ln = match self.payment_processors.get(&PaymentProcessorKey::new(
|
||||
quote.unit.clone(),
|
||||
PaymentMethod::Bolt11,
|
||||
)) {
|
||||
|
||||
@@ -53,17 +53,17 @@ pub struct Mint {
|
||||
///
|
||||
/// It is implemented in the cdk-signatory crate, and it can be embedded in the mint or it can
|
||||
/// be a gRPC client to a remote signatory server.
|
||||
pub signatory: Arc<dyn Signatory + Send + Sync>,
|
||||
signatory: Arc<dyn Signatory + Send + Sync>,
|
||||
/// Mint Storage backend
|
||||
pub localstore: Arc<dyn MintDatabase<database::Error> + Send + Sync>,
|
||||
localstore: Arc<dyn MintDatabase<database::Error> + Send + Sync>,
|
||||
/// Auth Storage backend (only available with auth feature)
|
||||
#[cfg(feature = "auth")]
|
||||
pub auth_localstore: Option<Arc<dyn MintAuthDatabase<Err = database::Error> + Send + Sync>>,
|
||||
/// Ln backends for mint
|
||||
pub ln:
|
||||
auth_localstore: Option<Arc<dyn MintAuthDatabase<Err = database::Error> + Send + Sync>>,
|
||||
/// Payment processors for mint
|
||||
payment_processors:
|
||||
HashMap<PaymentProcessorKey, Arc<dyn MintPayment<Err = cdk_payment::Error> + Send + Sync>>,
|
||||
/// Subscription manager
|
||||
pub pubsub_manager: Arc<PubSubManager>,
|
||||
pubsub_manager: Arc<PubSubManager>,
|
||||
#[cfg(feature = "auth")]
|
||||
oidc_client: Option<OidcClient>,
|
||||
/// In-memory keyset
|
||||
@@ -71,40 +71,23 @@ pub struct Mint {
|
||||
}
|
||||
|
||||
impl Mint {
|
||||
/// Get the payment processor for the given unit and payment method
|
||||
pub fn get_payment_processor(
|
||||
&self,
|
||||
unit: CurrencyUnit,
|
||||
payment_method: PaymentMethod,
|
||||
) -> Result<Arc<dyn MintPayment<Err = cdk_payment::Error> + Send + Sync>, Error> {
|
||||
let key = PaymentProcessorKey::new(unit.clone(), payment_method.clone());
|
||||
self.ln.get(&key).cloned().ok_or_else(|| {
|
||||
tracing::info!(
|
||||
"No payment processor set for pair {}, {}",
|
||||
unit,
|
||||
payment_method
|
||||
);
|
||||
Error::UnsupportedUnit
|
||||
})
|
||||
}
|
||||
|
||||
/// Create new [`Mint`] without authentication
|
||||
pub async fn new(
|
||||
mint_info: MintInfo,
|
||||
signatory: Arc<dyn Signatory + Send + Sync>,
|
||||
localstore: Arc<dyn MintDatabase<database::Error> + Send + Sync>,
|
||||
ln: HashMap<
|
||||
payment_processors: HashMap<
|
||||
PaymentProcessorKey,
|
||||
Arc<dyn MintPayment<Err = cdk_payment::Error> + Send + Sync>,
|
||||
>,
|
||||
) -> Result<Self, Error> {
|
||||
Self::new_internal(
|
||||
mint_info,
|
||||
signatory,
|
||||
localstore,
|
||||
#[cfg(feature = "auth")]
|
||||
None,
|
||||
ln,
|
||||
#[cfg(feature = "auth")]
|
||||
None,
|
||||
payment_processors,
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -112,21 +95,21 @@ impl Mint {
|
||||
/// Create new [`Mint`] with authentication support
|
||||
#[cfg(feature = "auth")]
|
||||
pub async fn new_with_auth(
|
||||
mint_info: MintInfo,
|
||||
signatory: Arc<dyn Signatory + Send + Sync>,
|
||||
localstore: Arc<dyn MintDatabase<database::Error> + Send + Sync>,
|
||||
auth_localstore: Arc<dyn MintAuthDatabase<Err = database::Error> + Send + Sync>,
|
||||
ln: HashMap<
|
||||
payment_processors: HashMap<
|
||||
PaymentProcessorKey,
|
||||
Arc<dyn MintPayment<Err = cdk_payment::Error> + Send + Sync>,
|
||||
>,
|
||||
open_id_discovery: String,
|
||||
) -> Result<Self, Error> {
|
||||
Self::new_internal(
|
||||
mint_info,
|
||||
signatory,
|
||||
localstore,
|
||||
Some(auth_localstore),
|
||||
ln,
|
||||
Some(open_id_discovery),
|
||||
payment_processors,
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -134,21 +117,17 @@ impl Mint {
|
||||
/// Internal function to create a new [`Mint`] with shared logic
|
||||
#[inline]
|
||||
async fn new_internal(
|
||||
mint_info: MintInfo,
|
||||
signatory: Arc<dyn Signatory + Send + Sync>,
|
||||
localstore: Arc<dyn MintDatabase<database::Error> + Send + Sync>,
|
||||
#[cfg(feature = "auth")] auth_localstore: Option<
|
||||
Arc<dyn database::MintAuthDatabase<Err = database::Error> + Send + Sync>,
|
||||
>,
|
||||
ln: HashMap<
|
||||
payment_processors: HashMap<
|
||||
PaymentProcessorKey,
|
||||
Arc<dyn MintPayment<Err = cdk_payment::Error> + Send + Sync>,
|
||||
>,
|
||||
#[cfg(feature = "auth")] open_id_discovery: Option<String>,
|
||||
) -> Result<Self, Error> {
|
||||
#[cfg(feature = "auth")]
|
||||
let oidc_client =
|
||||
open_id_discovery.map(|openid_discovery| OidcClient::new(openid_discovery.clone()));
|
||||
|
||||
let keysets = signatory.keysets().await?;
|
||||
if !keysets
|
||||
.keysets
|
||||
@@ -168,19 +147,57 @@ impl Mint {
|
||||
.count()
|
||||
);
|
||||
|
||||
let mint_store = localstore.clone();
|
||||
let mut tx = mint_store.begin_transaction().await?;
|
||||
tx.set_mint_info(mint_info.clone()).await?;
|
||||
tx.set_quote_ttl(QuoteTTL::default()).await?;
|
||||
tx.commit().await?;
|
||||
|
||||
Ok(Self {
|
||||
signatory,
|
||||
pubsub_manager: Arc::new(localstore.clone().into()),
|
||||
localstore,
|
||||
#[cfg(feature = "auth")]
|
||||
oidc_client,
|
||||
ln,
|
||||
oidc_client: mint_info.nuts.nut21.as_ref().map(|nut21| {
|
||||
OidcClient::new(
|
||||
nut21.openid_discovery.clone(),
|
||||
Some(nut21.client_id.clone()),
|
||||
)
|
||||
}),
|
||||
payment_processors,
|
||||
#[cfg(feature = "auth")]
|
||||
auth_localstore,
|
||||
keysets: Arc::new(ArcSwap::new(keysets.keysets.into())),
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the payment processor for the given unit and payment method
|
||||
pub fn get_payment_processor(
|
||||
&self,
|
||||
unit: CurrencyUnit,
|
||||
payment_method: PaymentMethod,
|
||||
) -> Result<Arc<dyn MintPayment<Err = cdk_payment::Error> + Send + Sync>, Error> {
|
||||
let key = PaymentProcessorKey::new(unit.clone(), payment_method.clone());
|
||||
self.payment_processors.get(&key).cloned().ok_or_else(|| {
|
||||
tracing::info!(
|
||||
"No payment processor set for pair {}, {}",
|
||||
unit,
|
||||
payment_method
|
||||
);
|
||||
Error::UnsupportedUnit
|
||||
})
|
||||
}
|
||||
|
||||
/// Localstore
|
||||
pub fn localstore(&self) -> Arc<dyn MintDatabase<database::Error> + Send + Sync> {
|
||||
Arc::clone(&self.localstore)
|
||||
}
|
||||
|
||||
/// Pub Sub manager
|
||||
pub fn pubsub_manager(&self) -> Arc<PubSubManager> {
|
||||
Arc::clone(&self.pubsub_manager)
|
||||
}
|
||||
|
||||
/// Get mint info
|
||||
#[instrument(skip_all)]
|
||||
pub async fn mint_info(&self) -> Result<MintInfo, Error> {
|
||||
@@ -259,7 +276,7 @@ impl Mint {
|
||||
Vec<PaymentProcessorKey>,
|
||||
)> = Vec::new();
|
||||
|
||||
for (key, ln) in self.ln.iter() {
|
||||
for (key, ln) in self.payment_processors.iter() {
|
||||
// Check if we already have this processor
|
||||
let found = processor_groups.iter_mut().find(|(proc_ref, _)| {
|
||||
// Compare Arc pointer equality using ptr_eq
|
||||
@@ -609,7 +626,7 @@ mod tests {
|
||||
.expect("Failed to create signatory"),
|
||||
);
|
||||
|
||||
Mint::new(signatory, localstore, HashMap::new())
|
||||
Mint::new(MintInfo::default(), signatory, localstore, HashMap::new())
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ impl Mint {
|
||||
method: PaymentMethod::Bolt11,
|
||||
};
|
||||
|
||||
let ln_backend = match self.ln.get(&ln_key) {
|
||||
let ln_backend = match self.payment_processors.get(&ln_key) {
|
||||
Some(ln_backend) => ln_backend,
|
||||
None => {
|
||||
tracing::warn!("No backend for ln key: {:?}", ln_key);
|
||||
|
||||
@@ -32,9 +32,9 @@ pub enum Error {
|
||||
/// Unsupported Algo
|
||||
#[error("Unsupported signing algo")]
|
||||
UnsupportedSigningAlgo,
|
||||
/// Access token not returned
|
||||
#[error("Error getting access token")]
|
||||
AccessTokenMissing,
|
||||
/// Invalid Client ID
|
||||
#[error("Invalid Client ID")]
|
||||
InvalidClientId,
|
||||
}
|
||||
|
||||
impl From<Error> for cdk_common::error::Error {
|
||||
@@ -58,6 +58,7 @@ pub struct OidcConfig {
|
||||
pub struct OidcClient {
|
||||
client: Client,
|
||||
openid_discovery: String,
|
||||
client_id: Option<String>,
|
||||
oidc_config: Arc<RwLock<Option<OidcConfig>>>,
|
||||
jwks_set: Arc<RwLock<Option<JwkSet>>>,
|
||||
}
|
||||
@@ -88,10 +89,11 @@ pub struct TokenResponse {
|
||||
|
||||
impl OidcClient {
|
||||
/// Create new [`OidcClient`]
|
||||
pub fn new(openid_discovery: String) -> Self {
|
||||
pub fn new(openid_discovery: String, client_id: Option<String>) -> Self {
|
||||
Self {
|
||||
client: Client::new(),
|
||||
openid_discovery,
|
||||
client_id,
|
||||
oidc_config: Arc::new(RwLock::new(None)),
|
||||
jwks_set: Arc::new(RwLock::new(None)),
|
||||
}
|
||||
@@ -192,12 +194,41 @@ impl OidcClient {
|
||||
validation
|
||||
};
|
||||
|
||||
if let Err(err) =
|
||||
decode::<HashMap<String, serde_json::Value>>(cat_jwt, &decoding_key, &validation)
|
||||
{
|
||||
match decode::<HashMap<String, serde_json::Value>>(cat_jwt, &decoding_key, &validation) {
|
||||
Ok(claims) => {
|
||||
tracing::debug!("Successfully verified cat");
|
||||
tracing::debug!("Claims: {:?}", claims.claims);
|
||||
if let Some(client_id) = &self.client_id {
|
||||
if let Some(token_client_id) = claims.claims.get("client_id") {
|
||||
if let Some(token_client_id_value) = token_client_id.as_str() {
|
||||
if token_client_id_value != client_id {
|
||||
tracing::warn!(
|
||||
"Client ID mismatch: expected {}, got {}",
|
||||
client_id,
|
||||
token_client_id_value
|
||||
);
|
||||
return Err(Error::InvalidClientId);
|
||||
}
|
||||
}
|
||||
} else if let Some(azp) = claims.claims.get("azp") {
|
||||
if let Some(azp_value) = azp.as_str() {
|
||||
if azp_value != client_id {
|
||||
tracing::warn!(
|
||||
"Client ID (azp) mismatch: expected {}, got {}",
|
||||
client_id,
|
||||
azp_value
|
||||
);
|
||||
return Err(Error::InvalidClientId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::debug!("Could not verify cat: {}", err);
|
||||
return Err(err.into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -268,8 +268,9 @@ impl Wallet {
|
||||
auth_wallet.protected_endpoints.write().await;
|
||||
*protected_endpoints = mint_info.protected_endpoints();
|
||||
|
||||
if let Some(oidc_client) =
|
||||
mint_info.openid_discovery().map(OidcClient::new)
|
||||
if let Some(oidc_client) = mint_info
|
||||
.openid_discovery()
|
||||
.map(|url| OidcClient::new(url, None))
|
||||
{
|
||||
auth_wallet.set_oidc_client(Some(oidc_client)).await;
|
||||
}
|
||||
@@ -277,7 +278,9 @@ impl Wallet {
|
||||
None => {
|
||||
tracing::info!("Mint has auth enabled creating auth wallet");
|
||||
|
||||
let oidc_client = mint_info.openid_discovery().map(OidcClient::new);
|
||||
let oidc_client = mint_info
|
||||
.openid_discovery()
|
||||
.map(|url| OidcClient::new(url, None));
|
||||
let new_auth_wallet = AuthWallet::new(
|
||||
self.mint_url.clone(),
|
||||
None,
|
||||
|
||||
Reference in New Issue
Block a user