Skip to content

Commit

Permalink
feat(contract invoke): support contract aliases when parsing address
Browse files Browse the repository at this point in the history
fixes: 1764
  • Loading branch information
willemneal authored and gitbutler-client committed Nov 30, 2024
1 parent 9b8013a commit d391741
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 26 deletions.
54 changes: 37 additions & 17 deletions cmd/soroban-cli/src/commands/contract/arg_parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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 })
Expand Down Expand Up @@ -125,7 +122,7 @@ pub fn build_host_function_parameters(
})
.collect::<Result<Vec<_>, 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()))?;
Expand Down Expand Up @@ -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<SigningKey>), 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()))
}
20 changes: 15 additions & 5 deletions cmd/soroban-cli/src/config/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,24 @@ impl Address {
) -> Result<xdr::MuxedAccount, Error> {
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<usize>,
) -> Result<xdr::MuxedAccount, Error> {
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<secret::Secret, Error> {
match &self {
Address::MuxedAccount(muxed_account) => Err(Error::CannotSign(muxed_account.clone())),
Expand Down
17 changes: 13 additions & 4 deletions cmd/soroban-cli/src/config/alias.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,19 @@ impl ContractAddress {
network_passphrase: &str,
) -> Result<stellar_strkey::Contract, locator::Error> {
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<stellar_strkey::Contract, locator::Error> {
locator
.get_contract_id(alias, network_passphrase)?
.ok_or_else(|| locator::Error::ContractNotFound(alias.to_owned()))

}
}
1 change: 1 addition & 0 deletions cmd/soroban-cli/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
57 changes: 57 additions & 0 deletions cmd/soroban-cli/src/config/sc_address.rs
Original file line number Diff line number Diff line change
@@ -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<Self, Self::Err> {
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<xdr::ScAddress, Error> {
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)),
}
}
}

0 comments on commit d391741

Please sign in to comment.