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

fix: Add ContractAddress type to consolidate contract id parsing with aliases #1692

Merged
merged 14 commits into from
Nov 15, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ pub(crate) fn invoke_hello_world(sandbox: &TestEnv, id: &str) {

fn hello_world_cmd(id: &str, arg: &str) -> contract::invoke::Cmd {
contract::invoke::Cmd {
contract_id: id.to_string(),
contract_id: id.parse().unwrap(),
slop: vec!["hello".into(), format!("--world={arg}").into()],
..Default::default()
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/soroban-cli/src/commands/contract/alias/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub struct Cmd {
#[command(flatten)]
network: network::Args,

/// The contract alias that will be removed.
/// The contract alias that will be used.
pub alias: String,

/// Overwrite the contract alias if it already exists.
Expand All @@ -41,7 +41,7 @@ pub enum Error {
AlreadyExist {
alias: String,
network_passphrase: String,
contract: String,
contract: stellar_strkey::Contract,
},
}

Expand Down
12 changes: 9 additions & 3 deletions cmd/soroban-cli/src/commands/contract/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use clap::{arg, command, Parser};
use crate::{
commands::{global, NetworkRunnable},
config::{
self, locator,
self, alias, locator,
network::{self, Network},
},
wasm, Pwd,
Expand All @@ -22,7 +22,7 @@ use crate::{
pub struct Cmd {
/// Contract ID to fetch
#[arg(long = "id", env = "STELLAR_CONTRACT_ID")]
pub contract_id: String,
pub contract_id: config::ContractAddress,
/// Where to write output otherwise stdout is used
#[arg(long, short = 'o')]
pub out_file: Option<std::path::PathBuf>,
Expand Down Expand Up @@ -111,6 +111,12 @@ impl NetworkRunnable for Cmd {
config: Option<&config::Args>,
) -> Result<Vec<u8>, Error> {
let network = config.map_or_else(|| self.network(), |c| Ok(c.get_network()?))?;
return Ok(wasm::fetch_from_contract(&self.contract_id, &network, &self.locator).await?);
Ok(wasm::fetch_from_contract(
&self
.contract_id
.resolve_contract_id(&self.locator, &network.network_passphrase)?,
&network,
)
.await?)
}
}
14 changes: 9 additions & 5 deletions cmd/soroban-cli/src/commands/contract/info/shared.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use std::path::PathBuf;

use crate::xdr;
use clap::arg;

use crate::{
commands::contract::info::shared::Error::InvalidWasmHash,
config::{locator, network},
config::{self, locator, network},
utils::rpc::get_remote_wasm_from_hash,
wasm::{self, Error::ContractIsStellarAsset},
xdr,
};

#[derive(Debug, clap::Args, Clone, Default)]
Expand All @@ -24,9 +24,9 @@ pub struct Args {
/// Wasm hash to get the data for
#[arg(long = "wasm-hash", group = "Source")]
pub wasm_hash: Option<String>,
/// Contract id to get the data for
/// Contract id or contract alias to get the data for
#[arg(long = "id", env = "STELLAR_CONTRACT_ID", group = "Source")]
pub contract_id: Option<String>,
pub contract_id: Option<config::ContractAddress>,
#[command(flatten)]
pub network: network::Args,
#[command(flatten)]
Expand Down Expand Up @@ -56,6 +56,8 @@ pub enum Error {
InvalidWasmHash(String),
#[error(transparent)]
Rpc(#[from] soroban_rpc::Error),
#[error(transparent)]
Locator(#[from] locator::Error),
}

pub async fn fetch_wasm(args: &Args) -> Result<Option<Vec<u8>>, Error> {
Expand All @@ -79,7 +81,9 @@ pub async fn fetch_wasm(args: &Args) -> Result<Option<Vec<u8>>, Error> {

get_remote_wasm_from_hash(&client, &hash).await?
} else if let Some(contract_id) = &args.contract_id {
let res = wasm::fetch_from_contract(contract_id, network, &args.locator).await;
let contract_id =
contract_id.resolve_contract_id(&args.locator, &network.network_passphrase)?;
let res = wasm::fetch_from_contract(&contract_id, network).await;
if let Some(ContractIsStellarAsset) = res.as_ref().err() {
return Ok(None);
}
Expand Down
10 changes: 6 additions & 4 deletions cmd/soroban-cli/src/commands/contract/invoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use soroban_spec::read::FromWasmError;

use super::super::events;
use super::arg_parsing;
use crate::config::alias;
use crate::{
assembled::simulate_and_assemble_transaction,
commands::{
Expand Down Expand Up @@ -40,7 +41,7 @@ use soroban_spec_tools::contract;
pub struct Cmd {
/// Contract ID to invoke
#[arg(long = "id", env = "STELLAR_CONTRACT_ID")]
pub contract_id: String,
pub contract_id: config::ContractAddress,
// For testing only
#[arg(skip)]
pub wasm: Option<std::path::PathBuf>,
Expand Down Expand Up @@ -123,6 +124,8 @@ pub enum Error {
GetSpecError(#[from] get_spec::Error),
#[error(transparent)]
ArgParsing(#[from] arg_parsing::Error),
#[error(transparent)]
ContractAddress(#[from] alias::Error),
}

impl From<Infallible> for Error {
Expand Down Expand Up @@ -195,9 +198,8 @@ impl NetworkRunnable for Cmd {
let network = config.get_network()?;
tracing::trace!(?network);
let contract_id = self
.config
.locator
.resolve_contract_id(&self.contract_id, &network.network_passphrase)?;
.contract_id
.resolve_contract_id(&config.locator, &network.network_passphrase)?;

let spec_entries = self.spec_entries()?;
if let Some(spec_entries) = &spec_entries {
Expand Down
14 changes: 9 additions & 5 deletions cmd/soroban-cli/src/commands/events.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use clap::{arg, command, Parser};
use std::io;

use crate::xdr::{self, Limits, ReadXdr};
use crate::{
config::alias,
xdr::{self, Limits, ReadXdr},
};

use super::{global, NetworkRunnable};
use crate::{
Expand Down Expand Up @@ -42,7 +45,7 @@ pub struct Cmd {
num_args = 1..=6,
help_heading = "FILTERS"
)]
contract_ids: Vec<String>,
contract_ids: Vec<config::ContractAddress>,
/// A set of (up to 4) topic filters to filter event topics on. A single
/// topic filter can contain 1-4 different segment filters, separated by
/// commas, with an asterisk (`*` character) indicating a wildcard segment.
Expand Down Expand Up @@ -121,6 +124,8 @@ pub enum Error {
Locator(#[from] locator::Error),
#[error(transparent)]
Config(#[from] config::Error),
#[error(transparent)]
ContractId(#[from] alias::Error),
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, clap::ValueEnum)]
Expand Down Expand Up @@ -218,9 +223,8 @@ impl NetworkRunnable for Cmd {
.contract_ids
.iter()
.map(|id| {
Ok(self
.locator
.resolve_contract_id(id, &network.network_passphrase)?
Ok(id
.resolve_contract_id(&self.locator, &network.network_passphrase)?
.to_string())
})
.collect::<Result<Vec<_>, Error>>()?;
Expand Down
43 changes: 42 additions & 1 deletion cmd/soroban-cli/src/config/alias.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,49 @@
use std::collections::HashMap;
use std::{collections::HashMap, convert::Infallible, str::FromStr};

use serde::{Deserialize, Serialize};

use super::locator;

#[derive(Serialize, Deserialize, Default)]
pub struct Data {
pub ids: HashMap<String, String>,
}

/// Address can be either a contract address, C.. or eventually an alias of a contract address.
#[derive(Clone, Debug)]
pub enum ContractAddress {
ContractId(stellar_strkey::Contract),
Alias(String),
}

impl Default for ContractAddress {
fn default() -> Self {
willemneal marked this conversation as resolved.
Show resolved Hide resolved
ContractAddress::Alias(String::default())
}
}

impl FromStr for ContractAddress {
type Err = Infallible;

fn from_str(value: &str) -> Result<Self, Self::Err> {
Ok(stellar_strkey::Contract::from_str(value).map_or_else(
|_| ContractAddress::Alias(value.to_string()),
ContractAddress::ContractId,
))
}
}

impl ContractAddress {
pub fn resolve_contract_id(
&self,
locator: &locator::Args,
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())),
}
}
}
25 changes: 15 additions & 10 deletions cmd/soroban-cli/src/config/locator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ pub enum Error {
CannotAccessAliasConfigFile,
#[error("cannot parse contract ID {0}: {1}")]
CannotParseContractId(String, DecodeError),
#[error("contract not found: {0}")]
ContractNotFound(String),
#[error("Failed to read upgrade check file: {path}: {error}")]
UpgradeCheckReadFailed { path: PathBuf, error: io::Error },
#[error("Failed to write upgrade check file: {path}: {error}")]
Expand Down Expand Up @@ -320,27 +322,30 @@ impl Args {
&self,
alias: &str,
network_passphrase: &str,
) -> Result<Option<String>, Error> {
) -> Result<Option<Contract>, Error> {
let Some(alias_data) = self.load_contract_from_alias(alias)? else {
return Ok(None);
};

Ok(alias_data.ids.get(network_passphrase).cloned())
alias_data
.ids
.get(network_passphrase)
.map(|id| id.parse())
.transpose()
.map_err(|e| Error::CannotParseContractId(alias.to_owned(), e))
}

pub fn resolve_contract_id(
&self,
alias_or_contract_id: &str,
network_passphrase: &str,
) -> Result<Contract, Error> {
let contract_id = self
.get_contract_id(alias_or_contract_id, network_passphrase)?
.unwrap_or_else(|| alias_or_contract_id.to_string());

Ok(Contract(
soroban_spec_tools::utils::contract_id_from_str(&contract_id)
.map_err(|e| Error::CannotParseContractId(contract_id.clone(), e))?,
))
let Some(contract) = self.get_contract_id(alias_or_contract_id, network_passphrase)? else {
return alias_or_contract_id
.parse()
.map_err(|e| Error::CannotParseContractId(alias_or_contract_id.to_owned(), e));
};
Ok(contract)
}
}

Expand Down
2 changes: 2 additions & 0 deletions cmd/soroban-cli/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub mod secret;
pub mod sign_with;
pub mod upgrade_check;

pub use alias::ContractAddress;

#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error(transparent)]
Expand Down
13 changes: 9 additions & 4 deletions cmd/soroban-cli/src/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::xdr::{
};
use crate::{
commands::contract::Durability,
config::{locator, network::Network},
config::{alias, locator, network::Network},
wasm,
};
use clap::arg;
Expand All @@ -22,6 +22,8 @@ pub enum Error {
Wasm(#[from] wasm::Error),
#[error(transparent)]
Locator(#[from] locator::Error),
#[error(transparent)]
ContractId(#[from] alias::Error),
}

#[derive(Debug, clap::Args, Clone)]
Expand All @@ -34,7 +36,7 @@ pub struct Args {
required_unless_present = "wasm",
required_unless_present = "wasm_hash"
)]
pub contract_id: Option<String>,
pub contract_id: Option<config::ContractAddress>,
/// Storage key (symbols only)
#[arg(long = "key", conflicts_with = "key_xdr")]
pub key: Option<Vec<String>>,
Expand Down Expand Up @@ -97,8 +99,11 @@ impl Args {
} else {
vec![ScVal::LedgerKeyContractInstance]
};
let contract =
locator.resolve_contract_id(self.contract_id.as_ref().unwrap(), network_passphrase)?;
let contract = self
.contract_id
.as_ref()
.unwrap()
.resolve_contract_id(locator, network_passphrase)?;

Ok(keys
.into_iter()
Expand Down
8 changes: 1 addition & 7 deletions cmd/soroban-cli/src/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,16 +121,10 @@ pub fn len(p: &Path) -> Result<u64, Error> {
}

pub async fn fetch_from_contract(
contract_id: &str,
stellar_strkey::Contract(contract_id): &stellar_strkey::Contract,
network: &Network,
locator: &locator::Args,
) -> Result<Vec<u8>, Error> {
tracing::trace!(?network);

let contract_id = &locator
.resolve_contract_id(contract_id, &network.network_passphrase)?
.0;

let client = network.rpc_client()?;
client
.verify_network_passphrase(Some(&network.network_passphrase))
Expand Down
Loading