Skip to main content

cdk_ffi/types/
keys.rs

1//! Key-related FFI types
2
3use std::collections::HashMap;
4
5use serde::{Deserialize, Serialize};
6
7use super::amount::CurrencyUnit;
8use crate::error::FfiError;
9
10/// FFI-compatible KeySetInfo
11#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
12pub struct KeySetInfo {
13    pub id: String,
14    pub unit: CurrencyUnit,
15    pub active: bool,
16    /// Input fee per thousand (ppk)
17    pub input_fee_ppk: u64,
18}
19
20impl From<cdk::nuts::KeySetInfo> for KeySetInfo {
21    fn from(keyset: cdk::nuts::KeySetInfo) -> Self {
22        Self {
23            id: keyset.id.to_string(),
24            unit: keyset.unit.into(),
25            active: keyset.active,
26            input_fee_ppk: keyset.input_fee_ppk,
27        }
28    }
29}
30
31impl TryFrom<KeySetInfo> for cdk::nuts::KeySetInfo {
32    type Error = FfiError;
33
34    fn try_from(keyset: KeySetInfo) -> Result<Self, Self::Error> {
35        use std::str::FromStr;
36
37        Ok(Self {
38            id: cdk::nuts::Id::from_str(&keyset.id)
39                .map_err(|e| FfiError::internal(format!("Invalid keyset ID: {}", e)))?,
40            unit: keyset.unit.into(),
41            active: keyset.active,
42            final_expiry: None,
43            input_fee_ppk: keyset.input_fee_ppk,
44        })
45    }
46}
47
48impl KeySetInfo {
49    /// Convert KeySetInfo to JSON string
50    pub fn to_json(&self) -> Result<String, FfiError> {
51        Ok(serde_json::to_string(self)?)
52    }
53}
54
55/// Decode KeySetInfo from JSON string
56#[uniffi::export]
57pub fn decode_key_set_info(json: String) -> Result<KeySetInfo, FfiError> {
58    Ok(serde_json::from_str(&json)?)
59}
60
61/// Encode KeySetInfo to JSON string
62#[uniffi::export]
63pub fn encode_key_set_info(info: KeySetInfo) -> Result<String, FfiError> {
64    Ok(serde_json::to_string(&info)?)
65}
66
67/// FFI-compatible KeysetFilter
68#[derive(Debug, Clone, Copy, uniffi::Enum)]
69pub enum KeysetFilter {
70    /// Only return active keysets
71    Active,
72    /// Return all keysets (active and inactive)
73    All,
74}
75
76impl From<KeysetFilter> for cdk_common::wallet::KeysetFilter {
77    fn from(filter: KeysetFilter) -> Self {
78        match filter {
79            KeysetFilter::Active => cdk_common::wallet::KeysetFilter::Active,
80            KeysetFilter::All => cdk_common::wallet::KeysetFilter::All,
81        }
82    }
83}
84
85/// FFI-compatible PublicKey
86#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
87#[serde(transparent)]
88pub struct PublicKey {
89    /// Hex-encoded public key
90    pub hex: String,
91}
92
93impl From<cdk::nuts::PublicKey> for PublicKey {
94    fn from(key: cdk::nuts::PublicKey) -> Self {
95        Self {
96            hex: key.to_string(),
97        }
98    }
99}
100
101impl TryFrom<PublicKey> for cdk::nuts::PublicKey {
102    type Error = FfiError;
103
104    fn try_from(key: PublicKey) -> Result<Self, Self::Error> {
105        key.hex
106            .parse()
107            .map_err(|e| FfiError::internal(format!("Invalid public key: {}", e)))
108    }
109}
110
111/// FFI-compatible Keys
112#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
113pub struct Keys {
114    /// Map of amount to public key hex
115    pub keys: HashMap<u64, String>,
116}
117
118impl From<cdk::nuts::Keys> for Keys {
119    fn from(keys: cdk::nuts::Keys) -> Self {
120        Self {
121            keys: keys
122                .keys()
123                .iter()
124                .map(|(amount, pubkey)| (u64::from(*amount), pubkey.to_string()))
125                .collect(),
126        }
127    }
128}
129
130impl TryFrom<Keys> for cdk::nuts::Keys {
131    type Error = FfiError;
132
133    fn try_from(keys: Keys) -> Result<Self, Self::Error> {
134        use std::collections::BTreeMap;
135        use std::str::FromStr;
136
137        // Convert the HashMap to BTreeMap with proper types
138        let mut keys_map = BTreeMap::new();
139        for (amount_u64, pubkey_hex) in keys.keys {
140            let amount = cdk::Amount::from(amount_u64);
141            let pubkey = cdk::nuts::PublicKey::from_str(&pubkey_hex)
142                .map_err(|e| FfiError::internal(format!("Invalid public key: {}", e)))?;
143            keys_map.insert(amount, pubkey);
144        }
145
146        Ok(cdk::nuts::Keys::new(keys_map))
147    }
148}
149
150impl Keys {
151    /// Convert Keys to JSON string
152    pub fn to_json(&self) -> Result<String, FfiError> {
153        Ok(serde_json::to_string(self)?)
154    }
155}
156
157/// Decode Keys from JSON string
158#[uniffi::export]
159pub fn decode_keys(json: String) -> Result<Keys, FfiError> {
160    Ok(serde_json::from_str(&json)?)
161}
162
163/// Encode Keys to JSON string
164#[uniffi::export]
165pub fn encode_keys(keys: Keys) -> Result<String, FfiError> {
166    Ok(serde_json::to_string(&keys)?)
167}
168
169/// FFI-compatible KeySet
170#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
171pub struct KeySet {
172    /// Keyset ID
173    pub id: String,
174    /// Currency unit
175    pub unit: CurrencyUnit,
176    /// Keyset state - indicates whether the mint will sign new outputs with this keyset
177    pub active: Option<bool>,
178    /// Input fee in parts per thousand (ppk) per input spent from this keyset
179    pub input_fee_ppk: u64,
180    /// The keys (map of amount to public key hex)
181    pub keys: HashMap<u64, String>,
182    /// Optional expiry timestamp
183    pub final_expiry: Option<u64>,
184}
185
186impl From<cdk::nuts::KeySet> for KeySet {
187    fn from(keyset: cdk::nuts::KeySet) -> Self {
188        Self {
189            id: keyset.id.to_string(),
190            unit: keyset.unit.into(),
191            active: keyset.active,
192            input_fee_ppk: keyset.input_fee_ppk,
193            keys: keyset
194                .keys
195                .keys()
196                .iter()
197                .map(|(amount, pubkey)| (u64::from(*amount), pubkey.to_string()))
198                .collect(),
199            final_expiry: keyset.final_expiry,
200        }
201    }
202}
203
204impl TryFrom<KeySet> for cdk::nuts::KeySet {
205    type Error = FfiError;
206
207    fn try_from(keyset: KeySet) -> Result<Self, Self::Error> {
208        use std::collections::BTreeMap;
209        use std::str::FromStr;
210
211        // Convert id
212        let id = cdk::nuts::Id::from_str(&keyset.id)
213            .map_err(|e| FfiError::internal(format!("Invalid keyset ID: {}", e)))?;
214
215        // Convert unit
216        let unit: cdk::nuts::CurrencyUnit = keyset.unit.into();
217
218        // Convert keys
219        let mut keys_map = BTreeMap::new();
220        for (amount_u64, pubkey_hex) in keyset.keys {
221            let amount = cdk::Amount::from(amount_u64);
222            let pubkey = cdk::nuts::PublicKey::from_str(&pubkey_hex)
223                .map_err(|e| FfiError::internal(format!("Invalid public key: {}", e)))?;
224            keys_map.insert(amount, pubkey);
225        }
226        let keys = cdk::nuts::Keys::new(keys_map);
227
228        Ok(cdk::nuts::KeySet {
229            id,
230            unit,
231            active: keyset.active,
232            input_fee_ppk: keyset.input_fee_ppk,
233            keys,
234            final_expiry: keyset.final_expiry,
235        })
236    }
237}
238
239impl KeySet {
240    /// Convert KeySet to JSON string
241    pub fn to_json(&self) -> Result<String, FfiError> {
242        Ok(serde_json::to_string(self)?)
243    }
244}
245
246/// Decode KeySet from JSON string
247#[uniffi::export]
248pub fn decode_key_set(json: String) -> Result<KeySet, FfiError> {
249    Ok(serde_json::from_str(&json)?)
250}
251
252/// Encode KeySet to JSON string
253#[uniffi::export]
254pub fn encode_key_set(keyset: KeySet) -> Result<String, FfiError> {
255    Ok(serde_json::to_string(&keyset)?)
256}
257
258/// FFI-compatible Id (for keyset IDs)
259#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)]
260#[serde(transparent)]
261pub struct Id {
262    pub hex: String,
263}
264
265impl From<cdk::nuts::Id> for Id {
266    fn from(id: cdk::nuts::Id) -> Self {
267        Self {
268            hex: id.to_string(),
269        }
270    }
271}
272
273impl TryFrom<Id> for cdk::nuts::Id {
274    type Error = FfiError;
275
276    fn try_from(id: Id) -> Result<Self, Self::Error> {
277        use std::str::FromStr;
278
279        Self::from_str(&id.hex).map_err(|e| FfiError::internal(format!("Invalid ID hex: {}", e)))
280    }
281}