lichen_client_sdk/
keypair.rs1pub use lichen_core::{Address, Keypair as CoreKeypair, PqPublicKey, PqSignature, Pubkey};
4
5pub struct Keypair(CoreKeypair);
7
8impl Keypair {
9 pub fn new() -> Self {
11 Self(CoreKeypair::new())
12 }
13
14 pub fn from_seed(seed: &[u8; 32]) -> Self {
16 Self(CoreKeypair::from_seed(seed))
17 }
18
19 pub fn pubkey(&self) -> Pubkey {
21 self.0.pubkey()
22 }
23
24 pub fn public_key(&self) -> PqPublicKey {
26 self.0.public_key()
27 }
28
29 pub fn to_seed(&self) -> [u8; 32] {
31 self.0.to_seed()
32 }
33
34 pub fn sign(&self, message: &[u8]) -> PqSignature {
36 self.0.sign(message)
37 }
38
39 pub fn verify(address: &Pubkey, message: &[u8], signature: &PqSignature) -> bool {
41 CoreKeypair::verify(address, message, signature)
42 }
43
44 pub fn inner(&self) -> &CoreKeypair {
46 &self.0
47 }
48}
49
50impl Default for Keypair {
51 fn default() -> Self {
52 Self::new()
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59
60 #[test]
61 fn new_keypair_has_unique_pubkey() {
62 let kp1 = Keypair::new();
63 let kp2 = Keypair::new();
64 assert_ne!(kp1.pubkey(), kp2.pubkey());
65 }
66
67 #[test]
68 fn from_seed_deterministic() {
69 let seed = [42u8; 32];
70 let kp1 = Keypair::from_seed(&seed);
71 let kp2 = Keypair::from_seed(&seed);
72 assert_eq!(kp1.pubkey(), kp2.pubkey());
73 }
74
75 #[test]
76 fn different_seeds_different_keys() {
77 let kp1 = Keypair::from_seed(&[1u8; 32]);
78 let kp2 = Keypair::from_seed(&[2u8; 32]);
79 assert_ne!(kp1.pubkey(), kp2.pubkey());
80 }
81
82 #[test]
83 fn to_seed_roundtrip() {
84 let seed = [99u8; 32];
85 let kp = Keypair::from_seed(&seed);
86 assert_eq!(kp.to_seed(), seed);
87 }
88
89 #[test]
90 fn public_key_roundtrip_matches_address() {
91 let kp = Keypair::new();
92 assert_eq!(kp.public_key().address(), kp.pubkey());
93 }
94
95 #[test]
96 fn sign_produces_valid_pq_signature() {
97 let kp = Keypair::new();
98 let sig = kp.sign(b"hello lichen");
99 assert!(sig.validate().is_ok());
100 assert_eq!(sig.signer_address(), kp.pubkey());
101 assert!(sig.sig.len() > 64);
102 }
103
104 #[test]
105 fn sign_deterministic() {
106 let kp = Keypair::from_seed(&[7u8; 32]);
107 let sig1 = kp.sign(b"msg");
108 let sig2 = kp.sign(b"msg");
109 assert_eq!(sig1, sig2);
110 }
111
112 #[test]
113 fn sign_different_messages_differ() {
114 let kp = Keypair::new();
115 let sig1 = kp.sign(b"aaa");
116 let sig2 = kp.sign(b"bbb");
117 assert_ne!(sig1, sig2);
118 }
119
120 #[test]
121 fn inner_returns_core_keypair() {
122 let kp = Keypair::from_seed(&[5u8; 32]);
123 assert_eq!(kp.inner().pubkey(), kp.pubkey());
124 }
125
126 #[test]
127 fn verify_accepts_self_contained_signature() {
128 let kp = Keypair::from_seed(&[6u8; 32]);
129 let message = b"pq rust sdk";
130 let signature = kp.sign(message);
131 assert!(Keypair::verify(&kp.pubkey(), message, &signature));
132 assert!(!Keypair::verify(&kp.pubkey(), b"tampered", &signature));
133 }
134
135 #[test]
136 fn default_works() {
137 let kp = Keypair::default();
138 let _ = kp.pubkey();
140 }
141
142 #[test]
143 fn pubkey_is_32_bytes() {
144 let kp = Keypair::new();
145 assert_eq!(kp.pubkey().0.len(), 32);
146 }
147}