mirror of
https://github.com/aljazceru/cdk.git
synced 2026-02-12 00:26:29 +01:00
feat: MintInfo and nuts builder
This commit is contained in:
@@ -35,6 +35,14 @@ cdk(NUT00): Rename `MintProofs` to `TokenV3Token` ([thesimplekid]).
|
||||
cdk: TokenV4 CBOR ([davidcaseria]/[thesimplekid]).
|
||||
cdk(wallet): `wallet::receive_proof` functions to claim specific proofs instead of encoded token ([thesimplekid]).
|
||||
cdk-cli: Flag on `send` to print v3 token, default is v4 ([thesimplekid]).
|
||||
cdk: `MintLightning` trait ([thesimplekid]).
|
||||
cdk-mintd: Mint binary ([thesimplekid]).
|
||||
cdk-cln: cln backend for mint ([thesimplekid]).
|
||||
cdk-axum: Mint axum server ([thesimplekid]).
|
||||
cdk: NUT06 `MintInfo` and `NUTs` builder ([thesimplekid]).
|
||||
|
||||
### Fixed
|
||||
cdk: NUT06 deseralize `MintInfo` ([thesimplekid]).
|
||||
|
||||
|
||||
## [v0.1.1]
|
||||
|
||||
@@ -61,10 +61,11 @@ pub async fn get_mint_bolt11_quote(
|
||||
into_response(Error::UnsupportedUnit)
|
||||
})?;
|
||||
|
||||
let amount = to_unit(payload.amount, &payload.unit, &ln.get_base_unit()).map_err(|err| {
|
||||
tracing::error!("Backed does not support unit: {}", err);
|
||||
into_response(Error::UnsupportedUnit)
|
||||
})?;
|
||||
let amount =
|
||||
to_unit(payload.amount, &payload.unit, &ln.get_settings().unit).map_err(|err| {
|
||||
tracing::error!("Backed does not support unit: {}", err);
|
||||
into_response(Error::UnsupportedUnit)
|
||||
})?;
|
||||
|
||||
let quote_expiry = unix_time() + state.quote_ttl;
|
||||
|
||||
@@ -370,7 +371,7 @@ pub async fn post_melt_bolt11(
|
||||
}
|
||||
};
|
||||
|
||||
let amount_spent = to_unit(pre.total_spent_msats, &ln.get_base_unit(), "e.unit)
|
||||
let amount_spent = to_unit(pre.total_spent_msats, &ln.get_settings().unit, "e.unit)
|
||||
.map_err(|_| into_response(Error::UnsupportedUnit))?;
|
||||
|
||||
(pre.payment_preimage, amount_spent.into())
|
||||
|
||||
@@ -9,6 +9,7 @@ use std::time::Duration;
|
||||
use async_trait::async_trait;
|
||||
use cdk::cdk_lightning::{
|
||||
self, to_unit, CreateInvoiceResponse, MintLightning, PayInvoiceResponse, PaymentQuoteResponse,
|
||||
Settings,
|
||||
};
|
||||
use cdk::mint::FeeReserve;
|
||||
use cdk::nuts::{CurrencyUnit, MeltQuoteBolt11Request, MeltQuoteState, MintQuoteState};
|
||||
@@ -34,16 +35,35 @@ pub struct Cln {
|
||||
rpc_socket: PathBuf,
|
||||
cln_client: Arc<Mutex<cln_rpc::ClnRpc>>,
|
||||
fee_reserve: FeeReserve,
|
||||
min_melt_amount: u64,
|
||||
max_melt_amount: u64,
|
||||
min_mint_amount: u64,
|
||||
max_mint_amount: u64,
|
||||
mint_enabled: bool,
|
||||
melt_enabled: bool,
|
||||
}
|
||||
|
||||
impl Cln {
|
||||
pub async fn new(rpc_socket: PathBuf, fee_reserve: FeeReserve) -> Result<Self, Error> {
|
||||
pub async fn new(
|
||||
rpc_socket: PathBuf,
|
||||
fee_reserve: FeeReserve,
|
||||
min_melt_amount: u64,
|
||||
max_melt_amount: u64,
|
||||
min_mint_amount: u64,
|
||||
max_mint_amount: u64,
|
||||
) -> Result<Self, Error> {
|
||||
let cln_client = cln_rpc::ClnRpc::new(&rpc_socket).await?;
|
||||
|
||||
Ok(Self {
|
||||
rpc_socket,
|
||||
cln_client: Arc::new(Mutex::new(cln_client)),
|
||||
fee_reserve,
|
||||
min_mint_amount,
|
||||
max_mint_amount,
|
||||
min_melt_amount,
|
||||
max_melt_amount,
|
||||
mint_enabled: true,
|
||||
melt_enabled: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -52,8 +72,17 @@ impl Cln {
|
||||
impl MintLightning for Cln {
|
||||
type Err = cdk_lightning::Error;
|
||||
|
||||
fn get_base_unit(&self) -> CurrencyUnit {
|
||||
CurrencyUnit::Msat
|
||||
fn get_settings(&self) -> Settings {
|
||||
Settings {
|
||||
mpp: true,
|
||||
min_mint_amount: self.min_mint_amount,
|
||||
max_mint_amount: self.max_mint_amount,
|
||||
min_melt_amount: self.min_melt_amount,
|
||||
max_melt_amount: self.max_melt_amount,
|
||||
unit: CurrencyUnit::Msat,
|
||||
mint_enabled: self.mint_enabled,
|
||||
melt_enabled: self.melt_enabled,
|
||||
}
|
||||
}
|
||||
|
||||
async fn wait_any_invoice(
|
||||
|
||||
@@ -58,11 +58,11 @@ pub struct Settings {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct MintInfo {
|
||||
/// name of the mint and should be recognizable
|
||||
pub name: Option<String>,
|
||||
pub name: String,
|
||||
/// hex pubkey of the mint
|
||||
pub pubkey: Option<PublicKey>,
|
||||
/// short description of the mint
|
||||
pub description: Option<String>,
|
||||
pub description: String,
|
||||
/// long description
|
||||
pub description_long: Option<String>,
|
||||
/// message of the day that the wallet must display to the user
|
||||
|
||||
@@ -12,10 +12,13 @@ use anyhow::{anyhow, Result};
|
||||
use axum::Router;
|
||||
use bip39::Mnemonic;
|
||||
use cdk::cdk_database::{self, MintDatabase};
|
||||
use cdk::cdk_lightning;
|
||||
use cdk::cdk_lightning::MintLightning;
|
||||
use cdk::mint::{FeeReserve, Mint};
|
||||
use cdk::nuts::{ContactInfo, CurrencyUnit, MintInfo, MintVersion, Nuts, PaymentMethod};
|
||||
use cdk::nuts::{
|
||||
nut04, nut05, ContactInfo, CurrencyUnit, MeltMethodSettings, MintInfo, MintMethodSettings,
|
||||
MintVersion, MppMethodSettings, Nuts, PaymentMethod,
|
||||
};
|
||||
use cdk::{cdk_lightning, Amount};
|
||||
use cdk_axum::LnKey;
|
||||
use cdk_cln::Cln;
|
||||
use cdk_redb::MintRedbDatabase;
|
||||
@@ -100,38 +103,14 @@ async fn main() -> anyhow::Result<()> {
|
||||
CARGO_PKG_VERSION.unwrap_or("Unknown").to_string(),
|
||||
);
|
||||
|
||||
let mint_info = MintInfo::new(
|
||||
settings.mint_info.name,
|
||||
settings.mint_info.pubkey,
|
||||
Some(mint_version),
|
||||
settings.mint_info.description,
|
||||
settings.mint_info.description_long,
|
||||
contact_info,
|
||||
Nuts::default(),
|
||||
settings.mint_info.motd,
|
||||
);
|
||||
|
||||
let relative_ln_fee = settings.ln.fee_percent;
|
||||
|
||||
let absolute_ln_fee_reserve = settings.ln.reserve_fee_min;
|
||||
|
||||
let mnemonic = Mnemonic::from_str(&settings.info.mnemonic)?;
|
||||
|
||||
let fee_reserve = FeeReserve {
|
||||
min_fee_reserve: absolute_ln_fee_reserve,
|
||||
percent_fee_reserve: relative_ln_fee,
|
||||
};
|
||||
|
||||
let mint = Mint::new(
|
||||
&settings.info.url,
|
||||
&mnemonic.to_seed_normalized(""),
|
||||
mint_info,
|
||||
localstore,
|
||||
absolute_ln_fee_reserve,
|
||||
relative_ln_fee,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let ln: Arc<dyn MintLightning<Err = cdk_lightning::Error> + Send + Sync> =
|
||||
match settings.ln.ln_backend {
|
||||
LnBackend::Cln => {
|
||||
@@ -146,10 +125,97 @@ async fn main() -> anyhow::Result<()> {
|
||||
)
|
||||
.ok_or(anyhow!("cln socket not defined"))?;
|
||||
|
||||
Arc::new(Cln::new(cln_socket, fee_reserve).await?)
|
||||
Arc::new(Cln::new(cln_socket, fee_reserve, 1000, 1000000, 1000, 100000).await?)
|
||||
}
|
||||
};
|
||||
|
||||
let mut ln_backends = HashMap::new();
|
||||
|
||||
ln_backends.insert(
|
||||
LnKey::new(CurrencyUnit::Sat, PaymentMethod::Bolt11),
|
||||
Arc::clone(&ln),
|
||||
);
|
||||
|
||||
let (nut04_settings, nut05_settings, mpp_settings): (
|
||||
nut04::Settings,
|
||||
nut05::Settings,
|
||||
Vec<MppMethodSettings>,
|
||||
) = ln_backends.iter().fold(
|
||||
(
|
||||
nut04::Settings::default(),
|
||||
nut05::Settings::default(),
|
||||
Vec::new(),
|
||||
),
|
||||
|(mut nut_04, mut nut_05, mut mpp), (key, ln)| {
|
||||
let settings = ln.get_settings();
|
||||
|
||||
let m = MppMethodSettings {
|
||||
method: key.method.clone(),
|
||||
unit: key.unit,
|
||||
mpp: settings.mpp,
|
||||
};
|
||||
|
||||
let n4 = MintMethodSettings {
|
||||
method: key.method.clone(),
|
||||
unit: key.unit,
|
||||
min_amount: Some(Amount::from(settings.min_mint_amount)),
|
||||
max_amount: Some(Amount::from(settings.max_mint_amount)),
|
||||
};
|
||||
|
||||
let n5 = MeltMethodSettings {
|
||||
method: key.method.clone(),
|
||||
unit: key.unit,
|
||||
min_amount: Some(Amount::from(settings.min_melt_amount)),
|
||||
max_amount: Some(Amount::from(settings.max_melt_amount)),
|
||||
};
|
||||
|
||||
nut_04.methods.push(n4);
|
||||
nut_05.methods.push(n5);
|
||||
mpp.push(m);
|
||||
|
||||
(nut_04, nut_05, mpp)
|
||||
},
|
||||
);
|
||||
|
||||
let nuts = Nuts::new()
|
||||
.nut04(nut04_settings)
|
||||
.nut05(nut05_settings)
|
||||
.nut15(mpp_settings);
|
||||
|
||||
let mut mint_info = MintInfo::new()
|
||||
.name(settings.mint_info.name)
|
||||
.version(mint_version)
|
||||
.description(settings.mint_info.description)
|
||||
.nuts(nuts);
|
||||
|
||||
if let Some(long_description) = &settings.mint_info.description_long {
|
||||
mint_info = mint_info.long_description(long_description);
|
||||
}
|
||||
|
||||
if let Some(contact_info) = contact_info {
|
||||
mint_info = mint_info.contact_info(contact_info);
|
||||
}
|
||||
|
||||
if let Some(pubkey) = settings.mint_info.pubkey {
|
||||
mint_info = mint_info.pubkey(pubkey);
|
||||
}
|
||||
|
||||
if let Some(motd) = settings.mint_info.motd {
|
||||
mint_info = mint_info.motd(motd);
|
||||
}
|
||||
|
||||
let mnemonic = Mnemonic::from_str(&settings.info.mnemonic)?;
|
||||
|
||||
let mint = Mint::new(
|
||||
&settings.info.url,
|
||||
&mnemonic.to_seed_normalized(""),
|
||||
mint_info,
|
||||
localstore,
|
||||
absolute_ln_fee_reserve,
|
||||
relative_ln_fee,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mint = Arc::new(mint);
|
||||
|
||||
// Check the status of any mint quotes that are pending
|
||||
@@ -166,13 +232,6 @@ async fn main() -> anyhow::Result<()> {
|
||||
.seconds_quote_is_valid_for
|
||||
.unwrap_or(DEFAULT_QUOTE_TTL_SECS);
|
||||
|
||||
let mut ln_backends = HashMap::new();
|
||||
|
||||
ln_backends.insert(
|
||||
LnKey::new(CurrencyUnit::Sat, PaymentMethod::Bolt11),
|
||||
Arc::clone(&ln),
|
||||
);
|
||||
|
||||
let v1_service =
|
||||
cdk_axum::create_mint_router(&mint_url, Arc::clone(&mint), ln_backends, quote_ttl).await?;
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ pub trait MintLightning {
|
||||
type Err: Into<Error> + From<Error>;
|
||||
|
||||
/// Base Unit
|
||||
fn get_base_unit(&self) -> CurrencyUnit;
|
||||
fn get_settings(&self) -> Settings;
|
||||
|
||||
/// Create a new invoice
|
||||
async fn create_invoice(
|
||||
@@ -115,6 +115,27 @@ pub struct PaymentQuoteResponse {
|
||||
pub fee: u64,
|
||||
}
|
||||
|
||||
/// Ln backend settings
|
||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Settings {
|
||||
/// MPP supported
|
||||
pub mpp: bool,
|
||||
/// Min amount to mint
|
||||
pub min_mint_amount: u64,
|
||||
/// Max amount to mint
|
||||
pub max_mint_amount: u64,
|
||||
/// Min amount to melt
|
||||
pub min_melt_amount: u64,
|
||||
/// Max amount to melt
|
||||
pub max_melt_amount: u64,
|
||||
/// Base unit of backend
|
||||
pub unit: CurrencyUnit,
|
||||
/// Minting enabled
|
||||
pub mint_enabled: bool,
|
||||
/// Melting enabled
|
||||
pub melt_enabled: bool,
|
||||
}
|
||||
|
||||
const MSAT_IN_SAT: u64 = 1000;
|
||||
|
||||
/// Helper function to convert units
|
||||
|
||||
@@ -197,22 +197,24 @@ pub struct MintBolt11Response {
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct MintMethodSettings {
|
||||
/// Payment Method e.g. bolt11
|
||||
method: PaymentMethod,
|
||||
pub method: PaymentMethod,
|
||||
/// Currency Unit e.g. sat
|
||||
unit: CurrencyUnit,
|
||||
pub unit: CurrencyUnit,
|
||||
/// Min Amount
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
min_amount: Option<Amount>,
|
||||
pub min_amount: Option<Amount>,
|
||||
/// Max Amount
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
max_amount: Option<Amount>,
|
||||
pub max_amount: Option<Amount>,
|
||||
}
|
||||
|
||||
/// Mint Settings
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct Settings {
|
||||
methods: Vec<MintMethodSettings>,
|
||||
disabled: bool,
|
||||
/// Methods to mint
|
||||
pub methods: Vec<MintMethodSettings>,
|
||||
/// Minting disabled
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
|
||||
@@ -241,22 +241,24 @@ impl From<MeltQuoteBolt11Response> for MeltBolt11Response {
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct MeltMethodSettings {
|
||||
/// Payment Method e.g. bolt11
|
||||
method: PaymentMethod,
|
||||
pub method: PaymentMethod,
|
||||
/// Currency Unit e.g. sat
|
||||
unit: CurrencyUnit,
|
||||
pub unit: CurrencyUnit,
|
||||
/// Min Amount
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
min_amount: Option<Amount>,
|
||||
pub min_amount: Option<Amount>,
|
||||
/// Max Amount
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
max_amount: Option<Amount>,
|
||||
pub max_amount: Option<Amount>,
|
||||
}
|
||||
|
||||
/// Melt Settings
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct Settings {
|
||||
methods: Vec<MeltMethodSettings>,
|
||||
disabled: bool,
|
||||
/// Methods to melt
|
||||
pub methods: Vec<MeltMethodSettings>,
|
||||
/// Minting disabled
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
|
||||
@@ -8,7 +8,7 @@ use serde::de::{self, SeqAccess, Visitor};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use super::nut01::PublicKey;
|
||||
use super::{nut04, nut05, nut15};
|
||||
use super::{nut04, nut05, nut15, MppMethodSettings};
|
||||
|
||||
/// Mint Version
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
@@ -83,27 +83,81 @@ pub struct MintInfo {
|
||||
}
|
||||
|
||||
impl MintInfo {
|
||||
#![allow(clippy::too_many_arguments)]
|
||||
/// Create new [`MintInfo`]
|
||||
pub fn new(
|
||||
name: Option<String>,
|
||||
pubkey: Option<PublicKey>,
|
||||
version: Option<MintVersion>,
|
||||
description: Option<String>,
|
||||
description_long: Option<String>,
|
||||
contact: Option<Vec<ContactInfo>>,
|
||||
nuts: Nuts,
|
||||
motd: Option<String>,
|
||||
) -> Self {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Set name
|
||||
pub fn name<S>(self, name: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
Self {
|
||||
name,
|
||||
pubkey,
|
||||
version,
|
||||
description,
|
||||
description_long,
|
||||
contact,
|
||||
nuts,
|
||||
motd,
|
||||
name: Some(name.into()),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Set pubkey
|
||||
pub fn pubkey(self, pubkey: PublicKey) -> Self {
|
||||
Self {
|
||||
pubkey: Some(pubkey),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Set [`MintVersion`]
|
||||
pub fn version(self, mint_version: MintVersion) -> Self {
|
||||
Self {
|
||||
version: Some(mint_version),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Set description
|
||||
pub fn description<S>(self, description: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
Self {
|
||||
description: Some(description.into()),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Set long description
|
||||
pub fn long_description<S>(self, description_long: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
Self {
|
||||
description_long: Some(description_long.into()),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Set contact info
|
||||
pub fn contact_info(self, contact_info: Vec<ContactInfo>) -> Self {
|
||||
Self {
|
||||
contact: Some(contact_info),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Set nuts
|
||||
pub fn nuts(self, nuts: Nuts) -> Self {
|
||||
Self { nuts, ..self }
|
||||
}
|
||||
|
||||
/// Set motd
|
||||
pub fn motd<S>(self, motd: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
Self {
|
||||
motd: Some(motd.into()),
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,7 +208,104 @@ pub struct Nuts {
|
||||
/// NUT15 Settings
|
||||
#[serde(default)]
|
||||
#[serde(rename = "15")]
|
||||
pub nut15: nut15::MppMethodSettings,
|
||||
pub nut15: nut15::Settings,
|
||||
}
|
||||
|
||||
impl Nuts {
|
||||
/// Create new [`Nuts`]
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Nut04 settings
|
||||
pub fn nut04(self, nut04_settings: nut04::Settings) -> Self {
|
||||
Self {
|
||||
nut04: nut04_settings,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Nut05 settings
|
||||
pub fn nut05(self, nut05_settings: nut05::Settings) -> Self {
|
||||
Self {
|
||||
nut05: nut05_settings,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Nut07 settings
|
||||
pub fn nut07(self, supported: bool) -> Self {
|
||||
Self {
|
||||
nut07: SupportedSettings { supported },
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Nut08 settings
|
||||
pub fn nut08(self, supported: bool) -> Self {
|
||||
Self {
|
||||
nut08: SupportedSettings { supported },
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Nut09 settings
|
||||
pub fn nut09(self, supported: bool) -> Self {
|
||||
Self {
|
||||
nut09: SupportedSettings { supported },
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Nut10 settings
|
||||
pub fn nut10(self, supported: bool) -> Self {
|
||||
Self {
|
||||
nut10: SupportedSettings { supported },
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Nut11 settings
|
||||
pub fn nut11(self, supported: bool) -> Self {
|
||||
Self {
|
||||
nut11: SupportedSettings { supported },
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Nut12 settings
|
||||
pub fn nut12(self, supported: bool) -> Self {
|
||||
Self {
|
||||
nut12: SupportedSettings { supported },
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Nut13 settings
|
||||
pub fn nut13(self, supported: bool) -> Self {
|
||||
Self {
|
||||
nut13: SupportedSettings { supported },
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Nut14 settings
|
||||
pub fn nut14(self, supported: bool) -> Self {
|
||||
Self {
|
||||
nut14: SupportedSettings { supported },
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Nut15 settings
|
||||
pub fn nut15(self, mpp_settings: Vec<MppMethodSettings>) -> Self {
|
||||
Self {
|
||||
nut15: nut15::Settings {
|
||||
methods: mpp_settings,
|
||||
},
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check state Settings
|
||||
|
||||
Reference in New Issue
Block a user