Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[cli] Allow for AIP-80 compliant strings while importing #15039

Merged
merged 4 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/aptos/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
All notable changes to the Aptos CLI will be captured in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and the format set out by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## Unreleased
- Add support for AIP-80 compliant strings when importing using the CLI arguments or manual input.

## [4.4.0] - 2024/11/06
- Fix typos in `aptos move compile` help text.
Expand Down
8 changes: 6 additions & 2 deletions crates/aptos/src/common/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ use crate::{
ConfigSearchMode, EncodingOptions, HardwareWalletOptions, PrivateKeyInputOptions,
ProfileConfig, ProfileOptions, PromptOptions, RngArgs, DEFAULT_PROFILE,
},
utils::{explorer_account_link, fund_account, prompt_yes_with_override, read_line},
utils::{
explorer_account_link, fund_account, prompt_yes_with_override, read_line,
strip_private_key_prefix,
},
},
};
use aptos_crypto::{ed25519::Ed25519PrivateKey, PrivateKey, ValidCryptoMaterialStringExt};
Expand Down Expand Up @@ -218,7 +221,8 @@ impl CliCommand<()> for InitTool {
.generate_ed25519_private_key()
}
} else {
Ed25519PrivateKey::from_encoded_string(input).map_err(|err| {
let stripped = strip_private_key_prefix(&input.to_string())?;
Ed25519PrivateKey::from_encoded_string(&stripped).map_err(|err| {
CliError::UnableToParse("Ed25519PrivateKey", err.to_string())
})?
}
Expand Down
13 changes: 7 additions & 6 deletions crates/aptos/src/common/types.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

use super::utils::{explorer_transaction_link, fund_account};
use super::utils::{explorer_transaction_link, fund_account, strip_private_key_prefix};
use crate::{
common::{
init::Network,
local_simulation,
utils::{
check_if_file_exists, create_dir_if_not_exist, dir_default_to_current,
get_account_with_state, get_auth_key, get_sequence_number, parse_json_file,
prompt_yes_with_override, read_from_file, start_logger, to_common_result,
to_common_success_result, write_to_file, write_to_file_with_opts,
check_if_file_exists, create_dir_if_not_exist, deserialize_private_key_with_prefix,
dir_default_to_current, get_account_with_state, get_auth_key, get_sequence_number,
parse_json_file, prompt_yes_with_override, read_from_file, start_logger,
to_common_result, to_common_success_result, write_to_file, write_to_file_with_opts,
write_to_user_only_file,
},
},
Expand Down Expand Up @@ -251,6 +251,7 @@ pub struct ProfileConfig {
pub network: Option<Network>,
/// Private key for commands.
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(deserialize_with = "deserialize_private_key_with_prefix")]
pub private_key: Option<Ed25519PrivateKey>,
/// Public key for commands
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down Expand Up @@ -691,7 +692,7 @@ pub trait ParsePrivateKey {
encoding.load_key("--private-key-file", file.as_path())?,
))
} else if let Some(ref key) = private_key {
let key = key.as_bytes().to_vec();
let key = strip_private_key_prefix(key)?.as_bytes().to_vec();
Ok(Some(encoding.decode_key("--private-key", key)?))
} else {
Ok(None)
Expand Down
58 changes: 57 additions & 1 deletion crates/aptos/src/common/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use aptos_types::{
use itertools::Itertools;
use move_core_types::{account_address::AccountAddress, language_storage::CORE_CODE_ADDRESS};
use reqwest::Url;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
#[cfg(unix)]
use std::os::unix::fs::OpenOptionsExt;
use std::{
Expand Down Expand Up @@ -597,3 +597,59 @@ pub fn explorer_transaction_link(
)
}
}

/// Strips the private key prefix for a given key string if it is AIP-80 compliant.
///
/// [Read about AIP-80](https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-80.md)
pub fn strip_private_key_prefix(key: &String) -> CliTypedResult<String> {
let disabled_prefixes = ["secp256k1-priv-"];
let enabled_prefixes = ["ed25519-priv-"];

// Check for disabled prefixes first
for prefix in disabled_prefixes {
if key.starts_with(prefix) {
return Err(CliError::UnexpectedError(format!(
"Private key not supported. Cannot parse private key with '{}' prefix.",
prefix
)));
}
}

// Try to strip enabled prefixes
for prefix in enabled_prefixes {
if key.starts_with(prefix) {
return Ok(key.strip_prefix(prefix).unwrap().to_string());
}
}

// If no prefix is found, return the original key
Ok(key.to_string())
}

/// Deserializes an Ed25519 private key with a prefix AIP-80 prefix if present.
///
/// [Read about AIP-80](https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-80.md)
pub fn deserialize_private_key_with_prefix<'de, D>(
deserializer: D,
) -> Result<Option<Ed25519PrivateKey>, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::Error;

// Deserialize the field as an Option<String>
let opt: Option<String> = Option::deserialize(deserializer)?;

// Transform Option<String> into Option<Ed25519PrivateKey>
opt.map_or(Ok(None), |s| {
// Use strip_private_key_prefix to handle the AIP-80 prefix
let stripped = strip_private_key_prefix(&s).map_err(D::Error::custom)?;

// Attempt deserialization with the stripped key
Ed25519PrivateKey::deserialize(serde::de::value::StrDeserializer::<D::Error>::new(
&stripped,
))
.map(Some)
.map_err(D::Error::custom)
})
}
Loading