diff --git a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs index 21fa2f383..54ab30fd3 100644 --- a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs +++ b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs @@ -9,12 +9,14 @@ use ed25519_dalek::SigningKey; use heck::ToKebabCase; use crate::xdr::{ - self, Hash, InvokeContractArgs, ScAddress, ScSpecEntry, ScSpecFunctionV0, ScSpecTypeDef, ScVal, - ScVec, + self, Hash, InvokeContractArgs, ScSpecEntry, ScSpecFunctionV0, ScSpecTypeDef, ScVal, ScVec, }; use crate::commands::txn_result::TxnResult; -use crate::config::{self}; +use crate::config::{ + self, + sc_address::{self, ScAddress}, +}; use soroban_spec_tools::Spec; #[derive(thiserror::Error, Debug)] @@ -43,6 +45,10 @@ pub enum Error { MissingArgument(String), #[error("")] MissingFileArg(PathBuf), + #[error(transparent)] + ScAddress(#[from] sc_address::Error), + #[error(transparent)] + Config(#[from] config::Error), } pub fn build_host_function_parameters( @@ -80,19 +86,10 @@ pub fn build_host_function_parameters( .map(|i| { let name = i.name.to_utf8_string()?; if let Some(mut val) = matches_.get_raw(&name) { - let mut s = val.next().unwrap().to_string_lossy().to_string(); - if matches!(i.type_, ScSpecTypeDef::Address) { - let cmd = crate::commands::keys::address::Cmd { - name: s.clone(), - hd_path: Some(0), - locator: config.locator.clone(), - }; - if let Ok(address) = cmd.public_key() { - s = address.to_string(); - } - if let Ok(key) = cmd.private_key() { - signers.push(key); - } + let s = val.next().unwrap().to_string_lossy().to_string(); + let (s, signer) = resolve_sc_address(&s, config)?; + if let Some(signer) = signer { + signers.push(signer); } spec.from_string(&s, &i.type_) .map_err(|error| Error::CannotParseArg { arg: name, error }) @@ -125,7 +122,7 @@ pub fn build_host_function_parameters( }) .collect::, Error>>()?; - let contract_address_arg = ScAddress::Contract(Hash(contract_id.0)); + let contract_address_arg = xdr::ScAddress::Contract(Hash(contract_id.0)); let function_symbol_arg = function .try_into() .map_err(|()| Error::FunctionNameTooLong(function.clone()))?; @@ -246,3 +243,26 @@ pub fn output_to_string( } Ok(TxnResult::Res(res_str)) } + +fn resolve_sc_address( + addr_or_alias: &str, + config: &config::Args, +) -> Result<(String, Option), Error> { + let sc_address: ScAddress = addr_or_alias.parse().unwrap(); + let account = match sc_address { + ScAddress::Address(addr) => return Ok((addr.to_string(), None)), + addr @ ScAddress::Alias(_) => { + let addr = addr.resolve(&config.locator, &config.get_network()?.network_passphrase)?; + match addr { + xdr::ScAddress::Account(account) => account.to_string(), + contract @ xdr::ScAddress::Contract(_) => return Ok((contract.to_string(), None)), + } + } + }; + let cmd = crate::commands::keys::address::Cmd { + name: addr_or_alias.to_string(), + hd_path: Some(0), + locator: config.locator.clone(), + }; + Ok((account, cmd.private_key().ok())) +} diff --git a/cmd/soroban-cli/src/config/address.rs b/cmd/soroban-cli/src/config/address.rs index 066bc8d91..aa3116a13 100644 --- a/cmd/soroban-cli/src/config/address.rs +++ b/cmd/soroban-cli/src/config/address.rs @@ -46,14 +46,24 @@ impl Address { ) -> Result { match self { Address::MuxedAccount(muxed_account) => Ok(muxed_account.clone()), - Address::AliasOrSecret(alias) => alias.parse().or_else(|_| { - Ok(xdr::MuxedAccount::Ed25519( - locator.read_identity(alias)?.public_key(hd_path)?.0.into(), - )) - }), + Address::AliasOrSecret(alias) => { + Self::resolve_muxed_account_with_alias(alias, locator, hd_path) + } } } + pub fn resolve_muxed_account_with_alias( + alias: &str, + locator: &locator::Args, + hd_path: Option, + ) -> Result { + alias.parse().or_else(|_| { + Ok(xdr::MuxedAccount::Ed25519( + locator.read_identity(alias)?.public_key(hd_path)?.0.into(), + )) + }) + } + pub fn resolve_secret(&self, locator: &locator::Args) -> Result { match &self { Address::MuxedAccount(muxed_account) => Err(Error::CannotSign(muxed_account.clone())), diff --git a/cmd/soroban-cli/src/config/alias.rs b/cmd/soroban-cli/src/config/alias.rs index 9d1d8c11b..aa7c65717 100644 --- a/cmd/soroban-cli/src/config/alias.rs +++ b/cmd/soroban-cli/src/config/alias.rs @@ -40,10 +40,19 @@ impl ContractAddress { network_passphrase: &str, ) -> Result { match self { - ContractAddress::ContractId(muxed_account) => Ok(*muxed_account), - ContractAddress::Alias(alias) => locator - .get_contract_id(alias, network_passphrase)? - .ok_or_else(|| locator::Error::ContractNotFound(alias.to_owned())), + ContractAddress::ContractId(contract) => Ok(*contract), + ContractAddress::Alias(alias) => Self::resolve_alias(alias, locator, network_passphrase), } } + + pub fn resolve_alias( + alias: &str, + locator: &locator::Args, + network_passphrase: &str, + ) -> Result { + locator + .get_contract_id(alias, network_passphrase)? + .ok_or_else(|| locator::Error::ContractNotFound(alias.to_owned())) + + } } diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index a429ff434..e4a34ce22 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -19,6 +19,7 @@ pub mod alias; pub mod data; pub mod locator; pub mod network; +pub mod sc_address; pub mod secret; pub mod sign_with; pub mod upgrade_check; diff --git a/cmd/soroban-cli/src/config/sc_address.rs b/cmd/soroban-cli/src/config/sc_address.rs new file mode 100644 index 000000000..26eaf9641 --- /dev/null +++ b/cmd/soroban-cli/src/config/sc_address.rs @@ -0,0 +1,57 @@ +use std::str::FromStr; + +use crate::xdr; + +use super::{address, locator, ContractAddress}; + +/// `ScAddress` can be either a resolved `xdr::ScAddress` or an alias of a `Contract` or `MuxedAccount`. +#[derive(Clone, Debug)] +pub enum ScAddress { + Address(xdr::ScAddress), + Alias(String), +} + +impl Default for ScAddress { + fn default() -> Self { + ScAddress::Alias(String::default()) + } +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Locator(#[from] locator::Error), + #[error(transparent)] + Address(#[from] address::Error), + #[error("Account alias not Found{0}")] + AccountAliasNotFound(String), +} + +impl FromStr for ScAddress { + type Err = Error; + + fn from_str(value: &str) -> Result { + Ok(xdr::ScAddress::from_str(value) + .map_or_else(|_| ScAddress::Alias(value.to_string()), ScAddress::Address)) + } +} + +impl ScAddress { + pub fn resolve( + self, + locator: &locator::Args, + network_passphrase: &str, + ) -> Result { + let alias = match self { + ScAddress::Address(addr) => return Ok(addr), + ScAddress::Alias(alias) => alias, + }; + let contract = ContractAddress::resolve_alias(&alias, locator, network_passphrase); + let muxed_account = super::Address::resolve_muxed_account_with_alias(&alias, locator, None); + match (contract, muxed_account) { + (Ok(contract), _) => Ok(xdr::ScAddress::Contract(xdr::Hash(contract.0))), + (_, Ok(muxed_account)) => Ok(xdr::ScAddress::Account(muxed_account.account_id())), + _ => Err(Error::AccountAliasNotFound(alias)), + } + } +}