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};
43
44pub trait Signer: Send + Sync {
75 fn account_id(&self) -> &AccountId;
77
78 fn key(&self) -> SigningKey;
86}
87
88impl Signer for Arc<dyn Signer> {
90 fn account_id(&self) -> &AccountId {
91 (**self).account_id()
92 }
93
94 fn key(&self) -> SigningKey {
95 (**self).key()
96 }
97}
98
99pub struct SigningKey {
125 public_key: PublicKey,
127 backend: Arc<dyn SigningBackend>,
129}
130
131impl SigningKey {
132 pub fn new(secret_key: SecretKey) -> Self {
134 let public_key = secret_key.public_key();
135 Self {
136 public_key,
137 backend: Arc::new(SecretKeyBackend { secret_key }),
138 }
139 }
140
141 pub fn public_key(&self) -> &PublicKey {
143 &self.public_key
144 }
145
146 pub async fn sign(&self, message: &[u8]) -> Result<Signature, SignerError> {
152 self.backend.sign(message).await
153 }
154
155 pub async fn sign_nep413(
176 &self,
177 account_id: &AccountId,
178 params: &SignMessageParams,
179 ) -> Result<SignedMessage, SignerError> {
180 let hash = nep413::serialize_message(params);
181 let signature = self.sign(hash.as_bytes()).await?;
182
183 Ok(SignedMessage {
184 account_id: account_id.clone(),
185 public_key: self.public_key.clone(),
186 signature,
187 state: params.state.clone(),
188 })
189 }
190}
191
192impl Clone for SigningKey {
193 fn clone(&self) -> Self {
194 Self {
195 public_key: self.public_key.clone(),
196 backend: self.backend.clone(),
197 }
198 }
199}
200
201impl std::fmt::Debug for SigningKey {
202 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203 f.debug_struct("SigningKey")
204 .field("public_key", &self.public_key)
205 .finish()
206 }
207}
208
209trait SigningBackend: Send + Sync {
218 fn sign(
219 &self,
220 message: &[u8],
221 ) -> Pin<Box<dyn Future<Output = Result<Signature, SignerError>> + Send + '_>>;
222}
223
224struct SecretKeyBackend {
226 secret_key: SecretKey,
227}
228
229impl SigningBackend for SecretKeyBackend {
230 fn sign(
231 &self,
232 message: &[u8],
233 ) -> Pin<Box<dyn Future<Output = Result<Signature, SignerError>> + Send + '_>> {
234 let sig = self.secret_key.sign(message);
235 Box::pin(async move { Ok(sig) })
236 }
237}
238
239#[derive(Clone)]
259pub struct InMemorySigner {
260 account_id: AccountId,
261 secret_key: SecretKey,
262 public_key: PublicKey,
263}
264
265impl InMemorySigner {
266 pub fn new(
277 account_id: impl AsRef<str>,
278 secret_key: impl AsRef<str>,
279 ) -> Result<Self, crate::error::Error> {
280 let account_id: AccountId = account_id.as_ref().parse()?;
281 let secret_key: SecretKey = secret_key.as_ref().parse()?;
282 let public_key = secret_key.public_key();
283
284 Ok(Self {
285 account_id,
286 secret_key,
287 public_key,
288 })
289 }
290
291 pub fn from_secret_key(account_id: AccountId, secret_key: SecretKey) -> Self {
293 let public_key = secret_key.public_key();
294 Self {
295 account_id,
296 secret_key,
297 public_key,
298 }
299 }
300
301 pub fn from_seed_phrase(
321 account_id: impl AsRef<str>,
322 phrase: impl AsRef<str>,
323 ) -> Result<Self, crate::error::Error> {
324 let account_id: AccountId = account_id.as_ref().parse()?;
325 let secret_key = SecretKey::from_seed_phrase(phrase)?;
326 Ok(Self::from_secret_key(account_id, secret_key))
327 }
328
329 pub fn from_seed_phrase_with_path(
349 account_id: impl AsRef<str>,
350 phrase: impl AsRef<str>,
351 hd_path: impl AsRef<str>,
352 ) -> Result<Self, crate::error::Error> {
353 let account_id: AccountId = account_id.as_ref().parse()?;
354 let secret_key = SecretKey::from_seed_phrase_with_path(phrase, hd_path)?;
355 Ok(Self::from_secret_key(account_id, secret_key))
356 }
357
358 pub fn public_key(&self) -> &PublicKey {
360 &self.public_key
361 }
362}
363
364impl std::fmt::Debug for InMemorySigner {
365 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
366 f.debug_struct("InMemorySigner")
367 .field("account_id", &self.account_id)
368 .field("public_key", &self.public_key)
369 .finish()
370 }
371}
372
373impl Signer for InMemorySigner {
374 fn account_id(&self) -> &AccountId {
375 &self.account_id
376 }
377
378 fn key(&self) -> SigningKey {
379 SigningKey::new(self.secret_key.clone())
380 }
381}
382
383#[derive(Clone)]
400pub struct FileSigner {
401 inner: InMemorySigner,
402}
403
404#[derive(serde::Deserialize)]
406struct CredentialFile {
407 #[serde(alias = "secret_key")]
408 private_key: String,
409}
410
411impl FileSigner {
412 pub fn new(
428 network: impl AsRef<str>,
429 account_id: impl AsRef<str>,
430 ) -> Result<Self, crate::error::Error> {
431 let home = dirs::home_dir().ok_or_else(|| {
432 crate::error::Error::Config("Could not determine home directory".to_string())
433 })?;
434 let path = home
435 .join(".near-credentials")
436 .join(network.as_ref())
437 .join(format!("{}.json", account_id.as_ref()));
438
439 Self::from_file(&path, account_id)
440 }
441
442 pub fn from_file(
449 path: impl AsRef<Path>,
450 account_id: impl AsRef<str>,
451 ) -> Result<Self, crate::error::Error> {
452 let content = std::fs::read_to_string(path.as_ref()).map_err(|e| {
453 crate::error::Error::Config(format!(
454 "Failed to read credentials file {}: {}",
455 path.as_ref().display(),
456 e
457 ))
458 })?;
459
460 let cred: CredentialFile = serde_json::from_str(&content).map_err(|e| {
461 crate::error::Error::Config(format!(
462 "Failed to parse credentials file {}: {}",
463 path.as_ref().display(),
464 e
465 ))
466 })?;
467
468 let inner = InMemorySigner::new(account_id, &cred.private_key)?;
469 Ok(Self { inner })
470 }
471
472 pub fn public_key(&self) -> &PublicKey {
474 self.inner.public_key()
475 }
476
477 pub fn into_inner(self) -> InMemorySigner {
479 self.inner
480 }
481}
482
483impl std::fmt::Debug for FileSigner {
484 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
485 f.debug_struct("FileSigner")
486 .field("account_id", &self.inner.account_id)
487 .field("public_key", &self.inner.public_key)
488 .finish()
489 }
490}
491
492impl Signer for FileSigner {
493 fn account_id(&self) -> &AccountId {
494 self.inner.account_id()
495 }
496
497 fn key(&self) -> SigningKey {
498 self.inner.key()
499 }
500}
501
502#[derive(Clone)]
521pub struct EnvSigner {
522 inner: InMemorySigner,
523}
524
525impl EnvSigner {
526 pub fn new() -> Result<Self, crate::error::Error> {
534 Self::from_env_vars("NEAR_ACCOUNT_ID", "NEAR_PRIVATE_KEY")
535 }
536
537 pub fn from_env_vars(account_var: &str, key_var: &str) -> Result<Self, crate::error::Error> {
544 let account_id = std::env::var(account_var).map_err(|_| {
545 crate::error::Error::Config(format!("Environment variable {} not set", account_var))
546 })?;
547
548 let private_key = std::env::var(key_var).map_err(|_| {
549 crate::error::Error::Config(format!("Environment variable {} not set", key_var))
550 })?;
551
552 let inner = InMemorySigner::new(&account_id, &private_key)?;
553 Ok(Self { inner })
554 }
555
556 pub fn public_key(&self) -> &PublicKey {
558 self.inner.public_key()
559 }
560
561 pub fn into_inner(self) -> InMemorySigner {
563 self.inner
564 }
565}
566
567impl std::fmt::Debug for EnvSigner {
568 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
569 f.debug_struct("EnvSigner")
570 .field("account_id", &self.inner.account_id)
571 .field("public_key", &self.inner.public_key)
572 .finish()
573 }
574}
575
576impl Signer for EnvSigner {
577 fn account_id(&self) -> &AccountId {
578 self.inner.account_id()
579 }
580
581 fn key(&self) -> SigningKey {
582 self.inner.key()
583 }
584}
585
586pub struct RotatingSigner {
642 account_id: AccountId,
643 keys: Vec<SecretKey>,
644 counter: AtomicUsize,
645}
646
647impl RotatingSigner {
648 pub fn new(
661 account_id: impl AsRef<str>,
662 keys: Vec<SecretKey>,
663 ) -> Result<Self, crate::error::Error> {
664 if keys.is_empty() {
665 return Err(crate::error::Error::Config(
666 "RotatingSigner requires at least one key".to_string(),
667 ));
668 }
669
670 let account_id: AccountId = account_id.as_ref().parse()?;
671
672 Ok(Self {
673 account_id,
674 keys,
675 counter: AtomicUsize::new(0),
676 })
677 }
678
679 pub fn from_signers(signers: Vec<InMemorySigner>) -> Result<Self, crate::error::Error> {
698 if signers.is_empty() {
699 return Err(crate::error::Error::Config(
700 "RotatingSigner requires at least one signer".to_string(),
701 ));
702 }
703
704 let account_id = signers[0].account_id().clone();
705 for signer in &signers[1..] {
706 if signer.account_id() != &account_id {
707 return Err(crate::error::Error::Config(format!(
708 "All signers must share the same account ID, got {} and {}",
709 account_id,
710 signer.account_id()
711 )));
712 }
713 }
714
715 let keys = signers.into_iter().map(|s| s.secret_key).collect();
716 Ok(Self {
717 account_id,
718 keys,
719 counter: AtomicUsize::new(0),
720 })
721 }
722
723 pub fn from_key_strings(
730 account_id: impl AsRef<str>,
731 keys: &[impl AsRef<str>],
732 ) -> Result<Self, crate::error::Error> {
733 let parsed_keys: Result<Vec<SecretKey>, _> =
734 keys.iter().map(|k| k.as_ref().parse()).collect();
735 Self::new(account_id, parsed_keys?)
736 }
737
738 pub fn key_count(&self) -> usize {
740 self.keys.len()
741 }
742
743 pub fn public_keys(&self) -> Vec<PublicKey> {
745 self.keys.iter().map(|sk| sk.public_key()).collect()
746 }
747
748 pub fn signing_keys(&self) -> Vec<SigningKey> {
753 self.keys
754 .iter()
755 .map(|sk| SigningKey::new(sk.clone()))
756 .collect()
757 }
758
759 pub fn into_per_key_signers(self) -> Vec<InMemorySigner> {
779 let account_id = self.account_id;
780 self.keys
781 .into_iter()
782 .map(|sk| InMemorySigner::from_secret_key(account_id.clone(), sk))
783 .collect()
784 }
785}
786
787impl std::fmt::Debug for RotatingSigner {
788 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
789 f.debug_struct("RotatingSigner")
790 .field("account_id", &self.account_id)
791 .field("key_count", &self.keys.len())
792 .field("counter", &self.counter.load(Ordering::Relaxed))
793 .finish()
794 }
795}
796
797impl Signer for RotatingSigner {
798 fn account_id(&self) -> &AccountId {
799 &self.account_id
800 }
801
802 fn key(&self) -> SigningKey {
803 let idx = self.counter.fetch_add(1, Ordering::Relaxed) % self.keys.len();
805 SigningKey::new(self.keys[idx].clone())
806 }
807}
808
809#[cfg(test)]
814mod tests {
815 use super::*;
816 use crate::types::{Action, CryptoHash, NearToken, Transaction};
817
818 #[tokio::test]
819 async fn test_in_memory_signer() {
820 let signer = InMemorySigner::new(
821 "alice.testnet",
822 "ed25519:3D4YudUahN1nawWogh8pAKSj92sUNMdbZGjn7kERKzYoTy8tnFQuwoGUC51DowKqorvkr2pytJSnwuSbsNVfqygr",
823 )
824 .unwrap();
825
826 assert_eq!(signer.account_id().as_str(), "alice.testnet");
827
828 let key = signer.key();
829 let message = b"test message";
830 let signature = key.sign(message).await.unwrap();
831
832 assert_eq!(key.public_key(), signer.public_key());
834 assert!(!signature.as_bytes().is_empty());
835 }
836
837 #[tokio::test]
838 async fn test_signature_consistency() {
839 let signer = InMemorySigner::new(
841 "alice.testnet",
842 "ed25519:3D4YudUahN1nawWogh8pAKSj92sUNMdbZGjn7kERKzYoTy8tnFQuwoGUC51DowKqorvkr2pytJSnwuSbsNVfqygr",
843 )
844 .unwrap();
845
846 let key = signer.key();
847 let message = b"test message";
848 let sig1 = key.sign(message).await.unwrap();
849 let sig2 = key.sign(message).await.unwrap();
850
851 assert_eq!(sig1.as_bytes(), sig2.as_bytes());
852 }
853
854 #[tokio::test]
855 async fn test_different_messages_different_signatures() {
856 let signer = InMemorySigner::new(
857 "alice.testnet",
858 "ed25519:3D4YudUahN1nawWogh8pAKSj92sUNMdbZGjn7kERKzYoTy8tnFQuwoGUC51DowKqorvkr2pytJSnwuSbsNVfqygr",
859 )
860 .unwrap();
861
862 let key = signer.key();
863 let sig1 = key.sign(b"message 1").await.unwrap();
864 let sig2 = key.sign(b"message 2").await.unwrap();
865
866 assert_ne!(sig1.as_bytes(), sig2.as_bytes());
867 }
868
869 #[tokio::test]
870 async fn test_transaction_signing_with_signer_trait() {
871 let secret_key = SecretKey::generate_ed25519();
872 let signer = InMemorySigner::from_secret_key("alice.testnet".parse().unwrap(), secret_key);
873
874 let key = signer.key();
876
877 let tx = Transaction::new(
879 signer.account_id().clone(),
880 key.public_key().clone(),
881 1,
882 "bob.testnet".parse().unwrap(),
883 CryptoHash::ZERO,
884 vec![Action::transfer(NearToken::from_near(1))],
885 );
886
887 let tx_hash = tx.get_hash();
889 let signature = key.sign(tx_hash.as_bytes()).await.unwrap();
890
891 assert_eq!(signature.as_bytes().len(), 64);
893
894 let signed_tx = crate::types::SignedTransaction {
896 transaction: tx,
897 signature,
898 };
899
900 let bytes = signed_tx.to_bytes();
902 assert!(!bytes.is_empty());
903
904 let base64 = signed_tx.to_base64();
906 assert!(!base64.is_empty());
907 }
908
909 #[tokio::test]
910 async fn test_rotating_signer() {
911 let keys = vec![
912 SecretKey::generate_ed25519(),
913 SecretKey::generate_ed25519(),
914 SecretKey::generate_ed25519(),
915 ];
916 let expected_public_keys: Vec<_> = keys.iter().map(|k| k.public_key()).collect();
917
918 let signer = RotatingSigner::new("bot.testnet", keys).unwrap();
919
920 let key1 = signer.key();
922 assert_eq!(key1.public_key(), &expected_public_keys[0]);
923
924 let key2 = signer.key();
925 assert_eq!(key2.public_key(), &expected_public_keys[1]);
926
927 let key3 = signer.key();
928 assert_eq!(key3.public_key(), &expected_public_keys[2]);
929
930 let key4 = signer.key();
932 assert_eq!(key4.public_key(), &expected_public_keys[0]);
933 }
934
935 #[tokio::test]
936 async fn test_rotating_signer_atomic_key_claiming() {
937 let keys = vec![SecretKey::generate_ed25519(), SecretKey::generate_ed25519()];
939 let expected_pks: Vec<_> = keys.iter().map(|k| k.public_key()).collect();
940
941 let signer = RotatingSigner::new("bot.testnet", keys.clone()).unwrap();
942 let message = b"test";
943
944 let key1 = signer.key();
946 assert_eq!(key1.public_key(), &expected_pks[0]);
947 let sig1 = key1.sign(message).await.unwrap();
948 let expected_sig1 = keys[0].sign(message);
950 assert_eq!(sig1.as_bytes(), expected_sig1.as_bytes());
951
952 let key2 = signer.key();
954 assert_eq!(key2.public_key(), &expected_pks[1]);
955 let sig2 = key2.sign(message).await.unwrap();
956 let expected_sig2 = keys[1].sign(message);
957 assert_eq!(sig2.as_bytes(), expected_sig2.as_bytes());
958
959 assert_ne!(sig1.as_bytes(), sig2.as_bytes());
961 }
962
963 #[test]
964 fn test_rotating_signer_empty_keys() {
965 let result = RotatingSigner::new("bot.testnet", vec![]);
966 assert!(result.is_err());
967 }
968
969 #[test]
970 fn test_env_signer_missing_vars() {
971 let result = EnvSigner::from_env_vars("NONEXISTENT_VAR_1", "NONEXISTENT_VAR_2");
973 assert!(result.is_err());
974 }
975
976 #[test]
977 fn test_signer_from_secret_key() {
978 let secret = SecretKey::generate_ed25519();
979 let expected_pk = secret.public_key();
980
981 let signer = InMemorySigner::from_secret_key("alice.testnet".parse().unwrap(), secret);
982
983 assert_eq!(signer.account_id().as_str(), "alice.testnet");
984 assert_eq!(signer.public_key(), &expected_pk);
985 }
986
987 #[test]
988 fn test_rotating_signer_key_count() {
989 let keys = vec![
990 SecretKey::generate_ed25519(),
991 SecretKey::generate_ed25519(),
992 SecretKey::generate_ed25519(),
993 ];
994
995 let signer = RotatingSigner::new("bot.testnet", keys).unwrap();
996
997 assert_eq!(signer.key_count(), 3);
998 assert_eq!(signer.public_keys().len(), 3);
999 }
1000
1001 #[test]
1002 fn test_rotating_signer_from_key_strings() {
1003 let keys = [
1004 "ed25519:3D4YudUahN1nawWogh8pAKSj92sUNMdbZGjn7kERKzYoTy8tnFQuwoGUC51DowKqorvkr2pytJSnwuSbsNVfqygr",
1005 "ed25519:4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi5kL6YJt9Z6iLqMBkVfqDH2Zj8bxqXTdMkNmvPcAD8LqCZ",
1006 ];
1007
1008 let signer = RotatingSigner::from_key_strings("bot.testnet", &keys).unwrap();
1009
1010 assert_eq!(signer.key_count(), 2);
1011 assert_eq!(signer.account_id().as_str(), "bot.testnet");
1012 }
1013
1014 #[test]
1015 fn test_in_memory_signer_debug_hides_secret() {
1016 let signer = InMemorySigner::new(
1017 "alice.testnet",
1018 "ed25519:3D4YudUahN1nawWogh8pAKSj92sUNMdbZGjn7kERKzYoTy8tnFQuwoGUC51DowKqorvkr2pytJSnwuSbsNVfqygr",
1019 )
1020 .unwrap();
1021
1022 let debug_str = format!("{:?}", signer);
1023
1024 assert!(debug_str.contains("alice.testnet"));
1026 assert!(debug_str.contains("public_key"));
1027 assert!(!debug_str.contains("secret_key"));
1028 assert!(!debug_str.contains("3D4YudUahN1nawWogh"));
1029 }
1030
1031 #[test]
1032 fn test_rotating_signer_from_signers() {
1033 let keys = vec![SecretKey::generate_ed25519(), SecretKey::generate_ed25519()];
1034 let expected_public_keys: Vec<_> = keys.iter().map(|k| k.public_key()).collect();
1035
1036 let signers: Vec<InMemorySigner> = keys
1037 .into_iter()
1038 .map(|sk| InMemorySigner::from_secret_key("bot.testnet".parse().unwrap(), sk))
1039 .collect();
1040
1041 let rotating = RotatingSigner::from_signers(signers).unwrap();
1042 assert_eq!(rotating.key_count(), 2);
1043 assert_eq!(rotating.public_keys(), expected_public_keys);
1044 }
1045
1046 #[test]
1047 fn test_rotating_signer_from_signers_mismatched_accounts() {
1048 let signers = vec![
1049 InMemorySigner::from_secret_key(
1050 "alice.testnet".parse().unwrap(),
1051 SecretKey::generate_ed25519(),
1052 ),
1053 InMemorySigner::from_secret_key(
1054 "bob.testnet".parse().unwrap(),
1055 SecretKey::generate_ed25519(),
1056 ),
1057 ];
1058
1059 let result = RotatingSigner::from_signers(signers);
1060 assert!(result.is_err());
1061 }
1062
1063 #[test]
1064 fn test_rotating_signer_signing_keys() {
1065 let keys = vec![
1066 SecretKey::generate_ed25519(),
1067 SecretKey::generate_ed25519(),
1068 SecretKey::generate_ed25519(),
1069 ];
1070 let expected_public_keys: Vec<_> = keys.iter().map(|k| k.public_key()).collect();
1071
1072 let signer = RotatingSigner::new("bot.testnet", keys).unwrap();
1073
1074 let counter_before = signer.counter.load(Ordering::Relaxed);
1076 let signing_keys = signer.signing_keys();
1077 let counter_after = signer.counter.load(Ordering::Relaxed);
1078 assert_eq!(counter_before, counter_after);
1079
1080 assert_eq!(signing_keys.len(), 3);
1082 for (sk, expected_pk) in signing_keys.iter().zip(&expected_public_keys) {
1083 assert_eq!(sk.public_key(), expected_pk);
1084 }
1085 }
1086
1087 #[test]
1088 fn test_rotating_signer_into_per_key_signers() {
1089 let keys = vec![SecretKey::generate_ed25519(), SecretKey::generate_ed25519()];
1090 let expected_public_keys: Vec<_> = keys.iter().map(|k| k.public_key()).collect();
1091
1092 let signer = RotatingSigner::new("bot.testnet", keys).unwrap();
1093 let per_key = signer.into_per_key_signers();
1094
1095 assert_eq!(per_key.len(), 2);
1096 for (ims, expected_pk) in per_key.iter().zip(&expected_public_keys) {
1097 assert_eq!(ims.account_id().as_str(), "bot.testnet");
1098 assert_eq!(ims.public_key(), expected_pk);
1099 }
1100 }
1101
1102 #[test]
1103 fn test_signing_key_is_clone() {
1104 let signer = InMemorySigner::new(
1105 "alice.testnet",
1106 "ed25519:3D4YudUahN1nawWogh8pAKSj92sUNMdbZGjn7kERKzYoTy8tnFQuwoGUC51DowKqorvkr2pytJSnwuSbsNVfqygr",
1107 )
1108 .unwrap();
1109
1110 let key = signer.key();
1111 let key_clone = key.clone();
1112
1113 assert_eq!(key.public_key(), key_clone.public_key());
1114 }
1115}