mirror of
https://github.com/aljazceru/cdk.git
synced 2026-02-15 10:06:12 +01:00
add FFI types for NUT-04 and NUT-05 (#1101)
* feat: add FFI types and conversion logic for NUT-04 and NUT-05 settings in `cdk-ffi` * feat: remove auth feature and deafult to having auth --------- Co-authored-by: thesimplekid <tsk@thesimplekid.com>
This commit is contained in:
@@ -388,9 +388,11 @@ pub enum Error {
|
||||
NUT20(#[from] crate::nuts::nut20::Error),
|
||||
/// NUT21 Error
|
||||
#[error(transparent)]
|
||||
#[cfg(feature = "auth")]
|
||||
NUT21(#[from] crate::nuts::nut21::Error),
|
||||
/// NUT22 Error
|
||||
#[error(transparent)]
|
||||
#[cfg(feature = "auth")]
|
||||
NUT22(#[from] crate::nuts::nut22::Error),
|
||||
/// NUT23 Error
|
||||
#[error(transparent)]
|
||||
|
||||
@@ -1467,9 +1467,314 @@ impl From<SupportedSettings> for cdk::nuts::nut06::SupportedSettings {
|
||||
}
|
||||
}
|
||||
|
||||
/// FFI-compatible Nuts settings (simplified - only includes basic boolean flags)
|
||||
// -----------------------------
|
||||
// NUT-04/05 FFI Types
|
||||
// -----------------------------
|
||||
|
||||
/// FFI-compatible MintMethodSettings (NUT-04)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
|
||||
pub struct MintMethodSettings {
|
||||
pub method: PaymentMethod,
|
||||
pub unit: CurrencyUnit,
|
||||
pub min_amount: Option<Amount>,
|
||||
pub max_amount: Option<Amount>,
|
||||
/// For bolt11, whether mint supports setting invoice description
|
||||
pub description: Option<bool>,
|
||||
}
|
||||
|
||||
impl From<cdk::nuts::nut04::MintMethodSettings> for MintMethodSettings {
|
||||
fn from(s: cdk::nuts::nut04::MintMethodSettings) -> Self {
|
||||
let description = match s.options {
|
||||
Some(cdk::nuts::nut04::MintMethodOptions::Bolt11 { description }) => Some(description),
|
||||
_ => None,
|
||||
};
|
||||
Self {
|
||||
method: s.method.into(),
|
||||
unit: s.unit.into(),
|
||||
min_amount: s.min_amount.map(Into::into),
|
||||
max_amount: s.max_amount.map(Into::into),
|
||||
description,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<MintMethodSettings> for cdk::nuts::nut04::MintMethodSettings {
|
||||
type Error = FfiError;
|
||||
|
||||
fn try_from(s: MintMethodSettings) -> Result<Self, Self::Error> {
|
||||
let options = match (s.method.clone(), s.description) {
|
||||
(PaymentMethod::Bolt11, Some(description)) => {
|
||||
Some(cdk::nuts::nut04::MintMethodOptions::Bolt11 { description })
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
Ok(Self {
|
||||
method: s.method.into(),
|
||||
unit: s.unit.into(),
|
||||
min_amount: s.min_amount.map(Into::into),
|
||||
max_amount: s.max_amount.map(Into::into),
|
||||
options,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// FFI-compatible Nut04 Settings
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
|
||||
pub struct Nut04Settings {
|
||||
pub methods: Vec<MintMethodSettings>,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl From<cdk::nuts::nut04::Settings> for Nut04Settings {
|
||||
fn from(s: cdk::nuts::nut04::Settings) -> Self {
|
||||
Self {
|
||||
methods: s.methods.into_iter().map(Into::into).collect(),
|
||||
disabled: s.disabled,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Nut04Settings> for cdk::nuts::nut04::Settings {
|
||||
type Error = FfiError;
|
||||
|
||||
fn try_from(s: Nut04Settings) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
methods: s
|
||||
.methods
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<Result<_, _>>()?,
|
||||
disabled: s.disabled,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// FFI-compatible MeltMethodSettings (NUT-05)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
|
||||
pub struct MeltMethodSettings {
|
||||
pub method: PaymentMethod,
|
||||
pub unit: CurrencyUnit,
|
||||
pub min_amount: Option<Amount>,
|
||||
pub max_amount: Option<Amount>,
|
||||
/// For bolt11, whether mint supports amountless invoices
|
||||
pub amountless: Option<bool>,
|
||||
}
|
||||
|
||||
impl From<cdk::nuts::nut05::MeltMethodSettings> for MeltMethodSettings {
|
||||
fn from(s: cdk::nuts::nut05::MeltMethodSettings) -> Self {
|
||||
let amountless = match s.options {
|
||||
Some(cdk::nuts::nut05::MeltMethodOptions::Bolt11 { amountless }) => Some(amountless),
|
||||
_ => None,
|
||||
};
|
||||
Self {
|
||||
method: s.method.into(),
|
||||
unit: s.unit.into(),
|
||||
min_amount: s.min_amount.map(Into::into),
|
||||
max_amount: s.max_amount.map(Into::into),
|
||||
amountless,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<MeltMethodSettings> for cdk::nuts::nut05::MeltMethodSettings {
|
||||
type Error = FfiError;
|
||||
|
||||
fn try_from(s: MeltMethodSettings) -> Result<Self, Self::Error> {
|
||||
let options = match (s.method.clone(), s.amountless) {
|
||||
(PaymentMethod::Bolt11, Some(amountless)) => {
|
||||
Some(cdk::nuts::nut05::MeltMethodOptions::Bolt11 { amountless })
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
Ok(Self {
|
||||
method: s.method.into(),
|
||||
unit: s.unit.into(),
|
||||
min_amount: s.min_amount.map(Into::into),
|
||||
max_amount: s.max_amount.map(Into::into),
|
||||
options,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// FFI-compatible Nut05 Settings
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
|
||||
pub struct Nut05Settings {
|
||||
pub methods: Vec<MeltMethodSettings>,
|
||||
pub disabled: bool,
|
||||
}
|
||||
|
||||
impl From<cdk::nuts::nut05::Settings> for Nut05Settings {
|
||||
fn from(s: cdk::nuts::nut05::Settings) -> Self {
|
||||
Self {
|
||||
methods: s.methods.into_iter().map(Into::into).collect(),
|
||||
disabled: s.disabled,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Nut05Settings> for cdk::nuts::nut05::Settings {
|
||||
type Error = FfiError;
|
||||
|
||||
fn try_from(s: Nut05Settings) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
methods: s
|
||||
.methods
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<Result<_, _>>()?,
|
||||
disabled: s.disabled,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// FFI-compatible ProtectedEndpoint (for auth nuts)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
|
||||
pub struct ProtectedEndpoint {
|
||||
/// HTTP method (GET, POST, etc.)
|
||||
pub method: String,
|
||||
/// Endpoint path
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
/// FFI-compatible ClearAuthSettings (NUT-21)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
|
||||
pub struct ClearAuthSettings {
|
||||
/// OpenID Connect discovery URL
|
||||
pub openid_discovery: String,
|
||||
/// OAuth 2.0 client ID
|
||||
pub client_id: String,
|
||||
/// Protected endpoints requiring clear authentication
|
||||
pub protected_endpoints: Vec<ProtectedEndpoint>,
|
||||
}
|
||||
|
||||
/// FFI-compatible BlindAuthSettings (NUT-22)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
|
||||
pub struct BlindAuthSettings {
|
||||
/// Maximum number of blind auth tokens that can be minted per request
|
||||
pub bat_max_mint: u64,
|
||||
/// Protected endpoints requiring blind authentication
|
||||
pub protected_endpoints: Vec<ProtectedEndpoint>,
|
||||
}
|
||||
|
||||
impl From<cdk::nuts::ClearAuthSettings> for ClearAuthSettings {
|
||||
fn from(settings: cdk::nuts::ClearAuthSettings) -> Self {
|
||||
Self {
|
||||
openid_discovery: settings.openid_discovery,
|
||||
client_id: settings.client_id,
|
||||
protected_endpoints: settings
|
||||
.protected_endpoints
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<ClearAuthSettings> for cdk::nuts::ClearAuthSettings {
|
||||
type Error = FfiError;
|
||||
|
||||
fn try_from(settings: ClearAuthSettings) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
openid_discovery: settings.openid_discovery,
|
||||
client_id: settings.client_id,
|
||||
protected_endpoints: settings
|
||||
.protected_endpoints
|
||||
.into_iter()
|
||||
.map(|e| e.try_into())
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<cdk::nuts::BlindAuthSettings> for BlindAuthSettings {
|
||||
fn from(settings: cdk::nuts::BlindAuthSettings) -> Self {
|
||||
Self {
|
||||
bat_max_mint: settings.bat_max_mint,
|
||||
protected_endpoints: settings
|
||||
.protected_endpoints
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<BlindAuthSettings> for cdk::nuts::BlindAuthSettings {
|
||||
type Error = FfiError;
|
||||
|
||||
fn try_from(settings: BlindAuthSettings) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
bat_max_mint: settings.bat_max_mint,
|
||||
protected_endpoints: settings
|
||||
.protected_endpoints
|
||||
.into_iter()
|
||||
.map(|e| e.try_into())
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<cdk::nuts::ProtectedEndpoint> for ProtectedEndpoint {
|
||||
fn from(endpoint: cdk::nuts::ProtectedEndpoint) -> Self {
|
||||
Self {
|
||||
method: match endpoint.method {
|
||||
cdk::nuts::Method::Get => "GET".to_string(),
|
||||
cdk::nuts::Method::Post => "POST".to_string(),
|
||||
},
|
||||
path: endpoint.path.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<ProtectedEndpoint> for cdk::nuts::ProtectedEndpoint {
|
||||
type Error = FfiError;
|
||||
|
||||
fn try_from(endpoint: ProtectedEndpoint) -> Result<Self, Self::Error> {
|
||||
let method = match endpoint.method.as_str() {
|
||||
"GET" => cdk::nuts::Method::Get,
|
||||
"POST" => cdk::nuts::Method::Post,
|
||||
_ => {
|
||||
return Err(FfiError::Generic {
|
||||
msg: format!(
|
||||
"Invalid HTTP method: {}. Only GET and POST are supported",
|
||||
endpoint.method
|
||||
),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
// Convert path string to RoutePath by matching against known paths
|
||||
let route_path = match endpoint.path.as_str() {
|
||||
"/v1/mint/quote/bolt11" => cdk::nuts::RoutePath::MintQuoteBolt11,
|
||||
"/v1/mint/bolt11" => cdk::nuts::RoutePath::MintBolt11,
|
||||
"/v1/melt/quote/bolt11" => cdk::nuts::RoutePath::MeltQuoteBolt11,
|
||||
"/v1/melt/bolt11" => cdk::nuts::RoutePath::MeltBolt11,
|
||||
"/v1/swap" => cdk::nuts::RoutePath::Swap,
|
||||
"/v1/checkstate" => cdk::nuts::RoutePath::Checkstate,
|
||||
"/v1/restore" => cdk::nuts::RoutePath::Restore,
|
||||
"/v1/auth/blind/mint" => cdk::nuts::RoutePath::MintBlindAuth,
|
||||
"/v1/mint/quote/bolt12" => cdk::nuts::RoutePath::MintQuoteBolt12,
|
||||
"/v1/mint/bolt12" => cdk::nuts::RoutePath::MintBolt12,
|
||||
"/v1/melt/quote/bolt12" => cdk::nuts::RoutePath::MeltQuoteBolt12,
|
||||
"/v1/melt/bolt12" => cdk::nuts::RoutePath::MeltBolt12,
|
||||
_ => {
|
||||
return Err(FfiError::Generic {
|
||||
msg: format!("Unknown route path: {}", endpoint.path),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
Ok(cdk::nuts::ProtectedEndpoint::new(method, route_path))
|
||||
}
|
||||
}
|
||||
|
||||
/// FFI-compatible Nuts settings (extended to include NUT-04 and NUT-05 settings)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
|
||||
pub struct Nuts {
|
||||
/// NUT04 Settings
|
||||
pub nut04: Nut04Settings,
|
||||
/// NUT05 Settings
|
||||
pub nut05: Nut05Settings,
|
||||
/// NUT07 Settings - Token state check
|
||||
pub nut07_supported: bool,
|
||||
/// NUT08 Settings - Lightning fee return
|
||||
@@ -1486,6 +1791,10 @@ pub struct Nuts {
|
||||
pub nut14_supported: bool,
|
||||
/// NUT20 Settings - Web sockets
|
||||
pub nut20_supported: bool,
|
||||
/// NUT21 Settings - Clear authentication
|
||||
pub nut21: Option<ClearAuthSettings>,
|
||||
/// NUT22 Settings - Blind authentication
|
||||
pub nut22: Option<BlindAuthSettings>,
|
||||
/// Supported currency units for minting
|
||||
pub mint_units: Vec<CurrencyUnit>,
|
||||
/// Supported currency units for melting
|
||||
@@ -1494,7 +1803,20 @@ pub struct Nuts {
|
||||
|
||||
impl From<cdk::nuts::Nuts> for Nuts {
|
||||
fn from(nuts: cdk::nuts::Nuts) -> Self {
|
||||
let mint_units = nuts
|
||||
.supported_mint_units()
|
||||
.into_iter()
|
||||
.map(|u| u.clone().into())
|
||||
.collect();
|
||||
let melt_units = nuts
|
||||
.supported_melt_units()
|
||||
.into_iter()
|
||||
.map(|u| u.clone().into())
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
nut04: nuts.nut04.clone().into(),
|
||||
nut05: nuts.nut05.clone().into(),
|
||||
nut07_supported: nuts.nut07.supported,
|
||||
nut08_supported: nuts.nut08.supported,
|
||||
nut09_supported: nuts.nut09.supported,
|
||||
@@ -1503,20 +1825,54 @@ impl From<cdk::nuts::Nuts> for Nuts {
|
||||
nut12_supported: nuts.nut12.supported,
|
||||
nut14_supported: nuts.nut14.supported,
|
||||
nut20_supported: nuts.nut20.supported,
|
||||
mint_units: nuts
|
||||
.supported_mint_units()
|
||||
.into_iter()
|
||||
.map(|u| u.clone().into())
|
||||
.collect(),
|
||||
melt_units: nuts
|
||||
.supported_melt_units()
|
||||
.into_iter()
|
||||
.map(|u| u.clone().into())
|
||||
.collect(),
|
||||
nut21: nuts.nut21.map(Into::into),
|
||||
nut22: nuts.nut22.map(Into::into),
|
||||
mint_units,
|
||||
melt_units,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Nuts> for cdk::nuts::Nuts {
|
||||
type Error = FfiError;
|
||||
|
||||
fn try_from(n: Nuts) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
nut04: n.nut04.try_into()?,
|
||||
nut05: n.nut05.try_into()?,
|
||||
nut07: cdk::nuts::nut06::SupportedSettings {
|
||||
supported: n.nut07_supported,
|
||||
},
|
||||
nut08: cdk::nuts::nut06::SupportedSettings {
|
||||
supported: n.nut08_supported,
|
||||
},
|
||||
nut09: cdk::nuts::nut06::SupportedSettings {
|
||||
supported: n.nut09_supported,
|
||||
},
|
||||
nut10: cdk::nuts::nut06::SupportedSettings {
|
||||
supported: n.nut10_supported,
|
||||
},
|
||||
nut11: cdk::nuts::nut06::SupportedSettings {
|
||||
supported: n.nut11_supported,
|
||||
},
|
||||
nut12: cdk::nuts::nut06::SupportedSettings {
|
||||
supported: n.nut12_supported,
|
||||
},
|
||||
nut14: cdk::nuts::nut06::SupportedSettings {
|
||||
supported: n.nut14_supported,
|
||||
},
|
||||
nut15: Default::default(),
|
||||
nut17: Default::default(),
|
||||
nut19: Default::default(),
|
||||
nut20: cdk::nuts::nut06::SupportedSettings {
|
||||
supported: n.nut20_supported,
|
||||
},
|
||||
nut21: n.nut21.map(|s| s.try_into()).transpose()?,
|
||||
nut22: n.nut22.map(|s| s.try_into()).transpose()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Nuts {
|
||||
/// Convert Nuts to JSON string
|
||||
pub fn to_json(&self) -> Result<String, FfiError> {
|
||||
@@ -1588,6 +1944,8 @@ impl From<cdk::nuts::MintInfo> for MintInfo {
|
||||
|
||||
impl From<MintInfo> for cdk::nuts::MintInfo {
|
||||
fn from(info: MintInfo) -> Self {
|
||||
// Convert FFI Nuts back to cdk::nuts::Nuts (best-effort)
|
||||
let nuts_cdk: cdk::nuts::Nuts = info.nuts.clone().try_into().unwrap_or_default();
|
||||
Self {
|
||||
name: info.name,
|
||||
pubkey: info.pubkey.and_then(|p| p.parse().ok()),
|
||||
@@ -1597,7 +1955,7 @@ impl From<MintInfo> for cdk::nuts::MintInfo {
|
||||
contact: info
|
||||
.contact
|
||||
.map(|contacts| contacts.into_iter().map(Into::into).collect()),
|
||||
nuts: cdk::nuts::Nuts::default(), // Simplified conversion
|
||||
nuts: nuts_cdk,
|
||||
icon_url: info.icon_url,
|
||||
urls: info.urls,
|
||||
motd: info.motd,
|
||||
|
||||
Reference in New Issue
Block a user