1mod error;
21
22use core::ops::Deref;
23
24pub use ed25519_dalek::{self, Signature, VerifyingKey};
25use ed25519_dalek::{SigningKey, Verifier};
26pub use error::Error;
27use zeroize::Zeroizing;
28
29#[derive(Debug, Clone)]
45pub struct Signer {
46 key: SigningKey,
47}
48
49impl Deref for Signer {
50 type Target = SigningKey;
51
52 #[inline]
53 fn deref(&self) -> &Self::Target {
54 &self.key
55 }
56}
57
58impl Signer {
59 #[must_use]
61 pub fn from_bytes(bytes: &[u8; 32]) -> Self {
62 Self {
63 key: SigningKey::from_bytes(bytes),
64 }
65 }
66
67 pub fn from_hex(hex_str: &str) -> Result<Self, Error> {
75 let hex_str = hex_str.strip_prefix("0x").unwrap_or(hex_str);
76 let bytes: [u8; 32] = hex::decode(hex_str)?.try_into().map_err(|v: Vec<u8>| {
77 Error::InvalidKey(format!("expected 32 bytes, got {}", v.len()))
78 })?;
79 Ok(Self::from_bytes(&bytes))
80 }
81
82 pub fn from_keypair_base58(b58: &str) -> Result<Self, Error> {
90 let decoded = bs58::decode(b58)
91 .into_vec()
92 .map_err(|e| Error::InvalidKeypair(e.to_string()))?;
93
94 if decoded.len() != 64 {
95 return Err(Error::InvalidKeypair(format!(
96 "expected 64 bytes, got {}",
97 decoded.len()
98 )));
99 }
100
101 let mut secret = [0u8; 32];
102 secret.copy_from_slice(&decoded[..32]);
103 let signer = Self::from_bytes(&secret);
104 secret.fill(0);
105 Ok(signer)
106 }
107
108 #[must_use]
114 pub fn random() -> Self {
115 let mut bytes = [0u8; 32];
116 getrandom::fill(&mut bytes).expect("system CSPRNG unavailable");
117 let signer = Self::from_bytes(&bytes);
118 bytes.fill(0);
119 signer
120 }
121
122 pub fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), Error> {
128 self.key.verifying_key().verify(msg, signature)?;
129 Ok(())
130 }
131
132 #[must_use]
138 pub fn sign_transaction_message(&self, message_bytes: &[u8]) -> Signature {
139 use ed25519_dalek::Signer as _;
140 self.key.sign(message_bytes)
141 }
142
143 #[must_use]
145 pub fn address(&self) -> String {
146 bs58::encode(self.key.verifying_key().as_bytes()).into_string()
147 }
148
149 #[must_use]
151 pub fn public_key_hex(&self) -> String {
152 hex::encode(self.key.verifying_key().as_bytes())
153 }
154
155 #[must_use]
159 pub fn keypair_base58(&self) -> Zeroizing<String> {
160 let vk = self.key.verifying_key();
161 let mut buf = [0u8; 64];
162 buf[..32].copy_from_slice(self.key.as_bytes());
163 buf[32..].copy_from_slice(vk.as_bytes());
164 let encoded = bs58::encode(&buf).into_string();
165 buf.fill(0);
166 Zeroizing::new(encoded)
167 }
168}
169
170#[cfg(feature = "kobe")]
171impl Signer {
172 pub fn from_derived(derived: &kobe_svm::DerivedAddress) -> Result<Self, Error> {
178 Self::from_hex(&derived.private_key_hex)
179 }
180
181 pub fn from_standard_wallet(wallet: &kobe_svm::StandardWallet) -> Result<Self, Error> {
187 Self::from_hex(&wallet.secret_hex())
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use ed25519_dalek::Signer as _;
194
195 use super::*;
196
197 #[test]
198 fn assert_send_sync() {
199 fn assert<T: Send + Sync>() {}
200 assert::<Signer>();
201 }
202
203 #[test]
204 fn assert_clone() {
205 let s = Signer::random();
206 let s2 = s.clone();
207 assert_eq!(s.address(), s2.address());
208 }
209
210 #[test]
211 fn random_address() {
212 let s = Signer::random();
213 let addr = s.address();
214 assert!(addr.len() >= 32 && addr.len() <= 44);
215 }
216
217 #[test]
218 fn hex_roundtrip() {
219 let s = Signer::random();
220 let hex_key = hex::encode(s.key.as_bytes());
221 let restored = Signer::from_hex(&hex_key).unwrap();
222 assert_eq!(s.address(), restored.address());
223 }
224
225 #[test]
226 fn keypair_base58_roundtrip() {
227 let s = Signer::random();
228 let b58 = s.keypair_base58();
229 let restored = Signer::from_keypair_base58(&b58).unwrap();
230 assert_eq!(s.address(), restored.address());
231 }
232
233 #[test]
234 fn from_bytes_deterministic() {
235 let key = [42u8; 32];
236 assert_eq!(
237 Signer::from_bytes(&key).address(),
238 Signer::from_bytes(&key).address()
239 );
240 }
241
242 #[test]
243 fn sign_and_verify() {
244 let s = Signer::random();
245 let sig = s.sign(b"hello solana");
246 s.verify(b"hello solana", &sig).unwrap();
247 }
248
249 #[test]
250 fn verify_wrong_message_fails() {
251 let s = Signer::random();
252 let sig = s.sign(b"correct");
253 assert!(s.verify(b"wrong", &sig).is_err());
254 }
255
256 #[test]
257 fn sign_transaction_message() {
258 let s = Signer::random();
259 let fake_msg = [0u8; 128];
260 let sig = s.sign_transaction_message(&fake_msg);
261 s.verify(&fake_msg, &sig).unwrap();
262 }
263
264 #[test]
265 fn public_key_hex_length() {
266 let s = Signer::random();
267 assert_eq!(s.public_key_hex().len(), 64);
268 }
269
270 #[test]
271 fn deref_to_signing_key() {
272 let s = Signer::random();
273 let _vk: VerifyingKey = s.verifying_key();
274 }
275}