near_api/signer/
keystore.rs1use futures::future::join_all;
2use near_api_types::{AccessKeyPermission, AccountId, PublicKey, SecretKey};
3use tracing::{debug, info, instrument, trace, warn};
4
5use crate::{
6 config::NetworkConfig,
7 errors::{KeyStoreError, SignerError},
8};
9
10use super::{AccountKeyPair, SignerTrait};
11
12const KEYSTORE_SIGNER_TARGET: &str = "near_api::signer::keystore";
13
14#[derive(Debug, Clone)]
15pub struct KeystoreSigner {
16 potential_pubkeys: Vec<PublicKey>,
17}
18
19#[async_trait::async_trait]
20impl SignerTrait for KeystoreSigner {
21 #[instrument(skip(self))]
22 async fn get_secret_key(
23 &self,
24 signer_id: &AccountId,
25 public_key: &PublicKey,
26 ) -> Result<SecretKey, SignerError> {
27 debug!(target: KEYSTORE_SIGNER_TARGET, "Searching for matching public key");
28 self.potential_pubkeys
29 .iter()
30 .find(|key| *key == public_key)
31 .ok_or(SignerError::PublicKeyIsNotAvailable)?;
32
33 info!(target: KEYSTORE_SIGNER_TARGET, "Retrieving secret key");
34 let secret = if let Ok(secret) =
36 Self::get_secret_key(signer_id, public_key.clone(), "mainnet").await
37 {
38 secret
39 } else {
40 Self::get_secret_key(signer_id, public_key.clone(), "testnet")
41 .await
42 .map_err(|_| SignerError::SecretKeyIsNotAvailable)?
43 };
44
45 info!(target: KEYSTORE_SIGNER_TARGET, "Secret key prepared successfully");
46 Ok(secret.private_key)
47 }
48
49 #[instrument(skip(self))]
50 fn get_public_key(&self) -> Result<PublicKey, SignerError> {
51 debug!(target: KEYSTORE_SIGNER_TARGET, "Retrieving first public key");
52 self.potential_pubkeys
53 .first()
54 .cloned()
55 .ok_or(SignerError::PublicKeyIsNotAvailable)
56 }
57}
58
59impl KeystoreSigner {
60 pub fn new_with_pubkey(pub_key: PublicKey) -> Self {
61 debug!(target: KEYSTORE_SIGNER_TARGET, "Creating new KeystoreSigner with public key");
62 Self {
63 potential_pubkeys: vec![pub_key],
64 }
65 }
66
67 #[instrument(skip(network), fields(account_id = %account_id, network_name = %network.network_name))]
68 pub async fn search_for_keys(
69 account_id: AccountId,
70 network: &NetworkConfig,
71 ) -> Result<Self, KeyStoreError> {
72 info!(target: KEYSTORE_SIGNER_TARGET, "Searching for keys for account");
73 let account_keys = crate::account::Account(account_id.clone())
74 .list_keys()
75 .fetch_from(network)
76 .await
77 .map_err(KeyStoreError::QueryError)?;
78
79 debug!(target: KEYSTORE_SIGNER_TARGET, "Filtering and collecting potential public keys");
80 let potential_pubkeys = account_keys
81 .data
82 .iter()
83 .filter(|(_, access_key)| {
85 matches!(access_key.permission, AccessKeyPermission::FullAccess)
86 })
87 .map(|(public_key, _)| public_key.clone())
88 .map(|key| Self::get_secret_key(&account_id, key, &network.network_name));
89 let potential_pubkeys: Vec<PublicKey> = join_all(potential_pubkeys)
90 .await
91 .into_iter()
92 .flat_map(|result| result.map(|keypair| keypair.public_key).ok())
93 .collect();
94
95 info!(target: KEYSTORE_SIGNER_TARGET, "KeystoreSigner created with {} potential public keys", potential_pubkeys.len());
96 Ok(Self { potential_pubkeys })
97 }
98
99 #[instrument(skip(public_key), fields(account_id = %account_id, network_name = %network_name))]
100 async fn get_secret_key(
101 account_id: &AccountId,
102 public_key: PublicKey,
103 network_name: &str,
104 ) -> Result<AccountKeyPair, KeyStoreError> {
105 trace!(target: KEYSTORE_SIGNER_TARGET, "Retrieving secret key from keyring");
106 let service_name =
107 std::borrow::Cow::Owned(format!("near-{}-{}", network_name, account_id.as_str()));
108 let user = format!("{account_id}:{public_key}");
109
110 let password = tokio::task::spawn_blocking(move || {
113 let password = keyring::Entry::new(&service_name, &user)?.get_password()?;
114
115 Ok::<_, KeyStoreError>(password)
116 })
117 .await
118 .unwrap_or_else(|tokio_join_error| Err(KeyStoreError::from(tokio_join_error)))?;
119
120 debug!(target: KEYSTORE_SIGNER_TARGET, "Deserializing account key pair");
121 Ok(serde_json::from_str(&password)?)
122 }
123}