lib.rs 8.9 KB

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