1use std::future::Future;
35use std::path::Path;
36use std::pin::Pin;
37use std::sync::Arc;
38use std::sync::atomic::{AtomicUsize, Ordering};
39
40use crate::error::SignerError;
41use crate::types::nep413::{self, SignMessageParams, SignedMessage};
42use crate::types::{AccountId, PublicKey, SecretKey, Signature, TryIntoAccountId};
43
44pub trait Signer: Send + Sync {
75 fn account_id(&self) -> &AccountId;
77
78 fn key(&self) -> SigningKey;
86
87 fn public_key(&self) -> PublicKey {
94 self.key().public_key().clone()
95 }
96}
97
98impl<T: Signer + ?Sized> Signer for Arc<T> {
104 fn account_id(&self) -> &AccountId {
105 (**self).account_id()
106 }
107
108 fn key(&self) -> SigningKey {
109 (**self).key()
110 }
111
112 fn public_key(&self) -> PublicKey {
113 (**self).public_key()
114 }
115}
116
117pub struct SigningKey {
143 public_key: PublicKey,
145 backend: Arc<dyn SigningBackend>,
147}
148
149impl SigningKey {
150 pub fn new(secret_key: SecretKey) -> Self {
152 let public_key = secret_key.public_key();
153 Self {
154 public_key,
155 backend: Arc::new(SecretKeyBackend { secret_key }),
156 }
157 }
158
159 pub fn public_key(&self) -> &PublicKey {
161 &self.public_key
162 }
163
164 pub async fn sign(&self, message: &[u8]) -> Result<Signature, SignerError> {
170 self.backend.sign(message).await
171 }
172
173 pub async fn sign_nep413(
194 &self,
195 account_id: &AccountId,
196 params: &SignMessageParams,
197 ) -> Result<SignedMessage, SignerError> {
198 let hash = nep413::serialize_message(params);
199 let signature = self.sign(hash.as_bytes()).await?;
200
201 Ok(SignedMessage {
202 account_id: account_id.clone(),
203 public_key: self.public_key.clone(),
204 signature,
205 state: params.state.clone(),
206 })
207 }
208}
209
210impl Clone for SigningKey {
211 fn clone(&self) -> Self {
212 Self {
213 public_key: self.public_key.clone(),
214 backend: self.backend.clone(),
215 }
216 }
217}
218
219impl std::fmt::Debug for SigningKey {
220 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
221 f.debug_struct("SigningKey")
222 .field("public_key", &self.public_key)
223 .finish()
224 }
225}
226
227trait SigningBackend: Send + Sync {
236 fn sign(
237 &self,
238 message: &[u8],
239 ) -> Pin<Box<dyn Future<Output = Result<Signature, SignerError>> + Send + '_>>;
240}
241
242struct SecretKeyBackend {
244 secret_key: SecretKey,
245}
246
247impl SigningBackend for SecretKeyBackend {
248 fn sign(
249 &self,
250 message: &[u8],
251 ) -> Pin<Box<dyn Future<Output = Result<Signature, SignerError>> + Send + '_>> {
252 let sig = self.secret_key.sign(message);
253 Box::pin(async move { Ok(sig) })
254 }
255}
256
257#[derive(Clone)]
277pub struct InMemorySigner {
278 account_id: AccountId,
279 secret_key: SecretKey,
280 public_key: PublicKey,
281}
282
283impl InMemorySigner {
284 pub fn new(
295 account_id: impl TryIntoAccountId,
296 secret_key: impl AsRef<str>,
297 ) -> Result<Self, crate::error::Error> {
298 let account_id: AccountId = account_id.try_into_account_id()?;
299 let secret_key: SecretKey = secret_key.as_ref().parse()?;
300 let public_key = secret_key.public_key();
301
302 Ok(Self {
303 account_id,
304 secret_key,
305 public_key,
306 })
307 }
308
309 pub fn from_secret_key(
315 account_id: impl TryIntoAccountId,
316 secret_key: SecretKey,
317 ) -> Result<Self, crate::error::Error> {
318 let account_id: AccountId = account_id.try_into_account_id()?;
319 let public_key = secret_key.public_key();
320 Ok(Self {
321 account_id,
322 secret_key,
323 public_key,
324 })
325 }
326
327 pub fn generate_implicit() -> Self {
340 let secret_key = SecretKey::generate_ed25519();
341 Self::implicit(secret_key)
342 }
343
344 pub fn implicit(secret_key: SecretKey) -> Self {
362 let public_key = secret_key.public_key();
363 let pk_bytes = public_key
364 .as_ed25519_bytes()
365 .expect("implicit accounts require an Ed25519 key");
366 let account_id: AccountId = hex::encode(pk_bytes)
367 .parse()
368 .expect("hex-encoded Ed25519 public key is a valid account ID");
369 Self {
370 account_id,
371 secret_key,
372 public_key,
373 }
374 }
375
376 pub fn from_seed_phrase(
396 account_id: impl TryIntoAccountId,
397 phrase: impl AsRef<str>,
398 ) -> Result<Self, crate::error::Error> {
399 let secret_key = SecretKey::from_seed_phrase(phrase)?;
400 Self::from_secret_key(account_id, secret_key)
401 }
402
403 pub fn from_seed_phrase_with_path(
423 account_id: impl TryIntoAccountId,
424 phrase: impl AsRef<str>,
425 hd_path: impl AsRef<str>,
426 ) -> Result<Self, crate::error::Error> {
427 let secret_key = SecretKey::from_seed_phrase_with_path(phrase, hd_path)?;
428 Self::from_secret_key(account_id, secret_key)
429 }
430
431 pub fn public_key(&self) -> &PublicKey {
433 &self.public_key
434 }
435}
436
437impl std::fmt::Debug for InMemorySigner {
438 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
439 f.debug_struct("InMemorySigner")
440 .field("account_id", &self.account_id)
441 .field("public_key", &self.public_key)
442 .finish()
443 }
444}
445
446impl Signer for InMemorySigner {
447 fn account_id(&self) -> &AccountId {
448 &self.account_id
449 }
450
451 fn key(&self) -> SigningKey {
452 SigningKey::new(self.secret_key.clone())
453 }
454}
455
456#[derive(Clone)]
473pub struct FileSigner {
474 inner: InMemorySigner,
475}
476
477#[derive(serde::Deserialize)]
479struct CredentialFile {
480 #[serde(alias = "secret_key")]
481 private_key: String,
482}
483
484impl FileSigner {
485 pub fn new(
501 network: impl AsRef<str>,
502 account_id: impl TryIntoAccountId,
503 ) -> Result<Self, crate::error::Error> {
504 let account_id: AccountId = account_id.try_into_account_id()?;
505 let home = dirs::home_dir().ok_or_else(|| {
506 crate::error::Error::Config("Could not determine home directory".to_string())
507 })?;
508 let path = home
509 .join(".near-credentials")
510 .join(network.as_ref())
511 .join(format!("{}.json", account_id));
512
513 Self::from_file(&path, account_id)
514 }
515
516 pub fn from_file(
523 path: impl AsRef<Path>,
524 account_id: impl TryIntoAccountId,
525 ) -> Result<Self, crate::error::Error> {
526 let content = std::fs::read_to_string(path.as_ref()).map_err(|e| {
527 crate::error::Error::Config(format!(
528 "Failed to read credentials file {}: {}",
529 path.as_ref().display(),
530 e
531 ))
532 })?;
533
534 let cred: CredentialFile = serde_json::from_str(&content).map_err(|e| {
535 crate::error::Error::Config(format!(
536 "Failed to parse credentials file {}: {}",
537 path.as_ref().display(),
538 e
539 ))
540 })?;
541
542 let inner = InMemorySigner::new(account_id, &cred.private_key)?;
543 Ok(Self { inner })
544 }
545
546 pub fn public_key(&self) -> &PublicKey {
548 self.inner.public_key()
549 }
550
551 pub fn into_inner(self) -> InMemorySigner {
553 self.inner
554 }
555}
556
557impl std::fmt::Debug for FileSigner {
558 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
559 f.debug_struct("FileSigner")
560 .field("account_id", &self.inner.account_id)
561 .field("public_key", &self.inner.public_key)
562 .finish()
563 }
564}
565
566impl Signer for FileSigner {
567 fn account_id(&self) -> &AccountId {
568 self.inner.account_id()
569 }
570
571 fn key(&self) -> SigningKey {
572 self.inner.key()
573 }
574}
575
576#[derive(Clone)]
595pub struct EnvSigner {
596 inner: InMemorySigner,
597}
598
599impl EnvSigner {
600 pub fn new() -> Result<Self, crate::error::Error> {
608 Self::from_env_vars("NEAR_ACCOUNT_ID", "NEAR_PRIVATE_KEY")
609 }
610
611 pub fn from_env_vars(account_var: &str, key_var: &str) -> Result<Self, crate::error::Error> {
618 let account_id = std::env::var(account_var).map_err(|_| {
619 crate::error::Error::Config(format!("Environment variable {} not set", account_var))
620 })?;
621
622 let private_key = std::env::var(key_var).map_err(|_| {
623 crate::error::Error::Config(format!("Environment variable {} not set", key_var))
624 })?;
625
626 let inner = InMemorySigner::new(account_id, &private_key)?;
627 Ok(Self { inner })
628 }
629
630 pub fn public_key(&self) -> &PublicKey {
632 self.inner.public_key()
633 }
634
635 pub fn into_inner(self) -> InMemorySigner {
637 self.inner
638 }
639}
640
641impl std::fmt::Debug for EnvSigner {
642 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
643 f.debug_struct("EnvSigner")
644 .field("account_id", &self.inner.account_id)
645 .field("public_key", &self.inner.public_key)
646 .finish()
647 }
648}
649
650impl Signer for EnvSigner {
651 fn account_id(&self) -> &AccountId {
652 self.inner.account_id()
653 }
654
655 fn key(&self) -> SigningKey {
656 self.inner.key()
657 }
658}
659
660pub struct RotatingSigner {
716 account_id: AccountId,
717 keys: Vec<SecretKey>,
718 counter: AtomicUsize,
719}
720
721impl RotatingSigner {
722 pub fn new(
735 account_id: impl TryIntoAccountId,
736 keys: Vec<SecretKey>,
737 ) -> Result<Self, crate::error::Error> {
738 if keys.is_empty() {
739 return Err(crate::error::Error::Config(
740 "RotatingSigner requires at least one key".to_string(),
741 ));
742 }
743
744 let account_id: AccountId = account_id.try_into_account_id()?;
745
746 Ok(Self {
747 account_id,
748 keys,
749 counter: AtomicUsize::new(0),
750 })
751 }
752
753 pub fn from_signers(signers: Vec<InMemorySigner>) -> Result<Self, crate::error::Error> {
772 if signers.is_empty() {
773 return Err(crate::error::Error::Config(
774 "RotatingSigner requires at least one signer".to_string(),
775 ));
776 }
777
778 let account_id = signers[0].account_id().clone();
779 for signer in &signers[1..] {
780 if signer.account_id() != &account_id {
781 return Err(crate::error::Error::Config(format!(
782 "All signers must share the same account ID, got {} and {}",
783 account_id,
784 signer.account_id()
785 )));
786 }
787 }
788
789 let keys = signers.into_iter().map(|s| s.secret_key).collect();
790 Ok(Self {
791 account_id,
792 keys,
793 counter: AtomicUsize::new(0),
794 })
795 }
796
797 pub fn from_key_strings(
804 account_id: impl TryIntoAccountId,
805 keys: &[impl AsRef<str>],
806 ) -> Result<Self, crate::error::Error> {
807 let parsed_keys: Result<Vec<SecretKey>, _> =
808 keys.iter().map(|k| k.as_ref().parse()).collect();
809 Self::new(account_id, parsed_keys?)
810 }
811
812 pub fn key_count(&self) -> usize {
814 self.keys.len()
815 }
816
817 pub fn public_keys(&self) -> Vec<PublicKey> {
819 self.keys.iter().map(|sk| sk.public_key()).collect()
820 }
821
822 pub fn signing_keys(&self) -> Vec<SigningKey> {
827 self.keys
828 .iter()
829 .map(|sk| SigningKey::new(sk.clone()))
830 .collect()
831 }
832
833 pub fn into_per_key_signers(self) -> Vec<InMemorySigner> {
853 let account_id = self.account_id;
854 self.keys
855 .into_iter()
856 .map(|sk| {
857 InMemorySigner::from_secret_key(account_id.clone(), sk)
858 .expect("AccountId is always a valid account ID")
859 })
860 .collect()
861 }
862}
863
864impl std::fmt::Debug for RotatingSigner {
865 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
866 f.debug_struct("RotatingSigner")
867 .field("account_id", &self.account_id)
868 .field("key_count", &self.keys.len())
869 .field("counter", &self.counter.load(Ordering::Relaxed))
870 .finish()
871 }
872}
873
874impl Signer for RotatingSigner {
875 fn account_id(&self) -> &AccountId {
876 &self.account_id
877 }
878
879 fn key(&self) -> SigningKey {
880 let idx = self.counter.fetch_add(1, Ordering::Relaxed) % self.keys.len();
882 SigningKey::new(self.keys[idx].clone())
883 }
884
885 fn public_key(&self) -> PublicKey {
886 let idx = self.counter.load(Ordering::Relaxed) % self.keys.len();
888 self.keys[idx].public_key()
889 }
890}
891
892#[cfg(test)]
897mod tests {
898 use super::*;
899 use crate::types::{Action, CryptoHash, NearToken, Transaction};
900
901 #[tokio::test]
902 async fn test_in_memory_signer() {
903 let signer = InMemorySigner::new(
904 "alice.testnet",
905 "ed25519:3D4YudUahN1nawWogh8pAKSj92sUNMdbZGjn7kERKzYoTy8tnFQuwoGUC51DowKqorvkr2pytJSnwuSbsNVfqygr",
906 )
907 .unwrap();
908
909 assert_eq!(signer.account_id().as_str(), "alice.testnet");
910
911 let key = signer.key();
912 let message = b"test message";
913 let signature = key.sign(message).await.unwrap();
914
915 assert_eq!(key.public_key(), signer.public_key());
917 assert!(!signature.as_bytes().is_empty());
918 }
919
920 #[tokio::test]
921 async fn test_signature_consistency() {
922 let signer = InMemorySigner::new(
924 "alice.testnet",
925 "ed25519:3D4YudUahN1nawWogh8pAKSj92sUNMdbZGjn7kERKzYoTy8tnFQuwoGUC51DowKqorvkr2pytJSnwuSbsNVfqygr",
926 )
927 .unwrap();
928
929 let key = signer.key();
930 let message = b"test message";
931 let sig1 = key.sign(message).await.unwrap();
932 let sig2 = key.sign(message).await.unwrap();
933
934 assert_eq!(sig1.as_bytes(), sig2.as_bytes());
935 }
936
937 #[tokio::test]
938 async fn test_different_messages_different_signatures() {
939 let signer = InMemorySigner::new(
940 "alice.testnet",
941 "ed25519:3D4YudUahN1nawWogh8pAKSj92sUNMdbZGjn7kERKzYoTy8tnFQuwoGUC51DowKqorvkr2pytJSnwuSbsNVfqygr",
942 )
943 .unwrap();
944
945 let key = signer.key();
946 let sig1 = key.sign(b"message 1").await.unwrap();
947 let sig2 = key.sign(b"message 2").await.unwrap();
948
949 assert_ne!(sig1.as_bytes(), sig2.as_bytes());
950 }
951
952 #[tokio::test]
953 async fn test_transaction_signing_with_signer_trait() {
954 let secret_key = SecretKey::generate_ed25519();
955 let signer = InMemorySigner::from_secret_key("alice.testnet", secret_key).unwrap();
956
957 let key = signer.key();
959
960 let tx = Transaction::new(
962 signer.account_id().clone(),
963 key.public_key().clone(),
964 1,
965 "bob.testnet".parse().unwrap(),
966 CryptoHash::ZERO,
967 vec![Action::transfer(NearToken::from_near(1))],
968 );
969
970 let tx_hash = tx.get_hash();
972 let signature = key.sign(tx_hash.as_bytes()).await.unwrap();
973
974 assert_eq!(signature.as_bytes().len(), 64);
976
977 let signed_tx = crate::types::SignedTransaction {
979 transaction: tx,
980 signature,
981 };
982
983 let bytes = signed_tx.to_bytes();
985 assert!(!bytes.is_empty());
986
987 let base64 = signed_tx.to_base64();
989 assert!(!base64.is_empty());
990 }
991
992 #[tokio::test]
993 async fn test_rotating_signer() {
994 let keys = vec![
995 SecretKey::generate_ed25519(),
996 SecretKey::generate_ed25519(),
997 SecretKey::generate_ed25519(),
998 ];
999 let expected_public_keys: Vec<_> = keys.iter().map(|k| k.public_key()).collect();
1000
1001 let signer = RotatingSigner::new("bot.testnet", keys).unwrap();
1002
1003 let key1 = signer.key();
1005 assert_eq!(key1.public_key(), &expected_public_keys[0]);
1006
1007 let key2 = signer.key();
1008 assert_eq!(key2.public_key(), &expected_public_keys[1]);
1009
1010 let key3 = signer.key();
1011 assert_eq!(key3.public_key(), &expected_public_keys[2]);
1012
1013 let key4 = signer.key();
1015 assert_eq!(key4.public_key(), &expected_public_keys[0]);
1016 }
1017
1018 #[tokio::test]
1019 async fn test_rotating_signer_atomic_key_claiming() {
1020 let keys = vec![SecretKey::generate_ed25519(), SecretKey::generate_ed25519()];
1022 let expected_pks: Vec<_> = keys.iter().map(|k| k.public_key()).collect();
1023
1024 let signer = RotatingSigner::new("bot.testnet", keys.clone()).unwrap();
1025 let message = b"test";
1026
1027 let key1 = signer.key();
1029 assert_eq!(key1.public_key(), &expected_pks[0]);
1030 let sig1 = key1.sign(message).await.unwrap();
1031 let expected_sig1 = keys[0].sign(message);
1033 assert_eq!(sig1.as_bytes(), expected_sig1.as_bytes());
1034
1035 let key2 = signer.key();
1037 assert_eq!(key2.public_key(), &expected_pks[1]);
1038 let sig2 = key2.sign(message).await.unwrap();
1039 let expected_sig2 = keys[1].sign(message);
1040 assert_eq!(sig2.as_bytes(), expected_sig2.as_bytes());
1041
1042 assert_ne!(sig1.as_bytes(), sig2.as_bytes());
1044 }
1045
1046 #[test]
1047 fn test_rotating_signer_empty_keys() {
1048 let result = RotatingSigner::new("bot.testnet", vec![]);
1049 assert!(result.is_err());
1050 }
1051
1052 #[test]
1053 fn test_env_signer_missing_vars() {
1054 let result = EnvSigner::from_env_vars("NONEXISTENT_VAR_1", "NONEXISTENT_VAR_2");
1056 assert!(result.is_err());
1057 }
1058
1059 #[test]
1060 fn test_signer_from_secret_key() {
1061 let secret = SecretKey::generate_ed25519();
1062 let expected_pk = secret.public_key();
1063
1064 let signer = InMemorySigner::from_secret_key("alice.testnet", secret).unwrap();
1065
1066 assert_eq!(signer.account_id().as_str(), "alice.testnet");
1067 assert_eq!(signer.public_key(), &expected_pk);
1068 }
1069
1070 #[test]
1071 fn test_from_secret_key_accepts_string() {
1072 let secret = SecretKey::generate_ed25519();
1073 let signer = InMemorySigner::from_secret_key("alice.testnet", secret);
1074 assert!(signer.is_ok());
1075 }
1076
1077 #[test]
1078 fn test_from_secret_key_rejects_invalid_account() {
1079 let secret = SecretKey::generate_ed25519();
1080 let signer = InMemorySigner::from_secret_key("", secret);
1081 assert!(signer.is_err());
1082 }
1083
1084 #[test]
1085 fn test_generate_implicit() {
1086 let signer = InMemorySigner::generate_implicit();
1087 let account_id = signer.account_id().as_str();
1088
1089 assert_eq!(account_id.len(), 64);
1091 assert!(account_id.chars().all(|c| c.is_ascii_hexdigit()));
1092
1093 let pk_bytes = signer.public_key().as_ed25519_bytes().unwrap();
1095 assert_eq!(account_id, hex::encode(pk_bytes));
1096 }
1097
1098 #[test]
1099 fn test_implicit() {
1100 let secret_key = SecretKey::generate_ed25519();
1101 let expected_pk = secret_key.public_key();
1102 let signer = InMemorySigner::implicit(secret_key);
1103
1104 let pk_bytes = expected_pk.as_ed25519_bytes().unwrap();
1106 assert_eq!(signer.account_id().as_str(), hex::encode(pk_bytes));
1107 assert_eq!(signer.public_key(), &expected_pk);
1108 }
1109
1110 #[test]
1111 fn test_generate_implicit_unique() {
1112 let signer1 = InMemorySigner::generate_implicit();
1113 let signer2 = InMemorySigner::generate_implicit();
1114 assert_ne!(signer1.account_id(), signer2.account_id());
1115 }
1116
1117 #[test]
1118 fn test_rotating_signer_key_count() {
1119 let keys = vec![
1120 SecretKey::generate_ed25519(),
1121 SecretKey::generate_ed25519(),
1122 SecretKey::generate_ed25519(),
1123 ];
1124
1125 let signer = RotatingSigner::new("bot.testnet", keys).unwrap();
1126
1127 assert_eq!(signer.key_count(), 3);
1128 assert_eq!(signer.public_keys().len(), 3);
1129 }
1130
1131 #[test]
1132 fn test_rotating_signer_from_key_strings() {
1133 let keys = [
1134 "ed25519:3D4YudUahN1nawWogh8pAKSj92sUNMdbZGjn7kERKzYoTy8tnFQuwoGUC51DowKqorvkr2pytJSnwuSbsNVfqygr",
1135 "ed25519:4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi5kL6YJt9Z6iLqMBkVfqDH2Zj8bxqXTdMkNmvPcAD8LqCZ",
1136 ];
1137
1138 let signer = RotatingSigner::from_key_strings("bot.testnet", &keys).unwrap();
1139
1140 assert_eq!(signer.key_count(), 2);
1141 assert_eq!(signer.account_id().as_str(), "bot.testnet");
1142 }
1143
1144 #[test]
1145 fn test_in_memory_signer_debug_hides_secret() {
1146 let signer = InMemorySigner::new(
1147 "alice.testnet",
1148 "ed25519:3D4YudUahN1nawWogh8pAKSj92sUNMdbZGjn7kERKzYoTy8tnFQuwoGUC51DowKqorvkr2pytJSnwuSbsNVfqygr",
1149 )
1150 .unwrap();
1151
1152 let debug_str = format!("{:?}", signer);
1153
1154 assert!(debug_str.contains("alice.testnet"));
1156 assert!(debug_str.contains("public_key"));
1157 assert!(!debug_str.contains("secret_key"));
1158 assert!(!debug_str.contains("3D4YudUahN1nawWogh"));
1159 }
1160
1161 #[test]
1162 fn test_rotating_signer_from_signers() {
1163 let keys = vec![SecretKey::generate_ed25519(), SecretKey::generate_ed25519()];
1164 let expected_public_keys: Vec<_> = keys.iter().map(|k| k.public_key()).collect();
1165
1166 let signers: Vec<InMemorySigner> = keys
1167 .into_iter()
1168 .map(|sk| InMemorySigner::from_secret_key("bot.testnet", sk).unwrap())
1169 .collect();
1170
1171 let rotating = RotatingSigner::from_signers(signers).unwrap();
1172 assert_eq!(rotating.key_count(), 2);
1173 assert_eq!(rotating.public_keys(), expected_public_keys);
1174 }
1175
1176 #[test]
1177 fn test_rotating_signer_from_signers_mismatched_accounts() {
1178 let signers = vec![
1179 InMemorySigner::from_secret_key("alice.testnet", SecretKey::generate_ed25519())
1180 .unwrap(),
1181 InMemorySigner::from_secret_key("bob.testnet", SecretKey::generate_ed25519()).unwrap(),
1182 ];
1183
1184 let result = RotatingSigner::from_signers(signers);
1185 assert!(result.is_err());
1186 }
1187
1188 #[test]
1189 fn test_rotating_signer_signing_keys() {
1190 let keys = vec![
1191 SecretKey::generate_ed25519(),
1192 SecretKey::generate_ed25519(),
1193 SecretKey::generate_ed25519(),
1194 ];
1195 let expected_public_keys: Vec<_> = keys.iter().map(|k| k.public_key()).collect();
1196
1197 let signer = RotatingSigner::new("bot.testnet", keys).unwrap();
1198
1199 let counter_before = signer.counter.load(Ordering::Relaxed);
1201 let signing_keys = signer.signing_keys();
1202 let counter_after = signer.counter.load(Ordering::Relaxed);
1203 assert_eq!(counter_before, counter_after);
1204
1205 assert_eq!(signing_keys.len(), 3);
1207 for (sk, expected_pk) in signing_keys.iter().zip(&expected_public_keys) {
1208 assert_eq!(sk.public_key(), expected_pk);
1209 }
1210 }
1211
1212 #[test]
1213 fn test_rotating_signer_into_per_key_signers() {
1214 let keys = vec![SecretKey::generate_ed25519(), SecretKey::generate_ed25519()];
1215 let expected_public_keys: Vec<_> = keys.iter().map(|k| k.public_key()).collect();
1216
1217 let signer = RotatingSigner::new("bot.testnet", keys).unwrap();
1218 let per_key = signer.into_per_key_signers();
1219
1220 assert_eq!(per_key.len(), 2);
1221 for (ims, expected_pk) in per_key.iter().zip(&expected_public_keys) {
1222 assert_eq!(ims.account_id().as_str(), "bot.testnet");
1223 assert_eq!(ims.public_key(), expected_pk);
1224 }
1225 }
1226
1227 #[test]
1228 fn test_rotating_signer_public_key_no_side_effect() {
1229 let keys = vec![
1230 SecretKey::generate_ed25519(),
1231 SecretKey::generate_ed25519(),
1232 SecretKey::generate_ed25519(),
1233 ];
1234 let pks: Vec<_> = keys.iter().map(|k| k.public_key()).collect();
1235
1236 let signer = RotatingSigner::new("bot.testnet", keys).unwrap();
1237
1238 assert_eq!(signer.public_key(), pks[0]);
1240 assert_eq!(signer.public_key(), pks[0]);
1241 assert_eq!(signer.public_key(), pks[0]);
1242
1243 let claimed = signer.key();
1245 assert_eq!(claimed.public_key(), &pks[0]);
1246
1247 assert_eq!(signer.public_key(), pks[1]);
1249
1250 let claimed = signer.key();
1252 assert_eq!(claimed.public_key(), &pks[1]);
1253 assert_eq!(signer.public_key(), pks[2]);
1254
1255 let claimed = signer.key();
1257 assert_eq!(claimed.public_key(), &pks[2]);
1258 assert_eq!(signer.public_key(), pks[0]);
1259 }
1260
1261 #[test]
1262 fn test_signing_key_is_clone() {
1263 let signer = InMemorySigner::new(
1264 "alice.testnet",
1265 "ed25519:3D4YudUahN1nawWogh8pAKSj92sUNMdbZGjn7kERKzYoTy8tnFQuwoGUC51DowKqorvkr2pytJSnwuSbsNVfqygr",
1266 )
1267 .unwrap();
1268
1269 let key = signer.key();
1270 let key_clone = key.clone();
1271
1272 assert_eq!(key.public_key(), key_clone.public_key());
1273 }
1274}