mirror of
https://github.com/aljazceru/cdk.git
synced 2026-02-02 03:35:57 +01:00
Update FFI Database Objects to Records (#1149)
This commit is contained in:
@@ -450,7 +450,9 @@ impl CdkWalletDatabase for WalletDatabaseBridge {
|
||||
.into_iter()
|
||||
.map(|info| {
|
||||
Ok(cdk::types::ProofInfo {
|
||||
proof: info.proof.inner.clone(),
|
||||
proof: info.proof.try_into().map_err(|e: FfiError| {
|
||||
cdk::cdk_database::Error::Database(e.to_string().into())
|
||||
})?,
|
||||
y: info.y.try_into().map_err(|e: FfiError| {
|
||||
cdk::cdk_database::Error::Database(e.to_string().into())
|
||||
})?,
|
||||
|
||||
@@ -175,10 +175,7 @@ impl MultiMintWallet {
|
||||
let proofs = self.inner.list_proofs().await?;
|
||||
let mut proofs_by_mint = HashMap::new();
|
||||
for (mint_url, mint_proofs) in proofs {
|
||||
let ffi_proofs: Vec<Arc<Proof>> = mint_proofs
|
||||
.into_iter()
|
||||
.map(|p| Arc::new(p.into()))
|
||||
.collect();
|
||||
let ffi_proofs: Vec<Proof> = mint_proofs.into_iter().map(|p| p.into()).collect();
|
||||
proofs_by_mint.insert(mint_url.to_string(), ffi_proofs);
|
||||
}
|
||||
Ok(proofs_by_mint)
|
||||
@@ -262,7 +259,7 @@ impl MultiMintWallet {
|
||||
.inner
|
||||
.mint(&cdk_mint_url, "e_id, conditions)
|
||||
.await?;
|
||||
Ok(proofs.into_iter().map(|p| Arc::new(p.into())).collect())
|
||||
Ok(proofs.into_iter().map(|p| p.into()).collect())
|
||||
}
|
||||
|
||||
/// Wait for a mint quote to be paid and automatically mint the proofs
|
||||
@@ -288,7 +285,7 @@ impl MultiMintWallet {
|
||||
timeout_secs,
|
||||
)
|
||||
.await?;
|
||||
Ok(proofs.into_iter().map(|p| Arc::new(p.into())).collect())
|
||||
Ok(proofs.into_iter().map(|p| p.into()).collect())
|
||||
}
|
||||
|
||||
/// Get a melt quote from a specific mint
|
||||
@@ -346,7 +343,7 @@ impl MultiMintWallet {
|
||||
|
||||
let result = self.inner.swap(amount.map(Into::into), conditions).await?;
|
||||
|
||||
Ok(result.map(|proofs| proofs.into_iter().map(|p| Arc::new(p.into())).collect()))
|
||||
Ok(result.map(|proofs| proofs.into_iter().map(|p| p.into()).collect()))
|
||||
}
|
||||
|
||||
/// List transactions from all mints
|
||||
@@ -437,7 +434,7 @@ impl MultiMintWallet {
|
||||
.inner
|
||||
.mint_blind_auth(&cdk_mint_url, amount.into())
|
||||
.await?;
|
||||
Ok(proofs.into_iter().map(|p| Arc::new(p.into())).collect())
|
||||
Ok(proofs.into_iter().map(|p| p.into()).collect())
|
||||
}
|
||||
|
||||
/// Get unspent auth proofs for a specific mint
|
||||
@@ -556,4 +553,4 @@ impl From<MultiMintSendOptions> for CdkMultiMintSendOptions {
|
||||
pub type BalanceMap = HashMap<String, Amount>;
|
||||
|
||||
/// Type alias for proofs by mint URL
|
||||
pub type ProofsByMint = HashMap<String, Vec<Arc<Proof>>>;
|
||||
pub type ProofsByMint = HashMap<String, Vec<Proof>>;
|
||||
|
||||
@@ -237,7 +237,7 @@ impl WalletDatabase for WalletPostgresDatabase {
|
||||
.into_iter()
|
||||
.map(|info| {
|
||||
Ok::<cdk::types::ProofInfo, FfiError>(cdk::types::ProofInfo {
|
||||
proof: info.proof.inner.clone(),
|
||||
proof: info.proof.try_into()?,
|
||||
y: info.y.try_into()?,
|
||||
mint_url: info.mint_url.try_into()?,
|
||||
state: info.state.into(),
|
||||
|
||||
@@ -272,7 +272,7 @@ impl WalletDatabase for WalletSqliteDatabase {
|
||||
.into_iter()
|
||||
.map(|info| {
|
||||
Ok::<cdk::types::ProofInfo, FfiError>(cdk::types::ProofInfo {
|
||||
proof: info.proof.inner.clone(),
|
||||
proof: info.proof.try_into()?,
|
||||
y: info.y.try_into()?,
|
||||
mint_url: info.mint_url.try_into()?,
|
||||
state: info.state.into(),
|
||||
|
||||
@@ -75,10 +75,7 @@ impl Token {
|
||||
// For now, return empty keysets to get all proofs
|
||||
let empty_keysets = vec![];
|
||||
let proofs = self.inner.proofs(&empty_keysets)?;
|
||||
Ok(proofs
|
||||
.into_iter()
|
||||
.map(|p| std::sync::Arc::new(p.into()))
|
||||
.collect())
|
||||
Ok(proofs.into_iter().map(|p| p.into()).collect())
|
||||
}
|
||||
|
||||
/// Convert token to raw bytes
|
||||
|
||||
@@ -44,78 +44,126 @@ impl From<ProofState> for CdkState {
|
||||
}
|
||||
|
||||
/// FFI-compatible Proof
|
||||
#[derive(Debug, uniffi::Object)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
|
||||
pub struct Proof {
|
||||
pub(crate) inner: cdk::nuts::Proof,
|
||||
/// Proof amount
|
||||
pub amount: Amount,
|
||||
/// Secret (as string)
|
||||
pub secret: String,
|
||||
/// Unblinded signature C (as hex string)
|
||||
pub c: String,
|
||||
/// Keyset ID (as hex string)
|
||||
pub keyset_id: String,
|
||||
/// Optional witness
|
||||
pub witness: Option<Witness>,
|
||||
/// Optional DLEQ proof
|
||||
pub dleq: Option<ProofDleq>,
|
||||
}
|
||||
|
||||
impl From<cdk::nuts::Proof> for Proof {
|
||||
fn from(proof: cdk::nuts::Proof) -> Self {
|
||||
Self { inner: proof }
|
||||
Self {
|
||||
amount: proof.amount.into(),
|
||||
secret: proof.secret.to_string(),
|
||||
c: proof.c.to_string(),
|
||||
keyset_id: proof.keyset_id.to_string(),
|
||||
witness: proof.witness.map(|w| w.into()),
|
||||
dleq: proof.dleq.map(|d| d.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Proof> for cdk::nuts::Proof {
|
||||
fn from(proof: Proof) -> Self {
|
||||
proof.inner
|
||||
}
|
||||
}
|
||||
impl TryFrom<Proof> for cdk::nuts::Proof {
|
||||
type Error = FfiError;
|
||||
|
||||
#[uniffi::export]
|
||||
impl Proof {
|
||||
/// Get the amount
|
||||
pub fn amount(&self) -> Amount {
|
||||
self.inner.amount.into()
|
||||
}
|
||||
fn try_from(proof: Proof) -> Result<Self, Self::Error> {
|
||||
use std::str::FromStr;
|
||||
|
||||
/// Get the secret as string
|
||||
pub fn secret(&self) -> String {
|
||||
self.inner.secret.to_string()
|
||||
}
|
||||
|
||||
/// Get the unblinded signature (C) as string
|
||||
pub fn c(&self) -> String {
|
||||
self.inner.c.to_string()
|
||||
}
|
||||
|
||||
/// Get the keyset ID as string
|
||||
pub fn keyset_id(&self) -> String {
|
||||
self.inner.keyset_id.to_string()
|
||||
}
|
||||
|
||||
/// Get the witness
|
||||
pub fn witness(&self) -> Option<Witness> {
|
||||
self.inner.witness.as_ref().map(|w| w.clone().into())
|
||||
}
|
||||
|
||||
/// Check if proof is active with given keyset IDs
|
||||
pub fn is_active(&self, active_keyset_ids: Vec<String>) -> bool {
|
||||
use cdk::nuts::Id;
|
||||
let ids: Vec<Id> = active_keyset_ids
|
||||
.into_iter()
|
||||
.filter_map(|id| Id::from_str(&id).ok())
|
||||
.collect();
|
||||
self.inner.is_active(&ids)
|
||||
}
|
||||
|
||||
/// Get the Y value (hash_to_curve of secret)
|
||||
pub fn y(&self) -> Result<String, FfiError> {
|
||||
Ok(self.inner.y()?.to_string())
|
||||
Ok(Self {
|
||||
amount: proof.amount.into(),
|
||||
secret: cdk::secret::Secret::from_str(&proof.secret)
|
||||
.map_err(|e| FfiError::Serialization { msg: e.to_string() })?,
|
||||
c: cdk::nuts::PublicKey::from_str(&proof.c)
|
||||
.map_err(|e| FfiError::InvalidCryptographicKey { msg: e.to_string() })?,
|
||||
keyset_id: Id::from_str(&proof.keyset_id)
|
||||
.map_err(|e| FfiError::Serialization { msg: e.to_string() })?,
|
||||
witness: proof.witness.map(|w| w.into()),
|
||||
dleq: proof.dleq.map(|d| d.into()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the DLEQ proof if present
|
||||
pub fn dleq(&self) -> Option<ProofDleq> {
|
||||
self.inner.dleq.as_ref().map(|d| d.clone().into())
|
||||
}
|
||||
/// Get the Y value (hash_to_curve of secret) for a proof
|
||||
#[uniffi::export]
|
||||
pub fn proof_y(proof: &Proof) -> Result<String, FfiError> {
|
||||
// Convert to CDK proof to calculate Y
|
||||
let cdk_proof: cdk::nuts::Proof = proof.clone().try_into()?;
|
||||
Ok(cdk_proof.y()?.to_string())
|
||||
}
|
||||
|
||||
/// Check if proof has DLEQ proof
|
||||
pub fn has_dleq(&self) -> bool {
|
||||
self.inner.dleq.is_some()
|
||||
/// Check if proof is active with given keyset IDs
|
||||
#[uniffi::export]
|
||||
pub fn proof_is_active(proof: &Proof, active_keyset_ids: Vec<String>) -> bool {
|
||||
use cdk::nuts::Id;
|
||||
let ids: Vec<Id> = active_keyset_ids
|
||||
.into_iter()
|
||||
.filter_map(|id| Id::from_str(&id).ok())
|
||||
.collect();
|
||||
|
||||
// A proof is active if its keyset_id is in the active list
|
||||
if let Ok(keyset_id) = Id::from_str(&proof.keyset_id) {
|
||||
ids.contains(&keyset_id)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if proof has DLEQ proof
|
||||
#[uniffi::export]
|
||||
pub fn proof_has_dleq(proof: &Proof) -> bool {
|
||||
proof.dleq.is_some()
|
||||
}
|
||||
|
||||
/// Verify HTLC witness on a proof
|
||||
#[uniffi::export]
|
||||
pub fn proof_verify_htlc(proof: &Proof) -> Result<(), FfiError> {
|
||||
let cdk_proof: cdk::nuts::Proof = proof.clone().try_into()?;
|
||||
cdk_proof
|
||||
.verify_htlc()
|
||||
.map_err(|e| FfiError::Generic { msg: e.to_string() })
|
||||
}
|
||||
|
||||
/// Verify DLEQ proof on a proof
|
||||
#[uniffi::export]
|
||||
pub fn proof_verify_dleq(
|
||||
proof: &Proof,
|
||||
mint_pubkey: super::keys::PublicKey,
|
||||
) -> Result<(), FfiError> {
|
||||
let cdk_proof: cdk::nuts::Proof = proof.clone().try_into()?;
|
||||
let cdk_pubkey: cdk::nuts::PublicKey = mint_pubkey.try_into()?;
|
||||
cdk_proof
|
||||
.verify_dleq(cdk_pubkey)
|
||||
.map_err(|e| FfiError::Generic { msg: e.to_string() })
|
||||
}
|
||||
|
||||
/// Sign a P2PK proof with a secret key, returning a new signed proof
|
||||
#[uniffi::export]
|
||||
pub fn proof_sign_p2pk(proof: Proof, secret_key_hex: String) -> Result<Proof, FfiError> {
|
||||
let mut cdk_proof: cdk::nuts::Proof = proof.try_into()?;
|
||||
let secret_key = cdk::nuts::SecretKey::from_hex(&secret_key_hex)
|
||||
.map_err(|e| FfiError::InvalidCryptographicKey { msg: e.to_string() })?;
|
||||
|
||||
cdk_proof
|
||||
.sign_p2pk(secret_key)
|
||||
.map_err(|e| FfiError::Generic { msg: e.to_string() })?;
|
||||
|
||||
Ok(cdk_proof.into())
|
||||
}
|
||||
|
||||
/// FFI-compatible Proofs (vector of Proof)
|
||||
pub type Proofs = Vec<std::sync::Arc<Proof>>;
|
||||
pub type Proofs = Vec<Proof>;
|
||||
|
||||
/// FFI-compatible DLEQ proof for proofs
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
|
||||
@@ -175,9 +223,12 @@ impl From<BlindSignatureDleq> for cdk::nuts::BlindSignatureDleq {
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper functions for Proofs
|
||||
/// Helper function to calculate total amount of proofs
|
||||
#[uniffi::export]
|
||||
pub fn proofs_total_amount(proofs: &Proofs) -> Result<Amount, FfiError> {
|
||||
let cdk_proofs: Vec<cdk::nuts::Proof> = proofs.iter().map(|p| p.inner.clone()).collect();
|
||||
let cdk_proofs: Result<Vec<cdk::nuts::Proof>, _> =
|
||||
proofs.iter().map(|p| p.clone().try_into()).collect();
|
||||
let cdk_proofs = cdk_proofs?;
|
||||
use cdk::nuts::ProofsMethods;
|
||||
Ok(cdk_proofs.total_amount()?.into())
|
||||
}
|
||||
@@ -420,7 +471,7 @@ impl TryFrom<SpendingConditions> for cdk::nuts::SpendingConditions {
|
||||
#[derive(Debug, Clone, uniffi::Record)]
|
||||
pub struct ProofInfo {
|
||||
/// Proof
|
||||
pub proof: std::sync::Arc<Proof>,
|
||||
pub proof: Proof,
|
||||
/// Y value (hash_to_curve of secret)
|
||||
pub y: super::keys::PublicKey,
|
||||
/// Mint URL
|
||||
@@ -436,7 +487,7 @@ pub struct ProofInfo {
|
||||
impl From<cdk::types::ProofInfo> for ProofInfo {
|
||||
fn from(info: cdk::types::ProofInfo) -> Self {
|
||||
Self {
|
||||
proof: std::sync::Arc::new(info.proof.into()),
|
||||
proof: info.proof.into(),
|
||||
y: info.y.into(),
|
||||
mint_url: info.mint_url.into(),
|
||||
state: info.state.into(),
|
||||
@@ -458,7 +509,7 @@ pub fn decode_proof_info(json: String) -> Result<ProofInfo, FfiError> {
|
||||
pub fn encode_proof_info(info: ProofInfo) -> Result<String, FfiError> {
|
||||
// Convert to cdk::types::ProofInfo for serialization
|
||||
let cdk_info = cdk::types::ProofInfo {
|
||||
proof: info.proof.inner.clone(),
|
||||
proof: info.proof.try_into()?,
|
||||
y: info.y.try_into()?,
|
||||
mint_url: info.mint_url.try_into()?,
|
||||
state: info.state.into(),
|
||||
|
||||
@@ -77,30 +77,25 @@ impl TryFrom<MintQuote> for cdk::wallet::MintQuote {
|
||||
}
|
||||
}
|
||||
|
||||
impl MintQuote {
|
||||
/// Get total amount (amount + fees)
|
||||
pub fn total_amount(&self) -> Amount {
|
||||
if let Some(amount) = self.amount {
|
||||
Amount::new(amount.value + self.amount_paid.value - self.amount_issued.value)
|
||||
} else {
|
||||
Amount::zero()
|
||||
}
|
||||
}
|
||||
/// Get total amount for a mint quote (amount paid)
|
||||
#[uniffi::export]
|
||||
pub fn mint_quote_total_amount(quote: &MintQuote) -> Result<Amount, FfiError> {
|
||||
let cdk_quote: cdk::wallet::MintQuote = quote.clone().try_into()?;
|
||||
Ok(cdk_quote.total_amount().into())
|
||||
}
|
||||
|
||||
/// Check if quote is expired
|
||||
pub fn is_expired(&self, current_time: u64) -> bool {
|
||||
current_time > self.expiry
|
||||
}
|
||||
/// Check if mint quote is expired
|
||||
#[uniffi::export]
|
||||
pub fn mint_quote_is_expired(quote: &MintQuote, current_time: u64) -> Result<bool, FfiError> {
|
||||
let cdk_quote: cdk::wallet::MintQuote = quote.clone().try_into()?;
|
||||
Ok(cdk_quote.is_expired(current_time))
|
||||
}
|
||||
|
||||
/// Get amount that can be minted
|
||||
pub fn amount_mintable(&self) -> Amount {
|
||||
Amount::new(self.amount_paid.value - self.amount_issued.value)
|
||||
}
|
||||
|
||||
/// Convert MintQuote to JSON string
|
||||
pub fn to_json(&self) -> Result<String, FfiError> {
|
||||
Ok(serde_json::to_string(self)?)
|
||||
}
|
||||
/// Get amount that can be minted from a mint quote
|
||||
#[uniffi::export]
|
||||
pub fn mint_quote_amount_mintable(quote: &MintQuote) -> Result<Amount, FfiError> {
|
||||
let cdk_quote: cdk::wallet::MintQuote = quote.clone().try_into()?;
|
||||
Ok(cdk_quote.amount_mintable().into())
|
||||
}
|
||||
|
||||
/// Decode MintQuote from JSON string
|
||||
@@ -117,7 +112,7 @@ pub fn encode_mint_quote(quote: MintQuote) -> Result<String, FfiError> {
|
||||
}
|
||||
|
||||
/// FFI-compatible MintQuoteBolt11Response
|
||||
#[derive(Debug, uniffi::Object)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
|
||||
pub struct MintQuoteBolt11Response {
|
||||
/// Quote ID
|
||||
pub quote: String,
|
||||
@@ -149,46 +144,8 @@ impl From<cdk::nuts::MintQuoteBolt11Response<String>> for MintQuoteBolt11Respons
|
||||
}
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
impl MintQuoteBolt11Response {
|
||||
/// Get quote ID
|
||||
pub fn quote(&self) -> String {
|
||||
self.quote.clone()
|
||||
}
|
||||
|
||||
/// Get request string
|
||||
pub fn request(&self) -> String {
|
||||
self.request.clone()
|
||||
}
|
||||
|
||||
/// Get state
|
||||
pub fn state(&self) -> QuoteState {
|
||||
self.state.clone()
|
||||
}
|
||||
|
||||
/// Get expiry
|
||||
pub fn expiry(&self) -> Option<u64> {
|
||||
self.expiry
|
||||
}
|
||||
|
||||
/// Get amount
|
||||
pub fn amount(&self) -> Option<Amount> {
|
||||
self.amount
|
||||
}
|
||||
|
||||
/// Get unit
|
||||
pub fn unit(&self) -> Option<CurrencyUnit> {
|
||||
self.unit.clone()
|
||||
}
|
||||
|
||||
/// Get pubkey
|
||||
pub fn pubkey(&self) -> Option<String> {
|
||||
self.pubkey.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// FFI-compatible MeltQuoteBolt11Response
|
||||
#[derive(Debug, uniffi::Object)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
|
||||
pub struct MeltQuoteBolt11Response {
|
||||
/// Quote ID
|
||||
pub quote: String,
|
||||
@@ -222,50 +179,6 @@ impl From<cdk::nuts::MeltQuoteBolt11Response<String>> for MeltQuoteBolt11Respons
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
impl MeltQuoteBolt11Response {
|
||||
/// Get quote ID
|
||||
pub fn quote(&self) -> String {
|
||||
self.quote.clone()
|
||||
}
|
||||
|
||||
/// Get amount
|
||||
pub fn amount(&self) -> Amount {
|
||||
self.amount
|
||||
}
|
||||
|
||||
/// Get fee reserve
|
||||
pub fn fee_reserve(&self) -> Amount {
|
||||
self.fee_reserve
|
||||
}
|
||||
|
||||
/// Get state
|
||||
pub fn state(&self) -> QuoteState {
|
||||
self.state.clone()
|
||||
}
|
||||
|
||||
/// Get expiry
|
||||
pub fn expiry(&self) -> u64 {
|
||||
self.expiry
|
||||
}
|
||||
|
||||
/// Get payment preimage
|
||||
pub fn payment_preimage(&self) -> Option<String> {
|
||||
self.payment_preimage.clone()
|
||||
}
|
||||
|
||||
/// Get request
|
||||
pub fn request(&self) -> Option<String> {
|
||||
self.request.clone()
|
||||
}
|
||||
|
||||
/// Get unit
|
||||
pub fn unit(&self) -> Option<CurrencyUnit> {
|
||||
self.unit.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// FFI-compatible PaymentMethod
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, uniffi::Enum)]
|
||||
pub enum PaymentMethod {
|
||||
|
||||
@@ -139,13 +139,9 @@ pub enum NotificationPayload {
|
||||
/// Proof state update
|
||||
ProofState { proof_states: Vec<ProofStateUpdate> },
|
||||
/// Mint quote update
|
||||
MintQuoteUpdate {
|
||||
quote: std::sync::Arc<MintQuoteBolt11Response>,
|
||||
},
|
||||
MintQuoteUpdate { quote: MintQuoteBolt11Response },
|
||||
/// Melt quote update
|
||||
MeltQuoteUpdate {
|
||||
quote: std::sync::Arc<MeltQuoteBolt11Response>,
|
||||
},
|
||||
MeltQuoteUpdate { quote: MeltQuoteBolt11Response },
|
||||
}
|
||||
|
||||
impl From<MintEvent<String>> for NotificationPayload {
|
||||
@@ -156,12 +152,12 @@ impl From<MintEvent<String>> for NotificationPayload {
|
||||
},
|
||||
cdk::nuts::NotificationPayload::MintQuoteBolt11Response(quote_resp) => {
|
||||
NotificationPayload::MintQuoteUpdate {
|
||||
quote: std::sync::Arc::new(quote_resp.into()),
|
||||
quote: quote_resp.into(),
|
||||
}
|
||||
}
|
||||
cdk::nuts::NotificationPayload::MeltQuoteBolt11Response(quote_resp) => {
|
||||
NotificationPayload::MeltQuoteUpdate {
|
||||
quote: std::sync::Arc::new(quote_resp.into()),
|
||||
quote: quote_resp.into(),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
||||
@@ -106,6 +106,21 @@ pub fn encode_transaction(transaction: Transaction) -> Result<String, FfiError>
|
||||
Ok(serde_json::to_string(&transaction)?)
|
||||
}
|
||||
|
||||
/// Check if a transaction matches the given filter conditions
|
||||
#[uniffi::export]
|
||||
pub fn transaction_matches_conditions(
|
||||
transaction: &Transaction,
|
||||
mint_url: Option<MintUrl>,
|
||||
direction: Option<TransactionDirection>,
|
||||
unit: Option<CurrencyUnit>,
|
||||
) -> Result<bool, FfiError> {
|
||||
let cdk_transaction: cdk::wallet::types::Transaction = transaction.clone().try_into()?;
|
||||
let cdk_mint_url = mint_url.map(|url| url.try_into()).transpose()?;
|
||||
let cdk_direction = direction.map(Into::into);
|
||||
let cdk_unit = unit.map(Into::into);
|
||||
Ok(cdk_transaction.matches_conditions(&cdk_mint_url, &cdk_direction, &cdk_unit))
|
||||
}
|
||||
|
||||
/// FFI-compatible TransactionDirection
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, uniffi::Enum)]
|
||||
pub enum TransactionDirection {
|
||||
@@ -163,7 +178,9 @@ impl TransactionId {
|
||||
|
||||
/// Create from proofs
|
||||
pub fn from_proofs(proofs: &Proofs) -> Result<Self, FfiError> {
|
||||
let cdk_proofs: Vec<cdk::nuts::Proof> = proofs.iter().map(|p| p.inner.clone()).collect();
|
||||
let cdk_proofs: Result<Vec<cdk::nuts::Proof>, _> =
|
||||
proofs.iter().map(|p| p.clone().try_into()).collect();
|
||||
let cdk_proofs = cdk_proofs?;
|
||||
let id = cdk::wallet::types::TransactionId::from_proofs(cdk_proofs)?;
|
||||
Ok(Self {
|
||||
hex: id.to_string(),
|
||||
|
||||
@@ -314,7 +314,7 @@ impl From<cdk::wallet::PreparedSend> for PreparedSend {
|
||||
.proofs()
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|p| std::sync::Arc::new(p.into()))
|
||||
.map(|p| p.into())
|
||||
.collect();
|
||||
Self {
|
||||
inner: Mutex::new(Some(prepared)),
|
||||
@@ -421,12 +421,9 @@ impl From<cdk::types::Melted> for Melted {
|
||||
Self {
|
||||
state: melted.state.into(),
|
||||
preimage: melted.preimage,
|
||||
change: melted.change.map(|proofs| {
|
||||
proofs
|
||||
.into_iter()
|
||||
.map(|p| std::sync::Arc::new(p.into()))
|
||||
.collect()
|
||||
}),
|
||||
change: melted
|
||||
.change
|
||||
.map(|proofs| proofs.into_iter().map(|p| p.into()).collect()),
|
||||
amount: melted.amount.into(),
|
||||
fee_paid: melted.fee_paid.into(),
|
||||
}
|
||||
|
||||
@@ -119,8 +119,9 @@ impl Wallet {
|
||||
options: ReceiveOptions,
|
||||
memo: Option<String>,
|
||||
) -> Result<Amount, FfiError> {
|
||||
let cdk_proofs: Vec<cdk::nuts::Proof> =
|
||||
proofs.into_iter().map(|p| p.inner.clone()).collect();
|
||||
let cdk_proofs: Result<Vec<cdk::nuts::Proof>, _> =
|
||||
proofs.into_iter().map(|p| p.try_into()).collect();
|
||||
let cdk_proofs = cdk_proofs?;
|
||||
|
||||
let amount = self
|
||||
.inner
|
||||
@@ -166,10 +167,7 @@ impl Wallet {
|
||||
.inner
|
||||
.mint("e_id, amount_split_target.into(), conditions)
|
||||
.await?;
|
||||
Ok(proofs
|
||||
.into_iter()
|
||||
.map(|p| std::sync::Arc::new(p.into()))
|
||||
.collect())
|
||||
Ok(proofs.into_iter().map(|p| p.into()).collect())
|
||||
}
|
||||
|
||||
/// Get a melt quote
|
||||
@@ -222,10 +220,7 @@ impl Wallet {
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(proofs
|
||||
.into_iter()
|
||||
.map(|p| std::sync::Arc::new(p.into()))
|
||||
.collect())
|
||||
Ok(proofs.into_iter().map(|p| p.into()).collect())
|
||||
}
|
||||
|
||||
/// Get a quote for a bolt12 melt
|
||||
@@ -248,8 +243,9 @@ impl Wallet {
|
||||
spending_conditions: Option<SpendingConditions>,
|
||||
include_fees: bool,
|
||||
) -> Result<Option<Proofs>, FfiError> {
|
||||
let cdk_proofs: Vec<cdk::nuts::Proof> =
|
||||
input_proofs.into_iter().map(|p| p.inner.clone()).collect();
|
||||
let cdk_proofs: Result<Vec<cdk::nuts::Proof>, _> =
|
||||
input_proofs.into_iter().map(|p| p.try_into()).collect();
|
||||
let cdk_proofs = cdk_proofs?;
|
||||
|
||||
// Convert spending conditions if provided
|
||||
let conditions = spending_conditions.map(|sc| sc.try_into()).transpose()?;
|
||||
@@ -265,12 +261,7 @@ impl Wallet {
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(result.map(|proofs| {
|
||||
proofs
|
||||
.into_iter()
|
||||
.map(|p| std::sync::Arc::new(p.into()))
|
||||
.collect()
|
||||
}))
|
||||
Ok(result.map(|proofs| proofs.into_iter().map(|p| p.into()).collect()))
|
||||
}
|
||||
|
||||
/// Get proofs by states
|
||||
@@ -291,7 +282,7 @@ impl Wallet {
|
||||
};
|
||||
|
||||
for proof in proofs {
|
||||
all_proofs.push(std::sync::Arc::new(proof.into()));
|
||||
all_proofs.push(proof.into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,8 +291,9 @@ impl Wallet {
|
||||
|
||||
/// Check if proofs are spent
|
||||
pub async fn check_proofs_spent(&self, proofs: Proofs) -> Result<Vec<bool>, FfiError> {
|
||||
let cdk_proofs: Vec<cdk::nuts::Proof> =
|
||||
proofs.into_iter().map(|p| p.inner.clone()).collect();
|
||||
let cdk_proofs: Result<Vec<cdk::nuts::Proof>, _> =
|
||||
proofs.into_iter().map(|p| p.try_into()).collect();
|
||||
let cdk_proofs = cdk_proofs?;
|
||||
|
||||
let proof_states = self.inner.check_proofs_spent(cdk_proofs).await?;
|
||||
// Convert ProofState to bool (spent = true, unspent = false)
|
||||
@@ -382,7 +374,9 @@ impl Wallet {
|
||||
|
||||
/// Reclaim unspent proofs (mark them as unspent in the database)
|
||||
pub async fn reclaim_unspent(&self, proofs: Proofs) -> Result<(), FfiError> {
|
||||
let cdk_proofs: Vec<cdk::nuts::Proof> = proofs.iter().map(|p| p.inner.clone()).collect();
|
||||
let cdk_proofs: Result<Vec<cdk::nuts::Proof>, _> =
|
||||
proofs.iter().map(|p| p.clone().try_into()).collect();
|
||||
let cdk_proofs = cdk_proofs?;
|
||||
self.inner.reclaim_unspent(cdk_proofs).await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -401,9 +395,11 @@ impl Wallet {
|
||||
) -> Result<Amount, FfiError> {
|
||||
let id = cdk::nuts::Id::from_str(&keyset_id)
|
||||
.map_err(|e| FfiError::Generic { msg: e.to_string() })?;
|
||||
let fee_and_amounts = self.inner.get_keyset_fees_and_amounts_by_id(id).await?;
|
||||
let total_fee = (proof_count as u64 * fee_and_amounts.fee()) / 1000; // fee is per thousand
|
||||
Ok(Amount::new(total_fee))
|
||||
let fee = self
|
||||
.inner
|
||||
.get_keyset_count_fee(&id, proof_count as u64)
|
||||
.await?;
|
||||
Ok(fee.into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -453,10 +449,7 @@ impl Wallet {
|
||||
/// Mint blind auth tokens
|
||||
pub async fn mint_blind_auth(&self, amount: Amount) -> Result<Proofs, FfiError> {
|
||||
let proofs = self.inner.mint_blind_auth(amount.into()).await?;
|
||||
Ok(proofs
|
||||
.into_iter()
|
||||
.map(|p| std::sync::Arc::new(p.into()))
|
||||
.collect())
|
||||
Ok(proofs.into_iter().map(|p| p.into()).collect())
|
||||
}
|
||||
|
||||
/// Get unspent auth proofs
|
||||
|
||||
@@ -18,7 +18,7 @@ use std::time::Duration;
|
||||
|
||||
use bip39::Mnemonic;
|
||||
use cdk_ffi::sqlite::WalletSqliteDatabase;
|
||||
use cdk_ffi::types::{Amount, CurrencyUnit, QuoteState, SplitTarget};
|
||||
use cdk_ffi::types::{encode_mint_quote, Amount, CurrencyUnit, QuoteState, SplitTarget};
|
||||
use cdk_ffi::wallet::Wallet as FfiWallet;
|
||||
use cdk_ffi::WalletConfig;
|
||||
use cdk_integration_tests::{get_mint_url_from_env, pay_if_regtest};
|
||||
@@ -157,7 +157,7 @@ async fn test_ffi_full_minting_flow() {
|
||||
);
|
||||
|
||||
// Calculate total amount of minted proofs
|
||||
let total_minted: u64 = mint_result.iter().map(|proof| proof.amount().value).sum();
|
||||
let total_minted: u64 = mint_result.iter().map(|proof| proof.amount.value).sum();
|
||||
assert_eq!(
|
||||
total_minted, mint_amount.value,
|
||||
"Total minted amount should equal requested amount"
|
||||
@@ -166,14 +166,11 @@ async fn test_ffi_full_minting_flow() {
|
||||
// Verify each proof has valid properties
|
||||
for proof in &mint_result {
|
||||
assert!(
|
||||
proof.amount().value > 0,
|
||||
proof.amount.value > 0,
|
||||
"Each proof should have positive amount"
|
||||
);
|
||||
assert!(
|
||||
!proof.secret().is_empty(),
|
||||
"Each proof should have a secret"
|
||||
);
|
||||
assert!(!proof.c().is_empty(), "Each proof should have a C value");
|
||||
assert!(!proof.secret.is_empty(), "Each proof should have a secret");
|
||||
assert!(!proof.c.is_empty(), "Each proof should have a C value");
|
||||
}
|
||||
|
||||
// Step 4: Verify wallet balance after minting
|
||||
@@ -235,7 +232,7 @@ async fn test_ffi_mint_quote_creation() {
|
||||
);
|
||||
|
||||
// Test quote JSON serialization (useful for bindings that need JSON)
|
||||
let quote_json = quote.to_json().expect("Quote should serialize to JSON");
|
||||
let quote_json = encode_mint_quote(quote.clone()).expect("Quote should serialize to JSON");
|
||||
assert!(!quote_json.is_empty(), "Quote JSON should not be empty");
|
||||
|
||||
println!(
|
||||
|
||||
Reference in New Issue
Block a user