Skip to content
Open
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
6 changes: 3 additions & 3 deletions crates/aptos/src/account/create_resource_account.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

use crate::{
account::derive_resource_account::ResourceAccountSeed,
common::types::{CliCommand, CliTypedResult, TransactionOptions, TransactionSummary},
use crate::common::{
resource_account_seed::ResourceAccountSeed,
types::{CliCommand, CliTypedResult, TransactionOptions, TransactionSummary},
};
use aptos_cached_packages::aptos_stdlib::resource_account_create_resource_account;
use aptos_rest_client::{
Expand Down
81 changes: 4 additions & 77 deletions crates/aptos/src/account/derive_resource_account.rs
Original file line number Diff line number Diff line change
@@ -1,86 +1,13 @@
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

use crate::common::types::{CliCommand, CliError, CliTypedResult};
use aptos_sdk::rest_client::aptos_api_types::HexEncodedBytes;
use crate::common::{
resource_account_seed::ResourceAccountSeed,
types::{CliCommand, CliTypedResult},
};
use aptos_types::account_address::{create_resource_address, AccountAddress};
use async_trait::async_trait;
use clap::Parser;
use std::{fmt::Formatter, str::FromStr};

/// Encoding for the Resource account seed
#[derive(Debug, Default, Clone, Copy)]
pub enum SeedEncoding {
#[default]
Bcs,
Hex,
Utf8,
}

const BCS: &str = "bcs";
const UTF_8: &str = "utf8";
const HEX: &str = "hex";

impl std::fmt::Display for SeedEncoding {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
SeedEncoding::Bcs => BCS,
SeedEncoding::Hex => HEX,
SeedEncoding::Utf8 => UTF_8,
})
}
}

impl FromStr for SeedEncoding {
type Err = CliError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
BCS => Ok(Self::Bcs),
HEX => Ok(Self::Hex),
UTF_8 | "utf-8" | "utf_8" => Ok(Self::Utf8),
_ => Err(CliError::UnableToParse(
"seed-encoding",
"For --seed-encoding please provide one of ['bcs','hex', 'utf8']".to_string(),
)),
}
}
}

/// A generic interface for allowing for different types of seed phrase inputs
///
/// The easiest to use is `string_seed` as it will match directly with the b"string" notation in Move.
#[derive(Debug, Parser)]
pub struct ResourceAccountSeed {
/// Resource account seed
///
/// Seed used in generation of the AccountId of the resource account
/// The seed will be converted to bytes using the encoding from `--seed-encoding`, defaults to `BCS`
#[clap(long)]
pub(crate) seed: String,

/// Resource account seed encoding
///
/// The encoding can be one of `Bcs`, `Utf8`, and `Hex`.
///
/// - Bcs is the legacy functionality of the CLI, it will BCS encode the string, but can be confusing for users e.g. `"ab" -> vector<u8>[0x2, 0x61, 0x62]`
/// - Utf8 will encode the string as raw UTF-8 bytes, similar to in Move `b"string"` e.g. `"ab" -> vector<u8>[0x61, 0x62]`
/// - Hex will encode the string as raw hex encoded bytes e.g. `"0x6162" -> vector<u8>[0x61, 0x62]`
#[clap(long, default_value_t = SeedEncoding::Bcs)]
pub(crate) seed_encoding: SeedEncoding,
}

impl ResourceAccountSeed {
pub fn seed(self) -> CliTypedResult<Vec<u8>> {
match self.seed_encoding {
SeedEncoding::Bcs => Ok(bcs::to_bytes(self.seed.as_str())?),
SeedEncoding::Utf8 => Ok(self.seed.as_bytes().to_vec()),
SeedEncoding::Hex => HexEncodedBytes::from_str(self.seed.as_str())
.map(|inner| inner.0)
.map_err(|err| CliError::UnableToParse("seed", err.to_string())),
}
}
}

/// Derive the address for a resource account
///
Expand Down
27 changes: 10 additions & 17 deletions crates/aptos/src/account/key_rotation.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

use crate::common::types::{
account_address_from_auth_key, account_address_from_public_key, AuthenticationKeyInputOptions,
CliCommand, CliConfig, CliError, CliTypedResult, ConfigSearchMode, EncodingOptions,
ExtractEd25519PublicKey, HardwareWalletOptions, ParseEd25519PrivateKey, ProfileConfig,
ProfileOptions, PublicKeyInputOptions, RestOptions, TransactionOptions, TransactionSummary,
use crate::common::{
init::lookup_address,
types::{
account_address_from_auth_key, account_address_from_public_key,
AuthenticationKeyInputOptions, CliCommand, CliConfig, CliError, CliTypedResult,
ConfigSearchMode, EncodingOptions, ExtractEd25519PublicKey, HardwareWalletOptions,
ParseEd25519PrivateKey, ProfileConfig, ProfileOptions, PublicKeyInputOptions, RestOptions,
TransactionOptions, TransactionSummary,
},
};
use aptos_cached_packages::aptos_stdlib;
use aptos_crypto::{
Expand All @@ -14,7 +18,7 @@ use aptos_crypto::{
PrivateKey, SigningKey,
};
use aptos_ledger;
use aptos_rest_client::{error::RestError, Client};
use aptos_rest_client::Client;
use aptos_types::{
account_address::AccountAddress,
account_config::{RotationProofChallenge, CORE_CODE_ADDRESS},
Expand Down Expand Up @@ -381,14 +385,3 @@ impl CliCommand<AccountAddress> for LookupAddress {
Ok(lookup_address(&rest_client, address, true).await?)
}
}

pub async fn lookup_address(
rest_client: &Client,
address_key: AccountAddress,
must_exist: bool,
) -> Result<AccountAddress, RestError> {
Ok(rest_client
.lookup_address(address_key, must_exist)
.await?
.into_inner())
}
190 changes: 190 additions & 0 deletions crates/aptos/src/common/compile_script_function.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

use crate::{
common::types::{CliError, CliTypedResult, PromptOptions},
move_tool::FrameworkPackageArgs,
};
use aptos_crypto::HashValue;
use aptos_framework::{BuildOptions, BuiltPackage};
use clap::Parser;
use move_model::metadata::{
CompilerVersion, LanguageVersion, LATEST_STABLE_COMPILER_VERSION,
LATEST_STABLE_LANGUAGE_VERSION,
};
use std::{
collections::BTreeMap,
fs,
path::{Path, PathBuf},
};
use tempfile::TempDir;

/// Compile a specified script.
#[derive(Parser, Default)]
pub struct CompileScriptFunction {
/// Path to the Move script for the proposal
#[clap(long, group = "script", value_parser)]
pub script_path: Option<PathBuf>,

/// Path to the Move script for the proposal
#[clap(long, group = "script", value_parser)]
pub compiled_script_path: Option<PathBuf>,

#[clap(flatten)]
pub framework_package_args: FrameworkPackageArgs,

#[clap(long)]
pub bytecode_version: Option<u32>,

/// Specify the version of the compiler.
/// Defaults to the latest stable compiler version (at least 2)
#[clap(long, value_parser = clap::value_parser!(CompilerVersion),
default_value = LATEST_STABLE_COMPILER_VERSION,)]
pub compiler_version: Option<CompilerVersion>,

/// Specify the language version to be supported.
/// Defaults to the latest stable language version (at least 2)
#[clap(long, value_parser = clap::value_parser!(LanguageVersion),
default_value = LATEST_STABLE_LANGUAGE_VERSION,)]
pub language_version: Option<LanguageVersion>,
}

impl CompileScriptFunction {
pub(crate) fn compile(
&self,
script_name: &str,
prompt_options: PromptOptions,
) -> CliTypedResult<(Vec<u8>, HashValue)> {
if let Some(compiled_script_path) = &self.compiled_script_path {
let bytes = std::fs::read(compiled_script_path).map_err(|e| {
CliError::IO(format!("Unable to read {:?}", self.compiled_script_path), e)
})?;
let hash = HashValue::sha3_256_of(bytes.as_slice());
return Ok((bytes, hash));
}

// Check script file
let script_path = self
.script_path
.as_ref()
.ok_or_else(|| {
CliError::CommandArgumentError(
"Must choose either --compiled-script-path or --script-path".to_string(),
)
})?
.as_path();
if !script_path.exists() {
return Err(CliError::CommandArgumentError(format!(
"{} does not exist",
script_path.display()
)));
} else if script_path.is_dir() {
return Err(CliError::CommandArgumentError(format!(
"{} is a directory",
script_path.display()
)));
}

// Compile script
compile_in_temp_dir(
script_name,
script_path,
&self.framework_package_args,
prompt_options,
self.bytecode_version,
self.language_version
.or_else(|| Some(LanguageVersion::latest_stable())),
self.compiler_version
.or_else(|| Some(CompilerVersion::latest_stable())),
)
}
}

pub fn compile_in_temp_dir(
script_name: &str,
script_path: &Path,
framework_package_args: &FrameworkPackageArgs,
prompt_options: PromptOptions,
bytecode_version: Option<u32>,
language_version: Option<LanguageVersion>,
compiler_version: Option<CompilerVersion>,
) -> CliTypedResult<(Vec<u8>, HashValue)> {
// Make a temporary directory for compilation
let temp_dir = TempDir::new().map_err(|err| {
CliError::UnexpectedError(format!("Failed to create temporary directory {}", err))
})?;

// Initialize a move directory
let package_dir = temp_dir.path();
framework_package_args.init_move_dir(
package_dir,
script_name,
BTreeMap::new(),
prompt_options,
)?;

// Insert the new script
let sources_dir = package_dir.join("sources");
let new_script_path = if let Some(file_name) = script_path.file_name() {
sources_dir.join(file_name)
} else {
// If for some reason we can't get the move file
sources_dir.join("script.move")
};
fs::copy(script_path, new_script_path.as_path()).map_err(|err| {
CliError::IO(
format!(
"Failed to copy {} to {}",
script_path.display(),
new_script_path.display()
),
err,
)
})?;

// Compile the script
compile_script(
framework_package_args.skip_fetch_latest_git_deps,
package_dir,
bytecode_version,
language_version,
compiler_version,
)
}

fn compile_script(
skip_fetch_latest_git_deps: bool,
package_dir: &Path,
bytecode_version: Option<u32>,
language_version: Option<LanguageVersion>,
compiler_version: Option<CompilerVersion>,
) -> CliTypedResult<(Vec<u8>, HashValue)> {
let build_options = BuildOptions {
with_srcs: false,
with_abis: false,
with_source_maps: false,
with_error_map: false,
skip_fetch_latest_git_deps,
bytecode_version,
language_version,
compiler_version,
..BuildOptions::default()
};

let pack = BuiltPackage::build(package_dir.to_path_buf(), build_options)
.map_err(|e| CliError::MoveCompilationError(format!("{:#}", e)))?;

let scripts_count = pack.script_count();

if scripts_count != 1 {
return Err(CliError::UnexpectedError(format!(
"Only one script can be prepared a time. Make sure one and only one script file \
is included in the Move package. Found {} scripts.",
scripts_count
)));
}

let bytes = pack.extract_script_code().pop().unwrap();
let hash = HashValue::sha3_256_of(bytes.as_slice());
Ok((bytes, hash))
}
Loading
Loading