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
Show file tree
Hide file tree
Changes from all 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
38 changes: 19 additions & 19 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions FULL_HELP_DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ Add contract alias

###### **Arguments:**

* `<ALIAS>` — The contract alias that will be removed
* `<ALIAS>` — The contract alias that will be used

###### **Options:**

Expand Down Expand Up @@ -522,7 +522,7 @@ Outputs no data when no data is present in the contract.

* `--wasm <WASM>` — Wasm file to extract the data from
* `--wasm-hash <WASM_HASH>` — Wasm hash to get the data for
* `--id <CONTRACT_ID>` — Contract id to get the data for
* `--id <CONTRACT_ID>` — Contract id or contract alias to get the data for
* `--rpc-url <RPC_URL>` — RPC server endpoint
* `--rpc-header <RPC_HEADERS>` — RPC Header(s) to include in requests to the RPC provider
* `--network-passphrase <NETWORK_PASSPHRASE>` — Network passphrase to sign the transaction sent to the rpc server
Expand Down Expand Up @@ -562,7 +562,7 @@ Outputs no data when no data is present in the contract.

* `--wasm <WASM>` — Wasm file to extract the data from
* `--wasm-hash <WASM_HASH>` — Wasm hash to get the data for
* `--id <CONTRACT_ID>` — Contract id to get the data for
* `--id <CONTRACT_ID>` — Contract id or contract alias to get the data for
* `--rpc-url <RPC_URL>` — RPC server endpoint
* `--rpc-header <RPC_HEADERS>` — RPC Header(s) to include in requests to the RPC provider
* `--network-passphrase <NETWORK_PASSPHRASE>` — Network passphrase to sign the transaction sent to the rpc server
Expand Down Expand Up @@ -602,7 +602,7 @@ Outputs no data when no data is present in the contract.

* `--wasm <WASM>` — Wasm file to extract the data from
* `--wasm-hash <WASM_HASH>` — Wasm hash to get the data for
* `--id <CONTRACT_ID>` — Contract id to get the data for
* `--id <CONTRACT_ID>` — Contract id or contract alias to get the data for
* `--rpc-url <RPC_URL>` — RPC server endpoint
* `--rpc-header <RPC_HEADERS>` — RPC Header(s) to include in requests to the RPC provider
* `--network-passphrase <NETWORK_PASSPHRASE>` — Network passphrase to sign the transaction sent to the rpc server
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,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
8 changes: 4 additions & 4 deletions cmd/soroban-cli/src/commands/contract/alias/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ pub struct Cmd {
pub config_locator: locator::Args,

#[command(flatten)]
network: network::Args,
pub 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 All @@ -57,7 +57,7 @@ impl Cmd {
.get_contract_id(&self.alias, network_passphrase)?;

if let Some(contract) = contract {
if contract != self.contract_id.to_string() && !self.overwrite {
if contract != self.contract_id && !self.overwrite {
return Err(Error::AlreadyExist {
alias: alias.to_string(),
network_passphrase: network_passphrase.to_string(),
Expand Down
10 changes: 8 additions & 2 deletions cmd/soroban-cli/src/commands/contract/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 Down Expand Up @@ -84,7 +86,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
7 changes: 3 additions & 4 deletions cmd/soroban-cli/src/commands/contract/invoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,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 @@ -217,9 +217,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
7 changes: 3 additions & 4 deletions cmd/soroban-cli/src/commands/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,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 @@ -218,9 +218,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 @@ -76,6 +76,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 @@ -332,27 +334,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
Loading
Loading