Skip to content

Commit

Permalink
Merge branch 'feat/contract_aliases_in_sc_addr_parsing' into feat/os_…
Browse files Browse the repository at this point in the history
…keychain
  • Loading branch information
willemneal committed Dec 20, 2024
2 parents 2241352 + 52502e2 commit 93fa181
Show file tree
Hide file tree
Showing 19 changed files with 270 additions and 58 deletions.
44 changes: 44 additions & 0 deletions cmd/crates/soroban-test/tests/it/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,3 +393,47 @@ fn set_default_network() {
.stdout(predicate::str::contains("STELLAR_NETWORK=testnet"))
.success();
}

#[test]
fn cannot_create_contract_with_test_name() {
let sandbox = TestEnv::default();
sandbox
.new_assert_cmd("keys")
.arg("generate")
.arg("--no-fund")
.arg("d")
.assert()
.success();
sandbox
.new_assert_cmd("contract")
.arg("alias")
.arg("add")
.arg("d")
.arg("--id=CA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAXE")
.assert()
.stderr(predicate::str::contains("cannot overlap with key"))
.failure();
}

#[test]
fn cannot_create_key_with_alias() {
let sandbox = TestEnv::default();
sandbox
.new_assert_cmd("contract")
.arg("alias")
.arg("add")
.arg("t")
.arg("--id=CA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAXE")
.assert()
.success();
sandbox
.new_assert_cmd("keys")
.arg("generate")
.arg("--no-fund")
.arg("t")
.assert()
.stderr(predicate::str::contains(
"cannot overlap with contract alias",
))
.failure();
}
26 changes: 25 additions & 1 deletion cmd/crates/soroban-test/tests/it/integration/custom_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use soroban_test::TestEnv;

use crate::integration::util::{deploy_custom, extend_contract};

use super::util::invoke_with_roundtrip;
use super::util::{invoke, invoke_with_roundtrip};

