-
Notifications
You must be signed in to change notification settings - Fork 60
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
FroVolod
committed
Jan 16, 2025
1 parent
366c3ee
commit 942ebb6
Showing
6 changed files
with
377 additions
and
0 deletions.
There are no files selected for viewing
114 changes: 114 additions & 0 deletions
114
src/commands/account/get_public_key/from_keychain/mod.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
use color_eyre::eyre::WrapErr; | ||
|
||
use crate::common::JsonRpcClientExt; | ||
use crate::common::RpcQueryResponseExt; | ||
|
||
#[derive(Debug, Clone, interactive_clap::InteractiveClap)] | ||
#[interactive_clap(input_context = crate::GlobalContext)] | ||
#[interactive_clap(output_context = PublicKeyFromKeychainContext)] | ||
pub struct PublicKeyFromKeychain { | ||
#[interactive_clap(skip_default_input_arg)] | ||
/// For which account do you need to view the public key? | ||
owner_account_id: crate::types::account_id::AccountId, | ||
#[interactive_clap(named_arg)] | ||
/// Select network | ||
network_config: crate::network::Network, | ||
} | ||
|
||
#[derive(Clone)] | ||
pub struct PublicKeyFromKeychainContext(crate::network::NetworkContext); | ||
|
||
impl PublicKeyFromKeychainContext { | ||
pub fn from_previous_context( | ||
previous_context: crate::GlobalContext, | ||
scope: &<PublicKeyFromKeychain as interactive_clap::ToInteractiveClapContextScope>::InteractiveClapContextScope, | ||
) -> color_eyre::eyre::Result<Self> { | ||
let account_id = scope.owner_account_id.clone(); | ||
|
||
let on_after_getting_network_callback: crate::network::OnAfterGettingNetworkCallback = | ||
std::sync::Arc::new({ | ||
move |network_config| { | ||
if previous_context.offline { | ||
eprintln!( | ||
"\nThe signer's public key cannot be verified and retrieved offline." | ||
); | ||
return Ok(()); | ||
} | ||
let service_name = std::borrow::Cow::Owned(format!( | ||
"near-{}-{}", | ||
network_config.network_name, &account_id | ||
)); | ||
|
||
let password = { | ||
let access_key_list = network_config | ||
.json_rpc_client() | ||
.blocking_call_view_access_key_list( | ||
&account_id.clone().into(), | ||
near_primitives::types::Finality::Final.into(), | ||
) | ||
.wrap_err_with(|| { | ||
format!("Failed to fetch access key list for {}", account_id) | ||
})? | ||
.access_key_list_view()?; | ||
|
||
let res = access_key_list | ||
.keys | ||
.into_iter() | ||
.filter(|key| { | ||
matches!( | ||
key.access_key.permission, | ||
near_primitives::views::AccessKeyPermissionView::FullAccess | ||
) | ||
}) | ||
.map(|key| key.public_key) | ||
.find_map(|public_key| { | ||
let keyring = keyring::Entry::new( | ||
&service_name, | ||
&format!("{}:{}", account_id, public_key), | ||
) | ||
.ok()?; | ||
keyring.get_password().ok() | ||
}); | ||
|
||
match res { | ||
Some(password) => password, | ||
None => { | ||
// no access keys found | ||
eprintln!("\nNo access keys found in keychain",); | ||
return Ok(()); | ||
} | ||
} | ||
}; | ||
|
||
let account_key_pair: crate::transaction_signature_options::AccountKeyPair = | ||
serde_json::from_str(&password).wrap_err("Error reading data")?; | ||
eprintln!("\nPublic key: {}", account_key_pair.public_key); | ||
|
||
Ok(()) | ||
} | ||
}); | ||
|
||
Ok(Self(crate::network::NetworkContext { | ||
config: previous_context.config, | ||
interacting_with_account_ids: vec![scope.owner_account_id.clone().into()], | ||
on_after_getting_network_callback, | ||
})) | ||
} | ||
} | ||
|
||
impl PublicKeyFromKeychain { | ||
pub fn input_owner_account_id( | ||
context: &crate::GlobalContext, | ||
) -> color_eyre::eyre::Result<Option<crate::types::account_id::AccountId>> { | ||
crate::common::input_signer_account_id_from_used_account_list( | ||
&context.config.credentials_home_dir, | ||
"For which account do you need to view the public key?", | ||
) | ||
} | ||
} | ||
|
||
impl From<PublicKeyFromKeychainContext> for crate::network::NetworkContext { | ||
fn from(item: PublicKeyFromKeychainContext) -> Self { | ||
item.0 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
#[derive(Debug, Clone, interactive_clap::InteractiveClap)] | ||
#[interactive_clap(input_context = crate::GlobalContext)] | ||
#[interactive_clap(output_context = PublicKeyFromLedgerContext)] | ||
pub struct PublicKeyFromLedger { | ||
#[interactive_clap(skip_default_input_arg)] | ||
seed_phrase_hd_path: crate::types::slip10::BIP32Path, | ||
} | ||
|
||
#[derive(Debug, Clone)] | ||
pub struct PublicKeyFromLedgerContext {} | ||
|
||
impl PublicKeyFromLedgerContext { | ||
pub fn from_previous_context( | ||
_previous_context: crate::GlobalContext, | ||
scope: &<PublicKeyFromLedger as interactive_clap::ToInteractiveClapContextScope>::InteractiveClapContextScope, | ||
) -> color_eyre::eyre::Result<Self> { | ||
let seed_phrase_hd_path = scope.seed_phrase_hd_path.clone(); | ||
eprintln!("Opening the NEAR application... Please approve opening the application"); | ||
near_ledger::open_near_application().map_err(|ledger_error| { | ||
color_eyre::Report::msg(format!("An error happened while trying to open the NEAR application on the ledger: {ledger_error:?}")) | ||
})?; | ||
|
||
std::thread::sleep(std::time::Duration::from_secs(1)); | ||
|
||
eprintln!( | ||
"Please allow getting the PublicKey on Ledger device (HD Path: {})", | ||
seed_phrase_hd_path | ||
); | ||
let public_key = near_ledger::get_public_key(seed_phrase_hd_path.into()).map_err( | ||
|near_ledger_error| { | ||
color_eyre::Report::msg(format!( | ||
"An error occurred while trying to get PublicKey from Ledger device: {:?}", | ||
near_ledger_error | ||
)) | ||
}, | ||
)?; | ||
eprintln!( | ||
"\nPublic key: {}", | ||
near_crypto::PublicKey::ED25519(near_crypto::ED25519PublicKey::from( | ||
public_key.to_bytes(), | ||
)) | ||
); | ||
|
||
Ok(Self {}) | ||
} | ||
} | ||
|
||
impl PublicKeyFromLedger { | ||
pub fn input_seed_phrase_hd_path( | ||
_context: &crate::GlobalContext, | ||
) -> color_eyre::eyre::Result<Option<crate::types::slip10::BIP32Path>> { | ||
crate::transaction_signature_options::sign_with_ledger::input_seed_phrase_hd_path() | ||
} | ||
} |
133 changes: 133 additions & 0 deletions
133
src/commands/account/get_public_key/from_legacy_keychain/mod.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
use color_eyre::eyre::WrapErr; | ||
|
||
use crate::common::JsonRpcClientExt; | ||
use crate::common::RpcQueryResponseExt; | ||
|
||
#[derive(Debug, Clone, interactive_clap::InteractiveClap)] | ||
#[interactive_clap(input_context = crate::GlobalContext)] | ||
#[interactive_clap(output_context = PublicKeyFromLegacyKeychainContext)] | ||
pub struct PublicKeyFromKeychain { | ||
#[interactive_clap(skip_default_input_arg)] | ||
/// For which account do you need to view the public key? | ||
owner_account_id: crate::types::account_id::AccountId, | ||
#[interactive_clap(named_arg)] | ||
/// Select network | ||
network_config: crate::network::Network, | ||
} | ||
|
||
#[derive(Clone)] | ||
pub struct PublicKeyFromLegacyKeychainContext(crate::network::NetworkContext); | ||
|
||
impl PublicKeyFromLegacyKeychainContext { | ||
pub fn from_previous_context( | ||
previous_context: crate::GlobalContext, | ||
scope: &<PublicKeyFromKeychain as interactive_clap::ToInteractiveClapContextScope>::InteractiveClapContextScope, | ||
) -> color_eyre::eyre::Result<Self> { | ||
let config = previous_context.config.clone(); | ||
let account_id = scope.owner_account_id.clone(); | ||
|
||
let on_after_getting_network_callback: crate::network::OnAfterGettingNetworkCallback = | ||
std::sync::Arc::new({ | ||
move |network_config| { | ||
let keychain_folder = config | ||
.credentials_home_dir | ||
.join(&network_config.network_name); | ||
let signer_keychain_folder = keychain_folder.join(account_id.to_string()); | ||
let signer_access_key_file_path: std::path::PathBuf = { | ||
if previous_context.offline { | ||
eprintln!( | ||
"\nThe signer's public key cannot be verified and retrieved offline." | ||
); | ||
return Ok(()); | ||
} | ||
if signer_keychain_folder.exists() { | ||
let full_access_key_filenames = network_config | ||
.json_rpc_client() | ||
.blocking_call_view_access_key_list( | ||
&account_id.clone().into(), | ||
near_primitives::types::Finality::Final.into(), | ||
) | ||
.wrap_err_with(|| { | ||
format!( | ||
"Failed to fetch access KeyList for {}", | ||
account_id | ||
) | ||
})? | ||
.access_key_list_view()? | ||
.keys | ||
.iter() | ||
.filter( | ||
|access_key_info| match access_key_info.access_key.permission { | ||
near_primitives::views::AccessKeyPermissionView::FullAccess => true, | ||
near_primitives::views::AccessKeyPermissionView::FunctionCall { | ||
.. | ||
} => false, | ||
}, | ||
) | ||
.map(|access_key_info| { | ||
format!( | ||
"{}.json", | ||
access_key_info.public_key.to_string().replace(":", "_") | ||
) | ||
.into() | ||
}) | ||
.collect::<std::collections::HashSet<std::ffi::OsString>>(); | ||
|
||
signer_keychain_folder | ||
.read_dir() | ||
.wrap_err("There are no access keys found in the keychain for the signer account. Import an access key for an account before signing transactions with keychain.")? | ||
.filter_map(Result::ok) | ||
.find(|entry| full_access_key_filenames.contains(&entry.file_name())) | ||
.map(|signer_access_key| signer_access_key.path()) | ||
.unwrap_or_else(|| keychain_folder.join(format!( | ||
"{}.json", | ||
account_id | ||
))) | ||
} else { | ||
keychain_folder.join(format!("{}.json", account_id)) | ||
} | ||
}; | ||
let signer_access_key_json = | ||
std::fs::read(&signer_access_key_file_path).wrap_err_with(|| { | ||
format!( | ||
"Access key file for account <{}> on network <{}> not found! \nSearch location: {:?}", | ||
account_id, | ||
network_config.network_name, signer_access_key_file_path | ||
) | ||
})?; | ||
let account_key_pair: crate::transaction_signature_options::AccountKeyPair = | ||
serde_json::from_slice(&signer_access_key_json).wrap_err_with(|| { | ||
format!( | ||
"Error reading data from file: {:?}", | ||
&signer_access_key_file_path | ||
) | ||
})?; | ||
eprintln!("\nPublic key: {}", account_key_pair.public_key); | ||
Ok(()) | ||
} | ||
}); | ||
|
||
Ok(Self(crate::network::NetworkContext { | ||
config: previous_context.config, | ||
interacting_with_account_ids: vec![scope.owner_account_id.clone().into()], | ||
on_after_getting_network_callback, | ||
})) | ||
} | ||
} | ||
|
||
impl PublicKeyFromKeychain { | ||
pub fn input_owner_account_id( | ||
context: &crate::GlobalContext, | ||
) -> color_eyre::eyre::Result<Option<crate::types::account_id::AccountId>> { | ||
crate::common::input_signer_account_id_from_used_account_list( | ||
&context.config.credentials_home_dir, | ||
"For which account do you need to view the public key?", | ||
) | ||
} | ||
} | ||
|
||
impl From<PublicKeyFromLegacyKeychainContext> for crate::network::NetworkContext { | ||
fn from(item: PublicKeyFromLegacyKeychainContext) -> Self { | ||
item.0 | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
src/commands/account/get_public_key/from_seed_phrase/mod.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
use std::str::FromStr; | ||
|
||
#[derive(Debug, Clone, interactive_clap::InteractiveClap)] | ||
#[interactive_clap(input_context = crate::GlobalContext)] | ||
#[interactive_clap(output_context = PublicKeyFromSeedPhraseContext)] | ||
pub struct PublicKeyFromSeedPhrase { | ||
/// Enter the seed-phrase: | ||
master_seed_phrase: String, | ||
} | ||
|
||
#[derive(Debug, Clone)] | ||
pub struct PublicKeyFromSeedPhraseContext; | ||
|
||
impl PublicKeyFromSeedPhraseContext { | ||
pub fn from_previous_context( | ||
_previous_context: crate::GlobalContext, | ||
scope: &<PublicKeyFromSeedPhrase as interactive_clap::ToInteractiveClapContextScope>::InteractiveClapContextScope, | ||
) -> color_eyre::eyre::Result<Self> { | ||
let seed_phrase_hd_path_default = slipped10::BIP32Path::from_str("m/44'/397'/0'").unwrap(); | ||
let public_key = crate::common::get_public_key_from_seed_phrase( | ||
seed_phrase_hd_path_default, | ||
&scope.master_seed_phrase, | ||
)?; | ||
eprintln!("\nPublic key: {}", public_key); | ||
|
||
Ok(Self) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
use strum::{EnumDiscriminants, EnumIter, EnumMessage}; | ||
|
||
mod from_keychain; | ||
#[cfg(feature = "ledger")] | ||
mod from_ledger; | ||
mod from_legacy_keychain; | ||
mod from_seed_phrase; | ||
|
||
#[derive(Debug, Clone, interactive_clap::InteractiveClap)] | ||
#[interactive_clap(context = crate::GlobalContext)] | ||
pub struct GetPublicKey { | ||
#[interactive_clap(subcommand)] | ||
get_public_key_mode: GetPublicKeyMode, | ||
} | ||
|
||
#[derive(Debug, Clone, EnumDiscriminants, interactive_clap::InteractiveClap)] | ||
#[interactive_clap(context = crate::GlobalContext)] | ||
#[strum_discriminants(derive(EnumMessage, EnumIter))] | ||
/// Where do you want to get the public key from? | ||
pub enum GetPublicKeyMode { | ||
#[cfg(feature = "ledger")] | ||
#[strum_discriminants(strum( | ||
message = "from-ledger - Get the public key stored on your Ledger Nano device" | ||
))] | ||
/// Get the public key stored on your Ledger Nano device | ||
FromLedger(self::from_ledger::PublicKeyFromLedger), | ||
#[strum_discriminants(strum( | ||
message = "from-seed-phrase - Get the public key with the seed phrase" | ||
))] | ||
/// Get the public key with the seed phrase | ||
FromSeedPhrase(self::from_seed_phrase::PublicKeyFromSeedPhrase), | ||
#[strum_discriminants(strum( | ||
message = "from-keychain - Get the public key stored in a secure keychain" | ||
))] | ||
/// Get the public key stored in a secure keychain | ||
FromKeychain(self::from_keychain::PublicKeyFromKeychain), | ||
#[strum_discriminants(strum( | ||
message = "from-legacy-keychain - Get the public key stored in the legacy keychain (compatible with the old near CLI)" | ||
))] | ||
/// Get the public key stored in the legacy keychain (compatible with the old near CLI) | ||
FromLegacyKeychain(self::from_legacy_keychain::PublicKeyFromKeychain), | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters