From b0bf739e13b61355c86fb830550d37fc6f8172d9 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Sat, 6 May 2023 07:45:17 -0700 Subject: [PATCH 1/7] [CLI] Add JSON input file support, docs (#7709) --- aptos-move/move-examples/cli_args/Move.toml | 9 +- .../cli_args/scripts/set_vals.move | 28 ++ .../cli_args/sources/cli_args.move | 66 +++-- crates/aptos/src/account/multisig_account.rs | 5 +- crates/aptos/src/common/types.rs | 254 +++++++++++++++--- crates/aptos/src/common/utils.rs | 9 +- crates/aptos/src/move_tool/mod.rs | 73 ++--- crates/aptos/src/test/mod.rs | 53 ++-- .../docs/move/move-on-aptos/cli.md | 251 +++++++++++++++++ .../tools/aptos-cli-tool/use-aptos-cli.md | 48 ---- 10 files changed, 602 insertions(+), 194 deletions(-) create mode 100644 aptos-move/move-examples/cli_args/scripts/set_vals.move diff --git a/aptos-move/move-examples/cli_args/Move.toml b/aptos-move/move-examples/cli_args/Move.toml index c263bcb53d723..2603182eaecb2 100644 --- a/aptos-move/move-examples/cli_args/Move.toml +++ b/aptos-move/move-examples/cli_args/Move.toml @@ -1,12 +1,11 @@ +# :!:>manifest [package] name = "CliArgs" version = "0.1.0" upgrade_policy = "compatible" [addresses] -deploy_address = "_" -std = "0x1" -aptos_framework = "0x1" +test_account = "_" -[dependencies] -AptosFramework = { local = "../../framework/aptos-framework" } \ No newline at end of file +[dependencies.AptosFramework] +local = "../../framework/aptos-framework" # <:!:manifest \ No newline at end of file diff --git a/aptos-move/move-examples/cli_args/scripts/set_vals.move b/aptos-move/move-examples/cli_args/scripts/set_vals.move new file mode 100644 index 0000000000000..a118712595a57 --- /dev/null +++ b/aptos-move/move-examples/cli_args/scripts/set_vals.move @@ -0,0 +1,28 @@ +// :!:>script +script { + use test_account::cli_args; + use std::vector; + + /// Get a `bool` vector where each element indicates `true` if the + /// corresponding element in `u8_vec` is greater than `u8_solo`. + /// Then pack `address_solo` in a `vector>` and + /// pass resulting argument set to public entry function. + fun set_vals( + account: signer, + u8_solo: u8, + u8_vec: vector, + address_solo: address, + ) { + let bool_vec = vector[]; + let i = 0; + while (i < vector::length(&u8_vec)) { + vector::push_back( + &mut bool_vec, + *vector::borrow(&u8_vec, i) > u8_solo + ); + i = i + 1; + }; + let addr_vec_vec = vector[vector[address_solo]]; + cli_args::set_vals(account, u8_solo, bool_vec, addr_vec_vec); + } +} // <:!:script diff --git a/aptos-move/move-examples/cli_args/sources/cli_args.move b/aptos-move/move-examples/cli_args/sources/cli_args.move index eea135dcdf264..cdd67e3b81dc8 100644 --- a/aptos-move/move-examples/cli_args/sources/cli_args.move +++ b/aptos-move/move-examples/cli_args/sources/cli_args.move @@ -1,37 +1,57 @@ -module deploy_address::cli_args { +// :!:>resource +module test_account::cli_args { use std::signer; + use aptos_std::type_info::{Self, TypeInfo}; - struct Holder has key { + + struct Holder has key, drop { u8_solo: u8, bool_vec: vector, address_vec_vec: vector>, - } - + type_info_1: TypeInfo, + type_info_2: TypeInfo, + } //<:!:resource - #[view] - public fun reveal(host: address): (u8, vector, vector>) acquires Holder { - let holder_ref = borrow_global(host); - (holder_ref.u8_solo, holder_ref.bool_vec, holder_ref.address_vec_vec) - } - public entry fun set_vals( + // :!:>setter + /// Set values in a `Holder` under `account`. + public entry fun set_vals( account: signer, u8_solo: u8, bool_vec: vector, address_vec_vec: vector>, ) acquires Holder { let account_addr = signer::address_of(&account); - if (!exists(account_addr)) { - move_to(&account, Holder { - u8_solo, - bool_vec, - address_vec_vec, - }) - } else { - let old_holder = borrow_global_mut(account_addr); - old_holder.u8_solo = u8_solo; - old_holder.bool_vec = bool_vec; - old_holder.address_vec_vec = address_vec_vec; - } + if (exists(account_addr)) { + move_from(account_addr); + }; + move_to(&account, Holder { + u8_solo, + bool_vec, + address_vec_vec, + type_info_1: type_info::type_of(), + type_info_2: type_info::type_of(), + }); + } //<:!:setter + + // :!:>view + #[view] + /// Reveal first three fields in host's `Holder`, as well as two + /// `bool` flags denoting if `T1` and `T2` respectively match + /// `Holder.type_info_1` and `Holder.type_info_2`. + public fun reveal(host: address): ( + u8, + vector, + vector>, + bool, + bool + ) acquires Holder { + let holder_ref = borrow_global(host); + (holder_ref.u8_solo, + holder_ref.bool_vec, + holder_ref.address_vec_vec, + type_info::type_of() == holder_ref.type_info_1, + type_info::type_of() == holder_ref.type_info_2) } -} + +} //<:!:view diff --git a/crates/aptos/src/account/multisig_account.rs b/crates/aptos/src/account/multisig_account.rs index 0d60380afd8a9..7cf01cf545b29 100644 --- a/crates/aptos/src/account/multisig_account.rs +++ b/crates/aptos/src/account/multisig_account.rs @@ -116,9 +116,8 @@ impl CliCommand for CreateTransaction { } async fn execute(self) -> CliTypedResult { - let payload = MultisigTransactionPayload::EntryFunction( - self.entry_function_args.create_entry_function_payload()?, - ); + let payload = + MultisigTransactionPayload::EntryFunction(self.entry_function_args.try_into()?); self.txn_options .submit_transaction(aptos_stdlib::multisig_account_create_transaction( self.multisig_account.multisig_address, diff --git a/crates/aptos/src/common/types.rs b/crates/aptos/src/common/types.rs index f3b7099c403dc..dd0a999d9b2ea 100644 --- a/crates/aptos/src/common/types.rs +++ b/crates/aptos/src/common/types.rs @@ -6,14 +6,15 @@ use crate::{ init::Network, utils::{ check_if_file_exists, create_dir_if_not_exist, dir_default_to_current, - get_account_with_state, get_auth_key, get_sequence_number, 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, + 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, }, }, config::GlobalConfig, genesis::git::from_yaml, - move_tool::{ArgWithType, MemberId}, + move_tool::{ArgWithType, FunctionArgType, MemberId}, }; use aptos_crypto::{ ed25519::{Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature}, @@ -24,7 +25,7 @@ use aptos_gas_profiling::FrameName; use aptos_global_constants::adjust_gas_headroom; use aptos_keygen::KeyGen; use aptos_rest_client::{ - aptos_api_types::{HashValue, MoveType, ViewRequest}, + aptos_api_types::{EntryFunctionId, HashValue, MoveType, ViewRequest}, error::RestError, Client, Transaction, }; @@ -32,8 +33,8 @@ use aptos_sdk::{transaction_builder::TransactionFactory, types::LocalAccount}; use aptos_types::{ chain_id::ChainId, transaction::{ - authenticator::AuthenticationKey, EntryFunction, SignedTransaction, TransactionPayload, - TransactionStatus, + authenticator::AuthenticationKey, EntryFunction, Script, SignedTransaction, + TransactionArgument, TransactionPayload, TransactionStatus, }, }; use async_trait::async_trait; @@ -1672,8 +1673,54 @@ pub struct RotationProofChallenge { pub new_public_key: Vec, } +/// Common options for interactions with a multisig account. +#[derive(Clone, Debug, Parser, Serialize)] +pub struct MultisigAccount { + /// The address of the multisig account to interact with. + #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] + pub(crate) multisig_address: AccountAddress, +} + +#[derive(Debug, Parser)] +pub struct TypeArgVec { + /// TypeTag arguments separated by spaces. + /// + /// Example: `u8 u16 u32 u64 u128 u256 bool address vector signer` + #[clap(long, multiple_values = true)] + pub(crate) type_args: Vec, +} + +impl TryFrom<&Vec> for TypeArgVec { + type Error = CliError; + + fn try_from(value: &Vec) -> Result { + let mut type_args = vec![]; + for string_ref in value { + type_args.push( + MoveType::from_str(string_ref) + .map_err(|err| CliError::UnableToParse("type argument", err.to_string()))?, + ); + } + Ok(TypeArgVec { type_args }) + } +} + +impl TryInto> for TypeArgVec { + type Error = CliError; + + fn try_into(self) -> Result, Self::Error> { + let mut type_tags: Vec = vec![]; + for type_arg in self.type_args { + type_tags.push( + TypeTag::try_from(type_arg) + .map_err(|err| CliError::UnableToParse("type argument", err.to_string()))?, + ); + } + Ok(type_tags) + } +} + #[derive(Debug, Parser)] -/// This is used for both entry functions and scripts. pub struct ArgWithTypeVec { /// Arguments combined with their type separated by spaces. /// @@ -1683,62 +1730,185 @@ pub struct ArgWithTypeVec { /// quotes based on your shell interpreter) /// /// Example: `address:0x1 bool:true u8:0 u256:1234 "bool:[true, false]" 'address:[["0xace", "0xbee"], []]'` - /// - /// Vector is wrapped in a reusable struct for uniform CLI documentation. #[clap(long, multiple_values = true)] pub(crate) args: Vec, } +impl TryFrom<&Vec> for ArgWithTypeVec { + type Error = CliError; + + fn try_from(value: &Vec) -> Result { + let mut args = vec![]; + for arg_json_ref in value { + let function_arg_type = FunctionArgType::from_str(&arg_json_ref.arg_type)?; + args.push(function_arg_type.parse_arg_json(&arg_json_ref.arg_value)?); + } + Ok(ArgWithTypeVec { args }) + } +} + +impl TryInto> for ArgWithTypeVec { + type Error = CliError; + + fn try_into(self) -> Result, Self::Error> { + let mut args = vec![]; + for arg in self.args { + args.push(arg.try_into()?); + } + Ok(args) + } +} + +impl TryInto>> for ArgWithTypeVec { + type Error = CliError; + + fn try_into(self) -> Result>, Self::Error> { + Ok(self + .args + .into_iter() + .map(|arg_with_type| arg_with_type.arg) + .collect()) + } +} + +impl TryInto> for ArgWithTypeVec { + type Error = CliError; + + fn try_into(self) -> Result, Self::Error> { + let mut args = vec![]; + for arg in self.args { + args.push(arg.to_json()?); + } + Ok(args) + } +} + /// Common options for constructing an entry function transaction payload. #[derive(Debug, Parser)] pub struct EntryFunctionArguments { /// Function name as `
::::` /// /// Example: `0x842ed41fad9640a2ad08fdd7d3e4f7f505319aac7d67e1c0dd6a7cce8732c7e3::message::set_message` - #[clap(long)] - pub function_id: MemberId, + #[clap(long, required_unless_present = "json-file")] + pub function_id: Option, + #[clap(flatten)] + pub(crate) type_arg_vec: TypeArgVec, #[clap(flatten)] pub(crate) arg_vec: ArgWithTypeVec, - /// TypeTag arguments separated by spaces. - /// - /// Example: `u8 u16 u32 u64 u128 u256 bool address vector signer` - #[clap(long, multiple_values = true)] - pub type_args: Vec, + /// JSON file specifying public entry function ID, type arguments, and arguments. + #[clap(long, parse(from_os_str), conflicts_with_all = &["function-id", "args", "type-args"])] + pub(crate) json_file: Option, } impl EntryFunctionArguments { - /// Construct and return an entry function payload from function_id, args, and type_args. - pub fn create_entry_function_payload(self) -> CliTypedResult { - let args: Vec> = self - .arg_vec - .args - .into_iter() - .map(|arg_with_type| arg_with_type.arg) - .collect(); - - let mut parsed_type_args: Vec = Vec::new(); - // These TypeArgs are used for generics - for type_arg in self.type_args.into_iter() { - let type_tag = TypeTag::try_from(type_arg.clone()) - .map_err(|err| CliError::UnableToParse("--type-args", err.to_string()))?; - parsed_type_args.push(type_tag) + /// Get instance as if all fields passed from command line, parsing JSON input file if needed. + fn check_json_file(self) -> CliTypedResult { + if self.json_file.is_none() { + Ok(self) + } else { + let json = + parse_json_file::(self.json_file.as_ref().unwrap())?; + Ok(EntryFunctionArguments { + function_id: Some(MemberId::from_str(&json.function_id)?), + type_arg_vec: TypeArgVec::try_from(&json.type_args)?, + arg_vec: ArgWithTypeVec::try_from(&json.args)?, + json_file: None, + }) } + } +} +impl TryInto for EntryFunctionArguments { + type Error = CliError; + + fn try_into(self) -> Result { + let entry_function_args = self.check_json_file()?; + let function_id = entry_function_args.function_id.unwrap(); Ok(EntryFunction::new( - self.function_id.module_id, - self.function_id.member_id, - parsed_type_args, - args, + function_id.module_id, + function_id.member_id, + entry_function_args.type_arg_vec.try_into()?, + entry_function_args.arg_vec.try_into()?, )) } } -/// Common options for interactions with a multisig account. -#[derive(Clone, Debug, Parser, Serialize)] -pub struct MultisigAccount { - /// The address of the multisig account to interact with. - #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] - pub(crate) multisig_address: AccountAddress, +impl TryInto for EntryFunctionArguments { + type Error = CliError; + + fn try_into(self) -> Result { + let entry_function_args = self.check_json_file()?; + let function_id = entry_function_args.function_id.unwrap(); + Ok(ViewRequest { + function: EntryFunctionId { + module: function_id.module_id.into(), + name: function_id.member_id.into(), + }, + type_arguments: entry_function_args.type_arg_vec.type_args, + arguments: entry_function_args.arg_vec.try_into()?, + }) + } +} + +/// Common options for constructing a script payload +#[derive(Debug, Parser)] +pub struct ScriptFunctionArguments { + #[clap(flatten)] + pub(crate) type_arg_vec: TypeArgVec, + #[clap(flatten)] + pub(crate) arg_vec: ArgWithTypeVec, + + /// JSON file specifying type arguments and arguments. + #[clap(long, parse(from_os_str), conflicts_with_all = &["args", "type-args"])] + pub(crate) json_file: Option, +} + +impl ScriptFunctionArguments { + /// Get instance as if all fields passed from command line, parsing JSON input file if needed. + fn check_json_file(self) -> CliTypedResult { + if self.json_file.is_none() { + Ok(self) + } else { + let json = + parse_json_file::(self.json_file.as_ref().unwrap())?; + Ok(ScriptFunctionArguments { + type_arg_vec: TypeArgVec::try_from(&json.type_args)?, + arg_vec: ArgWithTypeVec::try_from(&json.args)?, + json_file: None, + }) + } + } + + pub fn create_script_payload(self, bytecode: Vec) -> CliTypedResult { + let script_function_args = self.check_json_file()?; + Ok(TransactionPayload::Script(Script::new( + bytecode, + script_function_args.type_arg_vec.try_into()?, + script_function_args.arg_vec.try_into()?, + ))) + } +} + +#[derive(Deserialize)] +/// JSON file format for function arguments. +pub struct ArgWithTypeJSON { + arg_type: String, + arg_value: serde_json::Value, +} + +#[derive(Deserialize)] +/// JSON file format for entry function arguments. +struct EntryFunctionArgumentsJSON { + function_id: String, + type_args: Vec, + args: Vec, +} + +#[derive(Deserialize)] +/// JSON file format for script function arguments. +struct ScriptFunctionArgumentsJSON { + type_args: Vec, + args: Vec, } diff --git a/crates/aptos/src/common/utils.rs b/crates/aptos/src/common/utils.rs index 98b64b6220853..028ccd80c47ee 100644 --- a/crates/aptos/src/common/utils.rs +++ b/crates/aptos/src/common/utils.rs @@ -23,7 +23,7 @@ use aptos_types::{ use itertools::Itertools; use move_core_types::account_address::AccountAddress; use reqwest::Url; -use serde::Serialize; +use serde::{Deserialize, Serialize}; #[cfg(unix)] use std::os::unix::fs::OpenOptionsExt; use std::{ @@ -473,3 +473,10 @@ pub async fn profile_or_submit( .map(TransactionSummary::from) } } + +/// Try parsing JSON in file at path into a specified type. +pub fn parse_json_file Deserialize<'a>>(path_ref: &Path) -> CliTypedResult { + serde_json::from_slice::(&read_from_file(path_ref)?).map_err(|err| { + CliError::UnableToReadFile(format!("{}", path_ref.display()), err.to_string()) + }) +} diff --git a/crates/aptos/src/move_tool/mod.rs b/crates/aptos/src/move_tool/mod.rs index 0b135e3986a87..d93ed31a25c22 100644 --- a/crates/aptos/src/move_tool/mod.rs +++ b/crates/aptos/src/move_tool/mod.rs @@ -14,9 +14,10 @@ use crate::{ account::derive_resource_account::ResourceAccountSeed, common::{ types::{ - load_account_arg, ArgWithTypeVec, CliConfig, CliError, CliTypedResult, - ConfigSearchMode, EntryFunctionArguments, MoveManifestAccountWrapper, MovePackageDir, - ProfileOptions, PromptOptions, RestOptions, TransactionOptions, TransactionSummary, + load_account_arg, CliConfig, CliError, CliTypedResult, ConfigSearchMode, + EntryFunctionArguments, MoveManifestAccountWrapper, MovePackageDir, ProfileOptions, + PromptOptions, RestOptions, ScriptFunctionArguments, TransactionOptions, + TransactionSummary, }, utils::{ check_if_file_exists, create_dir_if_not_exist, dir_default_to_current, @@ -37,11 +38,10 @@ use aptos_framework::{ prover::ProverOptions, BuildOptions, BuiltPackage, }; use aptos_gas::{AbstractValueSizeGasParameters, NativeGasParameters}; -use aptos_rest_client::aptos_api_types::{EntryFunctionId, MoveType, ViewRequest}; use aptos_transactional_test_harness::run_aptos_test; use aptos_types::{ account_address::{create_resource_address, AccountAddress}, - transaction::{Script, TransactionArgument, TransactionPayload}, + transaction::{TransactionArgument, TransactionPayload}, }; use async_trait::async_trait; use clap::{ArgEnum, Parser, Subcommand}; @@ -52,18 +52,13 @@ use codespan_reporting::{ use itertools::Itertools; use move_cli::{self, base::test::UnitTestResult}; use move_command_line_common::env::MOVE_HOME; -use move_core_types::{ - identifier::Identifier, - language_storage::{ModuleId, TypeTag}, - u256::U256, -}; +use move_core_types::{identifier::Identifier, language_storage::ModuleId, u256::U256}; use move_package::{source_package::layout::SourcePackageLayout, BuildConfig}; use move_unit_test::UnitTestingConfig; pub use package_hooks::*; use serde::{Deserialize, Serialize}; use std::{ collections::BTreeMap, - convert::TryFrom, fmt::{Display, Formatter}, path::{Path, PathBuf}, str::FromStr, @@ -1102,10 +1097,11 @@ impl CliCommand for RunFunction { } async fn execute(self) -> CliTypedResult { - let payload = TransactionPayload::EntryFunction( - self.entry_function_args.create_entry_function_payload()?, - ); - profile_or_submit(payload, &self.txn_options).await + profile_or_submit( + TransactionPayload::EntryFunction(self.entry_function_args.try_into()?), + &self.txn_options, + ) + .await } } @@ -1125,21 +1121,9 @@ impl CliCommand> for ViewFunction { } async fn execute(self) -> CliTypedResult> { - let mut args: Vec = vec![]; - for arg in self.entry_function_args.arg_vec.args { - args.push(arg.to_json()?); - } - - let view_request = ViewRequest { - function: EntryFunctionId { - module: self.entry_function_args.function_id.module_id.into(), - name: self.entry_function_args.function_id.member_id.into(), - }, - type_arguments: self.entry_function_args.type_args, - arguments: args, - }; - - self.txn_options.view(view_request).await + self.txn_options + .view(self.entry_function_args.try_into()?) + .await } } @@ -1151,12 +1135,7 @@ pub struct RunScript { #[clap(flatten)] pub(crate) compile_proposal_args: CompileScriptFunction, #[clap(flatten)] - pub(crate) arg_vec: ArgWithTypeVec, - /// TypeTag arguments separated by spaces. - /// - /// Example: `u8 u16 u32 u64 u128 u256 bool address vector signer` - #[clap(long, multiple_values = true)] - pub(crate) type_args: Vec, + pub(crate) script_function_args: ScriptFunctionArguments, } #[async_trait] @@ -1170,23 +1149,11 @@ impl CliCommand for RunScript { .compile_proposal_args .compile("RunScript", self.txn_options.prompt_options)?; - let mut args: Vec = vec![]; - for arg in self.arg_vec.args { - args.push(arg.try_into()?); - } - - let mut type_args: Vec = Vec::new(); - - // These TypeArgs are used for generics - for type_arg in self.type_args.into_iter() { - let type_tag = TypeTag::try_from(type_arg) - .map_err(|err| CliError::UnableToParse("--type-args", err.to_string()))?; - type_args.push(type_tag) - } - - let payload = TransactionPayload::Script(Script::new(bytecode, type_args, args)); - - profile_or_submit(payload, &self.txn_options).await + profile_or_submit( + self.script_function_args.create_script_payload(bytecode)?, + &self.txn_options, + ) + .await } } diff --git a/crates/aptos/src/test/mod.rs b/crates/aptos/src/test/mod.rs index f80891b14cda6..dada85091fd82 100644 --- a/crates/aptos/src/test/mod.rs +++ b/crates/aptos/src/test/mod.rs @@ -16,7 +16,8 @@ use crate::{ CliTypedResult, EncodingOptions, EntryFunctionArguments, FaucetOptions, GasOptions, KeyType, MoveManifestAccountWrapper, MovePackageDir, OptionalPoolAddressArgs, PoolAddressArgs, PrivateKeyInputOptions, PromptOptions, PublicKeyInputOptions, - RestOptions, RngArgs, SaveFile, TransactionOptions, TransactionSummary, + RestOptions, RngArgs, SaveFile, ScriptFunctionArguments, TransactionOptions, + TransactionSummary, TypeArgVec, }, utils::write_to_file, }, @@ -310,22 +311,25 @@ impl CliTestFramework { ) -> CliTypedResult { RunFunction { entry_function_args: EntryFunctionArguments { - function_id: MemberId { + function_id: Some(MemberId { module_id: ModuleId::new(AccountAddress::ONE, ident_str!("coin").into()), member_id: ident_str!("transfer").into(), - }, + }), arg_vec: ArgWithTypeVec { args: vec![ ArgWithType::from_str("address:0xdeadbeefcafebabe").unwrap(), ArgWithType::from_str(&format!("u64:{}", amount)).unwrap(), ], }, - type_args: vec![MoveType::Struct(MoveStructTag::new( - AccountAddress::ONE.into(), - ident_str!("aptos_coin").into(), - ident_str!("AptosCoin").into(), - vec![], - ))], + type_arg_vec: TypeArgVec { + type_args: vec![MoveType::Struct(MoveStructTag::new( + AccountAddress::ONE.into(), + ident_str!("aptos_coin").into(), + ident_str!("AptosCoin").into(), + vec![], + ))], + }, + json_file: None, }, txn_options: self.transaction_options(sender_index, gas_options), } @@ -587,8 +591,9 @@ impl CliTestFramework { ) -> CliTypedResult { RunFunction { entry_function_args: EntryFunctionArguments { - function_id: MemberId::from_str("0x1::staking_contract::create_staking_contract") - .unwrap(), + function_id: Some( + MemberId::from_str("0x1::staking_contract::create_staking_contract").unwrap(), + ), arg_vec: ArgWithTypeVec { args: vec![ ArgWithType::address(self.account_id(operator_index)), @@ -598,7 +603,8 @@ impl CliTestFramework { ArgWithType::bytes(vec![]), ], }, - type_args: vec![], + type_arg_vec: TypeArgVec { type_args: vec![] }, + json_file: None, }, txn_options: self.transaction_options(owner_index, None), } @@ -909,12 +915,15 @@ impl CliTestFramework { } RunFunction { - txn_options: self.transaction_options(index, gas_options), entry_function_args: EntryFunctionArguments { - function_id, + function_id: Some(function_id), arg_vec: ArgWithTypeVec { args: parsed_args }, - type_args: parsed_type_args, + type_arg_vec: TypeArgVec { + type_args: parsed_type_args, + }, + json_file: None, }, + txn_options: self.transaction_options(index, gas_options), } .execute() .await @@ -976,8 +985,11 @@ impl CliTestFramework { framework_package_args, bytecode_version: None, }, - arg_vec: ArgWithTypeVec { args: Vec::new() }, - type_args: Vec::new(), + script_function_args: ScriptFunctionArguments { + type_arg_vec: TypeArgVec { type_args: vec![] }, + arg_vec: ArgWithTypeVec { args: vec![] }, + json_file: None, + }, } .execute() .await @@ -1002,8 +1014,11 @@ impl CliTestFramework { }, bytecode_version: None, }, - arg_vec: ArgWithTypeVec { args }, - type_args, + script_function_args: ScriptFunctionArguments { + type_arg_vec: TypeArgVec { type_args }, + arg_vec: ArgWithTypeVec { args }, + json_file: None, + }, } .execute() .await diff --git a/developer-docs-site/docs/move/move-on-aptos/cli.md b/developer-docs-site/docs/move/move-on-aptos/cli.md index 747ad61550095..ccfe91f725615 100644 --- a/developer-docs-site/docs/move/move-on-aptos/cli.md +++ b/developer-docs-site/docs/move/move-on-aptos/cli.md @@ -434,3 +434,254 @@ $ aptos move run --function-id default::message::set_message --args string:hello } } ``` + +## Arguments in JSON + +### Background + +This section references the [`CliArgs` example package](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/cli_args), which contains the following manifest: + + +```toml title="Move.toml" +:!: static/move-examples/cli_args/Move.toml manifest +``` + +Here, the package is deployed under the named address `test_account`. + +### Entry functions + +The only module in the package, `cli_args.move`, defines a simple `Holder` resource with fields of various data types: + +```rust title="Holder in cli_args.move" +:!: static/move-examples/cli_args/sources/cli_args.move resource +``` + +A public entry function with multi-nested vectors can be used to set the fields: + +```rust title="Setter function in cli_args.move" +:!: static/move-examples/cli_args/sources/cli_args.move setter +``` + +After the package has been published, `aptos move run` can be used to call `set_vals()`: + +```zsh title="Running function with nested vector arguments from CLI" +aptos move run \ + --function-id ::cli_args::set_vals \ + --private-key-file \ + --type-args \ + 0x1::account::Account \ + 0x1::chain_id::ChainId \ + --args \ + u8:123 \ + "bool:[false, true, false, false]" \ + 'address:[["0xace", "0xbee"], ["0xcad"], []]' +``` + +:::tip +To pass vectors (including nested vectors) as arguments from the command line, use JSON syntax escaped with quotes! +::: + +The function ID, type arguments, and arguments can alternatively be specified in a JSON file: + +```json title="entry_function_arguments.json" +{ + "function_id": "::cli_args::set_vals", + "type_args": [ + "0x1::account::Account", + "0x1::chain_id::ChainId" + ], + "args": [ + { + "arg_type": "u8", + "arg_value": 123 + }, + { + "arg_type": "bool", + "arg_value": [false, true, false, false] + }, + { + "arg_type": "address", + "arg_value": [ + [ + "0xace", + "0xbee" + ], + [ + "0xcad" + ], + [] + ] + } + ] +} +``` + +Here, the call to `aptos move run` looks like: + +```zsh +aptos move run \ + --private-key-file \ + --json-file entry_function_arguments.json +``` + +### View functions + +Once the values in a `Holder` have been set, the `reveal()` view function can be used to check the first three fields, and to compare type arguments against the last two fields: + +```rust title="View function" +:!: static/move-examples/cli_args/sources/cli_args.move view +``` + +This view function can be called with arguments specified either from the CLI or from a JSON file: + +```zsh title="Arguments via CLI" +aptos move view \ + --function-id ::cli_args::reveal \ + --type-args \ + 0x1::account::Account \ + 0x1::account::Account \ + --args address: +``` + +```zsh title="Arguments via JSON file" +aptos move view --json-file view_function_arguments.json +``` + +```json title="view_function_arguments.json" +{ + "function_id": "::cli_args::reveal", + "type_args": [ + "0x1::account::Account", + "0x1::account::Account" + ], + "args": [ + { + "arg_type": "address", + "arg_value": "" + } + ] +} +``` + +```zsh title="Output" +{ + "Result": [ + 123, + [ + false, + true, + false, + false + ], + [ + [ + "0xace", + "0xbee" + ], + [ + "0xcad" + ], + [] + ], + true, + false + ] +} +``` + +### Script functions + +The package also contains a script, `set_vals.move`, which is a wrapper for the setter function: + +```rust title="script" +:!: static/move-examples/cli_args/scripts/set_vals.move script +``` + +Here, `aptos move run-script` is run from inside the [`cli_args` package directory](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/cli_args): + +:::tip +Before trying out the below examples, compile the package with the correct named address via: + +```zsh +aptos move compile --named-addresses test_account= +``` +::: + +```zsh title="Arguments via CLI" +aptos move run-script \ + --compiled-script-path build/CliArgs/bytecode_scripts/set_vals.mv \ + --private-key-file \ + --type-args \ + 0x1::account::Account \ + 0x1::chain_id::ChainId \ + --args \ + u8:123 \ + "u8:[122, 123, 124, 125]" \ + address:"0xace" +``` + +```zsh title="Arguments via JSON file" +aptos move run-script \ + --compiled-script-path build/CliArgs/bytecode_scripts/set_vals.mv \ + --private-key-file \ + --json-file script_function_arguments.json +``` + +```json title="script_function_arguments.json" +{ + "type_args": [ + "0x1::account::Account", + "0x1::chain_id::ChainId" + ], + "args": [ + { + "arg_type": "u8", + "arg_value": 123 + }, + { + "arg_type": "u8", + "arg_value": [122, 123, 124, 125] + }, + { + "arg_type": "address", + "arg_value": "0xace" + } + ] +} +``` + +Both such script function invocations result in the following `reveal()` view function output: + +```zsh title="View function call" +aptos move view \ + --function-id ::cli_args::reveal \ + --type-args \ + 0x1::account::Account \ + 0x1::chain_id::ChainId \ + --args address: +``` + +```json title="View function output" +{ + "Result": [ + 123, + [ + false, + false, + true, + true + ], + [ + [ + "0xace" + ] + ], + true, + true + ] +} +``` + +:::note +As of the time of this writing, the `aptos` CLI only supports script function arguments for vectors of type `u8`, and only up to a vector depth of 1. Hence `vector
` and `vector>` are invalid script function argument types. +::: diff --git a/developer-docs-site/docs/tools/aptos-cli-tool/use-aptos-cli.md b/developer-docs-site/docs/tools/aptos-cli-tool/use-aptos-cli.md index 66d1c74f5691c..9d1fe51c17412 100644 --- a/developer-docs-site/docs/tools/aptos-cli-tool/use-aptos-cli.md +++ b/developer-docs-site/docs/tools/aptos-cli-tool/use-aptos-cli.md @@ -750,54 +750,6 @@ The `peer_config.yaml` file will be created in your current working directory, w Move examples can be found in the [Move section](../../move/move-on-aptos/cli). -You can also pass multi-nested vector arguments, like in the `cli_args` example from [move-examples](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples): - -:::tip -To pass vectors (including nested vectors) as arguments, use JSON syntax escaped with quotes! -::: - -```zsh -aptos move run \ - --function-id ::cli_args::set_vals \ - --private-key-file \ - --args \ - u8:123 \ - "bool:[false, true, false, false]" \ - 'address:[["0xace", "0xbee"], ["0xcad"], ["0xdee"], []]' -``` - -Then you can call the view function to see your arguments persisted on-chain! - -```zsh -aptos move view \ - --function-id ::cli_args::reveal \ - --args address: -{ - "Result": [ - 123, - [ - false, - true, - false, - false - ], - [ - [ - "0xace", - "0xbee" - ], - [ - "0xcad" - ], - [ - "0xdee" - ], - [] - ] - ] -} -``` - ## Node command examples This section summarizes how to run a local testnet with Aptos CLI. See [Run a Local Testnet with Aptos CLI](../../nodes/local-testnet/using-cli-to-run-a-local-testnet.md) for more details. From 28cb64d285dc76553c01483d2ed56dfd2c6713b1 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 15 May 2023 19:15:12 -0700 Subject: [PATCH 2/7] Include JSON files in example dir per @0xjinn Use literal file include for maintainability Update pnpm package spec for literal file include Update dictionary for literal file include See https://github.com/aptos-labs/aptos-core/pull/8054#issuecomment-1548797807 --- .../cli_args/entry_function_arguments.json | 30 +++++++ .../cli_args/script_function_arguments.json | 20 +++++ .../cli_args/view_function_arguments.json | 13 +++ .../docs/move/move-on-aptos/cli.md | 83 ++++--------------- developer-docs-site/package.json | 1 + developer-docs-site/pnpm-lock.yaml | 42 ++++++---- .../scripts/additional_dict.txt | 1 + 7 files changed, 105 insertions(+), 85 deletions(-) create mode 100644 aptos-move/move-examples/cli_args/entry_function_arguments.json create mode 100644 aptos-move/move-examples/cli_args/script_function_arguments.json create mode 100644 aptos-move/move-examples/cli_args/view_function_arguments.json diff --git a/aptos-move/move-examples/cli_args/entry_function_arguments.json b/aptos-move/move-examples/cli_args/entry_function_arguments.json new file mode 100644 index 0000000000000..10630aa4d91dd --- /dev/null +++ b/aptos-move/move-examples/cli_args/entry_function_arguments.json @@ -0,0 +1,30 @@ +{ + "function_id": "::cli_args::set_vals", + "type_args": [ + "0x1::account::Account", + "0x1::chain_id::ChainId" + ], + "args": [ + { + "arg_type": "u8", + "arg_value": 123 + }, + { + "arg_type": "bool", + "arg_value": [false, true, false, false] + }, + { + "arg_type": "address", + "arg_value": [ + [ + "0xace", + "0xbee" + ], + [ + "0xcad" + ], + [] + ] + } + ] +} \ No newline at end of file diff --git a/aptos-move/move-examples/cli_args/script_function_arguments.json b/aptos-move/move-examples/cli_args/script_function_arguments.json new file mode 100644 index 0000000000000..3756e9b1f8383 --- /dev/null +++ b/aptos-move/move-examples/cli_args/script_function_arguments.json @@ -0,0 +1,20 @@ +{ + "type_args": [ + "0x1::account::Account", + "0x1::chain_id::ChainId" + ], + "args": [ + { + "arg_type": "u8", + "arg_value": 123 + }, + { + "arg_type": "u8", + "arg_value": [122, 123, 124, 125] + }, + { + "arg_type": "address", + "arg_value": "0xace" + } + ] +} \ No newline at end of file diff --git a/aptos-move/move-examples/cli_args/view_function_arguments.json b/aptos-move/move-examples/cli_args/view_function_arguments.json new file mode 100644 index 0000000000000..17f5766297ce3 --- /dev/null +++ b/aptos-move/move-examples/cli_args/view_function_arguments.json @@ -0,0 +1,13 @@ +{ + "function_id": "::cli_args::reveal", + "type_args": [ + "0x1::account::Account", + "0x1::account::Account" + ], + "args": [ + { + "arg_type": "address", + "arg_value": "" + } + ] +} \ No newline at end of file diff --git a/developer-docs-site/docs/move/move-on-aptos/cli.md b/developer-docs-site/docs/move/move-on-aptos/cli.md index ccfe91f725615..979f85c3814c6 100644 --- a/developer-docs-site/docs/move/move-on-aptos/cli.md +++ b/developer-docs-site/docs/move/move-on-aptos/cli.md @@ -483,38 +483,10 @@ To pass vectors (including nested vectors) as arguments from the command line, u The function ID, type arguments, and arguments can alternatively be specified in a JSON file: -```json title="entry_function_arguments.json" -{ - "function_id": "::cli_args::set_vals", - "type_args": [ - "0x1::account::Account", - "0x1::chain_id::ChainId" - ], - "args": [ - { - "arg_type": "u8", - "arg_value": 123 - }, - { - "arg_type": "bool", - "arg_value": [false, true, false, false] - }, - { - "arg_type": "address", - "arg_value": [ - [ - "0xace", - "0xbee" - ], - [ - "0xcad" - ], - [] - ] - } - ] -} -``` +import CodeBlock from '@theme/CodeBlock'; +import entry_json_file from '!!raw-loader!../../../../aptos-move/move-examples/cli_args/entry_function_arguments.json'; + +{entry_json_file} Here, the call to `aptos move run` looks like: @@ -524,6 +496,10 @@ aptos move run \ --json-file entry_function_arguments.json ``` +:::tip +If you are trying to run the example yourself don't forget to substitute `` with an appropriate address in the JSON file from the [`CliArgs` example package](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/cli_args)! +::: + ### View functions Once the values in a `Holder` have been set, the `reveal()` view function can be used to check the first three fields, and to compare type arguments against the last two fields: @@ -547,21 +523,9 @@ aptos move view \ aptos move view --json-file view_function_arguments.json ``` -```json title="view_function_arguments.json" -{ - "function_id": "::cli_args::reveal", - "type_args": [ - "0x1::account::Account", - "0x1::account::Account" - ], - "args": [ - { - "arg_type": "address", - "arg_value": "" - } - ] -} -``` +import view_json_file from '!!raw-loader!../../../../aptos-move/move-examples/cli_args/view_function_arguments.json'; + +{view_json_file} ```zsh title="Output" { @@ -627,28 +591,9 @@ aptos move run-script \ --json-file script_function_arguments.json ``` -```json title="script_function_arguments.json" -{ - "type_args": [ - "0x1::account::Account", - "0x1::chain_id::ChainId" - ], - "args": [ - { - "arg_type": "u8", - "arg_value": 123 - }, - { - "arg_type": "u8", - "arg_value": [122, 123, 124, 125] - }, - { - "arg_type": "address", - "arg_value": "0xace" - } - ] -} -``` +import script_json_file from '!!raw-loader!../../../../aptos-move/move-examples/cli_args/script_function_arguments.json'; + +{script_json_file} Both such script function invocations result in the following `reveal()` view function output: diff --git a/developer-docs-site/package.json b/developer-docs-site/package.json index ca401dbb22b3b..ce25ae430e60c 100644 --- a/developer-docs-site/package.json +++ b/developer-docs-site/package.json @@ -41,6 +41,7 @@ "prism-react-renderer": "1.2.1", "process": "^0.11.10", "prop-types": "^15.8.1", + "raw-loader": "^4.0.2", "react": "17.0.2", "react-dom": "17.0.2", "react-markdown": "^8.0.7", diff --git a/developer-docs-site/pnpm-lock.yaml b/developer-docs-site/pnpm-lock.yaml index af7bf7ea7cf0d..d71b498db74bc 100644 --- a/developer-docs-site/pnpm-lock.yaml +++ b/developer-docs-site/pnpm-lock.yaml @@ -33,7 +33,7 @@ dependencies: version: 1.6.22(react@17.0.2) '@stoplight/elements': specifier: ^7.7.16 - version: 7.7.16(@babel/core@7.21.4)(react-dom@17.0.2)(react@17.0.2) + version: 7.7.16(react-dom@17.0.2)(react@17.0.2) '@types/prop-types': specifier: ^15.7.5 version: 15.7.5 @@ -64,6 +64,9 @@ dependencies: prop-types: specifier: ^15.8.1 version: 15.8.1 + raw-loader: + specifier: ^4.0.2 + version: 4.0.2(webpack@5.80.0) react: specifier: 17.0.2 version: 17.0.2 @@ -2901,7 +2904,7 @@ packages: webpack-sources: 3.2.3 dev: false - /@stoplight/elements-core@7.7.16(@babel/core@7.21.4)(react-dom@17.0.2)(react@17.0.2): + /@stoplight/elements-core@7.7.16(react-dom@17.0.2)(react@17.0.2): resolution: {integrity: sha512-P1DF+jyx9nQna+S30eAyr8J6gPIzAYTwKJEBnSCjdCkashPXu3X3nqi0Go9ykKnNDgrvlJyW9eFzEbY0bI5pHQ==} engines: {node: '>=14.13'} peerDependencies: @@ -2912,7 +2915,7 @@ packages: '@stoplight/json': 3.20.2 '@stoplight/json-schema-ref-parser': 9.2.2 '@stoplight/json-schema-sampler': 0.2.3 - '@stoplight/json-schema-viewer': 4.9.0(@babel/core@7.21.4)(@stoplight/markdown-viewer@5.6.0)(@stoplight/mosaic-code-viewer@1.40.0)(@stoplight/mosaic@1.40.0)(react-dom@17.0.2)(react@17.0.2) + '@stoplight/json-schema-viewer': 4.9.0(@stoplight/markdown-viewer@5.6.0)(@stoplight/mosaic-code-viewer@1.40.0)(@stoplight/mosaic@1.40.0)(react-dom@17.0.2)(react@17.0.2) '@stoplight/markdown-viewer': 5.6.0(@stoplight/mosaic-code-viewer@1.40.0)(@stoplight/mosaic@1.40.0)(react-dom@17.0.2)(react@17.0.2) '@stoplight/mosaic': 1.40.0(react-dom@17.0.2)(react@17.0.2) '@stoplight/mosaic-code-editor': 1.40.0(react-dom@17.0.2)(react@17.0.2) @@ -2923,7 +2926,7 @@ packages: '@stoplight/yaml': 4.2.3 classnames: 2.3.2 httpsnippet-lite: 3.0.5 - jotai: 1.3.9(@babel/core@7.21.4)(react-query@3.39.3)(react@17.0.2) + jotai: 1.3.9(react-query@3.39.3)(react@17.0.2) json-schema: 0.4.0 lodash: 4.17.21 nanoid: 3.3.6 @@ -2960,14 +2963,14 @@ packages: - xstate dev: false - /@stoplight/elements@7.7.16(@babel/core@7.21.4)(react-dom@17.0.2)(react@17.0.2): + /@stoplight/elements@7.7.16(react-dom@17.0.2)(react@17.0.2): resolution: {integrity: sha512-hIyzZkzC0Y/ehalfilW6b12NUtKmCAx86im/p/PVZ0xNgcDC2hPaYk6aVlzqxjWMHmzLQ6sxKtuynm5+0mafOw==} engines: {node: '>=14.13'} peerDependencies: react: '>=16.8' react-dom: '>=16.8' dependencies: - '@stoplight/elements-core': 7.7.16(@babel/core@7.21.4)(react-dom@17.0.2)(react@17.0.2) + '@stoplight/elements-core': 7.7.16(react-dom@17.0.2)(react@17.0.2) '@stoplight/http-spec': 5.7.2 '@stoplight/json': 3.20.2 '@stoplight/mosaic': 1.40.0(react-dom@17.0.2)(react@17.0.2) @@ -3078,7 +3081,7 @@ packages: magic-error: 0.0.1 dev: false - /@stoplight/json-schema-viewer@4.9.0(@babel/core@7.21.4)(@stoplight/markdown-viewer@5.6.0)(@stoplight/mosaic-code-viewer@1.40.0)(@stoplight/mosaic@1.40.0)(react-dom@17.0.2)(react@17.0.2): + /@stoplight/json-schema-viewer@4.9.0(@stoplight/markdown-viewer@5.6.0)(@stoplight/mosaic-code-viewer@1.40.0)(@stoplight/mosaic@1.40.0)(react-dom@17.0.2)(react@17.0.2): resolution: {integrity: sha512-xuOt1FFeFxiy/bJrD0++9rjKY0NlaxH7y6jDZGCMGmH0y2vykpC2ZbHji+ol7/rB5TvVp7oE0NwlpK8TVizinw==} engines: {node: '>=16'} peerDependencies: @@ -3097,7 +3100,7 @@ packages: '@types/json-schema': 7.0.11 classnames: 2.3.2 fnv-plus: 1.3.1 - jotai: 1.13.1(@babel/core@7.21.4)(react@17.0.2) + jotai: 1.13.1(react@17.0.2) lodash: 4.17.21 react: 17.0.2 react-dom: 17.0.2(react@17.0.2) @@ -3942,10 +3945,8 @@ packages: indent-string: 4.0.0 dev: false - /ajv-formats@2.1.1(ajv@8.12.0): + /ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - peerDependencies: - ajv: ^8.0.0 peerDependenciesMeta: ajv: optional: true @@ -6776,7 +6777,7 @@ packages: '@sideway/formula': 3.0.1 '@sideway/pinpoint': 2.0.0 - /jotai@1.13.1(@babel/core@7.21.4)(react@17.0.2): + /jotai@1.13.1(react@17.0.2): resolution: {integrity: sha512-RUmH1S4vLsG3V6fbGlKzGJnLrDcC/HNb5gH2AeA9DzuJknoVxSGvvg8OBB7lke+gDc4oXmdVsaKn/xDUhWZ0vw==} engines: {node: '>=12.20.0'} peerDependencies: @@ -6816,11 +6817,10 @@ packages: jotai-zustand: optional: true dependencies: - '@babel/core': 7.21.4 react: 17.0.2 dev: false - /jotai@1.3.9(@babel/core@7.21.4)(react-query@3.39.3)(react@17.0.2): + /jotai@1.3.9(react-query@3.39.3)(react@17.0.2): resolution: {integrity: sha512-b6DvH9gf+7TfjaboCO54g+C0yhaakIaUBtjLf0dk1p15FWCzNw/93sezdXy9cCaZ8qcEdMLJcjBwQlORmIq29g==} engines: {node: '>=12.7.0'} peerDependencies: @@ -6854,7 +6854,6 @@ packages: xstate: optional: true dependencies: - '@babel/core': 7.21.4 react: 17.0.2 react-query: 3.39.3(react-dom@17.0.2)(react@17.0.2) dev: false @@ -8949,6 +8948,17 @@ packages: unpipe: 1.0.0 dev: false + /raw-loader@4.0.2(webpack@5.80.0): + resolution: {integrity: sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + dependencies: + loader-utils: 2.0.4 + schema-utils: 3.1.2 + webpack: 5.80.0 + dev: false + /rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true @@ -9688,7 +9698,7 @@ packages: dependencies: '@types/json-schema': 7.0.11 ajv: 8.12.0 - ajv-formats: 2.1.1(ajv@8.12.0) + ajv-formats: 2.1.1 ajv-keywords: 5.1.0(ajv@8.12.0) dev: false diff --git a/developer-docs-site/scripts/additional_dict.txt b/developer-docs-site/scripts/additional_dict.txt index f61f220117832..bb816bef0cf3c 100644 --- a/developer-docs-site/scripts/additional_dict.txt +++ b/developer-docs-site/scripts/additional_dict.txt @@ -41,6 +41,7 @@ CPU CPUs ChainID Clippy +CodeBlock CoinStore CoinType CollectionData From 22beb1350acacfdfa41edcd14cf949f5dc9cd69a Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Mon, 15 May 2023 18:03:20 -0700 Subject: [PATCH 3/7] [CLI] Add multisig JSON governance, docs (#7709) Closes #7709 --- Cargo.lock | 2 + crates/aptos/Cargo.toml | 2 + crates/aptos/src/account/mod.rs | 2 + crates/aptos/src/account/multisig_account.rs | 123 ++- crates/aptos/src/common/types.rs | 62 +- crates/aptos/src/common/utils.rs | 7 + crates/aptos/src/move_tool/mod.rs | 97 ++- crates/aptos/src/test/mod.rs | 1 + .../docs/move/move-on-aptos/cli.md | 784 ++++++++++++++++++ .../tools/aptos-cli-tool/use-aptos-cli.md | 4 +- .../scripts/additional_dict.txt | 2 + 11 files changed, 1039 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1a9bccaba92c8..bd8e7f7650cb3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -202,6 +202,8 @@ dependencies = [ "serde 1.0.149", "serde_json", "serde_yaml 0.8.26", + "sha2 0.9.9", + "sha3 0.9.1", "shadow-rs", "tempfile", "termcolor", diff --git a/crates/aptos/Cargo.toml b/crates/aptos/Cargo.toml index da4c57665a483..13294161d12b6 100644 --- a/crates/aptos/Cargo.toml +++ b/crates/aptos/Cargo.toml @@ -76,6 +76,8 @@ self_update = { version = "0.34.0", features = ["archive-zip", "compression-zip- serde = { workspace = true } serde_json = { workspace = true } serde_yaml = { workspace = true } +sha2 = { workspace = true } +sha3 = { workspace = true } shadow-rs = { workspace = true } tempfile = { workspace = true } termcolor = { workspace = true } diff --git a/crates/aptos/src/account/mod.rs b/crates/aptos/src/account/mod.rs index 988f065a3783f..e886d780b9d1c 100644 --- a/crates/aptos/src/account/mod.rs +++ b/crates/aptos/src/account/mod.rs @@ -49,6 +49,7 @@ impl AccountTool { pub enum MultisigAccountTool { Approve(multisig_account::Approve), Create(multisig_account::Create), + CheckTransaction(multisig_account::CheckTransaction), CreateTransaction(multisig_account::CreateTransaction), Execute(multisig_account::Execute), ExecuteReject(multisig_account::ExecuteReject), @@ -60,6 +61,7 @@ impl MultisigAccountTool { match self { MultisigAccountTool::Approve(tool) => tool.execute_serialized().await, MultisigAccountTool::Create(tool) => tool.execute_serialized().await, + MultisigAccountTool::CheckTransaction(tool) => tool.execute_serialized().await, MultisigAccountTool::CreateTransaction(tool) => tool.execute_serialized().await, MultisigAccountTool::Execute(tool) => tool.execute_serialized().await, MultisigAccountTool::ExecuteReject(tool) => tool.execute_serialized().await, diff --git a/crates/aptos/src/account/multisig_account.rs b/crates/aptos/src/account/multisig_account.rs index 7cf01cf545b29..fffb25d9bd7bc 100644 --- a/crates/aptos/src/account/multisig_account.rs +++ b/crates/aptos/src/account/multisig_account.rs @@ -1,13 +1,16 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -use crate::common::types::{ - CliCommand, CliTypedResult, EntryFunctionArguments, MultisigAccount, TransactionOptions, - TransactionSummary, +use crate::common::{ + types::{ + CliCommand, CliError, CliTypedResult, EntryFunctionArguments, MultisigAccount, + TransactionOptions, TransactionSummary, + }, + utils::get_view_json_option_vec_ref, }; use aptos_cached_packages::aptos_stdlib; use aptos_rest_client::{ - aptos_api_types::{WriteResource, WriteSetChange}, + aptos_api_types::{HexEncodedBytes, ViewRequest, WriteResource, WriteSetChange}, Transaction, }; use aptos_types::{ @@ -18,6 +21,9 @@ use async_trait::async_trait; use bcs::to_bytes; use clap::Parser; use serde::Serialize; +use serde_json::json; +use sha2::Digest; +use sha3::Sha3_256; /// Create a new multisig account (v2) on-chain. /// @@ -107,6 +113,9 @@ pub struct CreateTransaction { pub(crate) txn_options: TransactionOptions, #[clap(flatten)] pub(crate) entry_function_args: EntryFunctionArguments, + /// Pass this flag if only storing transaction hash on-chain. Else full payload is stored + #[clap(long)] + pub(crate) hash_only: bool, } #[async_trait] @@ -116,18 +125,100 @@ impl CliCommand for CreateTransaction { } async fn execute(self) -> CliTypedResult { - let payload = - MultisigTransactionPayload::EntryFunction(self.entry_function_args.try_into()?); - self.txn_options - .submit_transaction(aptos_stdlib::multisig_account_create_transaction( + let multisig_transaction_payload_bytes = + to_bytes::(&self.entry_function_args.try_into()?)?; + let transaction_payload = if self.hash_only { + aptos_stdlib::multisig_account_create_transaction_with_hash( self.multisig_account.multisig_address, - to_bytes(&payload)?, - )) + Sha3_256::digest(&multisig_transaction_payload_bytes).to_vec(), + ) + } else { + aptos_stdlib::multisig_account_create_transaction( + self.multisig_account.multisig_address, + multisig_transaction_payload_bytes, + ) + }; + self.txn_options + .submit_transaction(transaction_payload) .await .map(|inner| inner.into()) } } +/// Check entry function against on-chain transaction proposal payload. +#[derive(Debug, Parser)] +pub struct CheckTransaction { + #[clap(flatten)] + pub(crate) multisig_account: MultisigAccount, + #[clap(flatten)] + pub(crate) txn_options: TransactionOptions, + #[clap(flatten)] + pub(crate) entry_function_args: EntryFunctionArguments, + /// Sequence number of multisig transaction to check + #[clap(long)] + pub(crate) sequence_number: u64, +} + +#[async_trait] +impl CliCommand for CheckTransaction { + fn command_name(&self) -> &'static str { + "CheckTransactionMultisig" + } + + async fn execute(self) -> CliTypedResult { + // Get multisig transaction via view function. + let multisig_transaction = &self + .txn_options + .view(ViewRequest { + function: "0x1::multisig_account::get_transaction".parse()?, + type_arguments: vec![], + arguments: vec![ + serde_json::Value::String(String::from( + &self.multisig_account.multisig_address, + )), + serde_json::Value::String(self.sequence_number.to_string()), + ], + }) + .await?[0]; + // Get reference to inner payload option from multisig transaction. + let multisig_payload_option_ref = + get_view_json_option_vec_ref(&multisig_transaction["payload"]); + // Get expected multisig transaction payload bytes from provided entry function. + let expected_multisig_transaction_payload_bytes = + to_bytes::(&self.entry_function_args.try_into()?)?; + // If full payload stored on-chain, get expected bytes and reference to actual hex option: + let (expected_bytes, actual_value_hex_option_ref) = + if !multisig_payload_option_ref.is_empty() { + ( + expected_multisig_transaction_payload_bytes, + multisig_payload_option_ref, + ) + // If only payload hash on-chain, get different compare values: + } else { + ( + Sha3_256::digest(&expected_multisig_transaction_payload_bytes).to_vec(), + get_view_json_option_vec_ref(&multisig_transaction["payload_hash"]), + ) + }; + // If expected bytes matches actual hex from view function: + if expected_bytes.eq(&actual_value_hex_option_ref[0] + .as_str() + .unwrap() + .parse::()? + .inner()) + { + // Return success message. + Ok(json!({ + "Status": "Transaction match", + "Multisig transaction": multisig_transaction + })) + } else { + // If a mismatch between expected bytes and actual hex, error out. + Err(CliError::UnexpectedError("Payload mismatch".to_string())) + } + } +} + /// Approve a multisig transaction. /// /// As one of the owners of the multisig, approve a transaction proposed for the multisig. @@ -206,6 +297,8 @@ pub struct Execute { pub(crate) multisig_account: MultisigAccount, #[clap(flatten)] pub(crate) txn_options: TransactionOptions, + #[clap(flatten)] + pub(crate) entry_function_args: EntryFunctionArguments, } #[async_trait] @@ -215,13 +308,11 @@ impl CliCommand for Execute { } async fn execute(self) -> CliTypedResult { - let payload = TransactionPayload::Multisig(Multisig { - multisig_address: self.multisig_account.multisig_address, - // TODO: Support passing an explicit payload - transaction_payload: None, - }); self.txn_options - .submit_transaction(payload) + .submit_transaction(TransactionPayload::Multisig(Multisig { + multisig_address: self.multisig_account.multisig_address, + transaction_payload: self.entry_function_args.try_into()?, + })) .await .map(|inner| inner.into()) } diff --git a/crates/aptos/src/common/types.rs b/crates/aptos/src/common/types.rs index dd0a999d9b2ea..bdba70098d94e 100644 --- a/crates/aptos/src/common/types.rs +++ b/crates/aptos/src/common/types.rs @@ -33,8 +33,8 @@ use aptos_sdk::{transaction_builder::TransactionFactory, types::LocalAccount}; use aptos_types::{ chain_id::ChainId, transaction::{ - authenticator::AuthenticationKey, EntryFunction, Script, SignedTransaction, - TransactionArgument, TransactionPayload, TransactionStatus, + authenticator::AuthenticationKey, EntryFunction, MultisigTransactionPayload, Script, + SignedTransaction, TransactionArgument, TransactionPayload, TransactionStatus, }, }; use async_trait::async_trait; @@ -844,7 +844,7 @@ pub struct SaveFile { } impl SaveFile { - /// Check if the key file exists already + /// Check if the `output_file` exists already pub fn check_file(&self) -> CliTypedResult<()> { check_if_file_exists(self.output_file.as_path(), self.prompt_options) } @@ -1789,7 +1789,7 @@ pub struct EntryFunctionArguments { /// Function name as `
::::` /// /// Example: `0x842ed41fad9640a2ad08fdd7d3e4f7f505319aac7d67e1c0dd6a7cce8732c7e3::message::set_message` - #[clap(long, required_unless_present = "json-file")] + #[clap(long)] pub function_id: Option, #[clap(flatten)] @@ -1804,9 +1804,15 @@ pub struct EntryFunctionArguments { impl EntryFunctionArguments { /// Get instance as if all fields passed from command line, parsing JSON input file if needed. - fn check_json_file(self) -> CliTypedResult { + fn check_input_style(self) -> CliTypedResult { if self.json_file.is_none() { - Ok(self) + if self.function_id.is_none() { + Err(CliError::CommandArgumentError( + "Must provide either function ID or JSON input file".to_string(), + )) + } else { + Ok(self) + } } else { let json = parse_json_file::(self.json_file.as_ref().unwrap())?; @@ -1824,7 +1830,7 @@ impl TryInto for EntryFunctionArguments { type Error = CliError; fn try_into(self) -> Result { - let entry_function_args = self.check_json_file()?; + let entry_function_args = self.check_input_style()?; let function_id = entry_function_args.function_id.unwrap(); Ok(EntryFunction::new( function_id.module_id, @@ -1835,11 +1841,31 @@ impl TryInto for EntryFunctionArguments { } } +impl TryInto for EntryFunctionArguments { + type Error = CliError; + + fn try_into(self) -> Result { + Ok(MultisigTransactionPayload::EntryFunction(self.try_into()?)) + } +} + +impl TryInto> for EntryFunctionArguments { + type Error = CliError; + + fn try_into(self) -> Result, Self::Error> { + if self.function_id.is_none() && self.json_file.is_none() { + Ok(None) + } else { + Ok(Some(self.try_into()?)) + } + } +} + impl TryInto for EntryFunctionArguments { type Error = CliError; fn try_into(self) -> Result { - let entry_function_args = self.check_json_file()?; + let entry_function_args = self.check_input_style()?; let function_id = entry_function_args.function_id.unwrap(); Ok(ViewRequest { function: EntryFunctionId { @@ -1867,7 +1893,7 @@ pub struct ScriptFunctionArguments { impl ScriptFunctionArguments { /// Get instance as if all fields passed from command line, parsing JSON input file if needed. - fn check_json_file(self) -> CliTypedResult { + fn check_input_style(self) -> CliTypedResult { if self.json_file.is_none() { Ok(self) } else { @@ -1882,7 +1908,7 @@ impl ScriptFunctionArguments { } pub fn create_script_payload(self, bytecode: Vec) -> CliTypedResult { - let script_function_args = self.check_json_file()?; + let script_function_args = self.check_input_style()?; Ok(TransactionPayload::Script(Script::new( bytecode, script_function_args.type_arg_vec.try_into()?, @@ -1891,19 +1917,19 @@ impl ScriptFunctionArguments { } } -#[derive(Deserialize)] +#[derive(Deserialize, Serialize)] /// JSON file format for function arguments. pub struct ArgWithTypeJSON { - arg_type: String, - arg_value: serde_json::Value, + pub(crate) arg_type: String, + pub(crate) arg_value: serde_json::Value, } -#[derive(Deserialize)] +#[derive(Deserialize, Serialize)] /// JSON file format for entry function arguments. -struct EntryFunctionArgumentsJSON { - function_id: String, - type_args: Vec, - args: Vec, +pub struct EntryFunctionArgumentsJSON { + pub(crate) function_id: String, + pub(crate) type_args: Vec, + pub(crate) args: Vec, } #[derive(Deserialize)] diff --git a/crates/aptos/src/common/utils.rs b/crates/aptos/src/common/utils.rs index 028ccd80c47ee..d4a258c7a28ed 100644 --- a/crates/aptos/src/common/utils.rs +++ b/crates/aptos/src/common/utils.rs @@ -480,3 +480,10 @@ pub fn parse_json_file Deserialize<'a>>(path_ref: &Path) -> CliTypedR CliError::UnableToReadFile(format!("{}", path_ref.display()), err.to_string()) }) } + +/// Return reference to inner option vector for view function JSON field known to have option. +/// +/// View functions represent an option as a JSON array titled `vec`. +pub fn get_view_json_option_vec_ref(value_ref: &serde_json::Value) -> &Vec { + value_ref["vec"].as_array().unwrap() +} diff --git a/crates/aptos/src/move_tool/mod.rs b/crates/aptos/src/move_tool/mod.rs index d93ed31a25c22..7d2fcf1367212 100644 --- a/crates/aptos/src/move_tool/mod.rs +++ b/crates/aptos/src/move_tool/mod.rs @@ -14,10 +14,10 @@ use crate::{ account::derive_resource_account::ResourceAccountSeed, common::{ types::{ - load_account_arg, CliConfig, CliError, CliTypedResult, ConfigSearchMode, - EntryFunctionArguments, MoveManifestAccountWrapper, MovePackageDir, ProfileOptions, - PromptOptions, RestOptions, ScriptFunctionArguments, TransactionOptions, - TransactionSummary, + load_account_arg, ArgWithTypeJSON, CliConfig, CliError, CliTypedResult, + ConfigSearchMode, EntryFunctionArguments, EntryFunctionArgumentsJSON, + MoveManifestAccountWrapper, MovePackageDir, ProfileOptions, PromptOptions, RestOptions, + SaveFile, ScriptFunctionArguments, TransactionOptions, TransactionSummary, }, utils::{ check_if_file_exists, create_dir_if_not_exist, dir_default_to_current, @@ -38,6 +38,9 @@ use aptos_framework::{ prover::ProverOptions, BuildOptions, BuiltPackage, }; use aptos_gas::{AbstractValueSizeGasParameters, NativeGasParameters}; +use aptos_rest_client::aptos_api_types::{ + self, EntryFunctionId, HexEncodedBytes, IdentifierWrapper, MoveModuleId, +}; use aptos_transactional_test_harness::run_aptos_test; use aptos_types::{ account_address::{create_resource_address, AccountAddress}, @@ -57,6 +60,7 @@ use move_package::{source_package::layout::SourcePackageLayout, BuildConfig}; use move_unit_test::UnitTestingConfig; pub use package_hooks::*; use serde::{Deserialize, Serialize}; +use serde_json::json; use std::{ collections::BTreeMap, fmt::{Display, Formatter}, @@ -625,6 +629,9 @@ pub struct PublishPackage { pub(crate) move_options: MovePackageDir, #[clap(flatten)] pub(crate) txn_options: TransactionOptions, + /// JSON output file to write publication transaction to instead of publishing on-chain + #[clap(long, parse(from_os_str))] + pub(crate) json_output_file: Option, } #[derive(ArgEnum, Clone, Copy, Debug)] @@ -717,6 +724,7 @@ impl CliCommand for PublishPackage { txn_options, override_size_check, included_artifacts_args, + json_output_file, } = self; let package_path = move_options.get_package_path()?; let options = included_artifacts_args.included_artifacts.build_options( @@ -728,10 +736,11 @@ impl CliCommand for PublishPackage { let compiled_units = package.extract_code(); // Send the compiled module and metadata using the code::publish_package_txn. - let metadata = package.extract_metadata()?; + let metadata_serialized = + bcs::to_bytes(&package.extract_metadata()?).expect("PackageMetadata has BCS"); let payload = aptos_cached_packages::aptos_stdlib::code_publish_package_txn( - bcs::to_bytes(&metadata).expect("PackageMetadata has BCS"), - compiled_units, + metadata_serialized.clone(), + compiled_units.clone(), ); let size = bcs::serialized_size(&payload)?; println!("package size {} bytes", size); @@ -743,7 +752,68 @@ impl CliCommand for PublishPackage { MAX_PUBLISH_PACKAGE_SIZE, size ))); } - profile_or_submit(payload, &txn_options).await + // If JSON output file specified, store entry function JSON file on disk. + if let Some(output_file) = json_output_file { + // Extract entry function data from publication payload. + let entry_function = payload.into_entry_function(); + let entry_function_id = EntryFunctionId { + module: MoveModuleId::from(entry_function.module().clone()), + name: IdentifierWrapper::from(entry_function.function()), + }; + let package_metadata_hex = HexEncodedBytes(metadata_serialized).to_string(); + let package_code_hex_vec: Vec = compiled_units + .clone() + .into_iter() + .map(|element| HexEncodedBytes(element).to_string()) + .collect(); + // Construct entry function JSON file representation from entry function data. + let json = EntryFunctionArgumentsJSON { + function_id: entry_function_id.to_string(), + type_args: vec![], + args: vec![ + ArgWithTypeJSON { + arg_type: "hex".to_string(), + arg_value: serde_json::Value::String(package_metadata_hex), + }, + ArgWithTypeJSON { + arg_type: "hex".to_string(), + arg_value: json!(package_code_hex_vec), + }, + ], + }; + // Create save file options for checking and saving file to disk. + let save_file = SaveFile { + output_file, + prompt_options: txn_options.prompt_options, + }; + save_file.check_file()?; + save_file.save_to_file( + "Publication entry function JSON file", + serde_json::to_string_pretty(&json) + .map_err(|err| CliError::UnexpectedError(format!("{}", err)))? + .as_bytes(), + )?; + Ok(TransactionSummary { + // Pass bogus hash for required struct field. + transaction_hash: aptos_api_types::HashValue::from(HashValue::zero()), + gas_used: None, + gas_unit_price: None, + pending: None, + sender: None, + sequence_number: None, + success: None, + timestamp_us: None, + version: None, + // Pass feedback message in available String field. + vm_status: Some(format!( + "Publication entry function JSON file saved to {}", + save_file.output_file.display() + )), + }) + // If no JSON output file specified, publish on-chain. + } else { + profile_or_submit(payload, &txn_options).await + } } } @@ -1205,7 +1275,9 @@ impl FunctionArgType { ) .map_err(|err| CliError::BCS("arg", err)), FunctionArgType::Hex => bcs::to_bytes( - &hex::decode(arg).map_err(|err| CliError::UnableToParse("hex", err.to_string()))?, + HexEncodedBytes::from_str(arg) + .map_err(|err| CliError::UnableToParse("hex", err.to_string()))? + .inner(), ) .map_err(|err| CliError::BCS("arg", err)), FunctionArgType::String => bcs::to_bytes(arg).map_err(|err| CliError::BCS("arg", err)), @@ -1238,9 +1310,10 @@ impl FunctionArgType { .map_err(|err| CliError::UnableToParse("u256", err.to_string()))?, ) .map_err(|err| CliError::BCS("arg", err)), - FunctionArgType::Raw => { - hex::decode(arg).map_err(|err| CliError::UnableToParse("raw", err.to_string())) - }, + FunctionArgType::Raw => Ok(HexEncodedBytes::from_str(arg) + .map_err(|err| CliError::UnableToParse("raw", err.to_string()))? + .inner() + .to_vec()), } } diff --git a/crates/aptos/src/test/mod.rs b/crates/aptos/src/test/mod.rs index dada85091fd82..bcc18e62ded00 100644 --- a/crates/aptos/src/test/mod.rs +++ b/crates/aptos/src/test/mod.rs @@ -868,6 +868,7 @@ impl CliTestFramework { included_artifacts_args: IncludedArtifactsArgs { included_artifacts: included_artifacts.unwrap_or(IncludedArtifacts::Sparse), }, + json_output_file: None, } .execute() .await diff --git a/developer-docs-site/docs/move/move-on-aptos/cli.md b/developer-docs-site/docs/move/move-on-aptos/cli.md index 979f85c3814c6..7402314e55f3f 100644 --- a/developer-docs-site/docs/move/move-on-aptos/cli.md +++ b/developer-docs-site/docs/move/move-on-aptos/cli.md @@ -630,3 +630,787 @@ aptos move view \ :::note As of the time of this writing, the `aptos` CLI only supports script function arguments for vectors of type `u8`, and only up to a vector depth of 1. Hence `vector
` and `vector>` are invalid script function argument types. ::: + + +## Multisig governance + +### Background + +This section builds upon the [Arguments in JSON](#arguments-in-json) section, and likewise references the [`CliArgs` example package](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/cli_args). + +:::tip +Set your working directory to [`aptos-move/move-examples/cli_args`](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/cli_args) to follow along: + +```bash +cd /aptos-core/aptos-move/move-examples/cli_args +``` +::: + + +For this example, Ace and Bee will conduct governance operations from a 2-of-2 multisig account. + +### Account creation + +Start by mining a vanity address for both signatories: + +```bash title=Command +aptos key generate \ + --vanity-prefix 0xace \ + --output-file ace.key +``` + +
Output + +```bash +{ + "Result": { + "PublicKey Path": "ace.key.pub", + "Account Address:": "{ + "Result": { + "PublicKey Path": "bee.key.pub", + "PrivateKey Path": "bee.key", + "Account Address:": "0xbee090156aa0efa1fd6242d194400ef46471e2eca80dcd654532319c8b0355d4" + } +}", + "PrivateKey Path": "ace.key" + } +} +``` + +
+ +```bash title=Command +aptos key generate \ + --vanity-prefix 0xbee \ + --output-file bee.key +``` + +
Output + +```bash +{ + "Result": { + "PublicKey Path": "bee.key.pub", + "PrivateKey Path": "bee.key", + "Account Address:": "0xbee090156aa0efa1fd6242d194400ef46471e2eca80dcd654532319c8b0355d4" + } +} +``` + +
+ +:::tip +The exact account address should vary for each run, though the vanity prefix should not. +::: + +Store Ace and Bee's addresses in shell variables so you can call them inline later on: + +```bash +# Your exact addresses should vary +ace_addr=0xacee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4 +bee_addr=0xbee090156aa0efa1fd6242d194400ef46471e2eca80dcd654532319c8b0355d4 +``` + +Now fund Ace's and Bee's accounts using the faucet: + +```bash title=Command +aptos account fund-with-faucet --account $ace_addr +``` + +
Output + +```bash +{ + "Result": "Added 100000000 Octas to account acee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4" +} +``` + +
+ +```bash title=Command +aptos account fund-with-faucet --account $bee_addr +``` + +
Output + +```bash +{ + "Result": "Added 100000000 Octas to account bee090156aa0efa1fd6242d194400ef46471e2eca80dcd654532319c8b0355d4" +} +``` + +
+ +Ace can now create a multisig account: + +```bash title=Command +aptos multisig create \ + --additional-owners $bee_addr \ + --num-signatures-required 2 \ + --private-key-file ace.key +``` + +
Output + +```bash +{ + "Result": { + "multisig_address": "2dc9b2fdba8ace3b9f96e5d3bb7ea04d39b1640020c8697eb0f1f4b33cad0d77", + "transaction_hash": "0x9b566b9357c1cda768948f6aaf951c6d5e5c3e1749c2fb3147b5eed371e962ee", + "gas_used": 1524, + "gas_unit_price": 100, + "sender": "acee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4", + "sequence_number": 0, + "success": true, + "timestamp_us": 1684644958857234, + "version": 525304840, + "vm_status": "Executed successfully" + } +} +``` + +
+ +Store the multisig address in a shell variable: + +```bash +# Your address should vary +multisig_addr=2dc9b2fdba8ace3b9f96e5d3bb7ea04d39b1640020c8697eb0f1f4b33cad0d77 +``` + +### Inspect the multisig + +Use the assorted [`multisig_account.move` view functions](https://github.com/aptos-labs/aptos-core/blob/9fa0102c3e474d99ea35a0a85c6893604be41611/aptos-move/framework/aptos-framework/sources/multisig_account.move#L237) to inspect the multisig: + +```bash title="Number of signatures required" +aptos move view \ + --function-id 0x1::multisig_account::num_signatures_required \ + --args \ + address:"$multisig_addr" +``` + +
Output + +```bash +{ + "Result": [ + "2" + ] +} +``` + +
+ +```bash title="Owners" +aptos move view \ + --function-id 0x1::multisig_account::owners \ + --args \ + address:"$multisig_addr" +``` + +
Output + +```bash +{ + "Result": [ + [ + "0xbee090156aa0efa1fd6242d194400ef46471e2eca80dcd654532319c8b0355d4", + "0xacee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4" + ] + ] +} +``` + +
+ +```bash title="Last resolved sequence number" +aptos move view \ + --function-id 0x1::multisig_account::last_resolved_sequence_number \ + --args \ + address:"$multisig_addr" +``` + +
Output + +```bash +{ + "Result": [ + "0" + ] +} +``` + +
+ +```bash title="Next sequence number" +aptos move view \ + --function-id 0x1::multisig_account::next_sequence_number \ + --args \ + address:"$multisig_addr" +``` + +
Output + +```bash +{ + "Result": [ + "1" + ] +} +``` + +
+ +### Enqueue a publication transaction + +The first multisig transaction enqueued will be a transaction for publication of the [`CliArgs` example package](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/cli_args). +First, generate a publication entry function JSON file: + +```bash title="Command" +aptos move publish \ + --named-addresses test_account=$multisig_addr \ + --json-output-file publication.json +``` + +
Output + +```bash +{ + "Result": { + "transaction_hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "vm_status": "Publication entry function JSON file saved to publication.json" + } +} +``` + +
+ +Now have Ace propose publication of the package from the multisig account, storing only the payload hash on-chain: + +```bash title="Command" +aptos multisig create-transaction \ + --multisig-address $multisig_addr \ + --json-file publication.json \ + --hash-only \ + --private-key-file ace.key +``` + +
Output + +```bash +{ + "Result": { + "transaction_hash": "0xde5dfd2ca09cf2b3ca040386633de5c1c8aee5842d49303c757eb14819e20a3f", + "gas_used": 510, + "gas_unit_price": 100, + "sender": "acee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4", + "sequence_number": 1, + "success": true, + "timestamp_us": 1684645051403399, + "version": 525305517, + "vm_status": "Executed successfully" + } +} +``` + +
+ +Note that the last resolved sequence number is still 0 because no transactions have been resolved: + +```bash title="Last resolved sequence number" +aptos move view \ + --function-id 0x1::multisig_account::last_resolved_sequence_number \ + --args \ + address:"$multisig_addr" +``` + +
Output + +```bash +{ + "Result": [ + "0" + ] +} +``` + +
+ +However the next sequence number has been incremented because a transaction has been enqueued: + +```bash title="Next sequence number" +aptos move view \ + --function-id 0x1::multisig_account::next_sequence_number \ + --args \ + address:"$multisig_addr" +``` + +
Output + +```bash +{ + "Result": [ + "2" + ] +} +``` + +
+ +The multisig transaction enqueued on-chain can now be inspected: + +```bash title="Get transaction" +aptos move view \ + --function-id 0x1::multisig_account::get_transaction \ + --args \ + address:"$multisig_addr" \ + String:1 +``` + +
Output + +```bash +{ + "Result": [ + { + "creation_time_secs": "1684645051", + "creator": "0xacee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4", + "payload": { + "vec": [] + }, + "payload_hash": { + "vec": [ + "0xce31dac5c29fd54c643119b4011a4991bd96141a21be10100d75336230417e89" + ] + }, + "votes": { + "data": [ + { + "key": "0xacee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4", + "value": true + } + ] + } + } + ] +} +``` + +
+ +Note from the above result that no payload is stored on-chain, and that Ace implicitly approved the transaction (voted `true`) upon the submission of the proposal. + +### Enqueue a governance parameter transaction + +Now have Bee enqueue a governance parameter setter transaction, storing the entire transaction payload on-chain: + +```bash title="Command" +aptos multisig create-transaction \ + --multisig-address $multisig_addr \ + --function-id $multisig_addr::cli_args::set_vals \ + --type-args \ + 0x1::account::Account \ + 0x1::chain_id::ChainId \ + --args \ + u8:123 \ + "bool:[false, true, false, false]" \ + 'address:[["0xace", "0xbee"], ["0xcad"], []]' \ + --private-key-file bee.key + +``` + +
Output + +```bash +{ + "Result": { + "transaction_hash": "0x92c7f7c103f2f7409ec0ede1325ce69c9357dc07423d1801d2c49eeee74d91ae", + "gas_used": 511, + "gas_unit_price": 100, + "sender": "bee090156aa0efa1fd6242d194400ef46471e2eca80dcd654532319c8b0355d4", + "sequence_number": 0, + "success": true, + "timestamp_us": 1684645156069617, + "version": 525306308, + "vm_status": "Executed successfully" + } +} +``` + +
+ +Note the next sequence number has been incremented again: + +```bash title="Next sequence number" +aptos move view \ + --function-id 0x1::multisig_account::next_sequence_number \ + --args \ + address:"$multisig_addr" +``` + +
Output + +```bash +{ + "Result": [ + "3" + ] +} +``` + +
+ +Now both the publication and parameter transactions are pending: + +```bash title="Get pending transactions" +aptos move view \ + --function-id 0x1::multisig_account::get_pending_transactions \ + --args \ + address:"$multisig_addr" +``` + +
Output + +```bash +{ + "Result": [ + [ + { + "creation_time_secs": "1684645051", + "creator": "0xacee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4", + "payload": { + "vec": [] + }, + "payload_hash": { + "vec": [ + "0xce31dac5c29fd54c643119b4011a4991bd96141a21be10100d75336230417e89" + ] + }, + "votes": { + "data": [ + { + "key": "0xacee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4", + "value": true + } + ] + } + }, + { + "creation_time_secs": "1684645156", + "creator": "0xbee090156aa0efa1fd6242d194400ef46471e2eca80dcd654532319c8b0355d4", + "payload": { + "vec": [ + "0x002dc9b2fdba8ace3b9f96e5d3bb7ea04d39b1640020c8697eb0f1f4b33cad0d7708636c695f61726773087365745f76616c7302070000000000000000000000000000000000000000000000000000000000000001076163636f756e74074163636f756e740007000000000000000000000000000000000000000000000000000000000000000108636861696e5f696407436861696e49640003017b0504000100006403020000000000000000000000000000000000000000000000000000000000000ace0000000000000000000000000000000000000000000000000000000000000bee010000000000000000000000000000000000000000000000000000000000000cad00" + ] + }, + "payload_hash": { + "vec": [] + }, + "votes": { + "data": [ + { + "key": "0xbee090156aa0efa1fd6242d194400ef46471e2eca80dcd654532319c8b0355d4", + "value": true + } + ] + } + } + ] + ] +} +``` + +
+ +### Execute the publication transaction + +Since only Ace has voted on the publication transaction (which he implicitly approved upon proposing) the transaction can't be executed yet: + +```bash title="Can be executed" +aptos move view \ + --function-id 0x1::multisig_account::can_be_executed \ + --args \ + address:"$multisig_addr" \ + String:1 +``` + +
Output + +```bash +{ + "Result": [ + false + ] +} +``` + +
+ +Before Bee votes, however, she checks that the payload hash stored on-chain matches the publication entry function JSON file: + +```bash title="Checking transaction" +aptos multisig check-transaction \ + --multisig-address $multisig_addr \ + --json-file publication.json \ + --sequence-number 1 +``` + +
Output + +```bash +{ + "Result": { + "Status": "Transaction match", + "Multisig transaction": { + "creation_time_secs": "1684645051", + "creator": "0xacee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4", + "payload": { + "vec": [] + }, + "payload_hash": { + "vec": [ + "0xce31dac5c29fd54c643119b4011a4991bd96141a21be10100d75336230417e89" + ] + }, + "votes": { + "data": [ + { + "key": "0xacee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4", + "value": true + } + ] + } + } + } +} +``` + +
+ +Since Bee has verified that the on-chain payload hash checks out against her locally-compiled package publication JSON file, she votes yes: + + +```bash title="Approving transaction" +aptos multisig approve \ + --multisig-address $multisig_addr \ + --sequence-number 1 \ + --private-key-file bee.key +``` + +
Output + +```bash +{ + "Result": { + "transaction_hash": "0x24a12b1839b2dd114780289ec6e36a4a33b1ab2a4c3f22dd9512873aed65723a", + "gas_used": 6, + "gas_unit_price": 100, + "sender": "bee090156aa0efa1fd6242d194400ef46471e2eca80dcd654532319c8b0355d4", + "sequence_number": 1, + "success": true, + "timestamp_us": 1684645251141034, + "version": 525307001, + "vm_status": "Executed successfully" + } +} +``` + +
+ +Now the transaction can be executed: + +```bash title="Can be executed" +aptos move view \ + --function-id 0x1::multisig_account::can_be_executed \ + --args \ + address:"$multisig_addr" \ + String:1 +``` + +
Output + +```bash +{ + "Result": [ + true + ] +} +``` + +
+ +Now either Ace or Bee can invoke the publication transaction from the multisig account, passing the full transaction payload since only the hash was stored on-chain: + +```bash title="Publication" +aptos multisig execute \ + --multisig-address $multisig_addr \ + --json-file publication.json \ + --private-key-file bee.key \ + --max-gas 10000 +``` + +:::tip +Pending the resolution of [#8304](https://github.com/aptos-labs/aptos-core/issues/8304), the transaction simulator (which is used to estimate gas costs) is broken for multisig transactions, so you will have to manually specify a max gas amount. +::: + +
Output + +Also pending the resolution of [#8304](https://github.com/aptos-labs/aptos-core/issues/8304), the CLI output for a successful multisig publication transaction execution results in an API error if only the payload hash has been stored on-chain, but the transaction can be manually verified using an explorer. + +
+ +### Execute the governance parameter transaction + +Since only Bee has voted on the governance parameter transaction (which she implicitly approved upon proposing), the transaction can't be executed yet: + +```bash title="Can be executed" +aptos move view \ + --function-id 0x1::multisig_account::can_be_executed \ + --args \ + address:"$multisig_addr" \ + String:2 +``` + +
Output + +```bash +{ + "Result": [ + false + ] +} +``` + +
+ +Before Ace votes, however, he checks that the payload stored on-chain matches the function arguments he expects: + +```bash title="Checking transaction" +aptos multisig check-transaction \ + --multisig-address $multisig_addr \ + --function-id $multisig_addr::cli_args::set_vals \ + --type-args \ + 0x1::account::Account \ + 0x1::chain_id::ChainId \ + --args \ + u8:123 \ + "bool:[false, true, false, false]" \ + 'address:[["0xace", "0xbee"], ["0xcad"], []]' \ + --sequence-number 2 +``` + +
Output + +```bash +{ + "Result": { + "Status": "Transaction match", + "Multisig transaction": { + "creation_time_secs": "1684645156", + "creator": "0xbee090156aa0efa1fd6242d194400ef46471e2eca80dcd654532319c8b0355d4", + "payload": { + "vec": [ + "0x002dc9b2fdba8ace3b9f96e5d3bb7ea04d39b1640020c8697eb0f1f4b33cad0d7708636c695f61726773087365745f76616c7302070000000000000000000000000000000000000000000000000000000000000001076163636f756e74074163636f756e740007000000000000000000000000000000000000000000000000000000000000000108636861696e5f696407436861696e49640003017b0504000100006403020000000000000000000000000000000000000000000000000000000000000ace0000000000000000000000000000000000000000000000000000000000000bee010000000000000000000000000000000000000000000000000000000000000cad00" + ] + }, + "payload_hash": { + "vec": [] + }, + "votes": { + "data": [ + { + "key": "0xbee090156aa0efa1fd6242d194400ef46471e2eca80dcd654532319c8b0355d4", + "value": true + } + ] + } + } + } +} +``` + +
+ +Note that the check fails if he modifies even a single argument: + +```bash title="Checking transaction with modified u8" +aptos multisig check-transaction \ + --multisig-address $multisig_addr \ + --function-id $multisig_addr::cli_args::set_vals \ + --type-args \ + 0x1::account::Account \ + 0x1::chain_id::ChainId \ + --args \ + u8:200 \ + "bool:[false, true, false, false]" \ + 'address:[["0xace", "0xbee"], ["0xcad"], []]' \ + --sequence-number 2 +``` + +
Output + +```bash +{ + "Error": "Unexpected error: Payload mismatch" +} +``` + +
+ +Ace approves the transaction: + +```bash title="Approving transaction" +aptos multisig approve \ + --multisig-address $multisig_addr \ + --sequence-number 2 \ + --private-key-file ace.key +``` + +
Output + +```bash +{ + "Result": { + "transaction_hash": "0x5cdc4fd171d7b2ae3676b0c4a9a3fa1523ca46fde205ec17cc0ef7c0c92108d5", + "gas_used": 6, + "gas_unit_price": 100, + "sender": "acee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4", + "sequence_number": 2, + "success": true, + "timestamp_us": 1684646050747796, + "version": 525312861, + "vm_status": "Executed successfully" + } +} +``` + +
+ +Since the payload was stored on-chain, it is not required to execute the pending transaction: + +```bash title="Publication" +aptos multisig execute \ + --multisig-address $multisig_addr \ + --private-key-file ace.key \ + --max-gas 10000 +``` + +
Output + +```bash +{ + "Result": { + "transaction_hash": "0x2cc091926460ac37e0bff280d5cc6a3a225838ff8f13dc224be6cd5be6725fea", + "gas_used": 505, + "gas_unit_price": 100, + "sender": "acee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4", + "sequence_number": 3, + "success": true, + "timestamp_us": 1684646121523835, + "version": 525313412, + "vm_status": "Executed successfully" + } +} +``` + +
diff --git a/developer-docs-site/docs/tools/aptos-cli-tool/use-aptos-cli.md b/developer-docs-site/docs/tools/aptos-cli-tool/use-aptos-cli.md index 9d1fe51c17412..31061cb68a3e1 100644 --- a/developer-docs-site/docs/tools/aptos-cli-tool/use-aptos-cli.md +++ b/developer-docs-site/docs/tools/aptos-cli-tool/use-aptos-cli.md @@ -29,8 +29,10 @@ SUBCOMMANDS: init Tool to initialize current directory for the aptos tool key Tool for generating, inspecting, and interacting with keys move Tool for Move related operations + multisig Tool for interacting with multisig accounts node Tool for operations related to nodes - stake Tool for manipulating stake + stake Tool for manipulating stake and stake pools + update Update the CLI itself ``` ### Command-specific help diff --git a/developer-docs-site/scripts/additional_dict.txt b/developer-docs-site/scripts/additional_dict.txt index bb816bef0cf3c..f707f3aef9379 100644 --- a/developer-docs-site/scripts/additional_dict.txt +++ b/developer-docs-site/scripts/additional_dict.txt @@ -96,6 +96,7 @@ EWITHDRAW EWRONG EZERO Eg +Enqueue EntryFunction EntryFunctionPayload Enums @@ -332,6 +333,7 @@ doesnt dr eg endian +enqueue entrancy enum enums From a4bbabba25d9e148b60f92dd0b01ee939cff5a07 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Wed, 24 May 2023 18:53:46 -0700 Subject: [PATCH 4/7] Address review comments from @movekevin Resolve https://github.com/aptos-labs/aptos-core/pull/8346#discussion_r1204684397 Resolve https://github.com/aptos-labs/aptos-core/pull/8346#discussion_r1204684899 Resolve https://github.com/aptos-labs/aptos-core/pull/8346#discussion_r1204686256 Resolve https://github.com/aptos-labs/aptos-core/pull/8346#discussion_r1204687387 Resolve https://github.com/aptos-labs/aptos-core/pull/8346#discussion_r1204687915 Resolve https://github.com/aptos-labs/aptos-core/pull/8346#discussion_r1204688767 Resolve https://github.com/aptos-labs/aptos-core/pull/8346#discussion_r1204689099 Resolve https://github.com/aptos-labs/aptos-core/pull/8346#discussion_r1204691288 Resolve https://github.com/aptos-labs/aptos-core/pull/8346#discussion_r1204696710 --- Cargo.lock | 2 - aptos-move/move-examples/cli_args/Move.toml | 3 +- .../cli_args/scripts/set_vals.move | 10 +- .../cli_args/sources/cli_args.move | 37 +- crates/aptos/Cargo.toml | 2 - crates/aptos/src/account/multisig_account.rs | 65 ++- crates/aptos/src/common/types.rs | 11 +- .../docs/move/move-on-aptos/cli.md | 500 +++++++++++------- .../scripts/additional_dict.txt | 1 + 9 files changed, 388 insertions(+), 243 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bd8e7f7650cb3..1a9bccaba92c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -202,8 +202,6 @@ dependencies = [ "serde 1.0.149", "serde_json", "serde_yaml 0.8.26", - "sha2 0.9.9", - "sha3 0.9.1", "shadow-rs", "tempfile", "termcolor", diff --git a/aptos-move/move-examples/cli_args/Move.toml b/aptos-move/move-examples/cli_args/Move.toml index 2603182eaecb2..4135245a7f7fe 100644 --- a/aptos-move/move-examples/cli_args/Move.toml +++ b/aptos-move/move-examples/cli_args/Move.toml @@ -1,4 +1,3 @@ -# :!:>manifest [package] name = "CliArgs" version = "0.1.0" @@ -8,4 +7,4 @@ upgrade_policy = "compatible" test_account = "_" [dependencies.AptosFramework] -local = "../../framework/aptos-framework" # <:!:manifest \ No newline at end of file +local = "../../framework/aptos-framework" \ No newline at end of file diff --git a/aptos-move/move-examples/cli_args/scripts/set_vals.move b/aptos-move/move-examples/cli_args/scripts/set_vals.move index a118712595a57..b4db341a75e69 100644 --- a/aptos-move/move-examples/cli_args/scripts/set_vals.move +++ b/aptos-move/move-examples/cli_args/scripts/set_vals.move @@ -13,15 +13,7 @@ script { u8_vec: vector, address_solo: address, ) { - let bool_vec = vector[]; - let i = 0; - while (i < vector::length(&u8_vec)) { - vector::push_back( - &mut bool_vec, - *vector::borrow(&u8_vec, i) > u8_solo - ); - i = i + 1; - }; + let bool_vec = vector::map_ref(&u8_vec, |e_ref| *e_ref > u8_solo); let addr_vec_vec = vector[vector[address_solo]]; cli_args::set_vals(account, u8_solo, bool_vec, addr_vec_vec); } diff --git a/aptos-move/move-examples/cli_args/sources/cli_args.move b/aptos-move/move-examples/cli_args/sources/cli_args.move index cdd67e3b81dc8..2995f2a0894b7 100644 --- a/aptos-move/move-examples/cli_args/sources/cli_args.move +++ b/aptos-move/move-examples/cli_args/sources/cli_args.move @@ -35,23 +35,30 @@ module test_account::cli_args { } //<:!:setter // :!:>view + struct RevealResult has drop { + u8_solo: u8, + bool_vec: vector, + address_vec_vec: vector>, + type_info_1_match: bool, + type_info_2_match: bool + } + #[view] - /// Reveal first three fields in host's `Holder`, as well as two - /// `bool` flags denoting if `T1` and `T2` respectively match - /// `Holder.type_info_1` and `Holder.type_info_2`. - public fun reveal(host: address): ( - u8, - vector, - vector>, - bool, - bool - ) acquires Holder { + /// Pack into a `RevealResult` the first three fields in host's + /// `Holder`, as well as two `bool` flags denoting if `T1` & `T2` + /// respectively match `Holder.type_info_1` & `Holder.type_info_2`, + /// then return the `RevealResult`. + public fun reveal(host: address): RevealResult acquires Holder { let holder_ref = borrow_global(host); - (holder_ref.u8_solo, - holder_ref.bool_vec, - holder_ref.address_vec_vec, - type_info::type_of() == holder_ref.type_info_1, - type_info::type_of() == holder_ref.type_info_2) + RevealResult { + u8_solo: holder_ref.u8_solo, + bool_vec: holder_ref.bool_vec, + address_vec_vec: holder_ref.address_vec_vec, + type_info_1_match: + type_info::type_of() == holder_ref.type_info_1, + type_info_2_match: + type_info::type_of() == holder_ref.type_info_2 + } } } //<:!:view diff --git a/crates/aptos/Cargo.toml b/crates/aptos/Cargo.toml index 13294161d12b6..da4c57665a483 100644 --- a/crates/aptos/Cargo.toml +++ b/crates/aptos/Cargo.toml @@ -76,8 +76,6 @@ self_update = { version = "0.34.0", features = ["archive-zip", "compression-zip- serde = { workspace = true } serde_json = { workspace = true } serde_yaml = { workspace = true } -sha2 = { workspace = true } -sha3 = { workspace = true } shadow-rs = { workspace = true } tempfile = { workspace = true } termcolor = { workspace = true } diff --git a/crates/aptos/src/account/multisig_account.rs b/crates/aptos/src/account/multisig_account.rs index fffb25d9bd7bc..293559cc2f907 100644 --- a/crates/aptos/src/account/multisig_account.rs +++ b/crates/aptos/src/account/multisig_account.rs @@ -4,11 +4,12 @@ use crate::common::{ types::{ CliCommand, CliError, CliTypedResult, EntryFunctionArguments, MultisigAccount, - TransactionOptions, TransactionSummary, + MultisigAccountWithSequenceNumber, TransactionOptions, TransactionSummary, }, utils::get_view_json_option_vec_ref, }; use aptos_cached_packages::aptos_stdlib; +use aptos_crypto::HashValue; use aptos_rest_client::{ aptos_api_types::{HexEncodedBytes, ViewRequest, WriteResource, WriteSetChange}, Transaction, @@ -22,8 +23,6 @@ use bcs::to_bytes; use clap::Parser; use serde::Serialize; use serde_json::json; -use sha2::Digest; -use sha3::Sha3_256; /// Create a new multisig account (v2) on-chain. /// @@ -115,7 +114,7 @@ pub struct CreateTransaction { pub(crate) entry_function_args: EntryFunctionArguments, /// Pass this flag if only storing transaction hash on-chain. Else full payload is stored #[clap(long)] - pub(crate) hash_only: bool, + pub(crate) store_hash_only: bool, } #[async_trait] @@ -127,10 +126,10 @@ impl CliCommand for CreateTransaction { async fn execute(self) -> CliTypedResult { let multisig_transaction_payload_bytes = to_bytes::(&self.entry_function_args.try_into()?)?; - let transaction_payload = if self.hash_only { + let transaction_payload = if self.store_hash_only { aptos_stdlib::multisig_account_create_transaction_with_hash( self.multisig_account.multisig_address, - Sha3_256::digest(&multisig_transaction_payload_bytes).to_vec(), + HashValue::sha3_256_of(&multisig_transaction_payload_bytes).to_vec(), ) } else { aptos_stdlib::multisig_account_create_transaction( @@ -149,14 +148,11 @@ impl CliCommand for CreateTransaction { #[derive(Debug, Parser)] pub struct CheckTransaction { #[clap(flatten)] - pub(crate) multisig_account: MultisigAccount, + pub(crate) multisig_account_with_sequence_number: MultisigAccountWithSequenceNumber, #[clap(flatten)] pub(crate) txn_options: TransactionOptions, #[clap(flatten)] pub(crate) entry_function_args: EntryFunctionArguments, - /// Sequence number of multisig transaction to check - #[clap(long)] - pub(crate) sequence_number: u64, } #[async_trait] @@ -174,9 +170,16 @@ impl CliCommand for CheckTransaction { type_arguments: vec![], arguments: vec![ serde_json::Value::String(String::from( - &self.multisig_account.multisig_address, + &self + .multisig_account_with_sequence_number + .multisig_account + .multisig_address, )), - serde_json::Value::String(self.sequence_number.to_string()), + serde_json::Value::String( + self.multisig_account_with_sequence_number + .sequence_number + .to_string(), + ), ], }) .await?[0]; @@ -186,18 +189,18 @@ impl CliCommand for CheckTransaction { // Get expected multisig transaction payload bytes from provided entry function. let expected_multisig_transaction_payload_bytes = to_bytes::(&self.entry_function_args.try_into()?)?; - // If full payload stored on-chain, get expected bytes and reference to actual hex option: + // If only storing payload hash on-chain, get expected hash bytes and actual hash hex: let (expected_bytes, actual_value_hex_option_ref) = - if !multisig_payload_option_ref.is_empty() { + if multisig_payload_option_ref.is_empty() { ( - expected_multisig_transaction_payload_bytes, - multisig_payload_option_ref, + HashValue::sha3_256_of(&expected_multisig_transaction_payload_bytes).to_vec(), + get_view_json_option_vec_ref(&multisig_transaction["payload_hash"]), ) - // If only payload hash on-chain, get different compare values: + // If full payload stored on-chain, get expected payload bytes and actual payload hex: } else { ( - Sha3_256::digest(&expected_multisig_transaction_payload_bytes).to_vec(), - get_view_json_option_vec_ref(&multisig_transaction["payload_hash"]), + expected_multisig_transaction_payload_bytes, + multisig_payload_option_ref, ) }; // If expected bytes matches actual hex from view function: @@ -227,11 +230,7 @@ impl CliCommand for CheckTransaction { #[derive(Debug, Parser)] pub struct Approve { #[clap(flatten)] - pub(crate) multisig_account: MultisigAccount, - /// The sequence number of the multisig transaction to approve. The sequence number increments - /// for every new multisig transaction. - #[clap(long)] - pub(crate) sequence_number: u64, + pub(crate) multisig_account_with_sequence_number: MultisigAccountWithSequenceNumber, #[clap(flatten)] pub(crate) txn_options: TransactionOptions, } @@ -245,8 +244,10 @@ impl CliCommand for Approve { async fn execute(self) -> CliTypedResult { self.txn_options .submit_transaction(aptos_stdlib::multisig_account_approve_transaction( - self.multisig_account.multisig_address, - self.sequence_number, + self.multisig_account_with_sequence_number + .multisig_account + .multisig_address, + self.multisig_account_with_sequence_number.sequence_number, )) .await .map(|inner| inner.into()) @@ -261,11 +262,7 @@ impl CliCommand for Approve { #[derive(Debug, Parser)] pub struct Reject { #[clap(flatten)] - pub(crate) multisig_account: MultisigAccount, - /// The sequence number of the multisig transaction to reject. The sequence number increments - /// for every new multisig transaction. - #[clap(long)] - pub(crate) sequence_number: u64, + pub(crate) multisig_account_with_sequence_number: MultisigAccountWithSequenceNumber, #[clap(flatten)] pub(crate) txn_options: TransactionOptions, } @@ -279,8 +276,10 @@ impl CliCommand for Reject { async fn execute(self) -> CliTypedResult { self.txn_options .submit_transaction(aptos_stdlib::multisig_account_reject_transaction( - self.multisig_account.multisig_address, - self.sequence_number, + self.multisig_account_with_sequence_number + .multisig_account + .multisig_address, + self.multisig_account_with_sequence_number.sequence_number, )) .await .map(|inner| inner.into()) diff --git a/crates/aptos/src/common/types.rs b/crates/aptos/src/common/types.rs index bdba70098d94e..13cd8fcff3c33 100644 --- a/crates/aptos/src/common/types.rs +++ b/crates/aptos/src/common/types.rs @@ -1676,11 +1676,20 @@ pub struct RotationProofChallenge { /// Common options for interactions with a multisig account. #[derive(Clone, Debug, Parser, Serialize)] pub struct MultisigAccount { - /// The address of the multisig account to interact with. + /// The address of the multisig account to interact with #[clap(long, parse(try_from_str=crate::common::types::load_account_arg))] pub(crate) multisig_address: AccountAddress, } +#[derive(Clone, Debug, Parser, Serialize)] +pub struct MultisigAccountWithSequenceNumber { + #[clap(flatten)] + pub(crate) multisig_account: MultisigAccount, + /// Multisig account sequence number to interact with + #[clap(long)] + pub(crate) sequence_number: u64, +} + #[derive(Debug, Parser)] pub struct TypeArgVec { /// TypeTag arguments separated by spaces. diff --git a/developer-docs-site/docs/move/move-on-aptos/cli.md b/developer-docs-site/docs/move/move-on-aptos/cli.md index 7402314e55f3f..217fe54b6947d 100644 --- a/developer-docs-site/docs/move/move-on-aptos/cli.md +++ b/developer-docs-site/docs/move/move-on-aptos/cli.md @@ -2,6 +2,8 @@ title: "Aptos Move CLI" --- +import CodeBlock from '@theme/CodeBlock'; + # Use the Aptos Move CLI The `aptos` tool is a command line interface (CLI) for developing on the Aptos blockchain, debugging, and for node operations. This document describes how to use the `aptos` CLI tool. To download or build the CLI, follow [Install Aptos CLI](../../tools/install-cli/index.md). @@ -437,17 +439,105 @@ $ aptos move run --function-id default::message::set_message --args string:hello ## Arguments in JSON -### Background +### Package info This section references the [`CliArgs` example package](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/cli_args), which contains the following manifest: +import move_toml from '!!raw-loader!../../../../aptos-move/move-examples/cli_args/Move.toml'; -```toml title="Move.toml" -:!: static/move-examples/cli_args/Move.toml manifest -``` +{move_toml} Here, the package is deployed under the named address `test_account`. +:::tip +Set your working directory to [`aptos-move/move-examples/cli_args`](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/cli_args) to follow along: + +```bash +cd /aptos-core/aptos-move/move-examples/cli_args +``` +::: + +### Deploying the package + +Start by mining a vanity address for Ace, who will deploy the package: + + +```bash title=Command +aptos key generate \ + --vanity-prefix 0xace \ + --output-file ace.key +``` + +
Output + +```bash +{ + "Result": { + "PublicKey Path": "ace.key.pub", + "PrivateKey Path": "ace.key", + "Account Address:": "0xace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2" + } +} +``` + +
+ +:::tip +The exact account address should vary for each run, though the vanity prefix should not. +::: + +Store Ace's address in a shell variable so you can call it inline later on: + +```bash +# Your exact address should vary +ace_addr=0xace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2 +``` + +Fund Ace's account with the faucet (either devnet or testnet): + +```bash title=Command +aptos account fund-with-faucet --account $ace_addr +``` + +
Output + +```bash +{ + "Result": "Added 100000000 Octas to account ace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2" +} +``` + +
+ +Now publish the package under Ace's account: + +```bash title=Command +aptos move publish \ + --named-addresses test_account=$ace_addr \ + --private-key-file ace.key \ + --assume-yes +``` + +
Output + +```bash +{ + "Result": { + "transaction_hash": "0x78e53928ec853a1c34d0e44aa6dd0ecc8234bdc0ab3d0634da171a6ac5d1b23c", + "gas_used": 1294, + "gas_unit_price": 100, + "sender": "ace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "sequence_number": 0, + "success": true, + "timestamp_us": 1684977028870268, + "version": 527676489, + "vm_status": "Executed successfully" + } +} +``` + +
+ ### Entry functions The only module in the package, `cli_args.move`, defines a simple `Holder` resource with fields of various data types: @@ -464,40 +554,81 @@ A public entry function with multi-nested vectors can be used to set the fields: After the package has been published, `aptos move run` can be used to call `set_vals()`: -```zsh title="Running function with nested vector arguments from CLI" +:::tip +To pass vectors (including nested vectors) as arguments from the command line, use JSON syntax escaped with quotes! +::: + +```bash title="Running function with nested vector arguments from CLI" aptos move run \ - --function-id ::cli_args::set_vals \ - --private-key-file \ + --function-id $ace_addr::cli_args::set_vals \ --type-args \ 0x1::account::Account \ 0x1::chain_id::ChainId \ --args \ u8:123 \ "bool:[false, true, false, false]" \ - 'address:[["0xace", "0xbee"], ["0xcad"], []]' + 'address:[["0xace", "0xbee"], ["0xcad"], []]' \ + --private-key-file ace.key \ + --assume-yes ``` -:::tip -To pass vectors (including nested vectors) as arguments from the command line, use JSON syntax escaped with quotes! -::: +
Output + +```bash +{ + "Result": { + "transaction_hash": "0x975e7026532aa6e14c97a27001efb2062c30a0c28e9a18b8b174333d88809c82", + "gas_used": 504, + "gas_unit_price": 100, + "sender": "ace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "sequence_number": 1, + "success": true, + "timestamp_us": 1684977491248278, + "version": 527679877, + "vm_status": "Executed successfully" + } +} +``` + +
The function ID, type arguments, and arguments can alternatively be specified in a JSON file: -import CodeBlock from '@theme/CodeBlock'; import entry_json_file from '!!raw-loader!../../../../aptos-move/move-examples/cli_args/entry_function_arguments.json'; {entry_json_file} Here, the call to `aptos move run` looks like: -```zsh +```bash title="Running function with JSON input file" aptos move run \ - --private-key-file \ - --json-file entry_function_arguments.json + --json-file entry_function_arguments.json \ + --private-key-file ace.key \ + --assume-yes +``` + +
Output + +```bash +{ + "Result": { + "transaction_hash": "0x44349fb8c8a78598f3f6af50177ee232228581a3dcc04220cbb2c91ec0e01a73", + "gas_used": 3, + "gas_unit_price": 100, + "sender": "ace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "sequence_number": 2, + "success": true, + "timestamp_us": 1684977758608985, + "version": 527681864, + "vm_status": "Executed successfully" + } +} ``` +
+ :::tip -If you are trying to run the example yourself don't forget to substitute `` with an appropriate address in the JSON file from the [`CliArgs` example package](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/cli_args)! +If you are trying to run the example yourself don't forget to substitute Ace's actual address for `` in `entry_function_arguments.json`! ::: ### View functions @@ -510,45 +641,51 @@ Once the values in a `Holder` have been set, the `reveal()` view function can be This view function can be called with arguments specified either from the CLI or from a JSON file: -```zsh title="Arguments via CLI" +```bash title="Arguments via CLI" aptos move view \ - --function-id ::cli_args::reveal \ + --function-id $ace_addr::cli_args::reveal \ --type-args \ 0x1::account::Account \ 0x1::account::Account \ - --args address: + --args address:$ace_addr ``` -```zsh title="Arguments via JSON file" +```bash title="Arguments via JSON file" aptos move view --json-file view_function_arguments.json ``` +:::tip +If you are trying to run the example yourself don't forget to substitute Ace's actual address for `` in `view_function_arguments.json` (twice)! +::: + import view_json_file from '!!raw-loader!../../../../aptos-move/move-examples/cli_args/view_function_arguments.json'; {view_json_file} -```zsh title="Output" +```bash title="Output" { "Result": [ - 123, - [ - false, - true, - false, - false - ], - [ - [ - "0xace", - "0xbee" + { + "address_vec_vec": [ + [ + "0xace", + "0xbee" + ], + [ + "0xcad" + ], + [] ], - [ - "0xcad" + "bool_vec": [ + false, + true, + false, + false ], - [] - ], - true, - false + "type_info_1_match": true, + "type_info_2_match": false, + "u8_solo": 123 + } ] } ``` @@ -566,63 +703,107 @@ Here, `aptos move run-script` is run from inside the [`cli_args` package directo :::tip Before trying out the below examples, compile the package with the correct named address via: -```zsh -aptos move compile --named-addresses test_account= +```bash +aptos move compile --named-addresses test_account=$ace_addr ``` ::: -```zsh title="Arguments via CLI" +```bash title="Arguments via CLI" aptos move run-script \ --compiled-script-path build/CliArgs/bytecode_scripts/set_vals.mv \ - --private-key-file \ --type-args \ 0x1::account::Account \ 0x1::chain_id::ChainId \ --args \ u8:123 \ "u8:[122, 123, 124, 125]" \ - address:"0xace" + address:"0xace" \ + --private-key-file ace.key \ + --assume-yes +``` + +
Output + +```bash +{ + "Result": { + "transaction_hash": "0x375d653ecd0e3e00852eefbbe72435479eae9d5e84acd7cc8c7b7f1bc2f2da96", + "gas_used": 3, + "gas_unit_price": 100, + "sender": "ace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "sequence_number": 3, + "success": true, + "timestamp_us": 1684978341019604, + "version": 527686516, + "vm_status": "Executed successfully" + } +} ``` -```zsh title="Arguments via JSON file" +
+ +```bash title="Arguments via JSON file" aptos move run-script \ --compiled-script-path build/CliArgs/bytecode_scripts/set_vals.mv \ - --private-key-file \ - --json-file script_function_arguments.json + --json-file script_function_arguments.json \ + --private-key-file ace.key \ + --assume-yes +``` + +
Output + +```bash +{ + "Result": { + "transaction_hash": "0x2bab4af9064c34e2b1ea756a44a893c8fb1580bf8af95ba2f454721e422748e9", + "gas_used": 3, + "gas_unit_price": 100, + "sender": "ace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "sequence_number": 4, + "success": true, + "timestamp_us": 1684978420803742, + "version": 527687139, + "vm_status": "Executed successfully" + } +} ``` +
+ import script_json_file from '!!raw-loader!../../../../aptos-move/move-examples/cli_args/script_function_arguments.json'; {script_json_file} Both such script function invocations result in the following `reveal()` view function output: -```zsh title="View function call" +```bash title="View function call" aptos move view \ - --function-id ::cli_args::reveal \ + --function-id $ace_addr::cli_args::reveal \ --type-args \ 0x1::account::Account \ 0x1::chain_id::ChainId \ - --args address: + --args address:$ace_addr ``` ```json title="View function output" { "Result": [ - 123, - [ - false, - false, - true, - true - ], - [ - [ - "0xace" - ] - ], - true, - true + { + "address_vec_vec": [ + [ + "0xace" + ] + ], + "bool_vec": [ + false, + false, + true, + true + ], + "type_info_1_match": true, + "type_info_2_match": true, + "u8_solo": 123 + } ] } ``` @@ -638,12 +819,9 @@ As of the time of this writing, the `aptos` CLI only supports script function ar This section builds upon the [Arguments in JSON](#arguments-in-json) section, and likewise references the [`CliArgs` example package](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/cli_args). -:::tip -Set your working directory to [`aptos-move/move-examples/cli_args`](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/cli_args) to follow along: -```bash -cd /aptos-core/aptos-move/move-examples/cli_args -``` +:::tip +If you would like to follow along, start by completing the [Arguments in JSON](#arguments-in-json) tutorial steps! ::: @@ -651,33 +829,7 @@ For this example, Ace and Bee will conduct governance operations from a 2-of-2 m ### Account creation -Start by mining a vanity address for both signatories: - -```bash title=Command -aptos key generate \ - --vanity-prefix 0xace \ - --output-file ace.key -``` - -
Output - -```bash -{ - "Result": { - "PublicKey Path": "ace.key.pub", - "Account Address:": "{ - "Result": { - "PublicKey Path": "bee.key.pub", - "PrivateKey Path": "bee.key", - "Account Address:": "0xbee090156aa0efa1fd6242d194400ef46471e2eca80dcd654532319c8b0355d4" - } -}", - "PrivateKey Path": "ace.key" - } -} -``` - -
+Since Ace's account was created during the [Arguments in JSON](#arguments-in-json) tutorial, start by mining a vanity address account for Bee too: ```bash title=Command aptos key generate \ @@ -692,7 +844,7 @@ aptos key generate \ "Result": { "PublicKey Path": "bee.key.pub", "PrivateKey Path": "bee.key", - "Account Address:": "0xbee090156aa0efa1fd6242d194400ef46471e2eca80dcd654532319c8b0355d4" + "Account Address:": "0xbee5ec8d0b63bce492047dc71aeb5c28094d462bafc890b57d3b091d71cad218" } } ``` @@ -703,29 +855,14 @@ aptos key generate \ The exact account address should vary for each run, though the vanity prefix should not. ::: -Store Ace and Bee's addresses in shell variables so you can call them inline later on: - -```bash -# Your exact addresses should vary -ace_addr=0xacee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4 -bee_addr=0xbee090156aa0efa1fd6242d194400ef46471e2eca80dcd654532319c8b0355d4 -``` - -Now fund Ace's and Bee's accounts using the faucet: - -```bash title=Command -aptos account fund-with-faucet --account $ace_addr -``` - -
Output +Store Bee's address in a shell variable so you can call it inline later on: ```bash -{ - "Result": "Added 100000000 Octas to account acee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4" -} +# Your exact address should vary +bee_addr=0xbee5ec8d0b63bce492047dc71aeb5c28094d462bafc890b57d3b091d71cad218 ``` -
+Fund Bee's account using the faucet: ```bash title=Command aptos account fund-with-faucet --account $bee_addr @@ -735,7 +872,7 @@ aptos account fund-with-faucet --account $bee_addr ```bash { - "Result": "Added 100000000 Octas to account bee090156aa0efa1fd6242d194400ef46471e2eca80dcd654532319c8b0355d4" + "Result": "Added 100000000 Octas to account bee5ec8d0b63bce492047dc71aeb5c28094d462bafc890b57d3b091d71cad218" } ``` @@ -747,7 +884,8 @@ Ace can now create a multisig account: aptos multisig create \ --additional-owners $bee_addr \ --num-signatures-required 2 \ - --private-key-file ace.key + --private-key-file ace.key \ + --assume-yes ```
Output @@ -755,15 +893,15 @@ aptos multisig create \ ```bash { "Result": { - "multisig_address": "2dc9b2fdba8ace3b9f96e5d3bb7ea04d39b1640020c8697eb0f1f4b33cad0d77", - "transaction_hash": "0x9b566b9357c1cda768948f6aaf951c6d5e5c3e1749c2fb3147b5eed371e962ee", + "multisig_address": "50e382f5670c093a84d97d91427389a08717e2aa1b2f8e60efb92fe57cb682d0", + "transaction_hash": "0x696e1d7782bb80546825690c097426afa7f484a08b3ae8a004154aa877d572ed", "gas_used": 1524, "gas_unit_price": 100, - "sender": "acee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4", - "sequence_number": 0, + "sender": "ace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "sequence_number": 5, "success": true, - "timestamp_us": 1684644958857234, - "version": 525304840, + "timestamp_us": 1684978792488964, + "version": 527690158, "vm_status": "Executed successfully" } } @@ -775,7 +913,7 @@ Store the multisig address in a shell variable: ```bash # Your address should vary -multisig_addr=2dc9b2fdba8ace3b9f96e5d3bb7ea04d39b1640020c8697eb0f1f4b33cad0d77 +multisig_addr=0x50e382f5670c093a84d97d91427389a08717e2aa1b2f8e60efb92fe57cb682d0 ``` ### Inspect the multisig @@ -814,8 +952,8 @@ aptos move view \ { "Result": [ [ - "0xbee090156aa0efa1fd6242d194400ef46471e2eca80dcd654532319c8b0355d4", - "0xacee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4" + "0xbee5ec8d0b63bce492047dc71aeb5c28094d462bafc890b57d3b091d71cad218", + "0xace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2" ] ] } @@ -891,8 +1029,9 @@ Now have Ace propose publication of the package from the multisig account, stori aptos multisig create-transaction \ --multisig-address $multisig_addr \ --json-file publication.json \ - --hash-only \ - --private-key-file ace.key + --store-hash-only \ + --private-key-file ace.key \ + --assume-yes ```
Output @@ -900,14 +1039,14 @@ aptos multisig create-transaction \ ```bash { "Result": { - "transaction_hash": "0xde5dfd2ca09cf2b3ca040386633de5c1c8aee5842d49303c757eb14819e20a3f", + "transaction_hash": "0x84a1932d91fdf31899bb430d723db26ae0919ece94c3c529d4d7efa4762954db", "gas_used": 510, "gas_unit_price": 100, - "sender": "acee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4", - "sequence_number": 1, + "sender": "ace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "sequence_number": 6, "success": true, - "timestamp_us": 1684645051403399, - "version": 525305517, + "timestamp_us": 1684978951763370, + "version": 527691441, "vm_status": "Executed successfully" } } @@ -973,20 +1112,20 @@ aptos move view \ { "Result": [ { - "creation_time_secs": "1684645051", - "creator": "0xacee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4", + "creation_time_secs": "1684978951", + "creator": "0xace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", "payload": { "vec": [] }, "payload_hash": { "vec": [ - "0xce31dac5c29fd54c643119b4011a4991bd96141a21be10100d75336230417e89" + "0x04bcaa228189c3603c23e8ba3a91924f8c30528fc91a1f60b88f1000518f99e1" ] }, "votes": { "data": [ { - "key": "0xacee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4", + "key": "0xace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", "value": true } ] @@ -1015,8 +1154,8 @@ aptos multisig create-transaction \ u8:123 \ "bool:[false, true, false, false]" \ 'address:[["0xace", "0xbee"], ["0xcad"], []]' \ - --private-key-file bee.key - + --private-key-file bee.key \ + --assume-yes ```
Output @@ -1024,14 +1163,14 @@ aptos multisig create-transaction \ ```bash { "Result": { - "transaction_hash": "0x92c7f7c103f2f7409ec0ede1325ce69c9357dc07423d1801d2c49eeee74d91ae", + "transaction_hash": "0xbd353d2e4ef9d49482f02defeaedcaf4c2f1fc957eac57472690ab17dee70988", "gas_used": 511, "gas_unit_price": 100, - "sender": "bee090156aa0efa1fd6242d194400ef46471e2eca80dcd654532319c8b0355d4", + "sender": "bee5ec8d0b63bce492047dc71aeb5c28094d462bafc890b57d3b091d71cad218", "sequence_number": 0, "success": true, - "timestamp_us": 1684645156069617, - "version": 525306308, + "timestamp_us": 1684979030036513, + "version": 527692060, "vm_status": "Executed successfully" } } @@ -1076,31 +1215,31 @@ aptos move view \ "Result": [ [ { - "creation_time_secs": "1684645051", - "creator": "0xacee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4", + "creation_time_secs": "1684978951", + "creator": "0xace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", "payload": { "vec": [] }, "payload_hash": { "vec": [ - "0xce31dac5c29fd54c643119b4011a4991bd96141a21be10100d75336230417e89" + "0x04bcaa228189c3603c23e8ba3a91924f8c30528fc91a1f60b88f1000518f99e1" ] }, "votes": { "data": [ { - "key": "0xacee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4", + "key": "0xace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", "value": true } ] } }, { - "creation_time_secs": "1684645156", - "creator": "0xbee090156aa0efa1fd6242d194400ef46471e2eca80dcd654532319c8b0355d4", + "creation_time_secs": "1684979030", + "creator": "0xbee5ec8d0b63bce492047dc71aeb5c28094d462bafc890b57d3b091d71cad218", "payload": { "vec": [ - "0x002dc9b2fdba8ace3b9f96e5d3bb7ea04d39b1640020c8697eb0f1f4b33cad0d7708636c695f61726773087365745f76616c7302070000000000000000000000000000000000000000000000000000000000000001076163636f756e74074163636f756e740007000000000000000000000000000000000000000000000000000000000000000108636861696e5f696407436861696e49640003017b0504000100006403020000000000000000000000000000000000000000000000000000000000000ace0000000000000000000000000000000000000000000000000000000000000bee010000000000000000000000000000000000000000000000000000000000000cad00" + "0x0050e382f5670c093a84d97d91427389a08717e2aa1b2f8e60efb92fe57cb682d008636c695f61726773087365745f76616c7302070000000000000000000000000000000000000000000000000000000000000001076163636f756e74074163636f756e740007000000000000000000000000000000000000000000000000000000000000000108636861696e5f696407436861696e49640003017b0504000100006403020000000000000000000000000000000000000000000000000000000000000ace0000000000000000000000000000000000000000000000000000000000000bee010000000000000000000000000000000000000000000000000000000000000cad00" ] }, "payload_hash": { @@ -1109,15 +1248,14 @@ aptos move view \ "votes": { "data": [ { - "key": "0xbee090156aa0efa1fd6242d194400ef46471e2eca80dcd654532319c8b0355d4", + "key": "0xbee5ec8d0b63bce492047dc71aeb5c28094d462bafc890b57d3b091d71cad218", "value": true } ] } } ] - ] -} + ```
@@ -1162,20 +1300,20 @@ aptos multisig check-transaction \ "Result": { "Status": "Transaction match", "Multisig transaction": { - "creation_time_secs": "1684645051", - "creator": "0xacee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4", + "creation_time_secs": "1684978951", + "creator": "0xace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", "payload": { "vec": [] }, "payload_hash": { "vec": [ - "0xce31dac5c29fd54c643119b4011a4991bd96141a21be10100d75336230417e89" + "0x04bcaa228189c3603c23e8ba3a91924f8c30528fc91a1f60b88f1000518f99e1" ] }, "votes": { "data": [ { - "key": "0xacee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4", + "key": "0xace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", "value": true } ] @@ -1194,7 +1332,8 @@ Since Bee has verified that the on-chain payload hash checks out against her loc aptos multisig approve \ --multisig-address $multisig_addr \ --sequence-number 1 \ - --private-key-file bee.key + --private-key-file bee.key \ + --assume-yes ```
Output @@ -1202,14 +1341,14 @@ aptos multisig approve \ ```bash { "Result": { - "transaction_hash": "0x24a12b1839b2dd114780289ec6e36a4a33b1ab2a4c3f22dd9512873aed65723a", + "transaction_hash": "0x9b80286a6f1ab70b4b2759193810b7f618451aa0fefcba1095b0ed74607aa684", "gas_used": 6, "gas_unit_price": 100, - "sender": "bee090156aa0efa1fd6242d194400ef46471e2eca80dcd654532319c8b0355d4", + "sender": "bee5ec8d0b63bce492047dc71aeb5c28094d462bafc890b57d3b091d71cad218", "sequence_number": 1, "success": true, - "timestamp_us": 1684645251141034, - "version": 525307001, + "timestamp_us": 1684979137080773, + "version": 527692937, "vm_status": "Executed successfully" } } @@ -1246,7 +1385,8 @@ aptos multisig execute \ --multisig-address $multisig_addr \ --json-file publication.json \ --private-key-file bee.key \ - --max-gas 10000 + --max-gas 10000 \ + --assume-yes ``` :::tip @@ -1306,11 +1446,11 @@ aptos multisig check-transaction \ "Result": { "Status": "Transaction match", "Multisig transaction": { - "creation_time_secs": "1684645156", - "creator": "0xbee090156aa0efa1fd6242d194400ef46471e2eca80dcd654532319c8b0355d4", + "creation_time_secs": "1684979030", + "creator": "0xbee5ec8d0b63bce492047dc71aeb5c28094d462bafc890b57d3b091d71cad218", "payload": { "vec": [ - "0x002dc9b2fdba8ace3b9f96e5d3bb7ea04d39b1640020c8697eb0f1f4b33cad0d7708636c695f61726773087365745f76616c7302070000000000000000000000000000000000000000000000000000000000000001076163636f756e74074163636f756e740007000000000000000000000000000000000000000000000000000000000000000108636861696e5f696407436861696e49640003017b0504000100006403020000000000000000000000000000000000000000000000000000000000000ace0000000000000000000000000000000000000000000000000000000000000bee010000000000000000000000000000000000000000000000000000000000000cad00" + "0x0050e382f5670c093a84d97d91427389a08717e2aa1b2f8e60efb92fe57cb682d008636c695f61726773087365745f76616c7302070000000000000000000000000000000000000000000000000000000000000001076163636f756e74074163636f756e740007000000000000000000000000000000000000000000000000000000000000000108636861696e5f696407436861696e49640003017b0504000100006403020000000000000000000000000000000000000000000000000000000000000ace0000000000000000000000000000000000000000000000000000000000000bee010000000000000000000000000000000000000000000000000000000000000cad00" ] }, "payload_hash": { @@ -1319,7 +1459,7 @@ aptos multisig check-transaction \ "votes": { "data": [ { - "key": "0xbee090156aa0efa1fd6242d194400ef46471e2eca80dcd654532319c8b0355d4", + "key": "0xbee5ec8d0b63bce492047dc71aeb5c28094d462bafc890b57d3b091d71cad218", "value": true } ] @@ -1363,7 +1503,8 @@ Ace approves the transaction: aptos multisig approve \ --multisig-address $multisig_addr \ --sequence-number 2 \ - --private-key-file ace.key + --private-key-file ace.key \ + --assume-yes ```
Output @@ -1371,14 +1512,14 @@ aptos multisig approve \ ```bash { "Result": { - "transaction_hash": "0x5cdc4fd171d7b2ae3676b0c4a9a3fa1523ca46fde205ec17cc0ef7c0c92108d5", + "transaction_hash": "0x3b443492c885f7338931e640a36d8d225a4f53ff17198cd2e3087b3a0887fcd2", "gas_used": 6, "gas_unit_price": 100, - "sender": "acee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4", - "sequence_number": 2, + "sender": "ace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "sequence_number": 7, "success": true, - "timestamp_us": 1684646050747796, - "version": 525312861, + "timestamp_us": 1684979313218098, + "version": 527694405, "vm_status": "Executed successfully" } } @@ -1392,7 +1533,8 @@ Since the payload was stored on-chain, it is not required to execute the pending aptos multisig execute \ --multisig-address $multisig_addr \ --private-key-file ace.key \ - --max-gas 10000 + --max-gas 10000 \ + --assume-yes ```
Output @@ -1400,14 +1542,14 @@ aptos multisig execute \ ```bash { "Result": { - "transaction_hash": "0x2cc091926460ac37e0bff280d5cc6a3a225838ff8f13dc224be6cd5be6725fea", + "transaction_hash": "0x20c0c1a2d8699cde1d70e07a77eae62b27acd900521efa641eb251dafabcd324", "gas_used": 505, "gas_unit_price": 100, - "sender": "acee3447860cd5f14801badcbf69dbdb98a0c315999ded339bb9d3606ac4faa4", - "sequence_number": 3, + "sender": "ace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "sequence_number": 8, "success": true, - "timestamp_us": 1684646121523835, - "version": 525313412, + "timestamp_us": 1684979342858131, + "version": 527694637, "vm_status": "Executed successfully" } } diff --git a/developer-docs-site/scripts/additional_dict.txt b/developer-docs-site/scripts/additional_dict.txt index f707f3aef9379..96bff44c2ccdf 100644 --- a/developer-docs-site/scripts/additional_dict.txt +++ b/developer-docs-site/scripts/additional_dict.txt @@ -334,6 +334,7 @@ dr eg endian enqueue +enqueued entrancy enum enums From a6d6be73ed7460ca6868a5d824375333d04beeeb Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Thu, 25 May 2023 09:07:06 -0700 Subject: [PATCH 5/7] Address more review comments from @movekevin Resolves https://github.com/aptos-labs/aptos-core/pull/8346#discussion_r1205108970 Resolves https://github.com/aptos-labs/aptos-core/pull/8346#discussion_r1205116995 --- crates/aptos/src/account/multisig_account.rs | 56 +++++++++----------- crates/aptos/src/common/utils.rs | 23 ++++++-- 2 files changed, 42 insertions(+), 37 deletions(-) diff --git a/crates/aptos/src/account/multisig_account.rs b/crates/aptos/src/account/multisig_account.rs index 293559cc2f907..8fbf91ccec01c 100644 --- a/crates/aptos/src/account/multisig_account.rs +++ b/crates/aptos/src/account/multisig_account.rs @@ -6,12 +6,12 @@ use crate::common::{ CliCommand, CliError, CliTypedResult, EntryFunctionArguments, MultisigAccount, MultisigAccountWithSequenceNumber, TransactionOptions, TransactionSummary, }, - utils::get_view_json_option_vec_ref, + utils::view_json_option_hex_as_bytes, }; use aptos_cached_packages::aptos_stdlib; use aptos_crypto::HashValue; use aptos_rest_client::{ - aptos_api_types::{HexEncodedBytes, ViewRequest, WriteResource, WriteSetChange}, + aptos_api_types::{ViewRequest, WriteResource, WriteSetChange}, Transaction, }; use aptos_types::{ @@ -183,41 +183,33 @@ impl CliCommand for CheckTransaction { ], }) .await?[0]; - // Get reference to inner payload option from multisig transaction. - let multisig_payload_option_ref = - get_view_json_option_vec_ref(&multisig_transaction["payload"]); - // Get expected multisig transaction payload bytes from provided entry function. - let expected_multisig_transaction_payload_bytes = - to_bytes::(&self.entry_function_args.try_into()?)?; - // If only storing payload hash on-chain, get expected hash bytes and actual hash hex: - let (expected_bytes, actual_value_hex_option_ref) = - if multisig_payload_option_ref.is_empty() { - ( - HashValue::sha3_256_of(&expected_multisig_transaction_payload_bytes).to_vec(), - get_view_json_option_vec_ref(&multisig_transaction["payload_hash"]), - ) - // If full payload stored on-chain, get expected payload bytes and actual payload hex: - } else { - ( - expected_multisig_transaction_payload_bytes, - multisig_payload_option_ref, - ) - }; - // If expected bytes matches actual hex from view function: - if expected_bytes.eq(&actual_value_hex_option_ref[0] - .as_str() - .unwrap() - .parse::()? - .inner()) - { - // Return success message. + // Get expected multisig transaction payload hash bytes from provided entry function. + let expected_payload_hash_bytes = + HashValue::sha3_256_of(&to_bytes::( + &self.entry_function_args.try_into()?, + )?) + .to_vec(); + // Get actual multisig payload hash field stored on-chain, as bytes. + let mut actual_payload_hash_bytes = + view_json_option_hex_as_bytes(&multisig_transaction["payload_hash"])?; + // If payload hash not stored on-chain, get actual payload hash from on-chain payload. + if actual_payload_hash_bytes.is_empty() { + actual_payload_hash_bytes = HashValue::sha3_256_of(&view_json_option_hex_as_bytes( + &multisig_transaction["payload"], + )?) + .to_vec(); + } + // If a match between expected payload hash and actual payload hash, return success message. + if expected_payload_hash_bytes.eq(&actual_payload_hash_bytes) { Ok(json!({ "Status": "Transaction match", "Multisig transaction": multisig_transaction })) + // If a mismatch between expected payload hash and actual payload hash, error out. } else { - // If a mismatch between expected bytes and actual hex, error out. - Err(CliError::UnexpectedError("Payload mismatch".to_string())) + Err(CliError::UnexpectedError( + "Transaction mismatch".to_string(), + )) } } } diff --git a/crates/aptos/src/common/utils.rs b/crates/aptos/src/common/utils.rs index d4a258c7a28ed..f17775e833050 100644 --- a/crates/aptos/src/common/utils.rs +++ b/crates/aptos/src/common/utils.rs @@ -13,7 +13,10 @@ use aptos_build_info::build_information; use aptos_crypto::ed25519::{Ed25519PrivateKey, Ed25519PublicKey}; use aptos_keygen::KeyGen; use aptos_logger::{debug, Level}; -use aptos_rest_client::{aptos_api_types::HashValue, Account, Client, State}; +use aptos_rest_client::{ + aptos_api_types::{HashValue, HexEncodedBytes}, + Account, Client, State, +}; use aptos_telemetry::service::telemetry_is_disabled; use aptos_types::{ account_address::create_multisig_account_address, @@ -481,9 +484,19 @@ pub fn parse_json_file Deserialize<'a>>(path_ref: &Path) -> CliTypedR }) } -/// Return reference to inner option vector for view function JSON field known to have option. +/// Convert a view function JSON field, known to have optional hex, into a bytes vector. /// -/// View functions represent an option as a JSON array titled `vec`. -pub fn get_view_json_option_vec_ref(value_ref: &serde_json::Value) -> &Vec { - value_ref["vec"].as_array().unwrap() +/// A view function return represents an option via an inner JSON array titled `vec`. +pub fn view_json_option_hex_as_bytes(option_ref: &serde_json::Value) -> CliTypedResult> { + let option_vec_ref = option_ref["vec"].as_array().unwrap(); + if option_vec_ref.is_empty() { + Ok(vec![]) + } else { + Ok(option_vec_ref[0] + .as_str() + .unwrap() + .parse::()? + .inner() + .to_vec()) + } } From 6aa74479f6b3f2b44f06cc0aea7efbd9acff1a51 Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Thu, 25 May 2023 18:37:03 -0700 Subject: [PATCH 6/7] Address/resolve assorted comments from @banool Resolve https://github.com/aptos-labs/aptos-core/pull/8346#pullrequestreview-1444598292 Resolve https://github.com/aptos-labs/aptos-core/pull/8346#discussion_r1205970944 Resolve https://github.com/aptos-labs/aptos-core/pull/8346#discussion_r1205984064 Resolve https://github.com/aptos-labs/aptos-core/pull/8346#discussion_r1205985668 Resolve https://github.com/aptos-labs/aptos-core/pull/8346#discussion_r1205987884 Resolve https://github.com/aptos-labs/aptos-core/pull/8346#discussion_r1205989174 Resolve https://github.com/aptos-labs/aptos-core/pull/8346#discussion_r1205990759 Resolve https://github.com/aptos-labs/aptos-core/pull/8346#discussion_r1205993911 Address https://github.com/aptos-labs/aptos-core/pull/8346#discussion_r1205996132 Resolve https://github.com/aptos-labs/aptos-core/pull/8346#pullrequestreview-1444662066 --- Cargo.lock | 3 +- .../cli_args/entry_function_arguments.json | 12 +- .../cli_args/script_function_arguments.json | 12 +- .../cli_args/view_function_arguments.json | 4 +- crates/aptos/CHANGELOG.md | 8 + crates/aptos/Cargo.toml | 3 +- crates/aptos/src/account/mod.rs | 6 +- crates/aptos/src/account/multisig_account.rs | 112 +++++---- crates/aptos/src/common/types.rs | 89 ++++--- crates/aptos/src/common/utils.rs | 50 ++-- crates/aptos/src/move_tool/mod.rs | 213 +++++++++-------- crates/aptos/src/test/mod.rs | 1 - .../docs/move/move-on-aptos/cli.md | 221 +++++++++--------- 13 files changed, 419 insertions(+), 315 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1a9bccaba92c8..9f7ec564304be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -136,7 +136,7 @@ dependencies = [ [[package]] name = "aptos" -version = "1.0.13" +version = "1.0.14" dependencies = [ "anyhow", "aptos-backup-cli", @@ -195,6 +195,7 @@ dependencies = [ "move-symbol-pool", "move-unit-test", "move-vm-runtime", + "once_cell", "rand 0.7.3", "regex", "reqwest", diff --git a/aptos-move/move-examples/cli_args/entry_function_arguments.json b/aptos-move/move-examples/cli_args/entry_function_arguments.json index 10630aa4d91dd..e37dc43edc1b1 100644 --- a/aptos-move/move-examples/cli_args/entry_function_arguments.json +++ b/aptos-move/move-examples/cli_args/entry_function_arguments.json @@ -6,16 +6,16 @@ ], "args": [ { - "arg_type": "u8", - "arg_value": 123 + "type": "u8", + "value": 123 }, { - "arg_type": "bool", - "arg_value": [false, true, false, false] + "type": "bool", + "value": [false, true, false, false] }, { - "arg_type": "address", - "arg_value": [ + "type": "address", + "value": [ [ "0xace", "0xbee" diff --git a/aptos-move/move-examples/cli_args/script_function_arguments.json b/aptos-move/move-examples/cli_args/script_function_arguments.json index 3756e9b1f8383..b6e2914f0767b 100644 --- a/aptos-move/move-examples/cli_args/script_function_arguments.json +++ b/aptos-move/move-examples/cli_args/script_function_arguments.json @@ -5,16 +5,16 @@ ], "args": [ { - "arg_type": "u8", - "arg_value": 123 + "type": "u8", + "value": 123 }, { - "arg_type": "u8", - "arg_value": [122, 123, 124, 125] + "type": "u8", + "value": [122, 123, 124, 125] }, { - "arg_type": "address", - "arg_value": "0xace" + "type": "address", + "value": "0xace" } ] } \ No newline at end of file diff --git a/aptos-move/move-examples/cli_args/view_function_arguments.json b/aptos-move/move-examples/cli_args/view_function_arguments.json index 17f5766297ce3..5a9ca89ede412 100644 --- a/aptos-move/move-examples/cli_args/view_function_arguments.json +++ b/aptos-move/move-examples/cli_args/view_function_arguments.json @@ -6,8 +6,8 @@ ], "args": [ { - "arg_type": "address", - "arg_value": "" + "type": "address", + "value": "" } ] } \ No newline at end of file diff --git a/crates/aptos/CHANGELOG.md b/crates/aptos/CHANGELOG.md index 6bb2eaf8e308b..769f52e061443 100644 --- a/crates/aptos/CHANGELOG.md +++ b/crates/aptos/CHANGELOG.md @@ -2,6 +2,14 @@ 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/). +## [1.0.14] - 2023/05/25 + +### Added +- Recursive nested vector parsing +- Multisig v2 governance support +- JSON support for both input files and CLI argument input + - **Breaking change**: You can no longer pass in a vector like this: `--arg vector
:0x1,0x2`, you must do it like this: `--arg 'address:["0x1", "0x2"]'` + ## [1.0.13] - 2023/04/27 ### Fixed * Previously `--skip-fetch-latest-git-deps` would not actually do anything when used with `aptos move test`. This has been fixed. diff --git a/crates/aptos/Cargo.toml b/crates/aptos/Cargo.toml index da4c57665a483..920ce0040e58f 100644 --- a/crates/aptos/Cargo.toml +++ b/crates/aptos/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "aptos" description = "Aptos tool for management of nodes and interacting with the blockchain" -version = "1.0.13" +version = "1.0.14" # Workspace inherited keys authors = { workspace = true } @@ -69,6 +69,7 @@ move-prover-boogie-backend = { workspace = true } move-symbol-pool = { workspace = true } move-unit-test = { workspace = true, features = [ "debugging" ] } move-vm-runtime = { workspace = true, features = [ "testing" ] } +once_cell = { workspace = true } rand = { workspace = true } regex = { workspace = true } reqwest = { workspace = true } diff --git a/crates/aptos/src/account/mod.rs b/crates/aptos/src/account/mod.rs index e886d780b9d1c..2421a74327360 100644 --- a/crates/aptos/src/account/mod.rs +++ b/crates/aptos/src/account/mod.rs @@ -49,11 +49,12 @@ impl AccountTool { pub enum MultisigAccountTool { Approve(multisig_account::Approve), Create(multisig_account::Create), - CheckTransaction(multisig_account::CheckTransaction), CreateTransaction(multisig_account::CreateTransaction), Execute(multisig_account::Execute), ExecuteReject(multisig_account::ExecuteReject), + ExecuteWithPayload(multisig_account::ExecuteWithPayload), Reject(multisig_account::Reject), + VerifyProposal(multisig_account::VerifyProposal), } impl MultisigAccountTool { @@ -61,11 +62,12 @@ impl MultisigAccountTool { match self { MultisigAccountTool::Approve(tool) => tool.execute_serialized().await, MultisigAccountTool::Create(tool) => tool.execute_serialized().await, - MultisigAccountTool::CheckTransaction(tool) => tool.execute_serialized().await, MultisigAccountTool::CreateTransaction(tool) => tool.execute_serialized().await, MultisigAccountTool::Execute(tool) => tool.execute_serialized().await, MultisigAccountTool::ExecuteReject(tool) => tool.execute_serialized().await, + MultisigAccountTool::ExecuteWithPayload(tool) => tool.execute_serialized().await, MultisigAccountTool::Reject(tool) => tool.execute_serialized().await, + MultisigAccountTool::VerifyProposal(tool) => tool.execute_serialized().await, } } } diff --git a/crates/aptos/src/account/multisig_account.rs b/crates/aptos/src/account/multisig_account.rs index 8fbf91ccec01c..6e26a81764732 100644 --- a/crates/aptos/src/account/multisig_account.rs +++ b/crates/aptos/src/account/multisig_account.rs @@ -6,12 +6,14 @@ use crate::common::{ CliCommand, CliError, CliTypedResult, EntryFunctionArguments, MultisigAccount, MultisigAccountWithSequenceNumber, TransactionOptions, TransactionSummary, }, - utils::view_json_option_hex_as_bytes, + utils::view_json_option_str, }; use aptos_cached_packages::aptos_stdlib; use aptos_crypto::HashValue; use aptos_rest_client::{ - aptos_api_types::{ViewRequest, WriteResource, WriteSetChange}, + aptos_api_types::{ + EntryFunctionId, HexEncodedBytes, ViewRequest, WriteResource, WriteSetChange, + }, Transaction, }; use aptos_types::{ @@ -21,9 +23,13 @@ use aptos_types::{ use async_trait::async_trait; use bcs::to_bytes; use clap::Parser; +use once_cell::sync::Lazy; use serde::Serialize; use serde_json::json; +static GET_TRANSACTION_ENTRY_FUNCTION: Lazy = + Lazy::new(|| "0x1::multisig_account::get_transaction".parse().unwrap()); + /// Create a new multisig account (v2) on-chain. /// /// This will create a new multisig account and make the sender one of the owners. @@ -144,9 +150,9 @@ impl CliCommand for CreateTransaction { } } -/// Check entry function against on-chain transaction proposal payload. +/// Verify entry function matches on-chain transaction proposal. #[derive(Debug, Parser)] -pub struct CheckTransaction { +pub struct VerifyProposal { #[clap(flatten)] pub(crate) multisig_account_with_sequence_number: MultisigAccountWithSequenceNumber, #[clap(flatten)] @@ -156,9 +162,9 @@ pub struct CheckTransaction { } #[async_trait] -impl CliCommand for CheckTransaction { +impl CliCommand for VerifyProposal { fn command_name(&self) -> &'static str { - "CheckTransactionMultisig" + "VerifyProposalMultisig" } async fn execute(self) -> CliTypedResult { @@ -166,7 +172,7 @@ impl CliCommand for CheckTransaction { let multisig_transaction = &self .txn_options .view(ViewRequest { - function: "0x1::multisig_account::get_transaction".parse()?, + function: GET_TRANSACTION_ENTRY_FUNCTION.clone(), type_arguments: vec![], arguments: vec![ serde_json::Value::String(String::from( @@ -183,34 +189,40 @@ impl CliCommand for CheckTransaction { ], }) .await?[0]; - // Get expected multisig transaction payload hash bytes from provided entry function. - let expected_payload_hash_bytes = - HashValue::sha3_256_of(&to_bytes::( - &self.entry_function_args.try_into()?, - )?) - .to_vec(); - // Get actual multisig payload hash field stored on-chain, as bytes. - let mut actual_payload_hash_bytes = - view_json_option_hex_as_bytes(&multisig_transaction["payload_hash"])?; - // If payload hash not stored on-chain, get actual payload hash from on-chain payload. - if actual_payload_hash_bytes.is_empty() { - actual_payload_hash_bytes = HashValue::sha3_256_of(&view_json_option_hex_as_bytes( - &multisig_transaction["payload"], - )?) - .to_vec(); - } - // If a match between expected payload hash and actual payload hash, return success message. - if expected_payload_hash_bytes.eq(&actual_payload_hash_bytes) { - Ok(json!({ + // Get expected multisig transaction payload hash hex from provided entry function. + let expected_payload_hash = HashValue::sha3_256_of( + &to_bytes::(&self.entry_function_args.try_into()?)?, + ) + .to_hex_literal(); + // Get on-chain payload hash. If full payload provided on-chain: + let actual_payload_hash = + if let Some(actual_payload) = view_json_option_str(&multisig_transaction["payload"])? { + // Actual payload hash is the hash of the on-chain payload. + HashValue::sha3_256_of(actual_payload.parse::()?.inner()) + .to_hex_literal() + // If full payload not provided, get payload hash directly from transaction proposal: + } else { + view_json_option_str(&multisig_transaction["payload_hash"])?.ok_or( + CliError::UnexpectedError( + "Neither payload nor payload hash provided on-chain".to_string(), + ), + )? + }; + // Get verification result based on if expected and actual payload hashes match. + let result = if expected_payload_hash.eq(&actual_payload_hash) { + json!({ "Status": "Transaction match", "Multisig transaction": multisig_transaction - })) - // If a mismatch between expected payload hash and actual payload hash, error out. + }) } else { - Err(CliError::UnexpectedError( - "Transaction mismatch".to_string(), - )) - } + json!({ + "Status": "Transaction mismatch", + "Provided payload hash": expected_payload_hash, + "On-chain payload hash": actual_payload_hash, + "For more information": "https://aptos.dev/move/move-on-aptos/cli#multisig-governance" + }) + }; + Ok(result) } } @@ -278,18 +290,13 @@ impl CliCommand for Reject { } } -/// Execute a proposed multisig transaction. -/// -/// The transaction to be executed needs to have as many approvals as the number of signatures -/// required. +/// Execute a proposed multisig transaction that has a full payload stored on-chain. #[derive(Debug, Parser)] pub struct Execute { #[clap(flatten)] pub(crate) multisig_account: MultisigAccount, #[clap(flatten)] pub(crate) txn_options: TransactionOptions, - #[clap(flatten)] - pub(crate) entry_function_args: EntryFunctionArguments, } #[async_trait] @@ -302,7 +309,34 @@ impl CliCommand for Execute { self.txn_options .submit_transaction(TransactionPayload::Multisig(Multisig { multisig_address: self.multisig_account.multisig_address, - transaction_payload: self.entry_function_args.try_into()?, + transaction_payload: None, + })) + .await + .map(|inner| inner.into()) + } +} + +/// Execute a proposed multisig transaction that has only a payload hash stored on-chain. +#[derive(Debug, Parser)] +pub struct ExecuteWithPayload { + #[clap(flatten)] + pub(crate) execute: Execute, + #[clap(flatten)] + pub(crate) entry_function_args: EntryFunctionArguments, +} + +#[async_trait] +impl CliCommand for ExecuteWithPayload { + fn command_name(&self) -> &'static str { + "ExecuteWithPayloadMultisig" + } + + async fn execute(self) -> CliTypedResult { + self.execute + .txn_options + .submit_transaction(TransactionPayload::Multisig(Multisig { + multisig_address: self.execute.multisig_account.multisig_address, + transaction_payload: Some(self.entry_function_args.try_into()?), })) .await .map(|inner| inner.into()) diff --git a/crates/aptos/src/common/types.rs b/crates/aptos/src/common/types.rs index 13cd8fcff3c33..40571a5e3182f 100644 --- a/crates/aptos/src/common/types.rs +++ b/crates/aptos/src/common/types.rs @@ -16,6 +16,7 @@ use crate::{ genesis::git::from_yaml, move_tool::{ArgWithType, FunctionArgType, MemberId}, }; +use anyhow::Context; use aptos_crypto::{ ed25519::{Ed25519PrivateKey, Ed25519PublicKey, Ed25519Signature}, x25519, PrivateKey, ValidCryptoMaterial, ValidCryptoMaterialStringExt, @@ -1750,7 +1751,7 @@ impl TryFrom<&Vec> for ArgWithTypeVec { let mut args = vec![]; for arg_json_ref in value { let function_arg_type = FunctionArgType::from_str(&arg_json_ref.arg_type)?; - args.push(function_arg_type.parse_arg_json(&arg_json_ref.arg_value)?); + args.push(function_arg_type.parse_arg_json(&arg_json_ref.value)?); } Ok(ArgWithTypeVec { args }) } @@ -1762,7 +1763,12 @@ impl TryInto> for ArgWithTypeVec { fn try_into(self) -> Result, Self::Error> { let mut args = vec![]; for arg in self.args { - args.push(arg.try_into()?); + args.push( + (&arg) + .try_into() + .context(format!("Failed to parse arg {:?}", arg)) + .map_err(|err| CliError::CommandArgumentError(err.to_string()))?, + ); } Ok(args) } @@ -1798,7 +1804,7 @@ pub struct EntryFunctionArguments { /// Function name as `
::::` /// /// Example: `0x842ed41fad9640a2ad08fdd7d3e4f7f505319aac7d67e1c0dd6a7cce8732c7e3::message::set_message` - #[clap(long)] + #[clap(long, required_unless_present = "json-file")] pub function_id: Option, #[clap(flatten)] @@ -1814,23 +1820,10 @@ pub struct EntryFunctionArguments { impl EntryFunctionArguments { /// Get instance as if all fields passed from command line, parsing JSON input file if needed. fn check_input_style(self) -> CliTypedResult { - if self.json_file.is_none() { - if self.function_id.is_none() { - Err(CliError::CommandArgumentError( - "Must provide either function ID or JSON input file".to_string(), - )) - } else { - Ok(self) - } + if let Some(json_path) = self.json_file { + Ok(parse_json_file::(&json_path)?.try_into()?) } else { - let json = - parse_json_file::(self.json_file.as_ref().unwrap())?; - Ok(EntryFunctionArguments { - function_id: Some(MemberId::from_str(&json.function_id)?), - type_arg_vec: TypeArgVec::try_from(&json.type_args)?, - arg_vec: ArgWithTypeVec::try_from(&json.args)?, - json_file: None, - }) + Ok(self) } } } @@ -1840,7 +1833,7 @@ impl TryInto for EntryFunctionArguments { fn try_into(self) -> Result { let entry_function_args = self.check_input_style()?; - let function_id = entry_function_args.function_id.unwrap(); + let function_id: MemberId = (&entry_function_args).try_into()?; Ok(EntryFunction::new( function_id.module_id, function_id.member_id, @@ -1858,15 +1851,15 @@ impl TryInto for EntryFunctionArguments { } } -impl TryInto> for EntryFunctionArguments { +impl TryInto for &EntryFunctionArguments { type Error = CliError; - fn try_into(self) -> Result, Self::Error> { - if self.function_id.is_none() && self.json_file.is_none() { - Ok(None) - } else { - Ok(Some(self.try_into()?)) - } + fn try_into(self) -> Result { + self.function_id + .clone() + .ok_or(CliError::CommandArgumentError( + "No function ID provided".to_string(), + )) } } @@ -1875,7 +1868,7 @@ impl TryInto for EntryFunctionArguments { fn try_into(self) -> Result { let entry_function_args = self.check_input_style()?; - let function_id = entry_function_args.function_id.unwrap(); + let function_id: MemberId = (&entry_function_args).try_into()?; Ok(ViewRequest { function: EntryFunctionId { module: function_id.module_id.into(), @@ -1903,16 +1896,10 @@ pub struct ScriptFunctionArguments { impl ScriptFunctionArguments { /// Get instance as if all fields passed from command line, parsing JSON input file if needed. fn check_input_style(self) -> CliTypedResult { - if self.json_file.is_none() { - Ok(self) + if let Some(json_path) = self.json_file { + Ok(parse_json_file::(&json_path)?.try_into()?) } else { - let json = - parse_json_file::(self.json_file.as_ref().unwrap())?; - Ok(ScriptFunctionArguments { - type_arg_vec: TypeArgVec::try_from(&json.type_args)?, - arg_vec: ArgWithTypeVec::try_from(&json.args)?, - json_file: None, - }) + Ok(self) } } @@ -1929,8 +1916,9 @@ impl ScriptFunctionArguments { #[derive(Deserialize, Serialize)] /// JSON file format for function arguments. pub struct ArgWithTypeJSON { + #[serde(rename = "type")] pub(crate) arg_type: String, - pub(crate) arg_value: serde_json::Value, + pub(crate) value: serde_json::Value, } #[derive(Deserialize, Serialize)] @@ -1941,9 +1929,34 @@ pub struct EntryFunctionArgumentsJSON { pub(crate) args: Vec, } +impl TryInto for EntryFunctionArgumentsJSON { + type Error = CliError; + + fn try_into(self) -> Result { + Ok(EntryFunctionArguments { + function_id: Some(MemberId::from_str(&self.function_id)?), + type_arg_vec: TypeArgVec::try_from(&self.type_args)?, + arg_vec: ArgWithTypeVec::try_from(&self.args)?, + json_file: None, + }) + } +} + #[derive(Deserialize)] /// JSON file format for script function arguments. struct ScriptFunctionArgumentsJSON { type_args: Vec, args: Vec, } + +impl TryInto for ScriptFunctionArgumentsJSON { + type Error = CliError; + + fn try_into(self) -> Result { + Ok(ScriptFunctionArguments { + type_arg_vec: TypeArgVec::try_from(&self.type_args)?, + arg_vec: ArgWithTypeVec::try_from(&self.args)?, + json_file: None, + }) + } +} diff --git a/crates/aptos/src/common/utils.rs b/crates/aptos/src/common/utils.rs index f17775e833050..c102142ce31ec 100644 --- a/crates/aptos/src/common/utils.rs +++ b/crates/aptos/src/common/utils.rs @@ -13,10 +13,7 @@ use aptos_build_info::build_information; use aptos_crypto::ed25519::{Ed25519PrivateKey, Ed25519PublicKey}; use aptos_keygen::KeyGen; use aptos_logger::{debug, Level}; -use aptos_rest_client::{ - aptos_api_types::{HashValue, HexEncodedBytes}, - Account, Client, State, -}; +use aptos_rest_client::{aptos_api_types::HashValue, Account, Client, State}; use aptos_telemetry::service::telemetry_is_disabled; use aptos_types::{ account_address::create_multisig_account_address, @@ -484,19 +481,40 @@ pub fn parse_json_file Deserialize<'a>>(path_ref: &Path) -> CliTypedR }) } -/// Convert a view function JSON field, known to have optional hex, into a bytes vector. +/// Convert a view function JSON field into a string option. /// -/// A view function return represents an option via an inner JSON array titled `vec`. -pub fn view_json_option_hex_as_bytes(option_ref: &serde_json::Value) -> CliTypedResult> { - let option_vec_ref = option_ref["vec"].as_array().unwrap(); - if option_vec_ref.is_empty() { - Ok(vec![]) +/// A view function JSON return represents an option via an inner JSON array titled `vec`. +pub fn view_json_option_str(option_ref: &serde_json::Value) -> CliTypedResult> { + if let Some(vec_field) = option_ref.get("vec") { + if let Some(vec_array) = vec_field.as_array() { + if vec_array.is_empty() { + Ok(None) + } else if vec_array.len() > 1 { + Err(CliError::UnexpectedError(format!( + "JSON `vec` array has more than one element: {:?}", + vec_array + ))) + } else { + let option_val_ref = &vec_array[0]; + if let Some(inner_str) = option_val_ref.as_str() { + Ok(Some(inner_str.to_string())) + } else { + Err(CliError::UnexpectedError(format!( + "JSON option is not a string: {}", + option_val_ref + ))) + } + } + } else { + Err(CliError::UnexpectedError(format!( + "JSON `vec` field is not an array: {}", + vec_field + ))) + } } else { - Ok(option_vec_ref[0] - .as_str() - .unwrap() - .parse::()? - .inner() - .to_vec()) + Err(CliError::UnexpectedError(format!( + "JSON field does not have an inner `vec` field: {}", + option_ref + ))) } } diff --git a/crates/aptos/src/move_tool/mod.rs b/crates/aptos/src/move_tool/mod.rs index 7d2fcf1367212..ca18b93ea491d 100644 --- a/crates/aptos/src/move_tool/mod.rs +++ b/crates/aptos/src/move_tool/mod.rs @@ -39,7 +39,7 @@ use aptos_framework::{ }; use aptos_gas::{AbstractValueSizeGasParameters, NativeGasParameters}; use aptos_rest_client::aptos_api_types::{ - self, EntryFunctionId, HexEncodedBytes, IdentifierWrapper, MoveModuleId, + EntryFunctionId, HexEncodedBytes, IdentifierWrapper, MoveModuleId, }; use aptos_transactional_test_harness::run_aptos_test; use aptos_types::{ @@ -78,6 +78,7 @@ use transactional_tests_runner::TransactionalTestOpts; /// about this code. #[derive(Subcommand)] pub enum MoveTool { + BuildPublishPayload(BuildPublishPayload), Clean(CleanPackage), Compile(CompilePackage), CompileScript(CompileScript), @@ -104,6 +105,7 @@ pub enum MoveTool { impl MoveTool { pub async fn execute(self) -> CliResult { match self { + MoveTool::BuildPublishPayload(tool) => tool.execute_serialized().await, MoveTool::Clean(tool) => tool.execute_serialized().await, MoveTool::Compile(tool) => tool.execute_serialized().await, MoveTool::CompileScript(tool) => tool.execute_serialized().await, @@ -629,9 +631,61 @@ pub struct PublishPackage { pub(crate) move_options: MovePackageDir, #[clap(flatten)] pub(crate) txn_options: TransactionOptions, - /// JSON output file to write publication transaction to instead of publishing on-chain +} + +struct PackagePublicationData { + metadata_serialized: Vec, + compiled_units: Vec>, + payload: TransactionPayload, +} + +/// Build a publication transaction payload and store it in a JSON output file. +#[derive(Parser)] +pub struct BuildPublishPayload { + #[clap(flatten)] + publish_package: PublishPackage, + /// JSON output file to write publication transaction to #[clap(long, parse(from_os_str))] - pub(crate) json_output_file: Option, + pub(crate) json_output_file: PathBuf, +} + +impl TryInto for &PublishPackage { + type Error = CliError; + + fn try_into(self) -> Result { + let package_path = self.move_options.get_package_path()?; + let options = self + .included_artifacts_args + .included_artifacts + .build_options( + self.move_options.skip_fetch_latest_git_deps, + self.move_options.named_addresses(), + self.move_options.bytecode_version, + ); + let package = BuiltPackage::build(package_path, options)?; + let compiled_units = package.extract_code(); + let metadata_serialized = + bcs::to_bytes(&package.extract_metadata()?).expect("PackageMetadata has BCS"); + let payload = aptos_cached_packages::aptos_stdlib::code_publish_package_txn( + metadata_serialized.clone(), + compiled_units.clone(), + ); + let size = bcs::serialized_size(&payload)?; + println!("package size {} bytes", size); + if !self.override_size_check && size > MAX_PUBLISH_PACKAGE_SIZE { + return Err(CliError::UnexpectedError(format!( + "The package is larger than {} bytes ({} bytes)! To lower the size \ + you may want to include less artifacts via `--included-artifacts`. \ + You can also override this check with `--override-size-check", + MAX_PUBLISH_PACKAGE_SIZE, size + ))); + } + Ok(PackagePublicationData { + metadata_serialized, + compiled_units, + payload, + }) + } } #[derive(ArgEnum, Clone, Copy, Debug)] @@ -719,101 +773,64 @@ impl CliCommand for PublishPackage { } async fn execute(self) -> CliTypedResult { - let PublishPackage { - move_options, - txn_options, - override_size_check, - included_artifacts_args, - json_output_file, - } = self; - let package_path = move_options.get_package_path()?; - let options = included_artifacts_args.included_artifacts.build_options( - move_options.skip_fetch_latest_git_deps, - move_options.named_addresses(), - move_options.bytecode_version, - ); - let package = BuiltPackage::build(package_path, options)?; - let compiled_units = package.extract_code(); + let package_publication_data: PackagePublicationData = (&self).try_into()?; + profile_or_submit(package_publication_data.payload, &self.txn_options).await + } +} - // Send the compiled module and metadata using the code::publish_package_txn. - let metadata_serialized = - bcs::to_bytes(&package.extract_metadata()?).expect("PackageMetadata has BCS"); - let payload = aptos_cached_packages::aptos_stdlib::code_publish_package_txn( - metadata_serialized.clone(), - compiled_units.clone(), - ); - let size = bcs::serialized_size(&payload)?; - println!("package size {} bytes", size); - if !override_size_check && size > MAX_PUBLISH_PACKAGE_SIZE { - return Err(CliError::UnexpectedError(format!( - "The package is larger than {} bytes ({} bytes)! To lower the size \ - you may want to include less artifacts via `--included-artifacts`. \ - You can also override this check with `--override-size-check", - MAX_PUBLISH_PACKAGE_SIZE, size - ))); - } - // If JSON output file specified, store entry function JSON file on disk. - if let Some(output_file) = json_output_file { - // Extract entry function data from publication payload. - let entry_function = payload.into_entry_function(); - let entry_function_id = EntryFunctionId { - module: MoveModuleId::from(entry_function.module().clone()), - name: IdentifierWrapper::from(entry_function.function()), - }; - let package_metadata_hex = HexEncodedBytes(metadata_serialized).to_string(); - let package_code_hex_vec: Vec = compiled_units - .clone() - .into_iter() - .map(|element| HexEncodedBytes(element).to_string()) - .collect(); - // Construct entry function JSON file representation from entry function data. - let json = EntryFunctionArgumentsJSON { - function_id: entry_function_id.to_string(), - type_args: vec![], - args: vec![ - ArgWithTypeJSON { - arg_type: "hex".to_string(), - arg_value: serde_json::Value::String(package_metadata_hex), - }, - ArgWithTypeJSON { - arg_type: "hex".to_string(), - arg_value: json!(package_code_hex_vec), - }, - ], - }; - // Create save file options for checking and saving file to disk. - let save_file = SaveFile { - output_file, - prompt_options: txn_options.prompt_options, - }; - save_file.check_file()?; - save_file.save_to_file( - "Publication entry function JSON file", - serde_json::to_string_pretty(&json) - .map_err(|err| CliError::UnexpectedError(format!("{}", err)))? - .as_bytes(), - )?; - Ok(TransactionSummary { - // Pass bogus hash for required struct field. - transaction_hash: aptos_api_types::HashValue::from(HashValue::zero()), - gas_used: None, - gas_unit_price: None, - pending: None, - sender: None, - sequence_number: None, - success: None, - timestamp_us: None, - version: None, - // Pass feedback message in available String field. - vm_status: Some(format!( - "Publication entry function JSON file saved to {}", - save_file.output_file.display() - )), - }) - // If no JSON output file specified, publish on-chain. - } else { - profile_or_submit(payload, &txn_options).await - } +#[async_trait] +impl CliCommand for BuildPublishPayload { + fn command_name(&self) -> &'static str { + "BuildPublishPayload" + } + + async fn execute(self) -> CliTypedResult { + let package_publication_data: PackagePublicationData = + (&self.publish_package).try_into()?; + // Extract entry function data from publication payload. + let entry_function = package_publication_data.payload.into_entry_function(); + let entry_function_id = EntryFunctionId { + module: MoveModuleId::from(entry_function.module().clone()), + name: IdentifierWrapper::from(entry_function.function()), + }; + let package_metadata_hex = + HexEncodedBytes(package_publication_data.metadata_serialized).to_string(); + let package_code_hex_vec: Vec = package_publication_data + .compiled_units + .into_iter() + .map(|element| HexEncodedBytes(element).to_string()) + .collect(); + // Construct entry function JSON file representation from entry function data. + let json = EntryFunctionArgumentsJSON { + function_id: entry_function_id.to_string(), + type_args: vec![], + args: vec![ + ArgWithTypeJSON { + arg_type: "hex".to_string(), + value: serde_json::Value::String(package_metadata_hex), + }, + ArgWithTypeJSON { + arg_type: "hex".to_string(), + value: json!(package_code_hex_vec), + }, + ], + }; + // Create save file options for checking and saving file to disk. + let save_file = SaveFile { + output_file: self.json_output_file, + prompt_options: self.publish_package.txn_options.prompt_options, + }; + save_file.check_file()?; + save_file.save_to_file( + "Publication entry function JSON file", + serde_json::to_string_pretty(&json) + .map_err(|err| CliError::UnexpectedError(format!("{}", err)))? + .as_bytes(), + )?; + Ok(format!( + "Publication payload entry function JSON file saved to {}", + save_file.output_file.display() + )) } } @@ -1554,7 +1571,7 @@ impl FromStr for ArgWithType { } } -impl TryInto for ArgWithType { +impl TryInto for &ArgWithType { type Error = CliError; fn try_into(self) -> Result { diff --git a/crates/aptos/src/test/mod.rs b/crates/aptos/src/test/mod.rs index bcc18e62ded00..dada85091fd82 100644 --- a/crates/aptos/src/test/mod.rs +++ b/crates/aptos/src/test/mod.rs @@ -868,7 +868,6 @@ impl CliTestFramework { included_artifacts_args: IncludedArtifactsArgs { included_artifacts: included_artifacts.unwrap_or(IncludedArtifacts::Sparse), }, - json_output_file: None, } .execute() .await diff --git a/developer-docs-site/docs/move/move-on-aptos/cli.md b/developer-docs-site/docs/move/move-on-aptos/cli.md index 217fe54b6947d..86f4b8232132e 100644 --- a/developer-docs-site/docs/move/move-on-aptos/cli.md +++ b/developer-docs-site/docs/move/move-on-aptos/cli.md @@ -473,9 +473,9 @@ aptos key generate \ ```bash { "Result": { + "Account Address:": "0xacef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46", "PublicKey Path": "ace.key.pub", - "PrivateKey Path": "ace.key", - "Account Address:": "0xace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2" + "PrivateKey Path": "ace.key" } } ``` @@ -490,7 +490,7 @@ Store Ace's address in a shell variable so you can call it inline later on: ```bash # Your exact address should vary -ace_addr=0xace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2 +ace_addr=0xacef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46 ``` Fund Ace's account with the faucet (either devnet or testnet): @@ -503,7 +503,7 @@ aptos account fund-with-faucet --account $ace_addr ```bash { - "Result": "Added 100000000 Octas to account ace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2" + "Result": "Added 100000000 Octas to account acef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46" } ``` @@ -523,14 +523,14 @@ aptos move publish \ ```bash { "Result": { - "transaction_hash": "0x78e53928ec853a1c34d0e44aa6dd0ecc8234bdc0ab3d0634da171a6ac5d1b23c", + "transaction_hash": "0x1d7b074dd95724c5459a1c30fe4cb3875e7b0478cc90c87c8e3f21381625bec1", "gas_used": 1294, "gas_unit_price": 100, - "sender": "ace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "sender": "acef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46", "sequence_number": 0, "success": true, - "timestamp_us": 1684977028870268, - "version": 527676489, + "timestamp_us": 1685077849297587, + "version": 528422121, "vm_status": "Executed successfully" } } @@ -577,14 +577,14 @@ aptos move run \ ```bash { "Result": { - "transaction_hash": "0x975e7026532aa6e14c97a27001efb2062c30a0c28e9a18b8b174333d88809c82", + "transaction_hash": "0x5e141dc6c28e86fa9f5594de93d07a014264ebadfb99be6db922a929eb1da24f", "gas_used": 504, "gas_unit_price": 100, - "sender": "ace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "sender": "acef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46", "sequence_number": 1, "success": true, - "timestamp_us": 1684977491248278, - "version": 527679877, + "timestamp_us": 1685077888820037, + "version": 528422422, "vm_status": "Executed successfully" } } @@ -612,14 +612,14 @@ aptos move run \ ```bash { "Result": { - "transaction_hash": "0x44349fb8c8a78598f3f6af50177ee232228581a3dcc04220cbb2c91ec0e01a73", + "transaction_hash": "0x60a32315bb48bf6d31629332f6b1a3471dd0cb016fdee8d0bb7dcd0be9833e60", "gas_used": 3, "gas_unit_price": 100, - "sender": "ace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "sender": "acef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46", "sequence_number": 2, "success": true, - "timestamp_us": 1684977758608985, - "version": 527681864, + "timestamp_us": 1685077961499641, + "version": 528422965, "vm_status": "Executed successfully" } } @@ -698,15 +698,25 @@ The package also contains a script, `set_vals.move`, which is a wrapper for the :!: static/move-examples/cli_args/scripts/set_vals.move script ``` -Here, `aptos move run-script` is run from inside the [`cli_args` package directory](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/cli_args): +First compile the package (this will compile the script): -:::tip -Before trying out the below examples, compile the package with the correct named address via: +```bash title=Compilation +aptos move compile --named-addresses test_account=$ace_addr +``` + +
Output ```bash -aptos move compile --named-addresses test_account=$ace_addr +{ + "Result": [ + "acef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46::cli_args" + ] +} ``` -::: + +
+ +Next, run `aptos move run-script`: ```bash title="Arguments via CLI" aptos move run-script \ @@ -727,14 +737,14 @@ aptos move run-script \ ```bash { "Result": { - "transaction_hash": "0x375d653ecd0e3e00852eefbbe72435479eae9d5e84acd7cc8c7b7f1bc2f2da96", + "transaction_hash": "0x1d644eba8187843cc43919469112339bc2c435a49a733ac813b7bc6c79770152", "gas_used": 3, "gas_unit_price": 100, - "sender": "ace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "sender": "acef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46", "sequence_number": 3, "success": true, - "timestamp_us": 1684978341019604, - "version": 527686516, + "timestamp_us": 1685078415935612, + "version": 528426413, "vm_status": "Executed successfully" } } @@ -755,14 +765,14 @@ aptos move run-script \ ```bash { "Result": { - "transaction_hash": "0x2bab4af9064c34e2b1ea756a44a893c8fb1580bf8af95ba2f454721e422748e9", + "transaction_hash": "0x840e2d6a5ab80d5a570effb3665f775f1755e0fd8d76e52bfa7241aaade883d7", "gas_used": 3, "gas_unit_price": 100, - "sender": "ace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "sender": "acef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46", "sequence_number": 4, "success": true, - "timestamp_us": 1684978420803742, - "version": 527687139, + "timestamp_us": 1685078516832128, + "version": 528427132, "vm_status": "Executed successfully" } } @@ -819,13 +829,11 @@ As of the time of this writing, the `aptos` CLI only supports script function ar This section builds upon the [Arguments in JSON](#arguments-in-json) section, and likewise references the [`CliArgs` example package](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/cli_args). - :::tip If you would like to follow along, start by completing the [Arguments in JSON](#arguments-in-json) tutorial steps! ::: - -For this example, Ace and Bee will conduct governance operations from a 2-of-2 multisig account. +For this example, Ace and Bee will conduct governance operations from a 2-of-2 "multisig v2" account (an on-chain multisig account per [`multisig_account.move`](https://github.com/aptos-labs/aptos-core/blob/main/aptos-move/framework/aptos-framework/sources/multisig_account.move)) ### Account creation @@ -844,7 +852,7 @@ aptos key generate \ "Result": { "PublicKey Path": "bee.key.pub", "PrivateKey Path": "bee.key", - "Account Address:": "0xbee5ec8d0b63bce492047dc71aeb5c28094d462bafc890b57d3b091d71cad218" + "Account Address:": "0xbeec980219d246581cef5166dc6ba5fb1e090c7a7786a5176d111a9029b16ddc" } } ``` @@ -859,7 +867,7 @@ Store Bee's address in a shell variable so you can call it inline later on: ```bash # Your exact address should vary -bee_addr=0xbee5ec8d0b63bce492047dc71aeb5c28094d462bafc890b57d3b091d71cad218 +bee_addr=0xbeec980219d246581cef5166dc6ba5fb1e090c7a7786a5176d111a9029b16ddc ``` Fund Bee's account using the faucet: @@ -872,7 +880,7 @@ aptos account fund-with-faucet --account $bee_addr ```bash { - "Result": "Added 100000000 Octas to account bee5ec8d0b63bce492047dc71aeb5c28094d462bafc890b57d3b091d71cad218" + "Result": "Added 100000000 Octas to account beec980219d246581cef5166dc6ba5fb1e090c7a7786a5176d111a9029b16ddc" } ``` @@ -893,15 +901,15 @@ aptos multisig create \ ```bash { "Result": { - "multisig_address": "50e382f5670c093a84d97d91427389a08717e2aa1b2f8e60efb92fe57cb682d0", - "transaction_hash": "0x696e1d7782bb80546825690c097426afa7f484a08b3ae8a004154aa877d572ed", + "multisig_address": "57478da34604655c68b1dcb89e4f4a9124b6c0ecc1c59a0931d58cc4e60ac5c5", + "transaction_hash": "0x849cc756de2d3b57210f5d32ae4b5e7d1f80e5d376233885944b6f3cc2124a05", "gas_used": 1524, "gas_unit_price": 100, - "sender": "ace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "sender": "acef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46", "sequence_number": 5, "success": true, - "timestamp_us": 1684978792488964, - "version": 527690158, + "timestamp_us": 1685078644186194, + "version": 528428043, "vm_status": "Executed successfully" } } @@ -913,7 +921,7 @@ Store the multisig address in a shell variable: ```bash # Your address should vary -multisig_addr=0x50e382f5670c093a84d97d91427389a08717e2aa1b2f8e60efb92fe57cb682d0 +multisig_addr=0x57478da34604655c68b1dcb89e4f4a9124b6c0ecc1c59a0931d58cc4e60ac5c5 ``` ### Inspect the multisig @@ -952,8 +960,8 @@ aptos move view \ { "Result": [ [ - "0xbee5ec8d0b63bce492047dc71aeb5c28094d462bafc890b57d3b091d71cad218", - "0xace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2" + "0xbeec980219d246581cef5166dc6ba5fb1e090c7a7786a5176d111a9029b16ddc", + "0xacef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46" ] ] } @@ -1002,22 +1010,20 @@ aptos move view \ ### Enqueue a publication transaction The first multisig transaction enqueued will be a transaction for publication of the [`CliArgs` example package](https://github.com/aptos-labs/aptos-core/tree/main/aptos-move/move-examples/cli_args). -First, generate a publication entry function JSON file: +First, generate a publication payload entry function JSON file: ```bash title="Command" -aptos move publish \ +aptos move build-publish-payload \ --named-addresses test_account=$multisig_addr \ - --json-output-file publication.json + --json-output-file publication.json \ + --assume-yes ```
Output ```bash { - "Result": { - "transaction_hash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "vm_status": "Publication entry function JSON file saved to publication.json" - } + "Result": "Publication payload entry function JSON file saved to publication.json" } ``` @@ -1039,14 +1045,14 @@ aptos multisig create-transaction \ ```bash { "Result": { - "transaction_hash": "0x84a1932d91fdf31899bb430d723db26ae0919ece94c3c529d4d7efa4762954db", + "transaction_hash": "0x70c75903f8e1b1c0069f1e84ef9583ad8000f24124b33a746c88d2b031f7fe2c", "gas_used": 510, "gas_unit_price": 100, - "sender": "ace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "sender": "acef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46", "sequence_number": 6, "success": true, - "timestamp_us": 1684978951763370, - "version": 527691441, + "timestamp_us": 1685078836492390, + "version": 528429447, "vm_status": "Executed successfully" } } @@ -1112,20 +1118,20 @@ aptos move view \ { "Result": [ { - "creation_time_secs": "1684978951", - "creator": "0xace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "creation_time_secs": "1685078836", + "creator": "0xacef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46", "payload": { "vec": [] }, "payload_hash": { "vec": [ - "0x04bcaa228189c3603c23e8ba3a91924f8c30528fc91a1f60b88f1000518f99e1" + "0x62b91159c1428c1ef488c7290771de458464bd665691d9653d195bc28e0d2080" ] }, "votes": { "data": [ { - "key": "0xace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "key": "0xacef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46", "value": true } ] @@ -1163,14 +1169,14 @@ aptos multisig create-transaction \ ```bash { "Result": { - "transaction_hash": "0xbd353d2e4ef9d49482f02defeaedcaf4c2f1fc957eac57472690ab17dee70988", + "transaction_hash": "0xd0a348072d5bfc5a2e5d444f92f0ecc10b978dad720b174303bc6d91342f27ec", "gas_used": 511, "gas_unit_price": 100, - "sender": "bee5ec8d0b63bce492047dc71aeb5c28094d462bafc890b57d3b091d71cad218", + "sender": "beec980219d246581cef5166dc6ba5fb1e090c7a7786a5176d111a9029b16ddc", "sequence_number": 0, "success": true, - "timestamp_us": 1684979030036513, - "version": 527692060, + "timestamp_us": 1685078954841650, + "version": 528430315, "vm_status": "Executed successfully" } } @@ -1215,31 +1221,31 @@ aptos move view \ "Result": [ [ { - "creation_time_secs": "1684978951", - "creator": "0xace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "creation_time_secs": "1685078836", + "creator": "0xacef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46", "payload": { "vec": [] }, "payload_hash": { "vec": [ - "0x04bcaa228189c3603c23e8ba3a91924f8c30528fc91a1f60b88f1000518f99e1" + "0x62b91159c1428c1ef488c7290771de458464bd665691d9653d195bc28e0d2080" ] }, "votes": { "data": [ { - "key": "0xace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "key": "0xacef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46", "value": true } ] } }, { - "creation_time_secs": "1684979030", - "creator": "0xbee5ec8d0b63bce492047dc71aeb5c28094d462bafc890b57d3b091d71cad218", + "creation_time_secs": "1685078954", + "creator": "0xbeec980219d246581cef5166dc6ba5fb1e090c7a7786a5176d111a9029b16ddc", "payload": { "vec": [ - "0x0050e382f5670c093a84d97d91427389a08717e2aa1b2f8e60efb92fe57cb682d008636c695f61726773087365745f76616c7302070000000000000000000000000000000000000000000000000000000000000001076163636f756e74074163636f756e740007000000000000000000000000000000000000000000000000000000000000000108636861696e5f696407436861696e49640003017b0504000100006403020000000000000000000000000000000000000000000000000000000000000ace0000000000000000000000000000000000000000000000000000000000000bee010000000000000000000000000000000000000000000000000000000000000cad00" + "0x0057478da34604655c68b1dcb89e4f4a9124b6c0ecc1c59a0931d58cc4e60ac5c508636c695f61726773087365745f76616c7302070000000000000000000000000000000000000000000000000000000000000001076163636f756e74074163636f756e740007000000000000000000000000000000000000000000000000000000000000000108636861696e5f696407436861696e49640003017b0504000100006403020000000000000000000000000000000000000000000000000000000000000ace0000000000000000000000000000000000000000000000000000000000000bee010000000000000000000000000000000000000000000000000000000000000cad00" ] }, "payload_hash": { @@ -1248,14 +1254,15 @@ aptos move view \ "votes": { "data": [ { - "key": "0xbee5ec8d0b63bce492047dc71aeb5c28094d462bafc890b57d3b091d71cad218", + "key": "0xbeec980219d246581cef5166dc6ba5fb1e090c7a7786a5176d111a9029b16ddc", "value": true } ] } } ] - + ] +} ```
@@ -1284,10 +1291,10 @@ aptos move view \
-Before Bee votes, however, she checks that the payload hash stored on-chain matches the publication entry function JSON file: +Before Bee votes, however, she verifies that the payload hash stored on-chain matches the publication entry function JSON file: -```bash title="Checking transaction" -aptos multisig check-transaction \ +```bash title="Verifying transaction proposal" +aptos multisig verify-proposal \ --multisig-address $multisig_addr \ --json-file publication.json \ --sequence-number 1 @@ -1300,20 +1307,20 @@ aptos multisig check-transaction \ "Result": { "Status": "Transaction match", "Multisig transaction": { - "creation_time_secs": "1684978951", - "creator": "0xace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "creation_time_secs": "1685078836", + "creator": "0xacef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46", "payload": { "vec": [] }, "payload_hash": { "vec": [ - "0x04bcaa228189c3603c23e8ba3a91924f8c30528fc91a1f60b88f1000518f99e1" + "0x62b91159c1428c1ef488c7290771de458464bd665691d9653d195bc28e0d2080" ] }, "votes": { "data": [ { - "key": "0xace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "key": "0xacef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46", "value": true } ] @@ -1341,14 +1348,14 @@ aptos multisig approve \ ```bash { "Result": { - "transaction_hash": "0x9b80286a6f1ab70b4b2759193810b7f618451aa0fefcba1095b0ed74607aa684", + "transaction_hash": "0xa5fb49f1077de6aa6d976e6bcc05e4c50c6cd061f1c87e8f1ea74e7a04a06bd1", "gas_used": 6, "gas_unit_price": 100, - "sender": "bee5ec8d0b63bce492047dc71aeb5c28094d462bafc890b57d3b091d71cad218", + "sender": "beec980219d246581cef5166dc6ba5fb1e090c7a7786a5176d111a9029b16ddc", "sequence_number": 1, "success": true, - "timestamp_us": 1684979137080773, - "version": 527692937, + "timestamp_us": 1685079892130861, + "version": 528437204, "vm_status": "Executed successfully" } } @@ -1381,7 +1388,7 @@ aptos move view \ Now either Ace or Bee can invoke the publication transaction from the multisig account, passing the full transaction payload since only the hash was stored on-chain: ```bash title="Publication" -aptos multisig execute \ +aptos multisig execute-with-payload \ --multisig-address $multisig_addr \ --json-file publication.json \ --private-key-file bee.key \ @@ -1423,10 +1430,10 @@ aptos move view \
-Before Ace votes, however, he checks that the payload stored on-chain matches the function arguments he expects: +Before Ace votes, however, he verifies that the payload stored on-chain matches the function arguments he expects: -```bash title="Checking transaction" -aptos multisig check-transaction \ +```bash title="Verifying transaction proposal" +aptos multisig verify-proposal \ --multisig-address $multisig_addr \ --function-id $multisig_addr::cli_args::set_vals \ --type-args \ @@ -1446,11 +1453,11 @@ aptos multisig check-transaction \ "Result": { "Status": "Transaction match", "Multisig transaction": { - "creation_time_secs": "1684979030", - "creator": "0xbee5ec8d0b63bce492047dc71aeb5c28094d462bafc890b57d3b091d71cad218", + "creation_time_secs": "1685078954", + "creator": "0xbeec980219d246581cef5166dc6ba5fb1e090c7a7786a5176d111a9029b16ddc", "payload": { "vec": [ - "0x0050e382f5670c093a84d97d91427389a08717e2aa1b2f8e60efb92fe57cb682d008636c695f61726773087365745f76616c7302070000000000000000000000000000000000000000000000000000000000000001076163636f756e74074163636f756e740007000000000000000000000000000000000000000000000000000000000000000108636861696e5f696407436861696e49640003017b0504000100006403020000000000000000000000000000000000000000000000000000000000000ace0000000000000000000000000000000000000000000000000000000000000bee010000000000000000000000000000000000000000000000000000000000000cad00" + "0x0057478da34604655c68b1dcb89e4f4a9124b6c0ecc1c59a0931d58cc4e60ac5c508636c695f61726773087365745f76616c7302070000000000000000000000000000000000000000000000000000000000000001076163636f756e74074163636f756e740007000000000000000000000000000000000000000000000000000000000000000108636861696e5f696407436861696e49640003017b0504000100006403020000000000000000000000000000000000000000000000000000000000000ace0000000000000000000000000000000000000000000000000000000000000bee010000000000000000000000000000000000000000000000000000000000000cad00" ] }, "payload_hash": { @@ -1459,7 +1466,7 @@ aptos multisig check-transaction \ "votes": { "data": [ { - "key": "0xbee5ec8d0b63bce492047dc71aeb5c28094d462bafc890b57d3b091d71cad218", + "key": "0xbeec980219d246581cef5166dc6ba5fb1e090c7a7786a5176d111a9029b16ddc", "value": true } ] @@ -1471,10 +1478,10 @@ aptos multisig check-transaction \
-Note that the check fails if he modifies even a single argument: +Note that the verification fails if he modifies even a single argument: -```bash title="Checking transaction with modified u8" -aptos multisig check-transaction \ +```bash title="Failed transaction verification with modified u8" +aptos multisig verify-proposal \ --multisig-address $multisig_addr \ --function-id $multisig_addr::cli_args::set_vals \ --type-args \ @@ -1491,7 +1498,12 @@ aptos multisig check-transaction \ ```bash { - "Error": "Unexpected error: Payload mismatch" + "Result": { + "Status": "Transaction mismatch", + "Provided payload hash": "0xe494b0072d6f940317344967cf0e818c80082375833708c773b0275f3ad07e51", + "On-chain payload hash": "0x070ed7c3f812f25f585461305d507b96a4e756f784e01c8c59901871267a1580", + "For more information": "https://aptos.dev/move/move-on-aptos/cli#multisig-governance" + } } ``` @@ -1512,14 +1524,14 @@ aptos multisig approve \ ```bash { "Result": { - "transaction_hash": "0x3b443492c885f7338931e640a36d8d225a4f53ff17198cd2e3087b3a0887fcd2", + "transaction_hash": "0x233427d95832234fa13dddad5e0b225d40168b4c2c6b84f5255eecc3e68401bf", "gas_used": 6, "gas_unit_price": 100, - "sender": "ace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "sender": "acef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46", "sequence_number": 7, "success": true, - "timestamp_us": 1684979313218098, - "version": 527694405, + "timestamp_us": 1685080266378400, + "version": 528439883, "vm_status": "Executed successfully" } } @@ -1529,7 +1541,7 @@ aptos multisig approve \ Since the payload was stored on-chain, it is not required to execute the pending transaction: -```bash title="Publication" +```bash title="Execution" aptos multisig execute \ --multisig-address $multisig_addr \ --private-key-file ace.key \ @@ -1542,17 +1554,16 @@ aptos multisig execute \ ```bash { "Result": { - "transaction_hash": "0x20c0c1a2d8699cde1d70e07a77eae62b27acd900521efa641eb251dafabcd324", + "transaction_hash": "0xbc99f929708a1058b223aa880d04607a78ebe503367ec4dab23af4a3bdb541b2", "gas_used": 505, "gas_unit_price": 100, - "sender": "ace93c3bdeef22d10a8482ca9d70dcdb4f654511db3ec531397944e42ad77ec2", + "sender": "acef1b9b7d4ab208b99fed60746d18dcd74865edb7eb3c3f1428233988e4ba46", "sequence_number": 8, "success": true, - "timestamp_us": 1684979342858131, - "version": 527694637, + "timestamp_us": 1685080344045461, + "version": 528440423, "vm_status": "Executed successfully" - } -} + ```
From d54be7701ed82cbb4461cbe4cfcc961465692e8f Mon Sep 17 00:00:00 2001 From: alnoki <43892045+alnoki@users.noreply.github.com> Date: Fri, 26 May 2023 10:01:40 -0700 Subject: [PATCH 7/7] Refactor verification failure per @banool request Addresses https://github.com/aptos-labs/aptos-core/pull/8346#discussion_r1206907888 --- crates/aptos/src/account/multisig_account.rs | 21 +++++++++---------- .../docs/move/move-on-aptos/cli.md | 7 +------ 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/crates/aptos/src/account/multisig_account.rs b/crates/aptos/src/account/multisig_account.rs index 6e26a81764732..b7a8ccf204fa0 100644 --- a/crates/aptos/src/account/multisig_account.rs +++ b/crates/aptos/src/account/multisig_account.rs @@ -209,20 +209,19 @@ impl CliCommand for VerifyProposal { )? }; // Get verification result based on if expected and actual payload hashes match. - let result = if expected_payload_hash.eq(&actual_payload_hash) { - json!({ + if expected_payload_hash.eq(&actual_payload_hash) { + Ok(json!({ "Status": "Transaction match", "Multisig transaction": multisig_transaction - }) + })) } else { - json!({ - "Status": "Transaction mismatch", - "Provided payload hash": expected_payload_hash, - "On-chain payload hash": actual_payload_hash, - "For more information": "https://aptos.dev/move/move-on-aptos/cli#multisig-governance" - }) - }; - Ok(result) + Err(CliError::UnexpectedError(format!( + "Transaction mismatch: The transaction you provided has a payload hash of \ + {expected_payload_hash}, but the on-chain transaction proposal you specified has \ + a payload hash of {actual_payload_hash}. For more info, see \ + https://aptos.dev/move/move-on-aptos/cli#multisig-governance" + ))) + } } } diff --git a/developer-docs-site/docs/move/move-on-aptos/cli.md b/developer-docs-site/docs/move/move-on-aptos/cli.md index 86f4b8232132e..d430866fcb589 100644 --- a/developer-docs-site/docs/move/move-on-aptos/cli.md +++ b/developer-docs-site/docs/move/move-on-aptos/cli.md @@ -1498,12 +1498,7 @@ aptos multisig verify-proposal \ ```bash { - "Result": { - "Status": "Transaction mismatch", - "Provided payload hash": "0xe494b0072d6f940317344967cf0e818c80082375833708c773b0275f3ad07e51", - "On-chain payload hash": "0x070ed7c3f812f25f585461305d507b96a4e756f784e01c8c59901871267a1580", - "For more information": "https://aptos.dev/move/move-on-aptos/cli#multisig-governance" - } + "Error": "Unexpected error: Transaction mismatch: The transaction you provided has a payload hash of 0xe494b0072d6f940317344967cf0e818c80082375833708c773b0275f3ad07e51, but the on-chain transaction proposal you specified has a payload hash of 0x070ed7c3f812f25f585461305d507b96a4e756f784e01c8c59901871267a1580. For more info, see https://aptos.dev/move/move-on-aptos/cli#multisig-governance" } ```