Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(contract invoke): support contract aliases when parsing address #1765

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 38 additions & 14 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 @@ -82,16 +88,10 @@ pub fn build_host_function_parameters(
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 (addr, signer) = resolve_sc_address(&s, config)?;
s = addr;
if let Some(signer) = signer {
signers.push(signer);
}
}
spec.from_string(&s, &i.type_)
Expand Down Expand Up @@ -125,7 +125,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 +246,27 @@ 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 addr_or_alias = addr_or_alias.trim_matches('"');
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()))
willemneal marked this conversation as resolved.
Show resolved Hide resolved
}
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
18 changes: 14 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,20 @@ 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 {
willemneal marked this conversation as resolved.
Show resolved Hide resolved
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)),
}
}
}