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)]
owner_account_id: crate::types::account_id::AccountId,
#[interactive_clap(named_arg)]
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 => {
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")?;
if let crate::Verbosity::Interactive | crate::Verbosity::TeachMe =
previous_context.verbosity
{
eprint!("Public key (printed to stdout): ");
}
println!("{}", 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
}
}