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, PublicKeyError, 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(PublicKeyError::PublicKeyIsNotAvailable)?;
32
33 info!(target: KEYSTORE_SIGNER_TARGET, "Retrieving secret key");
34 let secret =
36 if let Ok(secret) = Self::get_secret_key(signer_id, public_key, "mainnet").await {
37 secret
38 } else {
39 Self::get_secret_key(signer_id, public_key, "testnet")
40 .await
41 .map_err(|_| SignerError::SecretKeyIsNotAvailable)?
42 };
43
44 info!(target: KEYSTORE_SIGNER_TARGET, "Secret key prepared successfully");
45 Ok(secret.private_key)
46 }
47
48 #[instrument(skip(self))]
49 fn get_public_key(&self) -> Result<PublicKey, PublicKeyError> {
50 debug!(target: KEYSTORE_SIGNER_TARGET, "Retrieving first public key");
51 self.potential_pubkeys
52 .first()
53 .cloned()
54 .ok_or(PublicKeyError::PublicKeyIsNotAvailable)
55 }
56}
57
58impl KeystoreSigner {
59 pub fn new_with_pubkey(pub_key: PublicKey) -> Self {
60 debug!(target: KEYSTORE_SIGNER_TARGET, "Creating new KeystoreSigner with public key");
61 Self {
62 potential_pubkeys: vec![pub_key],
63 }
64 }
65
66 #[instrument(skip(network), fields(account_id = %account_id, network_name = %network.network_name))]
67 pub async fn search_for_keys(
68 account_id: AccountId,
69 network: &NetworkConfig,
70 ) -> Result<Self, KeyStoreError> {
71 info!(target: KEYSTORE_SIGNER_TARGET, "Searching for keys for account");
72 let account_keys = crate::account::Account(account_id.clone())
73 .list_keys()
74 .fetch_from(network)
75 .await
76 .map_err(KeyStoreError::QueryError)?;
77
78 debug!(target: KEYSTORE_SIGNER_TARGET, "Filtering and collecting potential public keys");
79 let potential_pubkeys = account_keys
80 .data
81 .iter()
82 .filter(|(_, access_key)| {
84 matches!(access_key.permission, AccessKeyPermission::FullAccess)
85 })
86 .map(|(public_key, _)| *public_key)
87 .map(|key| Self::get_secret_key(&account_id, key, &network.network_name));
88 let potential_pubkeys: Vec<PublicKey> = join_all(potential_pubkeys)
89 .await
90 .into_iter()
91 .flat_map(|result| result.map(|keypair| keypair.public_key).ok())
92 .collect();
93
94 info!(target: KEYSTORE_SIGNER_TARGET, "KeystoreSigner created with {} potential public keys", potential_pubkeys.len());
95 Ok(Self { potential_pubkeys })
96 }
97
98 #[instrument(skip(public_key), fields(account_id = %account_id, network_name = %network_name))]
99 async fn get_secret_key(
100 account_id: &AccountId,
101 public_key: PublicKey,
102 network_name: &str,
103 ) -> Result<AccountKeyPair, KeyStoreError> {
104 trace!(target: KEYSTORE_SIGNER_TARGET, "Retrieving secret key from keyring");
105 let service_name =
106 std::borrow::Cow::Owned(format!("near-{}-{}", network_name, account_id.as_str()));
107 let user = format!("{account_id}:{public_key}");
108
109 let password = tokio::task::spawn_blocking(move || {
112 let password = keyring::Entry::new(&service_name, &user)?.get_password()?;
113
114 Ok::<_, KeyStoreError>(password)
115 })
116 .await
117 .unwrap_or_else(|tokio_join_error| Err(KeyStoreError::from(tokio_join_error)))?;
118
119 debug!(target: KEYSTORE_SIGNER_TARGET, "Deserializing account key pair");
120 Ok(serde_json::from_str(&password)?)
121 }
122}