lib.rs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. //! CDK FFI Bindings
  2. //!
  3. //! UniFFI bindings for the CDK Wallet and related types.
  4. #![warn(clippy::unused_async)]
  5. pub mod database;
  6. pub mod error;
  7. pub mod multi_mint_wallet;
  8. #[cfg(feature = "postgres")]
  9. pub mod postgres;
  10. pub mod sqlite;
  11. pub mod token;
  12. pub mod types;
  13. pub mod wallet;
  14. pub use database::*;
  15. pub use error::*;
  16. pub use multi_mint_wallet::*;
  17. pub use types::*;
  18. pub use wallet::*;
  19. uniffi::setup_scaffolding!();
  20. #[cfg(test)]
  21. mod tests {
  22. use super::*;
  23. #[test]
  24. fn test_amount_conversion() {
  25. let amount = Amount::new(1000);
  26. assert_eq!(amount.value, 1000);
  27. assert!(!amount.is_zero());
  28. let zero = Amount::zero();
  29. assert!(zero.is_zero());
  30. }
  31. #[test]
  32. fn test_currency_unit_conversion() {
  33. use cdk::nuts::CurrencyUnit as CdkCurrencyUnit;
  34. let unit = CurrencyUnit::Sat;
  35. let cdk_unit: CdkCurrencyUnit = unit.into();
  36. let back: CurrencyUnit = cdk_unit.into();
  37. assert_eq!(back, CurrencyUnit::Sat);
  38. }
  39. #[test]
  40. fn test_mint_url_creation() {
  41. let url = MintUrl::new("https://mint.example.com".to_string());
  42. assert!(url.is_ok());
  43. let invalid_url = MintUrl::new("not-a-url".to_string());
  44. assert!(invalid_url.is_err());
  45. }
  46. #[test]
  47. fn test_send_options_default() {
  48. let options = SendOptions::default();
  49. assert!(options.memo.is_none());
  50. assert!(options.conditions.is_none());
  51. assert!(matches!(options.amount_split_target, SplitTarget::None));
  52. assert!(matches!(options.send_kind, SendKind::OnlineExact));
  53. assert!(!options.include_fee);
  54. assert!(options.max_proofs.is_none());
  55. assert!(options.metadata.is_empty());
  56. }
  57. #[test]
  58. fn test_receive_options_default() {
  59. let options = ReceiveOptions::default();
  60. assert!(matches!(options.amount_split_target, SplitTarget::None));
  61. assert!(options.p2pk_signing_keys.is_empty());
  62. assert!(options.preimages.is_empty());
  63. assert!(options.metadata.is_empty());
  64. }
  65. #[test]
  66. fn test_send_memo() {
  67. let memo_text = "Test memo".to_string();
  68. let memo = SendMemo {
  69. memo: memo_text.clone(),
  70. include_memo: true,
  71. };
  72. assert_eq!(memo.memo, memo_text);
  73. assert!(memo.include_memo);
  74. }
  75. #[test]
  76. fn test_split_target_variants() {
  77. let split_none = SplitTarget::None;
  78. assert!(matches!(split_none, SplitTarget::None));
  79. let amount = Amount::new(1000);
  80. let split_value = SplitTarget::Value { amount };
  81. assert!(matches!(split_value, SplitTarget::Value { .. }));
  82. let amounts = vec![Amount::new(100), Amount::new(200)];
  83. let split_values = SplitTarget::Values { amounts };
  84. assert!(matches!(split_values, SplitTarget::Values { .. }));
  85. }
  86. #[test]
  87. fn test_send_kind_variants() {
  88. let online_exact = SendKind::OnlineExact;
  89. assert!(matches!(online_exact, SendKind::OnlineExact));
  90. let tolerance = Amount::new(50);
  91. let online_tolerance = SendKind::OnlineTolerance { tolerance };
  92. assert!(matches!(online_tolerance, SendKind::OnlineTolerance { .. }));
  93. let offline_exact = SendKind::OfflineExact;
  94. assert!(matches!(offline_exact, SendKind::OfflineExact));
  95. let offline_tolerance = SendKind::OfflineTolerance { tolerance };
  96. assert!(matches!(
  97. offline_tolerance,
  98. SendKind::OfflineTolerance { .. }
  99. ));
  100. }
  101. #[test]
  102. fn test_secret_key_from_hex() {
  103. // Test valid hex string (64 characters)
  104. let valid_hex = "a".repeat(64);
  105. let secret_key = SecretKey::from_hex(valid_hex.clone());
  106. assert!(secret_key.is_ok());
  107. assert_eq!(secret_key.unwrap().hex, valid_hex);
  108. // Test invalid length
  109. let invalid_length = "a".repeat(32); // 32 chars instead of 64
  110. let secret_key = SecretKey::from_hex(invalid_length);
  111. assert!(secret_key.is_err());
  112. // Test invalid characters
  113. let invalid_chars = "g".repeat(64); // 'g' is not a valid hex character
  114. let secret_key = SecretKey::from_hex(invalid_chars);
  115. assert!(secret_key.is_err());
  116. }
  117. #[test]
  118. fn test_secret_key_random() {
  119. let key1 = SecretKey::random();
  120. let key2 = SecretKey::random();
  121. // Keys should be different
  122. assert_ne!(key1.hex, key2.hex);
  123. // Keys should be valid hex (64 characters)
  124. assert_eq!(key1.hex.len(), 64);
  125. assert_eq!(key2.hex.len(), 64);
  126. assert!(key1.hex.chars().all(|c| c.is_ascii_hexdigit()));
  127. assert!(key2.hex.chars().all(|c| c.is_ascii_hexdigit()));
  128. }
  129. #[test]
  130. fn test_send_options_with_all_fields() {
  131. use std::collections::HashMap;
  132. let memo = SendMemo {
  133. memo: "Test memo".to_string(),
  134. include_memo: true,
  135. };
  136. let mut metadata = HashMap::new();
  137. metadata.insert("key1".to_string(), "value1".to_string());
  138. let conditions = SpendingConditions::P2PK {
  139. pubkey: "02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc"
  140. .to_string(),
  141. conditions: None,
  142. };
  143. let options = SendOptions {
  144. memo: Some(memo),
  145. conditions: Some(conditions),
  146. amount_split_target: SplitTarget::Value {
  147. amount: Amount::new(1000),
  148. },
  149. send_kind: SendKind::OnlineTolerance {
  150. tolerance: Amount::new(50),
  151. },
  152. include_fee: true,
  153. max_proofs: Some(10),
  154. metadata,
  155. };
  156. assert!(options.memo.is_some());
  157. assert!(options.conditions.is_some());
  158. assert!(matches!(
  159. options.amount_split_target,
  160. SplitTarget::Value { .. }
  161. ));
  162. assert!(matches!(
  163. options.send_kind,
  164. SendKind::OnlineTolerance { .. }
  165. ));
  166. assert!(options.include_fee);
  167. assert_eq!(options.max_proofs, Some(10));
  168. assert!(!options.metadata.is_empty());
  169. }
  170. #[test]
  171. fn test_receive_options_with_all_fields() {
  172. use std::collections::HashMap;
  173. let secret_key = SecretKey::random();
  174. let mut metadata = HashMap::new();
  175. metadata.insert("key1".to_string(), "value1".to_string());
  176. let options = ReceiveOptions {
  177. amount_split_target: SplitTarget::Values {
  178. amounts: vec![Amount::new(100), Amount::new(200)],
  179. },
  180. p2pk_signing_keys: vec![secret_key],
  181. preimages: vec!["preimage1".to_string(), "preimage2".to_string()],
  182. metadata,
  183. };
  184. assert!(matches!(
  185. options.amount_split_target,
  186. SplitTarget::Values { .. }
  187. ));
  188. assert_eq!(options.p2pk_signing_keys.len(), 1);
  189. assert_eq!(options.preimages.len(), 2);
  190. assert!(!options.metadata.is_empty());
  191. }
  192. #[test]
  193. fn test_wallet_config() {
  194. let config = WalletConfig {
  195. target_proof_count: None,
  196. };
  197. assert!(config.target_proof_count.is_none());
  198. let config_with_values = WalletConfig {
  199. target_proof_count: Some(5),
  200. };
  201. assert_eq!(config_with_values.target_proof_count, Some(5));
  202. }
  203. #[test]
  204. fn test_mnemonic_generation() {
  205. // Test mnemonic generation
  206. let mnemonic = generate_mnemonic().unwrap();
  207. assert!(!mnemonic.is_empty());
  208. assert_eq!(mnemonic.split_whitespace().count(), 12);
  209. // Verify it's a valid mnemonic by trying to parse it
  210. use bip39::Mnemonic;
  211. let parsed = Mnemonic::parse(&mnemonic);
  212. assert!(parsed.is_ok());
  213. }
  214. #[test]
  215. fn test_mnemonic_validation() {
  216. // Test with valid mnemonic
  217. let mnemonic = generate_mnemonic().unwrap();
  218. use bip39::Mnemonic;
  219. let parsed = Mnemonic::parse(&mnemonic);
  220. assert!(parsed.is_ok());
  221. // Test with invalid mnemonic
  222. let invalid_mnemonic = "invalid mnemonic phrase that should not work";
  223. let parsed_invalid = Mnemonic::parse(invalid_mnemonic);
  224. assert!(parsed_invalid.is_err());
  225. // Test mnemonic word count variations
  226. let mnemonic_12 = generate_mnemonic().unwrap();
  227. assert_eq!(mnemonic_12.split_whitespace().count(), 12);
  228. }
  229. #[test]
  230. fn test_mnemonic_to_entropy() {
  231. // Test with generated mnemonic
  232. let mnemonic = generate_mnemonic().unwrap();
  233. let entropy = mnemonic_to_entropy(mnemonic.clone()).unwrap();
  234. // For a 12-word mnemonic, entropy should be 16 bytes (128 bits)
  235. assert_eq!(entropy.len(), 16);
  236. // Test that we can recreate the mnemonic from entropy
  237. use bip39::Mnemonic;
  238. let recreated_mnemonic = Mnemonic::from_entropy(&entropy).unwrap();
  239. assert_eq!(recreated_mnemonic.to_string(), mnemonic);
  240. // Test with invalid mnemonic
  241. let invalid_result = mnemonic_to_entropy("invalid mnemonic".to_string());
  242. assert!(invalid_result.is_err());
  243. }
  244. }