fn invoke_custom(e: &TestEnv, id: &str, func: &str) -> assert_cmd::Command {
let mut s = e.new_assert_cmd("contract");
Expand Down Expand Up @@ -40,7 +40,9 @@ async fn parse() {
negative_i32(sandbox, id).await;
negative_i64(sandbox, id).await;
account_address(sandbox, id).await;
account_address_with_alias(sandbox, id).await;
contract_address(sandbox, id).await;
contract_address_with_alias(sandbox, id).await;
bytes(sandbox, id).await;
const_enum(sandbox, id).await;
number_arg_return_ok(sandbox, id);
Expand Down Expand Up @@ -237,6 +239,12 @@ async fn account_address(sandbox: &TestEnv, id: &str) {
.await;
}

async fn account_address_with_alias(sandbox: &TestEnv, id: &str) {
let res = invoke(sandbox, id, "addresse", &json!("test").to_string()).await;
let test = format!("\"{}\"", super::tx::operations::test_address(sandbox));
assert_eq!(test, res);
}

async fn contract_address(sandbox: &TestEnv, id: &str) {
invoke_with_roundtrip(
sandbox,
Expand All @@ -247,6 +255,22 @@ async fn contract_address(sandbox: &TestEnv, id: &str) {
.await;
}

async fn contract_address_with_alias(sandbox: &TestEnv, id: &str) {
sandbox
.new_assert_cmd("contract")
.arg("alias")
.arg("add")
.arg("test_contract")
.arg("--id=CA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAXE")
.assert()
.success();
let res = invoke(sandbox, id, "addresse", &json!("test_contract").to_string()).await;
assert_eq!(
res,
"\"CA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAXE\""
);
}

async fn bytes(sandbox: &TestEnv, id: &str) {
invoke_with_roundtrip(sandbox, id, "bytes", json!("7374656c6c6172")).await;
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/crates/soroban-test/tests/it/integration/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use soroban_test::{AssertExt, TestEnv};

use crate::integration::util::{deploy_contract, DeployKind, HELLO_WORLD};

mod operations;
pub mod operations;

#[tokio::test]
async fn simulate() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::integration::{
util::{deploy_contract, DeployKind, HELLO_WORLD},
};

fn test_address(sandbox: &TestEnv) -> String {
pub fn test_address(sandbox: &TestEnv) -> String {
sandbox
.new_assert_cmd("keys")
.arg("address")
Expand Down
11 changes: 7 additions & 4 deletions cmd/crates/soroban-test/tests/it/integration/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,19 @@ pub const CUSTOM_TYPES: &Wasm = &Wasm::Custom("test-wasms", "test_custom_types")
pub const CUSTOM_ACCOUNT: &Wasm = &Wasm::Custom("test-wasms", "test_custom_account");
pub const SWAP: &Wasm = &Wasm::Custom("test-wasms", "test_swap");

pub async fn invoke(sandbox: &TestEnv, id: &str, func: &str, data: &str) -> String {
sandbox
.invoke_with_test(&["--id", id, "--", func, &format!("--{func}"), data])
.await
.unwrap()
}
pub async fn invoke_with_roundtrip<D>(e: &TestEnv, id: &str, func: &str, data: D)
where
D: Display,
{
let data = data.to_string();
println!("{data}");
let res = e
.invoke_with_test(&["--id", id, "--", func, &format!("--{func}"), &data])
.await
.unwrap();
let res = invoke(e, id, func, &data).await;
assert_eq!(res, data);
}

Expand Down
64 changes: 53 additions & 11 deletions cmd/soroban-cli/src/commands/contract/arg_parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,22 @@ use std::convert::TryInto;
use std::ffi::OsString;
use std::fmt::Debug;
use std::path::PathBuf;
use std::str::FromStr;

use clap::value_parser;
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, address, secret};
use crate::config::{
self, address,
sc_address::{self, UnresolvedScAddress},
secret,
};
use soroban_spec_tools::Spec;

#[derive(thiserror::Error, Debug)]
Expand Down Expand Up @@ -47,6 +51,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 @@ -84,14 +92,18 @@ 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();
let mut s = val
.next()
.unwrap()
.to_string_lossy()
.trim_matches('"')
.to_string();
if matches!(i.type_, ScSpecTypeDef::Address) {
let addr: address::Address = s.parse()?;
if let Ok(address) = addr.resolve_muxed_account(&config.locator, None) {
s = address.to_string();
}
if let Ok(key) = addr.resolve_secret(&config.locator) {
signers.push(SigningKey::from_bytes(&key.private_key(None)?.0));
let addr = resolve_address(&s, config)?;
let signer = resolve_signer(&s, config);
s = addr;
if let Some(signer) = signer {
signers.push(signer);
}
}
spec.from_string(&s, &i.type_)
Expand Down Expand Up @@ -125,7 +137,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 +258,33 @@ pub fn output_to_string(
}
Ok(TxnResult::Res(res_str))
}

fn resolve_address(addr_or_alias: &str, config: &config::Args) -> Result<String, Error> {
let sc_address: UnresolvedScAddress = addr_or_alias.parse().unwrap();
let account = match sc_address {
UnresolvedScAddress::Resolved(addr) => addr.to_string(),
addr @ UnresolvedScAddress::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(_) => contract.to_string(),
}
}
};
Ok(account)
}

fn resolve_signer(addr_or_alias: &str, config: &config::Args) -> Option<SigningKey> {
let bytes = if let Ok(a) = stellar_strkey::ed25519::PrivateKey::from_str(addr_or_alias) {
a.0
} else {
config
.locator
.read_identity(addr_or_alias)
.ok()?
.private_key(None)
.ok()?
.0
};
Some(SigningKey::from_bytes(&bytes))
}
2 changes: 1 addition & 1 deletion 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: config::ContractAddress,
pub contract_id: config::UnresolvedContract,
/// Where to write output otherwise stdout is used
#[arg(long, short = 'o')]
pub out_file: Option<std::path::PathBuf>,
Expand Down
2 changes: 1 addition & 1 deletion cmd/soroban-cli/src/commands/contract/info/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub struct Args {
conflicts_with = "wasm",
conflicts_with = "wasm_hash"
)]
pub contract_id: Option<config::ContractAddress>,
pub contract_id: Option<config::UnresolvedContract>,
#[command(flatten)]
pub network: network::Args,
#[command(flatten)]
Expand Down
2 changes: 1 addition & 1 deletion 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: config::ContractAddress,
pub contract_id: config::UnresolvedContract,
// For testing only
#[arg(skip)]
pub wasm: Option<std::path::PathBuf>,
Expand Down
2 changes: 1 addition & 1 deletion 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<config::ContractAddress>,
contract_ids: Vec<config::UnresolvedContract>,
/// 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
2 changes: 1 addition & 1 deletion cmd/soroban-cli/src/commands/keys/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub enum Error {
#[group(skip)]
pub struct Cmd {
/// Name of identity to lookup, default test identity used if not provided
pub name: address::Address,
pub name: address::UnresolvedMuxedAccount,

/// If identity is a seed phrase use this hd path, default is 0
#[arg(long)]
Expand Down
4 changes: 2 additions & 2 deletions cmd/soroban-cli/src/commands/snapshot/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ use crate::{
tx::builder,
utils::get_name_from_stellar_asset_contract_storage,
};
use crate::{config::address::Address, utils::http};
use crate::{config::address::UnresolvedMuxedAccount, utils::http};

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)]
pub enum Output {
Expand Down Expand Up @@ -413,7 +413,7 @@ impl Cmd {
// Resolve an account address to an account id. The address can be a
// G-address or a key name (as in `stellar keys address NAME`).
fn resolve_account(&self, address: &str) -> Option<AccountId> {
let address: Address = address.parse().ok()?;
let address: UnresolvedMuxedAccount = address.parse().ok()?;

Some(AccountId(xdr::PublicKey::PublicKeyTypeEd25519(
match address.resolve_muxed_account(&self.locator, None).ok()? {
Expand Down
2 changes: 1 addition & 1 deletion cmd/soroban-cli/src/commands/tx/op/add/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub struct Args {
visible_alias = "op-source",
env = "STELLAR_OPERATION_SOURCE_ACCOUNT"
)]
pub operation_source_account: Option<address::Address>,
pub operation_source_account: Option<address::UnresolvedMuxedAccount>,
}

impl Args {
Expand Down
Loading

0 comments on commit 93fa181

Please sign in to comment.