From 1ffc25578c1cbc1ef8b99d9651b5cee49a0ce2a8 Mon Sep 17 00:00:00 2001 From: Jane Wang Date: Mon, 16 Sep 2024 16:22:02 -0400 Subject: [PATCH 01/55] Update README (#1607) --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 46c6d6816..c39d883c2 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ This repo is home to the Stellar CLI, the command-line multi-tool for running an ## Table of Contents - [Documentation](#documentation) +- [Cookbook](#cookbook) - [Installation](#installation) - [Installation with Experimental Features](#installation-with-experimental-features) - [Autocomplete](#autocomplete) @@ -23,6 +24,9 @@ This repo is home to the Stellar CLI, the command-line multi-tool for running an For installation options see below, for usage instructions [see the full help docs](FULL_HELP_DOCS.md). +## Cookbook +To understand how to get the most of the Stellar CLI, see the [Stellar CLI Cookbook](https://github.com/stellar/stellar-cli/tree/main/cookbook) for recipes and a collection of resources to teach you how to use the CLI. Examples of recipes included in the CLI cookbook include: send payments, manage contract lifecycle, extend contract instance/storage/wasm, and more. + ## Installation Install the latest version from source: ``` @@ -76,4 +80,5 @@ Find issues to contribute to [here](https://github.com/stellar/stellar-cli/contr ## Additional Developer Resources - Developer Docs CLI Examples: https://developers.stellar.org/docs/smart-contracts/guides/cli -- Video Tutorial: https://developers.stellar.org/meetings/2024/06/27 +- Video Tutorial on `network container`, `keys`, and `contract init`: https://developers.stellar.org/meetings/2024/06/27 +- Video Tutorial on `alias` and `snapshot`: https://developers.stellar.org/meetings/2024/09/12 From 62ae1d7b87ad1eea59749ccd2e3ad52c9ce8608e Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Tue, 17 Sep 2024 14:57:39 -0700 Subject: [PATCH 02/55] Rename contract (#1609) --- cmd/crates/soroban-test/tests/it/build.rs | 2 +- .../contract-init-template/contracts/hello_world/src/lib.rs | 4 ++-- .../contract-init-template/contracts/hello_world/src/test.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/crates/soroban-test/tests/it/build.rs b/cmd/crates/soroban-test/tests/it/build.rs index 19f0bc041..aba6aadf5 100644 --- a/cmd/crates/soroban-test/tests/it/build.rs +++ b/cmd/crates/soroban-test/tests/it/build.rs @@ -72,7 +72,7 @@ fn build_no_package_found() { .failure() .stderr(predicate::eq( "\ -error: package nopkgwiththisname not found +❌ error: package nopkgwiththisname not found ", )); } diff --git a/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/src/lib.rs b/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/src/lib.rs index f3eb78a36..0b701fd82 100644 --- a/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/src/lib.rs +++ b/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/src/lib.rs @@ -2,10 +2,10 @@ use soroban_sdk::{contract, contractimpl, vec, Env, String, Vec}; #[contract] -pub struct HelloContract; +pub struct Contract; #[contractimpl] -impl HelloContract { +impl Contract { pub fn hello(env: Env, to: String) -> Vec { vec![&env, String::from_str(&env, "Hello"), to] } diff --git a/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/src/test.rs b/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/src/test.rs index 4b250446a..af69a7ecb 100644 --- a/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/src/test.rs +++ b/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/src/test.rs @@ -6,8 +6,8 @@ use soroban_sdk::{vec, Env, String}; #[test] fn test() { let env = Env::default(); - let contract_id = env.register_contract(None, HelloContract); - let client = HelloContractClient::new(&env, &contract_id); + let contract_id = env.register_contract(None, Contract); + let client = ContractClient::new(&env, &contract_id); let words = client.hello(&String::from_str(&env, "Dev")); assert_eq!( From 0a0cac0846e1178b4df0ee62f8c752ca70b7b83f Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Wed, 18 Sep 2024 21:39:21 +1000 Subject: [PATCH 03/55] Change output of events in logs from Rust debug to JSON (#1600) --- .../soroban-test/tests/it/integration/hello_world.rs | 10 +++++----- cmd/soroban-cli/src/log/event.rs | 7 +++++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/cmd/crates/soroban-test/tests/it/integration/hello_world.rs b/cmd/crates/soroban-test/tests/it/integration/hello_world.rs index 20b680997..e523efb8f 100644 --- a/cmd/crates/soroban-test/tests/it/integration/hello_world.rs +++ b/cmd/crates/soroban-test/tests/it/integration/hello_world.rs @@ -394,12 +394,12 @@ fn invoke_log(sandbox: &TestEnv, id: &str) { .assert() .success() .stderr(predicates::str::contains( - "INFO contract_event: soroban_cli::log::event: 1: DiagnosticEvent {", + "INFO contract_event: soroban_cli::log::event: 1:", )) - .stderr(predicates::str::contains("StringM(hello)")) + .stderr(predicates::str::contains("hello")) .stderr(predicates::str::contains( - "INFO log_event: soroban_cli::log::event: 2: DiagnosticEvent", + "INFO log_event: soroban_cli::log::event: 2:", )) - .stderr(predicates::str::contains("StringM(hello {})")) - .stderr(predicates::str::contains("StringM(world)")); + .stderr(predicates::str::contains("hello {}")) + .stderr(predicates::str::contains("world")); } diff --git a/cmd/soroban-cli/src/log/event.rs b/cmd/soroban-cli/src/log/event.rs index 8a4fedfbf..4ff7debb9 100644 --- a/cmd/soroban-cli/src/log/event.rs +++ b/cmd/soroban-cli/src/log/event.rs @@ -1,6 +1,7 @@ use tracing::{debug, info, span, Level}; use crate::xdr; +use xdr::WriteXdr; pub fn events(events: &[xdr::DiagnosticEvent]) { for (i, event) in events.iter().enumerate() { @@ -14,10 +15,12 @@ pub fn events(events: &[xdr::DiagnosticEvent]) { let _enter = span.enter(); + let xdr = event.to_xdr_base64(xdr::Limits::none()).unwrap(); + let json = serde_json::to_string(event).unwrap(); if span.metadata().unwrap().level() == &Level::INFO { - info!("{i}: {event:#?}"); + info!("{i}: {xdr} {json}"); } else { - debug!("{i}: {event:#?}"); + debug!("{i}: {xdr} {json}"); } } } From ff57159bb68807c8b2525ad19840edc19d921d80 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Mon, 23 Sep 2024 22:37:38 -0400 Subject: [PATCH 04/55] feat: add `tx sign` (#1590) Co-authored-by: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> --- FULL_HELP_DOCS.md | 19 +++++ Makefile | 4 +- .../soroban-test/tests/it/integration/tx.rs | 32 ++++++- cmd/soroban-cli/src/commands/tx/mod.rs | 6 ++ cmd/soroban-cli/src/commands/tx/sign.rs | 45 ++++++++++ cmd/soroban-cli/src/config/locator.rs | 8 ++ cmd/soroban-cli/src/config/mod.rs | 14 ++-- cmd/soroban-cli/src/config/secret.rs | 21 ++++- cmd/soroban-cli/src/config/sign_with.rs | 53 ++++++++++++ cmd/soroban-cli/src/print.rs | 3 +- cmd/soroban-cli/src/signer.rs | 84 +++++++++++++------ cmd/soroban-cli/src/utils.rs | 30 +------ 12 files changed, 258 insertions(+), 61 deletions(-) create mode 100644 cmd/soroban-cli/src/commands/tx/sign.rs create mode 100644 cmd/soroban-cli/src/config/sign_with.rs diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index b7480cb5e..9ba32c35a 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -1291,6 +1291,7 @@ Sign, Simulate, and Send transactions * `simulate` — Simulate a transaction envelope from stdin * `hash` — Calculate the hash of a transaction envelope from stdin +* `sign` — Sign a transaction envelope appending the signature to the envelope @@ -1326,6 +1327,24 @@ Calculate the hash of a transaction envelope from stdin +## `stellar tx sign` + +Sign a transaction envelope appending the signature to the envelope + +**Usage:** `stellar tx sign [OPTIONS]` + +###### **Options:** + +* `--sign-with-key ` — Sign with a local key. Can be an identity (--sign-with-key alice), a secret key (--sign-with-key SC36…), or a seed phrase (--sign-with-key "kite urban…"). If using seed phrase, `--hd-path` defaults to the `0` path +* `--hd-path ` — If using a seed phrase to sign, sets which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` +* `--rpc-url ` — RPC server endpoint +* `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server +* `--network ` — Name of network to use from config +* `--global` — Use global config +* `--config-dir ` — Location of config directory, default is "." + + + ## `stellar xdr` Decode and encode XDR diff --git a/Makefile b/Makefile index 7e307b16c..6d55aacc4 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,8 @@ endif REPOSITORY_BRANCH := "$(shell git rev-parse --abbrev-ref HEAD)" BUILD_TIMESTAMP ?= $(shell date '+%Y-%m-%dT%H:%M:%S') +SOROBAN_PORT?=8000 + # The following works around incompatibility between the rust and the go linkers - # the rust would generate an object file with min-version of 13.0 where-as the go # compiler would generate a binary compatible with 12.3 and up. To align these @@ -53,7 +55,7 @@ test: build-test cargo test e2e-test: - cargo test --test it -- --ignored + cargo test --features it --test it -- integration check: cargo clippy --all-targets diff --git a/cmd/crates/soroban-test/tests/it/integration/tx.rs b/cmd/crates/soroban-test/tests/it/integration/tx.rs index bcb880b18..c3d2cebd6 100644 --- a/cmd/crates/soroban-test/tests/it/integration/tx.rs +++ b/cmd/crates/soroban-test/tests/it/integration/tx.rs @@ -4,7 +4,7 @@ use soroban_test::{AssertExt, TestEnv}; use crate::integration::util::{deploy_contract, DeployKind, HELLO_WORLD}; #[tokio::test] -async fn txn_simulate() { +async fn simulate() { let sandbox = &TestEnv::new(); let xdr_base64_build_only = deploy_contract(sandbox, HELLO_WORLD, DeployKind::BuildOnly).await; let xdr_base64_sim_only = deploy_contract(sandbox, HELLO_WORLD, DeployKind::SimOnly).await; @@ -49,3 +49,33 @@ async fn txn_hash() { assert_eq!(hash.trim(), expected_hash); } + +#[tokio::test] +async fn build_simulate_sign_send() { + let sandbox = &TestEnv::new(); + sandbox + .new_assert_cmd("contract") + .arg("install") + .args(["--wasm", HELLO_WORLD.path().as_os_str().to_str().unwrap()]) + .assert() + .success(); + + let tx_simulated = deploy_contract(sandbox, HELLO_WORLD, DeployKind::SimOnly).await; + dbg!("{tx_simulated}"); + + let tx_signed = sandbox + .new_assert_cmd("tx") + .arg("sign") + .arg("--sign-with-key=test") + .write_stdin(tx_simulated.as_bytes()) + .assert() + .success() + .stdout_as_str(); + dbg!("{tx_signed}"); + + // TODO: Replace with calling tx send when that command is added. + let tx_signed = TransactionEnvelope::from_xdr_base64(tx_signed, Limits::none()).unwrap(); + let client = soroban_rpc::Client::new(&sandbox.rpc_url).unwrap(); + let rpc_result = client.send_transaction_polling(&tx_signed).await.unwrap(); + assert_eq!(rpc_result.status, "SUCCESS"); +} diff --git a/cmd/soroban-cli/src/commands/tx/mod.rs b/cmd/soroban-cli/src/commands/tx/mod.rs index 59f07228a..5f0f90c4c 100644 --- a/cmd/soroban-cli/src/commands/tx/mod.rs +++ b/cmd/soroban-cli/src/commands/tx/mod.rs @@ -3,6 +3,7 @@ use clap::Parser; use super::global; pub mod hash; +pub mod sign; pub mod simulate; pub mod xdr; @@ -12,6 +13,8 @@ pub enum Cmd { Simulate(simulate::Cmd), /// Calculate the hash of a transaction envelope from stdin Hash(hash::Cmd), + /// Sign a transaction envelope appending the signature to the envelope + Sign(sign::Cmd), } #[derive(thiserror::Error, Debug)] @@ -22,6 +25,8 @@ pub enum Error { /// An error during hash calculation #[error(transparent)] Hash(#[from] hash::Error), + #[error(transparent)] + Sign(#[from] sign::Error), } impl Cmd { @@ -29,6 +34,7 @@ impl Cmd { match self { Cmd::Simulate(cmd) => cmd.run(global_args).await?, Cmd::Hash(cmd) => cmd.run(global_args)?, + Cmd::Sign(cmd) => cmd.run(global_args).await?, }; Ok(()) } diff --git a/cmd/soroban-cli/src/commands/tx/sign.rs b/cmd/soroban-cli/src/commands/tx/sign.rs new file mode 100644 index 000000000..92fef7417 --- /dev/null +++ b/cmd/soroban-cli/src/commands/tx/sign.rs @@ -0,0 +1,45 @@ +use crate::{ + commands::global, + config::{locator, network, sign_with}, + xdr::{self, Limits, WriteXdr}, +}; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + XdrArgs(#[from] super::xdr::Error), + #[error(transparent)] + Network(#[from] network::Error), + #[error(transparent)] + Locator(#[from] locator::Error), + #[error(transparent)] + SignWith(#[from] sign_with::Error), + #[error(transparent)] + Xdr(#[from] xdr::Error), +} + +#[derive(Debug, clap::Parser, Clone)] +#[group(skip)] +pub struct Cmd { + #[command(flatten)] + pub sign_with: sign_with::Args, + #[command(flatten)] + pub network: network::Args, + #[command(flatten)] + pub locator: locator::Args, +} + +impl Cmd { + #[allow(clippy::unused_async)] + pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { + let tx_env = super::xdr::tx_envelope_from_stdin()?; + let tx_env_signed = self.sign_with.sign_tx_env( + tx_env, + &self.locator, + &self.network.get(&self.locator)?, + global_args.quiet, + )?; + println!("{}", tx_env_signed.to_xdr_base64(Limits::none())?); + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/config/locator.rs b/cmd/soroban-cli/src/config/locator.rs index 86d1004f6..bc167c977 100644 --- a/cmd/soroban-cli/src/config/locator.rs +++ b/cmd/soroban-cli/src/config/locator.rs @@ -217,6 +217,14 @@ impl Args { KeyType::Identity.read_with_global(name, &self.local_config()?) } + pub fn key(&self, key_or_name: &str) -> Result { + if let Ok(signer) = key_or_name.parse::() { + Ok(signer) + } else { + self.read_identity(key_or_name) + } + } + pub fn read_network(&self, name: &str) -> Result { let res = KeyType::Network.read_with_global(name, &self.local_config()?); if let Err(Error::ConfigMissing(_, _)) = &res { diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index ef286f6a0..48eb6dbe8 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -6,7 +6,8 @@ use serde::{Deserialize, Serialize}; use soroban_rpc::Client; use crate::{ - signer, + print::Print, + signer::{self, LocalKey, Signer, SignerKind}, xdr::{Transaction, TransactionEnvelope}, Pwd, }; @@ -18,6 +19,7 @@ pub mod data; pub mod locator; pub mod network; pub mod secret; +pub mod sign_with; pub mod upgrade_check; #[derive(thiserror::Error, Debug)] @@ -65,10 +67,12 @@ impl Args { #[allow(clippy::unused_async)] pub async fn sign(&self, tx: Transaction) -> Result { let key = self.key_pair()?; - let Network { - network_passphrase, .. - } = &self.get_network()?; - Ok(signer::sign_tx(&key, &tx, network_passphrase)?) + let network = &self.get_network()?; + let signer = Signer { + kind: SignerKind::Local(LocalKey { key }), + printer: Print::new(false), + }; + Ok(signer.sign_tx(tx, network)?) } pub async fn sign_soroban_authorizations( diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index b5b1dd747..ff1fb7b72 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -3,7 +3,11 @@ use serde::{Deserialize, Serialize}; use std::{io::Write, str::FromStr}; use stellar_strkey::ed25519::{PrivateKey, PublicKey}; -use crate::utils; +use crate::print::Print; +use crate::{ + signer::{self, LocalKey, Signer, SignerKind}, + utils, +}; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -21,6 +25,8 @@ pub enum Error { Ed25519(#[from] ed25519_dalek::SignatureError), #[error("Invalid address {0}")] InvalidAddress(String), + #[error(transparent)] + Signer(#[from] signer::Error), } #[derive(Debug, clap::Args, Clone)] @@ -120,6 +126,19 @@ impl Secret { )?) } + pub fn signer(&self, index: Option, quiet: bool) -> Result { + let kind = match self { + Secret::SecretKey { .. } | Secret::SeedPhrase { .. } => { + let key = self.key_pair(index)?; + SignerKind::Local(LocalKey { key }) + } + }; + Ok(Signer { + kind, + printer: Print::new(quiet), + }) + } + pub fn key_pair(&self, index: Option) -> Result { Ok(utils::into_signing_key(&self.private_key(index)?)) } diff --git a/cmd/soroban-cli/src/config/sign_with.rs b/cmd/soroban-cli/src/config/sign_with.rs new file mode 100644 index 000000000..685945194 --- /dev/null +++ b/cmd/soroban-cli/src/config/sign_with.rs @@ -0,0 +1,53 @@ +use crate::{signer, xdr::TransactionEnvelope}; +use clap::arg; + +use super::{ + locator, + network::{self, Network}, + secret, +}; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Network(#[from] network::Error), + #[error(transparent)] + Signer(#[from] signer::Error), + #[error(transparent)] + Secret(#[from] secret::Error), + #[error(transparent)] + Locator(#[from] locator::Error), + #[error(transparent)] + Rpc(#[from] soroban_rpc::Error), + #[error("No sign with key provided")] + NoSignWithKey, + #[error(transparent)] + StrKey(#[from] stellar_strkey::DecodeError), +} + +#[derive(Debug, clap::Args, Clone, Default)] +#[group(skip)] +pub struct Args { + /// Sign with a local key. Can be an identity (--sign-with-key alice), a secret key (--sign-with-key SC36…), or a seed phrase (--sign-with-key "kite urban…"). If using seed phrase, `--hd-path` defaults to the `0` path. + #[arg(long, env = "STELLAR_SIGN_WITH_KEY")] + pub sign_with_key: Option, + + #[arg(long, requires = "sign_with_key")] + /// If using a seed phrase to sign, sets which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` + pub hd_path: Option, +} + +impl Args { + pub fn sign_tx_env( + &self, + tx: TransactionEnvelope, + locator: &locator::Args, + network: &Network, + quiet: bool, + ) -> Result { + let key_or_name = self.sign_with_key.as_deref().ok_or(Error::NoSignWithKey)?; + let secret = locator.key(key_or_name)?; + let signer = secret.signer(self.hd_path, quiet)?; + Ok(signer.sign_tx_env(tx, network)?) + } +} diff --git a/cmd/soroban-cli/src/print.rs b/cmd/soroban-cli/src/print.rs index f772eb649..5b98687bd 100644 --- a/cmd/soroban-cli/src/print.rs +++ b/cmd/soroban-cli/src/print.rs @@ -3,8 +3,7 @@ use std::{env, fmt::Display}; use soroban_env_host::xdr::{Error as XdrError, Transaction}; use crate::{ - config::network::Network, - utils::{explorer_url_for_transaction, transaction_hash}, + config::network::Network, utils::explorer_url_for_transaction, utils::transaction_hash, }; const TERMS: &[&str] = &["Apple_Terminal", "vscode"]; diff --git a/cmd/soroban-cli/src/signer.rs b/cmd/soroban-cli/src/signer.rs index 580a61a5e..36ae55053 100644 --- a/cmd/soroban-cli/src/signer.rs +++ b/cmd/soroban-cli/src/signer.rs @@ -1,4 +1,4 @@ -use ed25519_dalek::ed25519::signature::Signer; +use ed25519_dalek::ed25519::signature::Signer as _; use sha2::{Digest, Sha256}; use soroban_env_host::xdr::{ @@ -6,10 +6,11 @@ use soroban_env_host::xdr::{ InvokeHostFunctionOp, Limits, Operation, OperationBody, PublicKey, ScAddress, ScMap, ScSymbol, ScVal, Signature, SignatureHint, SorobanAddressCredentials, SorobanAuthorizationEntry, SorobanAuthorizedFunction, SorobanCredentials, Transaction, TransactionEnvelope, - TransactionSignaturePayload, TransactionSignaturePayloadTaggedTransaction, - TransactionV1Envelope, Uint256, WriteXdr, + TransactionV1Envelope, Uint256, VecM, WriteXdr, }; +use crate::{config::network::Network, print::Print, utils::transaction_hash}; + #[derive(thiserror::Error, Debug)] pub enum Error { #[error("Contract addresses are not supported to sign auth entries {address}")] @@ -24,6 +25,8 @@ pub enum Error { UserCancelledSigning, #[error(transparent)] Xdr(#[from] xdr::Error), + #[error("Only Transaction envelope V1 type is supported")] + UnsupportedTransactionEnvelopeType, } fn requires_auth(txn: &Transaction) -> Option { @@ -189,29 +192,62 @@ fn sign_soroban_authorization_entry( Ok(auth) } -pub fn sign_tx( - key: &ed25519_dalek::SigningKey, - tx: &Transaction, - network_passphrase: &str, -) -> Result { - let tx_hash = hash(tx, network_passphrase)?; - let tx_signature = key.sign(&tx_hash); +pub struct Signer { + pub kind: SignerKind, + pub printer: Print, +} - let decorated_signature = DecoratedSignature { - hint: SignatureHint(key.verifying_key().to_bytes()[28..].try_into()?), - signature: Signature(tx_signature.to_bytes().try_into()?), - }; +#[allow(clippy::module_name_repetitions)] +pub enum SignerKind { + Local(LocalKey), +} - Ok(TransactionEnvelope::Tx(TransactionV1Envelope { - tx: tx.clone(), - signatures: [decorated_signature].try_into()?, - })) +impl Signer { + pub fn sign_tx( + &self, + tx: Transaction, + network: &Network, + ) -> Result { + let tx_env = TransactionEnvelope::Tx(TransactionV1Envelope { + tx, + signatures: VecM::default(), + }); + self.sign_tx_env(tx_env, network) + } + + pub fn sign_tx_env( + &self, + tx_env: TransactionEnvelope, + network: &Network, + ) -> Result { + match tx_env { + TransactionEnvelope::Tx(TransactionV1Envelope { tx, signatures }) => { + let tx_hash = transaction_hash(&tx, &network.network_passphrase)?; + self.printer + .infoln(format!("Signing transaction: {}", hex::encode(tx_hash),)); + let decorated_signature = match &self.kind { + SignerKind::Local(key) => key.sign_tx_hash(tx_hash)?, + }; + let mut sigs = signatures.into_vec(); + sigs.push(decorated_signature); + Ok(TransactionEnvelope::Tx(TransactionV1Envelope { + tx, + signatures: sigs.try_into()?, + })) + } + _ => Err(Error::UnsupportedTransactionEnvelopeType), + } + } } -pub fn hash(tx: &Transaction, network_passphrase: &str) -> Result<[u8; 32], xdr::Error> { - let signature_payload = TransactionSignaturePayload { - network_id: Hash(Sha256::digest(network_passphrase).into()), - tagged_transaction: TransactionSignaturePayloadTaggedTransaction::Tx(tx.clone()), - }; - Ok(Sha256::digest(signature_payload.to_xdr(Limits::none())?).into()) +pub struct LocalKey { + pub key: ed25519_dalek::SigningKey, +} + +impl LocalKey { + pub fn sign_tx_hash(&self, tx_hash: [u8; 32]) -> Result { + let hint = SignatureHint(self.key.verifying_key().to_bytes()[28..].try_into()?); + let signature = Signature(self.key.sign(&tx_hash).to_bytes().to_vec().try_into()?); + Ok(DecoratedSignature { hint, signature }) + } } diff --git a/cmd/soroban-cli/src/utils.rs b/cmd/soroban-cli/src/utils.rs index f8ebb8b37..f5827f75b 100644 --- a/cmd/soroban-cli/src/utils.rs +++ b/cmd/soroban-cli/src/utils.rs @@ -1,13 +1,11 @@ -use ed25519_dalek::Signer; use phf::phf_map; use sha2::{Digest, Sha256}; use stellar_strkey::ed25519::PrivateKey; use soroban_env_host::xdr::{ - Asset, ContractIdPreimage, DecoratedSignature, Error as XdrError, Hash, HashIdPreimage, - HashIdPreimageContractId, Limits, ScMap, ScMapEntry, ScVal, Signature, SignatureHint, - Transaction, TransactionEnvelope, TransactionSignaturePayload, - TransactionSignaturePayloadTaggedTransaction, TransactionV1Envelope, WriteXdr, + Asset, ContractIdPreimage, Error as XdrError, Hash, HashIdPreimage, HashIdPreimageContractId, + Limits, ScMap, ScMapEntry, ScVal, Transaction, TransactionSignaturePayload, + TransactionSignaturePayloadTaggedTransaction, WriteXdr, }; pub use soroban_spec_tools::contract as contract_spec; @@ -49,28 +47,6 @@ pub fn explorer_url_for_contract(network: &Network, contract_id: &str) -> Option .map(|base_url| format!("{base_url}/contract/{contract_id}")) } -/// # Errors -/// -/// Might return an error -pub fn sign_transaction( - key: &ed25519_dalek::SigningKey, - tx: &Transaction, - network_passphrase: &str, -) -> Result { - let tx_hash = transaction_hash(tx, network_passphrase)?; - let tx_signature = key.sign(&tx_hash); - - let decorated_signature = DecoratedSignature { - hint: SignatureHint(key.verifying_key().to_bytes()[28..].try_into()?), - signature: Signature(tx_signature.to_bytes().try_into()?), - }; - - Ok(TransactionEnvelope::Tx(TransactionV1Envelope { - tx: tx.clone(), - signatures: vec![decorated_signature].try_into()?, - })) -} - /// # Errors /// /// Might return an error From ce3190925d780dc674de99851e895d77f59a39da Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 24 Sep 2024 00:42:19 -0400 Subject: [PATCH 05/55] feat: tx send (#1592) Co-authored-by: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> --- FULL_HELP_DOCS.md | 17 ++++++ .../soroban-test/tests/it/integration/tx.rs | 16 +++-- cmd/soroban-cli/src/commands/tx/mod.rs | 8 ++- cmd/soroban-cli/src/commands/tx/send.rs | 59 +++++++++++++++++++ 4 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 cmd/soroban-cli/src/commands/tx/send.rs diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index 9ba32c35a..43e8370f6 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -1292,6 +1292,7 @@ Sign, Simulate, and Send transactions * `simulate` — Simulate a transaction envelope from stdin * `hash` — Calculate the hash of a transaction envelope from stdin * `sign` — Sign a transaction envelope appending the signature to the envelope +* `send` — Send a transaction envelope to the network @@ -1345,6 +1346,22 @@ Sign a transaction envelope appending the signature to the envelope +## `stellar tx send` + +Send a transaction envelope to the network + +**Usage:** `stellar tx send [OPTIONS]` + +###### **Options:** + +* `--rpc-url ` — RPC server endpoint +* `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server +* `--network ` — Name of network to use from config +* `--global` — Use global config +* `--config-dir ` — Location of config directory, default is "." + + + ## `stellar xdr` Decode and encode XDR diff --git a/cmd/crates/soroban-test/tests/it/integration/tx.rs b/cmd/crates/soroban-test/tests/it/integration/tx.rs index c3d2cebd6..34c6f086d 100644 --- a/cmd/crates/soroban-test/tests/it/integration/tx.rs +++ b/cmd/crates/soroban-test/tests/it/integration/tx.rs @@ -1,7 +1,8 @@ +use soroban_rpc::GetTransactionResponse; use soroban_sdk::xdr::{Limits, ReadXdr, TransactionEnvelope, WriteXdr}; use soroban_test::{AssertExt, TestEnv}; -use crate::integration::util::{deploy_contract, DeployKind, HELLO_WORLD}; +use crate::integration::util::{deploy_contract, deploy_hello, DeployKind, HELLO_WORLD}; #[tokio::test] async fn simulate() { @@ -73,9 +74,12 @@ async fn build_simulate_sign_send() { .stdout_as_str(); dbg!("{tx_signed}"); - // TODO: Replace with calling tx send when that command is added. - let tx_signed = TransactionEnvelope::from_xdr_base64(tx_signed, Limits::none()).unwrap(); - let client = soroban_rpc::Client::new(&sandbox.rpc_url).unwrap(); - let rpc_result = client.send_transaction_polling(&tx_signed).await.unwrap(); - assert_eq!(rpc_result.status, "SUCCESS"); + let output = sandbox + .new_assert_cmd("tx") + .arg("send") + .write_stdin(tx_signed.as_bytes()) + .assert() + .success() + .stdout_as_str(); + assert_eq!(output, "SUCCESS"); } diff --git a/cmd/soroban-cli/src/commands/tx/mod.rs b/cmd/soroban-cli/src/commands/tx/mod.rs index 5f0f90c4c..587a75fda 100644 --- a/cmd/soroban-cli/src/commands/tx/mod.rs +++ b/cmd/soroban-cli/src/commands/tx/mod.rs @@ -3,6 +3,7 @@ use clap::Parser; use super::global; pub mod hash; +pub mod send; pub mod sign; pub mod simulate; pub mod xdr; @@ -15,18 +16,20 @@ pub enum Cmd { Hash(hash::Cmd), /// Sign a transaction envelope appending the signature to the envelope Sign(sign::Cmd), + /// Send a transaction envelope to the network + Send(send::Cmd), } #[derive(thiserror::Error, Debug)] pub enum Error { - /// An error during the simulation #[error(transparent)] Simulate(#[from] simulate::Error), - /// An error during hash calculation #[error(transparent)] Hash(#[from] hash::Error), #[error(transparent)] Sign(#[from] sign::Error), + #[error(transparent)] + Send(#[from] send::Error), } impl Cmd { @@ -35,6 +38,7 @@ impl Cmd { Cmd::Simulate(cmd) => cmd.run(global_args).await?, Cmd::Hash(cmd) => cmd.run(global_args)?, Cmd::Sign(cmd) => cmd.run(global_args).await?, + Cmd::Send(cmd) => cmd.run(global_args).await?, }; Ok(()) } diff --git a/cmd/soroban-cli/src/commands/tx/send.rs b/cmd/soroban-cli/src/commands/tx/send.rs new file mode 100644 index 000000000..c3856114d --- /dev/null +++ b/cmd/soroban-cli/src/commands/tx/send.rs @@ -0,0 +1,59 @@ +use async_trait::async_trait; +use soroban_rpc::GetTransactionResponse; + +use crate::commands::{global, NetworkRunnable}; +use crate::config::{self, locator, network}; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + XdrArgs(#[from] super::xdr::Error), + #[error(transparent)] + Network(#[from] network::Error), + #[error(transparent)] + Config(#[from] config::Error), + #[error(transparent)] + Rpc(#[from] crate::rpc::Error), + #[error(transparent)] + SerdeJson(#[from] serde_json::Error), +} + +#[derive(Debug, clap::Parser, Clone)] +#[group(skip)] +/// Command to send a transaction envelope to the network +/// e.g. `cat file.txt | soroban tx send` +pub struct Cmd { + #[clap(flatten)] + pub network: network::Args, + #[clap(flatten)] + pub locator: locator::Args, +} + +impl Cmd { + pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { + let response = self.run_against_rpc_server(Some(global_args), None).await?; + println!("{}", serde_json::to_string_pretty(&response)?); + Ok(()) + } +} + +#[async_trait] +impl NetworkRunnable for Cmd { + type Error = Error; + + type Result = GetTransactionResponse; + async fn run_against_rpc_server( + &self, + _: Option<&global::Args>, + config: Option<&config::Args>, + ) -> Result { + let network = if let Some(config) = config { + config.get_network()? + } else { + self.network.get(&self.locator)? + }; + let client = crate::rpc::Client::new(&network.rpc_url)?; + let tx_env = super::xdr::tx_envelope_from_stdin()?; + Ok(client.send_transaction_polling(&tx_env).await?) + } +} From ac5cc57559d69c5b78eed55891d5b0300f1f14b2 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Tue, 24 Sep 2024 00:52:47 -0400 Subject: [PATCH 06/55] chore: update testcontainers (#1602) Co-authored-by: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> --- Cargo.lock | 719 +++++++++++++++--- Cargo.toml | 16 +- cmd/crates/stellar-ledger/Cargo.toml | 2 +- .../tests/test/emulator_tests.rs | 87 +-- .../stellar-ledger/tests/utils/speculos.rs | 78 +- 5 files changed, 690 insertions(+), 212 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 788fc8e59..c683baba4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -386,7 +386,7 @@ checksum = "531b97fb4cd3dfdce92c35dedbfdc1f0b9d8091c8ca943d6dae340ef5012d514" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -542,14 +542,20 @@ dependencies = [ "futures-core", "futures-util", "hex", + "home", "http 1.1.0", "http-body-util", "hyper 1.4.1", "hyper-named-pipe", + "hyper-rustls 0.26.0", "hyper-util", "hyperlocal-next", "log", "pin-project-lite", + "rustls 0.22.4", + "rustls-native-certs 0.7.3", + "rustls-pemfile 2.1.3", + "rustls-pki-types", "serde", "serde_derive", "serde_json", @@ -621,7 +627,7 @@ dependencies = [ "num-bigint", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -742,7 +748,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -944,7 +950,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" dependencies = [ "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -984,7 +990,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -1011,7 +1017,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -1028,7 +1034,7 @@ checksum = "b45506e3c66512b0a65d291a6b452128b7b1dd9841e20d1e151addbd2c00ea50" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -1052,7 +1058,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -1063,9 +1069,15 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.39", + "syn 2.0.77", ] +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + [[package]] name = "der" version = "0.7.9" @@ -1094,7 +1106,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -1148,6 +1160,15 @@ dependencies = [ "dirs-sys 0.3.7", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys 0.4.1", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -1198,6 +1219,17 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "docker_credential" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31951f49556e34d90ed28342e1df7e1cb7a229c4cab0aecc627b5d91edd41d07" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] + [[package]] name = "dotenvy" version = "0.15.7" @@ -1323,6 +1355,18 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enum-as-inner" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "env_filter" version = "0.1.2" @@ -1606,7 +1650,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -1719,7 +1763,7 @@ dependencies = [ "gix-worktree-state", "once_cell", "parking_lot", - "reqwest", + "reqwest 0.11.27", "smallvec", "thiserror", ] @@ -2031,7 +2075,7 @@ checksum = "999ce923619f88194171a67fb3e6d613653b8d4d6078b529b15a765da0edcc17" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -2331,7 +2375,7 @@ dependencies = [ "gix-quote", "gix-sec", "gix-url", - "reqwest", + "reqwest 0.11.27", "thiserror", ] @@ -2495,6 +2539,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "h2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap 2.3.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -2555,6 +2618,51 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hickory-proto" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07698b8420e2f0d6447a436ba999ec85d8fbf2a398bbd737b82cac4a2e96e512" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna 0.4.0", + "ipnet", + "once_cell", + "rand", + "thiserror", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "hickory-resolver" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28757f23aa75c98f254cf0405e6d8c25b831b32921b050a66692427679b1f243" +dependencies = [ + "cfg-if", + "futures-util", + "hickory-proto", + "ipconfig", + "lru-cache", + "once_cell", + "parking_lot", + "rand", + "resolv-conf", + "smallvec", + "thiserror", + "tokio", + "tracing", +] + [[package]] name = "hidapi" version = "1.5.0" @@ -2595,6 +2703,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + [[package]] name = "http" version = "0.2.12" @@ -2707,7 +2826,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", "httparse", @@ -2730,6 +2849,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", + "h2 0.4.6", "http 1.1.0", "http-body 1.0.1", "httparse", @@ -2766,9 +2886,47 @@ dependencies = [ "hyper 0.14.30", "log", "rustls 0.21.12", - "rustls-native-certs", + "rustls-native-certs 0.6.3", + "tokio", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "hyper-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.4.1", + "hyper-util", + "log", + "rustls 0.22.4", + "rustls-native-certs 0.7.3", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.25.0", + "tower-service", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.4.1", + "hyper-util", + "rustls 0.23.12", + "rustls-native-certs 0.8.0", + "rustls-pki-types", "tokio", - "tokio-rustls", + "tokio-rustls 0.26.0", + "tower-service", + "webpki-roots 0.26.3", ] [[package]] @@ -2848,6 +3006,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "idna" version = "0.5.0" @@ -2952,6 +3120,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "ipconfig" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" +dependencies = [ + "socket2 0.5.7", + "widestring", + "windows-sys 0.48.0", + "winreg", +] + [[package]] name = "ipnet" version = "2.9.0" @@ -3033,7 +3213,7 @@ checksum = "5f80c17f62c7653ce767e3d7288b793dfec920f97067ceb189ebdd3570f2bc20" dependencies = [ "async-trait", "hyper 0.14.30", - "hyper-rustls", + "hyper-rustls 0.24.2", "jsonrpsee-core", "jsonrpsee-types", "serde", @@ -3209,6 +3389,12 @@ dependencies = [ "cc", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "linux-raw-sys" version = "0.3.8" @@ -3240,6 +3426,21 @@ dependencies = [ "value-bag", ] +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + [[package]] name = "matchers" version = "0.1.0" @@ -3257,7 +3458,7 @@ checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -3372,7 +3573,7 @@ checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -3447,7 +3648,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -3531,6 +3732,31 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "parse-display" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914a1c2265c98e2446911282c6ac86d8524f495792c38c5bd884f80499c7538a" +dependencies = [ + "parse-display-derive", + "regex", + "regex-syntax 0.8.4", +] + +[[package]] +name = "parse-display-derive" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ae7800a4c974efd12df917266338e79a7a74415173caf7e70aa0a0707345281" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "regex-syntax 0.8.4", + "structmeta", + "syn 2.0.77", +] + [[package]] name = "paste" version = "1.0.15" @@ -3598,7 +3824,7 @@ dependencies = [ "phf_shared 0.11.2", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -3642,7 +3868,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -3794,7 +4020,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ "proc-macro2", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -3808,9 +4034,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -3821,11 +4047,65 @@ version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "744a264d26b88a6a7e37cbad97953fa233b94d585236310bcbc88474b4092d79" +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quinn" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash 2.0.0", + "rustls 0.23.12", + "socket2 0.5.7", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +dependencies = [ + "bytes", + "rand", + "ring", + "rustc-hash 2.0.0", + "rustls 0.23.12", + "slab", + "thiserror", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-udp" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bffec3605b73c6f1754535084a85229fa8a30f86014e6c81aeec4abb68b0285" +dependencies = [ + "libc", + "once_cell", + "socket2 0.5.7", + "tracing", + "windows-sys 0.52.0", +] + [[package]] name = "quote" -version = "1.0.33" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -3950,11 +4230,11 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2", + "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", "hyper 0.14.30", - "hyper-rustls", + "hyper-rustls 0.24.2", "hyper-tls", "ipnet", "js-sys", @@ -3965,15 +4245,15 @@ dependencies = [ "percent-encoding", "pin-project-lite", "rustls 0.21.12", - "rustls-pemfile", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-native-tls", - "tokio-rustls", + "tokio-rustls 0.24.1", "tower-service", "url", "wasm-bindgen", @@ -3983,6 +4263,62 @@ dependencies = [ "winreg", ] +[[package]] +name = "reqwest" +version = "0.12.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.4.6", + "hickory-resolver", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-rustls 0.27.3", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls 0.23.12", + "rustls-native-certs 0.7.3", + "rustls-pemfile 2.1.3", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "tokio", + "tokio-rustls 0.26.0", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots 0.26.3", + "windows-registry", +] + +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error", +] + [[package]] name = "rfc6979" version = "0.4.0" @@ -4049,7 +4385,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.39", + "syn 2.0.77", "walkdir", ] @@ -4075,6 +4411,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + [[package]] name = "rustc_version" version = "0.4.0" @@ -4123,6 +4465,20 @@ dependencies = [ "sct", ] +[[package]] +name = "rustls" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +dependencies = [ + "log", + "ring", + "rustls-pki-types", + "rustls-webpki 0.102.6", + "subtle", + "zeroize", +] + [[package]] name = "rustls" version = "0.23.12" @@ -4145,7 +4501,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", - "rustls-pemfile", + "rustls-pemfile 1.0.4", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-native-certs" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +dependencies = [ + "openssl-probe", + "rustls-pemfile 2.1.3", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" +dependencies = [ + "openssl-probe", + "rustls-pemfile 2.1.3", + "rustls-pki-types", "schannel", "security-framework", ] @@ -4159,6 +4541,16 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pemfile" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +dependencies = [ + "base64 0.22.1", + "rustls-pki-types", +] + [[package]] name = "rustls-pki-types" version = "1.8.0" @@ -4246,7 +4638,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -4329,7 +4721,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cec6914e06f503f83e431e1762c82003c5233b1dffb6185e51e4c40dd1c26eaa" dependencies = [ "slipped10", - "stellar-strkey", + "stellar-strkey 0.0.8", "thiserror", "tiny-bip39", ] @@ -4362,7 +4754,7 @@ checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -4373,7 +4765,7 @@ checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -4405,7 +4797,7 @@ checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -4456,7 +4848,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -4481,7 +4873,7 @@ checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -4663,14 +5055,14 @@ dependencies = [ [[package]] name = "soroban-builtin-sdk-macros" -version = "21.2.0" +version = "21.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44877373b3dc6c662377cb1600e3a62706d75e484b6064f9cd22e467c676b159" +checksum = "2f57a68ef8777e28e274de0f3a88ad9a5a41d9a2eb461b4dd800b086f0e83b80" dependencies = [ "itertools 0.11.0", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -4692,7 +5084,7 @@ dependencies = [ "crate-git-revision 0.0.4", "csv", "directories", - "dirs", + "dirs 4.0.0", "dotenvy", "ed25519-dalek 2.1.1", "ethnum", @@ -4736,7 +5128,7 @@ dependencies = [ "soroban-spec-tools", "soroban-spec-typescript", "stellar-rpc-client", - "stellar-strkey", + "stellar-strkey 0.0.11", "stellar-xdr", "strsim 0.10.0", "strum 0.17.1", @@ -4762,9 +5154,9 @@ dependencies = [ [[package]] name = "soroban-env-common" -version = "21.2.0" +version = "21.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "590add16843a61b01844e19e89bccaaee6aa21dc76809017b0662c17dc139ee9" +checksum = "2fd1c89463835fe6da996318156d39f424b4f167c725ec692e5a7a2d4e694b3d" dependencies = [ "arbitrary", "crate-git-revision 0.0.6", @@ -4781,9 +5173,9 @@ dependencies = [ [[package]] name = "soroban-env-guest" -version = "21.2.0" +version = "21.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ec8dc43acdd6c7e7b371acf44fc1a7dac24934ae3b2f05fafd618818548176" +checksum = "6bfb2536811045d5cd0c656a324cbe9ce4467eb734c7946b74410d90dea5d0ce" dependencies = [ "soroban-env-common", "static_assertions", @@ -4791,9 +5183,9 @@ dependencies = [ [[package]] name = "soroban-env-host" -version = "21.2.0" +version = "21.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e25aaffe0c62eb65e0e349f725b4b8b13ad0764d78a15aab5bbccb5c4797726" +checksum = "2b7a32c28f281c423189f1298960194f0e0fc4eeb72378028171e556d8cd6160" dependencies = [ "backtrace", "curve25519-dalek 4.1.3", @@ -4818,15 +5210,15 @@ dependencies = [ "soroban-env-common", "soroban-wasmi", "static_assertions", - "stellar-strkey", + "stellar-strkey 0.0.8", "wasmparser 0.116.1", ] [[package]] name = "soroban-env-macros" -version = "21.2.0" +version = "21.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e16b761459fdf3c4b62b24df3941498d14e5246e6fadfb4774ed8114d243aa4" +checksum = "242926fe5e0d922f12d3796cd7cd02dd824e5ef1caa088f45fce20b618309f64" dependencies = [ "itertools 0.11.0", "proc-macro2", @@ -4834,7 +5226,7 @@ dependencies = [ "serde", "serde_json", "stellar-xdr", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -4843,9 +5235,9 @@ version = "21.5.0" [[package]] name = "soroban-ledger-snapshot" -version = "21.2.0" +version = "21.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "667a040c424d5a479387a6ca71f9591c364cdd2cfdfb1f4227753b4d1bab2a80" +checksum = "9cf596b2083946a95914a55d7d29cee6a8095b515fd06211851f45bf6af5a496" dependencies = [ "serde", "serde_json", @@ -4857,9 +5249,9 @@ dependencies = [ [[package]] name = "soroban-sdk" -version = "21.2.0" +version = "21.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e76cfdcf2610f97d595db27dd164abe8f38c00b745e822220c2874c185d9c6f" +checksum = "69e39bf9e8ab05579c836e8e5be5f2f4c5ba75e7337ece20e975e82fc3a9d41e" dependencies = [ "arbitrary", "bytes-lit", @@ -4872,14 +5264,14 @@ dependencies = [ "soroban-env-host", "soroban-ledger-snapshot", "soroban-sdk-macros", - "stellar-strkey", + "stellar-strkey 0.0.8", ] [[package]] name = "soroban-sdk-macros" -version = "21.5.0" +version = "21.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0772e87f2f675afdb4617b3e084bee7d92204d32cf1cd664a756fd0f4af5608e" +checksum = "da03fa00b8ca6e392f013359c06d790d2d379f9c8d6f8a6dfe563ec64311e5d3" dependencies = [ "crate-git-revision 0.0.6", "darling", @@ -4892,14 +5284,14 @@ dependencies = [ "soroban-spec", "soroban-spec-rust", "stellar-xdr", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] name = "soroban-spec" -version = "21.5.0" +version = "21.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40917f326155433baf8d6fa36b3006c19139cf0acc01acf4eaf2516b3eaa23e3" +checksum = "64c723195463d8742bcb481520bd8b8325da66c39ea236ad46261e6af992e8a8" dependencies = [ "base64 0.13.1", "stellar-xdr", @@ -4923,9 +5315,9 @@ dependencies = [ [[package]] name = "soroban-spec-rust" -version = "21.5.0" +version = "21.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11343e04efdaf247f0cd9c8517cb093f60bd2490f8ee76c9f86e2c50308d443" +checksum = "c0f1b0ec2af54e38f138910f09e101b100130efe625f69ece51c76dd4f06f8b2" dependencies = [ "prettyplease", "proc-macro2", @@ -4933,7 +5325,7 @@ dependencies = [ "sha2 0.10.8", "soroban-spec", "stellar-xdr", - "syn 2.0.39", + "syn 2.0.77", "thiserror", ] @@ -4948,7 +5340,7 @@ dependencies = [ "serde_json", "soroban-env-host", "soroban-spec", - "stellar-strkey", + "stellar-strkey 0.0.11", "stellar-xdr", "thiserror", "tokio", @@ -4996,7 +5388,7 @@ dependencies = [ "soroban-spec", "soroban-spec-tools", "stellar-rpc-client", - "stellar-strkey", + "stellar-strkey 0.0.11", "thiserror", "tokio", "toml 0.8.19", @@ -5007,9 +5399,9 @@ dependencies = [ [[package]] name = "soroban-token-sdk" -version = "21.2.0" +version = "21.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a758c3af83d174cd5f512d28a1c03983fc10888f75ebcc884ce303e782d409" +checksum = "0c6ede0da76646037f3ea5db9ccd37830865444bb24f137cb8f0af8282c784f5" dependencies = [ "soroban-sdk", ] @@ -5075,7 +5467,7 @@ dependencies = [ "once_cell", "phf", "pretty_assertions", - "reqwest", + "reqwest 0.11.27", "sep5", "serde", "serde_derive", @@ -5086,7 +5478,7 @@ dependencies = [ "soroban-env-host", "soroban-spec", "stellar-rpc-client", - "stellar-strkey", + "stellar-strkey 0.0.11", "stellar-xdr", "test-case", "testcontainers", @@ -5111,7 +5503,7 @@ dependencies = [ "serde-aux", "serde_json", "sha2 0.10.8", - "stellar-strkey", + "stellar-strkey 0.0.8", "stellar-xdr", "termcolor", "termcolor_output", @@ -5131,6 +5523,16 @@ dependencies = [ "thiserror", ] +[[package]] +name = "stellar-strkey" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0989c9a05eccbd08b60e603a1c7e3ed3ec92c0de73b8681fc964d272ab2b2697" +dependencies = [ + "crate-git-revision 0.0.6", + "data-encoding", +] + [[package]] name = "stellar-xdr" version = "21.2.0" @@ -5147,7 +5549,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "stellar-strkey", + "stellar-strkey 0.0.8", "thiserror", ] @@ -5176,6 +5578,29 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "structmeta" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e1575d8d40908d70f6fd05537266b90ae71b15dbbe7a8b7dffa2b759306d329" +dependencies = [ + "proc-macro2", + "quote", + "structmeta-derive", + "syn 2.0.77", +] + +[[package]] +name = "structmeta-derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "strum" version = "0.17.1" @@ -5232,9 +5657,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.39" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", @@ -5247,6 +5672,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -5347,7 +5781,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -5358,7 +5792,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", "test-case-core", ] @@ -5407,38 +5841,51 @@ dependencies = [ [[package]] name = "testcontainers" -version = "0.15.0" -source = "git+https://github.com/testcontainers/testcontainers-rs.git?rev=4b3e4f08a2c0bdf521636b03f959f004e6d216aa#4b3e4f08a2c0bdf521636b03f959f004e6d216aa" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "725cbe485aafddfd8b2d01665937c95498d894c07fabd9c4e06a53c7da4ccc56" dependencies = [ + "async-trait", + "bollard", "bollard-stubs", + "bytes", + "dirs 5.0.1", + "docker_credential", + "either", "futures", - "hex", - "hmac 0.12.1", "log", - "rand", + "memchr", + "parse-display", + "pin-project-lite", + "reqwest 0.12.7", "serde", "serde_json", - "sha2 0.10.8", + "serde_with", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "url", ] [[package]] name = "thiserror" -version = "1.0.55" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e3de26b0965292219b4287ff031fcba86837900fe9cd2b34ea8ad893c0953d2" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.55" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "268026685b2be38d7103e9e507c938a1fcb3d7e6eb15e87870b617bf37b6d581" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -5495,7 +5942,7 @@ dependencies = [ "once_cell", "pbkdf2", "rand", - "rustc-hash", + "rustc-hash 1.1.0", "sha2 0.10.8", "thiserror", "unicode-normalization", @@ -5553,7 +6000,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -5576,6 +6023,39 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls 0.22.4", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls 0.23.12", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.11" @@ -5704,7 +6184,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -5846,8 +6326,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", - "idna", + "idna 0.5.0", "percent-encoding", + "serde", ] [[package]] @@ -5941,7 +6422,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", "wasm-bindgen-shared", ] @@ -5975,7 +6456,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6120,6 +6601,12 @@ dependencies = [ "rustix 0.38.34", ] +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + [[package]] name = "winapi" version = "0.3.9" @@ -6160,6 +6647,36 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -6360,7 +6877,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] [[package]] @@ -6380,5 +6897,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.77", ] diff --git a/Cargo.toml b/Cargo.toml index c3108dc78..f7185b9ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,16 +15,16 @@ version = "21.5.0" rust-version = "1.79.0" [workspace.dependencies.soroban-env-host] -version = "=21.2.0" +version = "=21.2.1" [workspace.dependencies.soroban-simulation] -version = "21.2.0" +version = "=21.2.1" [workspace.dependencies.soroban-spec] -version = "=21.5.0" +version = "=21.7.2" [workspace.dependencies.soroban-spec-rust] -version = "=21.5.0" +version = "=21.7.2" [workspace.dependencies.soroban-spec-json] version = "=21.5.0" @@ -39,13 +39,13 @@ version = "21.5.0" path = "./cmd/crates/soroban-spec-tools" [workspace.dependencies.soroban-sdk] -version = "=21.2.0" +version = "=21.7.2" [workspace.dependencies.soroban-token-sdk] -version = "=21.2.0" +version = "=21.7.2" [workspace.dependencies.soroban-ledger-snapshot] -version = "=21.2.0" +version = "=21.7.2" [workspace.dependencies.soroban-cli] version = "=21.5.0" @@ -60,7 +60,7 @@ version = "21.2.0" default-features = true [workspace.dependencies] -stellar-strkey = "0.0.8" +stellar-strkey = "0.0.11" sep5 = "0.0.4" base64 = "0.21.2" thiserror = "1.0.46" diff --git a/cmd/crates/stellar-ledger/Cargo.toml b/cmd/crates/stellar-ledger/Cargo.toml index d8847c7d7..b8894b71d 100644 --- a/cmd/crates/stellar-ledger/Cargo.toml +++ b/cmd/crates/stellar-ledger/Cargo.toml @@ -49,7 +49,7 @@ pretty_assertions = "1.2.1" serial_test = "3.0.0" httpmock = "0.7.0-rc.1" test-case = "*" -testcontainers = { git = "https://github.com/testcontainers/testcontainers-rs.git", rev = "4b3e4f08a2c0bdf521636b03f959f004e6d216aa" } +testcontainers = "0.20.1" [features] diff --git a/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs b/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs index 6e9235bb4..d45981eef 100644 --- a/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs +++ b/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs @@ -17,8 +17,7 @@ use std::{collections::HashMap, time::Duration}; use stellar_xdr::curr::{ Memo, MuxedAccount, PaymentOp, Preconditions, SequenceNumber, TransactionExt, }; - -use testcontainers::{clients, core::Port, RunnableImage}; +use testcontainers::{core::ContainerPort, runners::AsyncRunner, ContainerAsync, ImageExt}; use tokio::time::sleep; static PORT_RANGE: Lazy>> = Lazy::new(|| Mutex::new(40000..50000)); @@ -40,21 +39,16 @@ mod test_helpers { } use test_case::test_case; -use test_helpers::test::{ - emulator_http_transport::EmulatorHttpTransport, - speculos::{Args, Speculos}, -}; +use test_helpers::test::{emulator_http_transport::EmulatorHttpTransport, speculos::Speculos}; #[test_case("nanos".to_string() ; "when the device is NanoS")] #[test_case("nanox".to_string() ; "when the device is NanoX")] #[test_case("nanosp".to_string() ; "when the device is NanoS Plus")] #[tokio::test] async fn test_get_public_key(ledger_device_model: String) { - let runnable_image = get_runnable_image(ledger_device_model.clone()); - let docker = clients::Cli::default(); - let node = docker.run(runnable_image); - let host_port = node.get_host_port_ipv4(9998); - let ui_host_port: u16 = node.get_host_port_ipv4(5000); + let container = get_container(ledger_device_model.clone()).await; + let host_port = container.get_host_port_ipv4(9998).await.unwrap(); + let ui_host_port: u16 = container.get_host_port_ipv4(5000).await.unwrap(); wait_for_emulator_start_text(ui_host_port).await; let ledger = ledger(host_port).await; @@ -68,13 +62,10 @@ async fn test_get_public_key(ledger_device_model: String) { assert_eq!(public_key_string, expected_public_key); } Err(e) => { - node.stop(); println!("{e}"); assert!(false); } } - - node.stop(); } #[test_case("nanos".to_string() ; "when the device is NanoS")] @@ -82,11 +73,9 @@ async fn test_get_public_key(ledger_device_model: String) { #[test_case("nanosp".to_string() ; "when the device is NanoS Plus")] #[tokio::test] async fn test_get_app_configuration(ledger_device_model: String) { - let runnable_image = get_runnable_image(ledger_device_model.clone()); - let docker = clients::Cli::default(); - let node = docker.run(runnable_image); - let host_port = node.get_host_port_ipv4(9998); - let ui_host_port: u16 = node.get_host_port_ipv4(5000); + let container = get_container(ledger_device_model.clone()).await; + let host_port = container.get_host_port_ipv4(9998).await.unwrap(); + let ui_host_port: u16 = container.get_host_port_ipv4(5000).await.unwrap(); wait_for_emulator_start_text(ui_host_port).await; let ledger = ledger(host_port).await; @@ -96,13 +85,10 @@ async fn test_get_app_configuration(ledger_device_model: String) { assert_eq!(config, vec![0, 5, 0, 3]); } Err(e) => { - node.stop(); println!("{e}"); assert!(false); } }; - - node.stop(); } #[test_case("nanos".to_string() ; "when the device is NanoS")] @@ -110,11 +96,9 @@ async fn test_get_app_configuration(ledger_device_model: String) { #[test_case("nanosp".to_string() ; "when the device is NanoS Plus")] #[tokio::test] async fn test_sign_tx(ledger_device_model: String) { - let runnable_image = get_runnable_image(ledger_device_model.clone()); - let docker = clients::Cli::default(); - let node = docker.run(runnable_image); - let host_port = node.get_host_port_ipv4(9998); - let ui_host_port: u16 = node.get_host_port_ipv4(5000); + let container = get_container(ledger_device_model.clone()).await; + let host_port = container.get_host_port_ipv4(9998).await.unwrap(); + let ui_host_port: u16 = container.get_host_port_ipv4(5000).await.unwrap(); wait_for_emulator_start_text(ui_host_port).await; let ledger = Arc::new(ledger(host_port).await); @@ -185,13 +169,10 @@ async fn test_sign_tx(ledger_device_model: String) { assert_eq!( hex::encode(response), "5c2f8eb41e11ab922800071990a25cf9713cc6e7c43e50e0780ddc4c0c6da50c784609ef14c528a12f520d8ea9343b49083f59c51e3f28af8c62b3edeaade60e"); } Err(e) => { - node.stop(); println!("{e}"); assert!(false); } }; - - node.stop(); } #[test_case("nanos".to_string() ; "when the device is NanoS")] @@ -199,11 +180,9 @@ async fn test_sign_tx(ledger_device_model: String) { #[test_case("nanosp".to_string() ; "when the device is NanoS Plus")] #[tokio::test] async fn test_sign_tx_hash_when_hash_signing_is_not_enabled(ledger_device_model: String) { - let runnable_image = get_runnable_image(ledger_device_model.clone()); - let docker = clients::Cli::default(); - let node = docker.run(runnable_image); - let host_port = node.get_host_port_ipv4(9998); - let ui_host_port: u16 = node.get_host_port_ipv4(5000); + let container = get_container(ledger_device_model.clone()).await; + let host_port = container.get_host_port_ipv4(9998).await.unwrap(); + let ui_host_port: u16 = container.get_host_port_ipv4(5000).await.unwrap(); wait_for_emulator_start_text(ui_host_port).await; let ledger = ledger(host_port).await; @@ -216,11 +195,8 @@ async fn test_sign_tx_hash_when_hash_signing_is_not_enabled(ledger_device_model: assert_eq!(msg, "Ledger APDU retcode: 0x6C66"); // this error code is SW_TX_HASH_SIGNING_MODE_NOT_ENABLED https://github.com/LedgerHQ/app-stellar/blob/develop/docs/COMMANDS.md } else { - node.stop(); panic!("Unexpected result: {:?}", result); } - - node.stop(); } #[test_case("nanos".to_string() ; "when the device is NanoS")] @@ -228,11 +204,9 @@ async fn test_sign_tx_hash_when_hash_signing_is_not_enabled(ledger_device_model: #[test_case("nanosp".to_string() ; "when the device is NanoS Plus")] #[tokio::test] async fn test_sign_tx_hash_when_hash_signing_is_enabled(ledger_device_model: String) { - let runnable_image = get_runnable_image(ledger_device_model.clone()); - let docker = clients::Cli::default(); - let node = docker.run(runnable_image); - let host_port = node.get_host_port_ipv4(9998); - let ui_host_port: u16 = node.get_host_port_ipv4(5000); + let container = get_container(ledger_device_model.clone()).await; + let host_port = container.get_host_port_ipv4(9998).await.unwrap(); + let ui_host_port: u16 = container.get_host_port_ipv4(5000).await.unwrap(); wait_for_emulator_start_text(ui_host_port).await; enable_hash_signing(ui_host_port).await; @@ -248,7 +222,6 @@ async fn test_sign_tx_hash_when_hash_signing_is_enabled(ledger_device_model: Str ) { Ok(()) => {} Err(e) => { - node.stop(); panic!("Unexpected result: {e}"); } } @@ -267,12 +240,9 @@ async fn test_sign_tx_hash_when_hash_signing_is_enabled(ledger_device_model: Str assert_eq!( hex::encode(response), "e0fa9d19f34ddd494bbb794645fc82eb5ebab29e74160f1b1d5697e749aada7c6b367236df87326b0fdc921ed39702242fc8b14414f4e0ee3e775f1fd0208101"); } Err(e) => { - node.stop(); panic!("Unexpected result: {e}"); } } - - node.stop(); } async fn click(ui_host_port: u16, url: &str) { @@ -330,23 +300,14 @@ struct EventsResponse { events: Vec, } -fn get_runnable_image(ledger_device_model: String) -> RunnableImage { - let args = Args { - ledger_device_model, - }; - let runnable_image: RunnableImage = (Speculos::new(), args).into(); - - // doing this to randomize the ports on the host so that parallel tests don't clobber each other +async fn get_container(ledger_device_model: String) -> ContainerAsync { let (tcp_port_1, tcp_port_2) = get_available_ports(2); - runnable_image - .with_mapped_port(Port { - local: tcp_port_1, - internal: 9998, - }) - .with_mapped_port(Port { - local: tcp_port_2, - internal: 5000, - }) + Speculos::new(ledger_device_model) + .with_mapped_port(tcp_port_1, ContainerPort::Tcp(9998)) + .with_mapped_port(tcp_port_2, ContainerPort::Tcp(5000)) + .start() + .await + .unwrap() } fn get_available_ports(n: usize) -> (u16, u16) { diff --git a/cmd/crates/stellar-ledger/tests/utils/speculos.rs b/cmd/crates/stellar-ledger/tests/utils/speculos.rs index caccdfc38..57439b0af 100644 --- a/cmd/crates/stellar-ledger/tests/utils/speculos.rs +++ b/cmd/crates/stellar-ledger/tests/utils/speculos.rs @@ -1,5 +1,8 @@ -use std::{collections::HashMap, path::PathBuf}; -use testcontainers::{core::WaitFor, Image, ImageArgs}; +use std::{borrow::Cow, collections::HashMap, path::PathBuf}; +use testcontainers::{ + core::{Mount, WaitFor}, + Image, +}; const NAME: &str = "docker.io/zondax/builder-zemu"; const TAG: &str = "speculos-3a3439f6b45eca7f56395673caaf434c202e7005"; @@ -24,72 +27,69 @@ impl From<&Map> for HashMap { } #[derive(Debug, Default)] -pub struct Speculos(HashMap, HashMap); +pub struct Speculos { + env: HashMap, + volumes: Vec, + cmd: String, +} + const DEFAULT_APP_PATH: &str = "/project/app/bin"; impl Speculos { #[allow(dead_code)] - pub fn new() -> Self { + pub fn new(ledger_device_model: String) -> Self { #[allow(unused_mut)] let apps_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("tests") .join("test_fixtures") .join("apps"); - let mut volumes = HashMap::new(); - volumes.insert( - apps_dir.to_str().unwrap().to_string(), - DEFAULT_APP_PATH.to_string(), - ); - Speculos(ENV.into(), volumes) - } -} - -#[derive(Debug, Clone)] -pub struct Args { - pub ledger_device_model: String, -} - -impl Default for Args { - fn default() -> Self { - Self { - ledger_device_model: "nanos".to_string(), + let volumes = vec![Mount::bind_mount( + apps_dir.to_str().unwrap(), + DEFAULT_APP_PATH, + )]; + let cmd = Self::get_cmd(ledger_device_model); + Speculos { + env: ENV.into(), + volumes, + cmd, } } -} -impl ImageArgs for Args { - fn into_iterator(self) -> Box> { - let device_model = self.ledger_device_model.clone(); + fn get_cmd(ledger_device_model: String) -> String { + let device_model = ledger_device_model.clone(); let container_elf_path = match device_model.as_str() { "nanos" => format!("{DEFAULT_APP_PATH}/stellarNanoSApp.elf"), "nanosp" => format!("{DEFAULT_APP_PATH}/stellarNanoSPApp.elf"), "nanox" => format!("{DEFAULT_APP_PATH}/stellarNanoXApp.elf"), _ => panic!("Unsupported device model"), }; - let command_string = format!("/home/zondax/speculos/speculos.py --log-level speculos:DEBUG --color JADE_GREEN --display headless -s {TEST_SEED_PHRASE} -m {device_model} {container_elf_path}"); - Box::new(vec![command_string].into_iter()) + format!("/home/zondax/speculos/speculos.py --log-level speculos:DEBUG --color JADE_GREEN --display headless -s {TEST_SEED_PHRASE} -m {device_model} {container_elf_path}") } } impl Image for Speculos { - type Args = Args; - - fn name(&self) -> String { - NAME.to_owned() + fn name(&self) -> &str { + NAME } - fn tag(&self) -> String { - TAG.to_owned() + fn tag(&self) -> &str { + TAG } fn ready_conditions(&self) -> Vec { vec![WaitFor::message_on_stdout("HTTP proxy started...")] } - fn env_vars(&self) -> Box + '_> { - Box::new(self.0.iter()) + fn env_vars( + &self, + ) -> impl IntoIterator>, impl Into>)> { + self.env.clone().into_iter().collect::>() } - fn volumes(&self) -> Box + '_> { - Box::new(self.1.iter()) + fn mounts(&self) -> impl IntoIterator { + self.volumes.iter() } -} + + fn cmd(&self) -> impl IntoIterator>> { + vec![self.cmd.clone()].into_iter() + } +} \ No newline at end of file From 9f24abeb5ccae57e68a8c95eaebbf2d9cc1380bb Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 24 Sep 2024 17:02:45 -0400 Subject: [PATCH 07/55] fix: bug in tx sign and send test (#1620) * fix: bug in tx sign and send test * fix: update min rust version * fix: windows clippy --- Cargo.toml | 2 +- cmd/crates/soroban-test/tests/it/integration/tx.rs | 5 ++--- cmd/crates/soroban-test/tests/it/plugin.rs | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f7185b9ed..78b9517c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ exclude = ["cmd/crates/soroban-test/tests/fixtures/hello"] [workspace.package] version = "21.5.0" -rust-version = "1.79.0" +rust-version = "1.81.0" [workspace.dependencies.soroban-env-host] version = "=21.2.1" diff --git a/cmd/crates/soroban-test/tests/it/integration/tx.rs b/cmd/crates/soroban-test/tests/it/integration/tx.rs index 34c6f086d..8e586e577 100644 --- a/cmd/crates/soroban-test/tests/it/integration/tx.rs +++ b/cmd/crates/soroban-test/tests/it/integration/tx.rs @@ -74,12 +74,11 @@ async fn build_simulate_sign_send() { .stdout_as_str(); dbg!("{tx_signed}"); - let output = sandbox + sandbox .new_assert_cmd("tx") .arg("send") .write_stdin(tx_signed.as_bytes()) .assert() .success() - .stdout_as_str(); - assert_eq!(output, "SUCCESS"); + .stdout(predicates::str::contains("SUCCESS")); } diff --git a/cmd/crates/soroban-test/tests/it/plugin.rs b/cmd/crates/soroban-test/tests/it/plugin.rs index 7d55d1e53..450902d65 100644 --- a/cmd/crates/soroban-test/tests/it/plugin.rs +++ b/cmd/crates/soroban-test/tests/it/plugin.rs @@ -37,7 +37,7 @@ fn has_no_path() { assert_cmd::Command::cargo_bin("soroban") .unwrap_or_else(|_| assert_cmd::Command::new("soroban")) .arg("hello") - .env("PATH", &target_bin()) + .env("PATH", target_bin()) .assert() .stdout("Hello, world!\n"); } From 3c5cc83d28b75c121f94805a8e8846a9d7ab8988 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Tue, 24 Sep 2024 22:54:53 -0400 Subject: [PATCH 08/55] feat: tx sign --sign-with-lab (#1604) Co-authored-by: Willem Wyndham Co-authored-by: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> --- Cargo.lock | 32 ++++++++++++ Cargo.toml | 2 +- FULL_HELP_DOCS.md | 1 + .../soroban-test/tests/it/integration/tx.rs | 3 +- cmd/soroban-cli/Cargo.toml | 2 + cmd/soroban-cli/src/commands/tx/sign.rs | 2 +- cmd/soroban-cli/src/config/mod.rs | 2 +- cmd/soroban-cli/src/config/secret.rs | 9 ++-- cmd/soroban-cli/src/config/sign_with.rs | 28 ++++++++-- cmd/soroban-cli/src/signer.rs | 51 +++++++++++++++---- 10 files changed, 107 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c683baba4..cd3761b51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3138,6 +3138,25 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -3625,6 +3644,17 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "open" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a877bf6abd716642a53ef1b89fb498923a4afca5c754f9050b4d081c05c4b3" +dependencies = [ + "is-wsl", + "libc", + "pathdiff", +] + [[package]] name = "openssl" version = "0.10.55" @@ -5104,6 +5134,7 @@ dependencies = [ "jsonrpsee-core", "jsonrpsee-http-client", "num-bigint", + "open", "openssl", "pathdiff", "phf", @@ -5146,6 +5177,7 @@ dependencies = [ "tracing-subscriber", "ulid", "ureq", + "url", "walkdir", "wasm-opt", "wasmparser 0.90.0", diff --git a/Cargo.toml b/Cargo.toml index 78b9517c5..4cf12b4d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,7 +93,7 @@ termcolor = "1.1.3" termcolor_output = "1.0.1" ed25519-dalek = ">= 2.1.1" -# networking +# networking http = "1.0.0" jsonrpsee-http-client = "0.20.1" jsonrpsee-core = "0.20.1" diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index 43e8370f6..b3645c850 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -1338,6 +1338,7 @@ Sign a transaction envelope appending the signature to the envelope * `--sign-with-key ` — Sign with a local key. Can be an identity (--sign-with-key alice), a secret key (--sign-with-key SC36…), or a seed phrase (--sign-with-key "kite urban…"). If using seed phrase, `--hd-path` defaults to the `0` path * `--hd-path ` — If using a seed phrase to sign, sets which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` +* `--sign-with-lab` — Sign with * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config diff --git a/cmd/crates/soroban-test/tests/it/integration/tx.rs b/cmd/crates/soroban-test/tests/it/integration/tx.rs index 8e586e577..ef2d1e26c 100644 --- a/cmd/crates/soroban-test/tests/it/integration/tx.rs +++ b/cmd/crates/soroban-test/tests/it/integration/tx.rs @@ -1,8 +1,7 @@ -use soroban_rpc::GetTransactionResponse; use soroban_sdk::xdr::{Limits, ReadXdr, TransactionEnvelope, WriteXdr}; use soroban_test::{AssertExt, TestEnv}; -use crate::integration::util::{deploy_contract, deploy_hello, DeployKind, HELLO_WORLD}; +use crate::integration::util::{deploy_contract, DeployKind, HELLO_WORLD}; #[tokio::test] async fn simulate() { diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index 1f381eb42..75b562bb6 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -123,6 +123,8 @@ humantime = "2.1.0" phf = { version = "0.11.2", features = ["macros"] } semver = "1.0.0" glob = "0.3.1" +open = "5.3.0" +url = "2.5.2" # For hyper-tls [target.'cfg(unix)'.dependencies] diff --git a/cmd/soroban-cli/src/commands/tx/sign.rs b/cmd/soroban-cli/src/commands/tx/sign.rs index 92fef7417..c696f8586 100644 --- a/cmd/soroban-cli/src/commands/tx/sign.rs +++ b/cmd/soroban-cli/src/commands/tx/sign.rs @@ -34,7 +34,7 @@ impl Cmd { pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { let tx_env = super::xdr::tx_envelope_from_stdin()?; let tx_env_signed = self.sign_with.sign_tx_env( - tx_env, + &tx_env, &self.locator, &self.network.get(&self.locator)?, global_args.quiet, diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index 48eb6dbe8..274713e19 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -70,7 +70,7 @@ impl Args { let network = &self.get_network()?; let signer = Signer { kind: SignerKind::Local(LocalKey { key }), - printer: Print::new(false), + print: Print::new(false), }; Ok(signer.sign_tx(tx, network)?) } diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index ff1fb7b72..a7fd86fda 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -3,8 +3,8 @@ use serde::{Deserialize, Serialize}; use std::{io::Write, str::FromStr}; use stellar_strkey::ed25519::{PrivateKey, PublicKey}; -use crate::print::Print; use crate::{ + print::Print, signer::{self, LocalKey, Signer, SignerKind}, utils, }; @@ -126,17 +126,14 @@ impl Secret { )?) } - pub fn signer(&self, index: Option, quiet: bool) -> Result { + pub fn signer(&self, index: Option, print: Print) -> Result { let kind = match self { Secret::SecretKey { .. } | Secret::SeedPhrase { .. } => { let key = self.key_pair(index)?; SignerKind::Local(LocalKey { key }) } }; - Ok(Signer { - kind, - printer: Print::new(quiet), - }) + Ok(Signer { kind, print }) } pub fn key_pair(&self, index: Option) -> Result { diff --git a/cmd/soroban-cli/src/config/sign_with.rs b/cmd/soroban-cli/src/config/sign_with.rs index 685945194..db1c44d14 100644 --- a/cmd/soroban-cli/src/config/sign_with.rs +++ b/cmd/soroban-cli/src/config/sign_with.rs @@ -1,4 +1,8 @@ -use crate::{signer, xdr::TransactionEnvelope}; +use crate::{ + print::Print, + signer::{self, Signer, SignerKind}, + xdr::{self, TransactionEnvelope}, +}; use clap::arg; use super::{ @@ -23,6 +27,8 @@ pub enum Error { NoSignWithKey, #[error(transparent)] StrKey(#[from] stellar_strkey::DecodeError), + #[error(transparent)] + Xdr(#[from] xdr::Error), } #[derive(Debug, clap::Args, Clone, Default)] @@ -35,19 +41,31 @@ pub struct Args { #[arg(long, requires = "sign_with_key")] /// If using a seed phrase to sign, sets which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` pub hd_path: Option, + + /// Sign with + #[arg(long, conflicts_with = "sign_with_key", env = "STELLAR_SIGN_WITH_LAB")] + pub sign_with_lab: bool, } impl Args { pub fn sign_tx_env( &self, - tx: TransactionEnvelope, + tx: &TransactionEnvelope, locator: &locator::Args, network: &Network, quiet: bool, ) -> Result { - let key_or_name = self.sign_with_key.as_deref().ok_or(Error::NoSignWithKey)?; - let secret = locator.key(key_or_name)?; - let signer = secret.signer(self.hd_path, quiet)?; + let print = Print::new(quiet); + let signer = if self.sign_with_lab { + Signer { + kind: SignerKind::Lab, + print, + } + } else { + let key_or_name = self.sign_with_key.as_deref().ok_or(Error::NoSignWithKey)?; + let secret = locator.key(key_or_name)?; + secret.signer(self.hd_path, print)? + }; Ok(signer.sign_tx_env(tx, network)?) } } diff --git a/cmd/soroban-cli/src/signer.rs b/cmd/soroban-cli/src/signer.rs index 36ae55053..bf02fdb17 100644 --- a/cmd/soroban-cli/src/signer.rs +++ b/cmd/soroban-cli/src/signer.rs @@ -27,6 +27,12 @@ pub enum Error { Xdr(#[from] xdr::Error), #[error("Only Transaction envelope V1 type is supported")] UnsupportedTransactionEnvelopeType, + #[error(transparent)] + Url(#[from] url::ParseError), + #[error(transparent)] + Open(#[from] std::io::Error), + #[error("Returning a signature from Lab is not yet supported; Transaction can be found and submitted in lab")] + ReturningSignatureFromLab, } fn requires_auth(txn: &Transaction) -> Option { @@ -194,12 +200,13 @@ fn sign_soroban_authorization_entry( pub struct Signer { pub kind: SignerKind, - pub printer: Print, + pub print: Print, } -#[allow(clippy::module_name_repetitions)] +#[allow(clippy::module_name_repetitions, clippy::large_enum_variant)] pub enum SignerKind { Local(LocalKey), + Lab, } impl Signer { @@ -212,26 +219,27 @@ impl Signer { tx, signatures: VecM::default(), }); - self.sign_tx_env(tx_env, network) + self.sign_tx_env(&tx_env, network) } pub fn sign_tx_env( &self, - tx_env: TransactionEnvelope, + tx_env: &TransactionEnvelope, network: &Network, ) -> Result { - match tx_env { + match &tx_env { TransactionEnvelope::Tx(TransactionV1Envelope { tx, signatures }) => { - let tx_hash = transaction_hash(&tx, &network.network_passphrase)?; - self.printer + let tx_hash = transaction_hash(tx, &network.network_passphrase)?; + self.print .infoln(format!("Signing transaction: {}", hex::encode(tx_hash),)); let decorated_signature = match &self.kind { SignerKind::Local(key) => key.sign_tx_hash(tx_hash)?, + SignerKind::Lab => Lab::sign_tx_env(tx_env, network, &self.print)?, }; - let mut sigs = signatures.into_vec(); + let mut sigs = signatures.clone().into_vec(); sigs.push(decorated_signature); Ok(TransactionEnvelope::Tx(TransactionV1Envelope { - tx, + tx: tx.clone(), signatures: sigs.try_into()?, })) } @@ -251,3 +259,28 @@ impl LocalKey { Ok(DecoratedSignature { hint, signature }) } } + +pub struct Lab; + +impl Lab { + const URL: &str = "https://lab.stellar.org/transaction/cli-sign"; + + pub fn sign_tx_env( + tx_env: &TransactionEnvelope, + network: &Network, + printer: &Print, + ) -> Result { + let xdr = tx_env.to_xdr_base64(Limits::none())?; + + let mut url = url::Url::parse(Self::URL)?; + url.query_pairs_mut() + .append_pair("networkPassphrase", &network.network_passphrase) + .append_pair("xdr", &xdr); + let url = url.to_string(); + + printer.globeln(format!("Opening lab to sign transaction: {url}")); + open::that(url)?; + + Err(Error::ReturningSignatureFromLab) + } +} From 6b4df0e66a885b22af7a5830ba4dccfde7602333 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Wed, 25 Sep 2024 00:32:04 -0400 Subject: [PATCH 09/55] feat: allow source account to be a public key (#1619) Co-authored-by: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> --- .../src/commands/contract/deploy/asset.rs | 9 +++---- .../src/commands/contract/deploy/wasm.rs | 22 +++++++-------- .../src/commands/contract/extend.rs | 8 +++--- .../src/commands/contract/install.rs | 27 ++++++++++--------- .../src/commands/contract/invoke.rs | 8 +++--- .../src/commands/contract/restore.rs | 7 +++-- cmd/soroban-cli/src/config/mod.rs | 15 +++++++++++ 7 files changed, 53 insertions(+), 43 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs index 51baa55e9..da7b6b805 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs @@ -97,11 +97,10 @@ impl NetworkRunnable for Cmd { client .verify_network_passphrase(Some(&network.network_passphrase)) .await?; - let key = config.key_pair()?; + let key = config.source_account()?; // Get the account sequence number - let public_strkey = - stellar_strkey::ed25519::PublicKey(key.verifying_key().to_bytes()).to_string(); + let public_strkey = key.to_string(); // TODO: use symbols for the method names (both here and in serve) let account_details = client.get_account(&public_strkey).await?; let sequence: i64 = account_details.seq_num.into(); @@ -141,7 +140,7 @@ fn build_wrap_token_tx( sequence: i64, fee: u32, _network_passphrase: &str, - key: &ed25519_dalek::SigningKey, + key: &stellar_strkey::ed25519::PublicKey, ) -> Result { let contract = ScAddress::Contract(contract_id.clone()); let mut read_write = vec![ @@ -180,7 +179,7 @@ fn build_wrap_token_tx( }; Ok(Transaction { - source_account: MuxedAccount::Ed25519(Uint256(key.verifying_key().to_bytes())), + source_account: MuxedAccount::Ed25519(Uint256(key.0)), fee, seq_num: SequenceNumber(sequence), cond: Preconditions::None, diff --git a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs index 74491249f..84138d432 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs @@ -212,12 +212,10 @@ impl NetworkRunnable for Cmd { client .verify_network_passphrase(Some(&network.network_passphrase)) .await?; - let key = config.key_pair()?; + let key = config.source_account()?; // Get the account sequence number - let public_strkey = - stellar_strkey::ed25519::PublicKey(key.verifying_key().to_bytes()).to_string(); - + let public_strkey = key.to_string(); let account_details = client.get_account(&public_strkey).await?; let sequence: i64 = account_details.seq_num.into(); let (txn, contract_id) = build_create_contract_tx( @@ -274,11 +272,9 @@ fn build_create_contract_tx( fee: u32, network_passphrase: &str, salt: [u8; 32], - key: &ed25519_dalek::SigningKey, + key: &stellar_strkey::ed25519::PublicKey, ) -> Result<(Transaction, Hash), Error> { - let source_account = AccountId(PublicKey::PublicKeyTypeEd25519( - key.verifying_key().to_bytes().into(), - )); + let source_account = AccountId(PublicKey::PublicKeyTypeEd25519(key.0.into())); let contract_id_preimage = ContractIdPreimage::Address(ContractIdPreimageFromAddress { address: ScAddress::Account(source_account), @@ -297,7 +293,7 @@ fn build_create_contract_tx( }), }; let tx = Transaction { - source_account: MuxedAccount::Ed25519(Uint256(key.verifying_key().to_bytes())), + source_account: MuxedAccount::Ed25519(Uint256(key.0)), fee, seq_num: SequenceNumber(sequence), cond: Preconditions::None, @@ -325,8 +321,12 @@ mod tests { 1, "Public Global Stellar Network ; September 2015", [0u8; 32], - &utils::parse_secret_key("SBFGFF27Y64ZUGFAIG5AMJGQODZZKV2YQKAVUUN4HNE24XZXD2OEUVUP") - .unwrap(), + &stellar_strkey::ed25519::PublicKey( + utils::parse_secret_key("SBFGFF27Y64ZUGFAIG5AMJGQODZZKV2YQKAVUUN4HNE24XZXD2OEUVUP") + .unwrap() + .verifying_key() + .to_bytes(), + ), ); assert!(result.is_ok()); diff --git a/cmd/soroban-cli/src/commands/contract/extend.rs b/cmd/soroban-cli/src/commands/contract/extend.rs index 5901c71b1..6666fcce7 100644 --- a/cmd/soroban-cli/src/commands/contract/extend.rs +++ b/cmd/soroban-cli/src/commands/contract/extend.rs @@ -132,17 +132,15 @@ impl NetworkRunnable for Cmd { tracing::trace!(?network); let keys = self.key.parse_keys(&config.locator, &network)?; let client = Client::new(&network.rpc_url)?; - let key = config.key_pair()?; + let key = config.source_account()?; let extend_to = self.ledgers_to_extend(); // Get the account sequence number - let public_strkey = - stellar_strkey::ed25519::PublicKey(key.verifying_key().to_bytes()).to_string(); - let account_details = client.get_account(&public_strkey).await?; + let account_details = client.get_account(&key.to_string()).await?; let sequence: i64 = account_details.seq_num.into(); let tx = Transaction { - source_account: MuxedAccount::Ed25519(Uint256(key.verifying_key().to_bytes())), + source_account: MuxedAccount::Ed25519(Uint256(key.0)), fee: self.fee.fee, seq_num: SequenceNumber(sequence + 1), cond: Preconditions::None, diff --git a/cmd/soroban-cli/src/commands/contract/install.rs b/cmd/soroban-cli/src/commands/contract/install.rs index 98c866271..5a1842b7b 100644 --- a/cmd/soroban-cli/src/commands/contract/install.rs +++ b/cmd/soroban-cli/src/commands/contract/install.rs @@ -126,16 +126,14 @@ impl NetworkRunnable for Cmd { } } - let key = config.key_pair()?; - // Get the account sequence number - let public_strkey = - stellar_strkey::ed25519::PublicKey(key.verifying_key().to_bytes()).to_string(); - let account_details = client.get_account(&public_strkey).await?; + let source_account = config.source_account()?; + + let account_details = client.get_account(&source_account.to_string()).await?; let sequence: i64 = account_details.seq_num.into(); let (tx_without_preflight, hash) = - build_install_contract_code_tx(&contract, sequence + 1, self.fee.fee, &key)?; + build_install_contract_code_tx(&contract, sequence + 1, self.fee.fee, &source_account)?; if self.fee.build_only { return Ok(TxnResult::Txn(tx_without_preflight)); @@ -255,14 +253,12 @@ pub(crate) fn build_install_contract_code_tx( source_code: &[u8], sequence: i64, fee: u32, - key: &ed25519_dalek::SigningKey, + key: &stellar_strkey::ed25519::PublicKey, ) -> Result<(Transaction, Hash), XdrError> { let hash = utils::contract_hash(source_code)?; let op = Operation { - source_account: Some(MuxedAccount::Ed25519(Uint256( - key.verifying_key().to_bytes(), - ))), + source_account: Some(MuxedAccount::Ed25519(Uint256(key.0))), body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { host_function: HostFunction::UploadContractWasm(source_code.try_into()?), auth: VecM::default(), @@ -270,7 +266,7 @@ pub(crate) fn build_install_contract_code_tx( }; let tx = Transaction { - source_account: MuxedAccount::Ed25519(Uint256(key.verifying_key().to_bytes())), + source_account: MuxedAccount::Ed25519(Uint256(key.0)), fee, seq_num: SequenceNumber(sequence), cond: Preconditions::None, @@ -292,8 +288,13 @@ mod tests { b"foo", 300, 1, - &utils::parse_secret_key("SBFGFF27Y64ZUGFAIG5AMJGQODZZKV2YQKAVUUN4HNE24XZXD2OEUVUP") - .unwrap(), + &stellar_strkey::ed25519::PublicKey::from_payload( + utils::parse_secret_key("SBFGFF27Y64ZUGFAIG5AMJGQODZZKV2YQKAVUUN4HNE24XZXD2OEUVUP") + .unwrap() + .verifying_key() + .as_bytes(), + ) + .unwrap(), ); assert!(result.is_ok()); diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index 293867c76..fa90ef9b3 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -216,12 +216,10 @@ impl NetworkRunnable for Cmd { client .verify_network_passphrase(Some(&network.network_passphrase)) .await?; - let key = config.key_pair()?; - // Get the account sequence number - let public_strkey = - stellar_strkey::ed25519::PublicKey(key.verifying_key().to_bytes()).to_string(); - client.get_account(&public_strkey).await? + client + .get_account(&config.source_account()?.to_string()) + .await? }; let sequence: i64 = account_details.seq_num.into(); let AccountId(PublicKey::PublicKeyTypeEd25519(account_id)) = account_details.account_id; diff --git a/cmd/soroban-cli/src/commands/contract/restore.rs b/cmd/soroban-cli/src/commands/contract/restore.rs index e3e8e65ae..cf6a33248 100644 --- a/cmd/soroban-cli/src/commands/contract/restore.rs +++ b/cmd/soroban-cli/src/commands/contract/restore.rs @@ -135,16 +135,15 @@ impl NetworkRunnable for Cmd { tracing::trace!(?network); let entry_keys = self.key.parse_keys(&config.locator, &network)?; let client = Client::new(&network.rpc_url)?; - let key = config.key_pair()?; + let key = config.source_account()?; // Get the account sequence number - let public_strkey = - stellar_strkey::ed25519::PublicKey(key.verifying_key().to_bytes()).to_string(); + let public_strkey = key.to_string(); let account_details = client.get_account(&public_strkey).await?; let sequence: i64 = account_details.seq_num.into(); let tx = Transaction { - source_account: MuxedAccount::Ed25519(Uint256(key.verifying_key().to_bytes())), + source_account: MuxedAccount::Ed25519(Uint256(key.0)), fee: self.fee.fee, seq_num: SequenceNumber(sequence + 1), cond: Preconditions::None, diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index 274713e19..511c5a53c 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -34,6 +34,8 @@ pub enum Error { Rpc(#[from] soroban_rpc::Error), #[error(transparent)] Signer(#[from] signer::Error), + #[error(transparent)] + StellarStrkey(#[from] stellar_strkey::DecodeError), } #[derive(Debug, clap::Args, Clone, Default)] @@ -55,6 +57,19 @@ pub struct Args { } impl Args { + // TODO: Replace PublicKey with MuxedAccount once https://github.com/stellar/rs-stellar-xdr/pull/396 is merged. + pub fn source_account(&self) -> Result { + if let Ok(secret) = self.locator.read_identity(&self.source_account) { + Ok(stellar_strkey::ed25519::PublicKey( + secret.key_pair(self.hd_path)?.verifying_key().to_bytes(), + )) + } else { + Ok(stellar_strkey::ed25519::PublicKey::from_string( + &self.source_account, + )?) + } + } + pub fn key_pair(&self) -> Result { let key = self.account(&self.source_account)?; Ok(key.key_pair(self.hd_path)?) From 9e976f1d5efbd836c96ff9d26f8bdd6147c84d58 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 13:56:23 +0000 Subject: [PATCH 10/55] Bump the all-actions group with 4 updates (#1538) Bumps the all-actions group with 4 updates: [actions/checkout](https://github.com/actions/checkout), [actions/upload-artifact](https://github.com/actions/upload-artifact), [actions/github-script](https://github.com/actions/github-script) and [actions/cache](https://github.com/actions/cache). Updates `actions/checkout` from 3 to 4 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) Updates `actions/upload-artifact` from 3 to 4 - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v3...v4) Updates `actions/github-script` from 3 to 7 - [Release notes](https://github.com/actions/github-script/releases) - [Commits](https://github.com/actions/github-script/compare/v3...v7) Updates `actions/cache` from 3 to 4 - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major dependency-group: all-actions - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major dependency-group: all-actions - dependency-name: actions/github-script dependency-type: direct:production update-type: version-update:semver-major dependency-group: all-actions - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major dependency-group: all-actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/binaries.yml | 6 +++--- .github/workflows/bindings-ts.yml | 4 ++-- .github/workflows/dependency-check.yml | 4 ++-- .github/workflows/full-help-docs.yml | 4 ++-- .github/workflows/ledger-emulator.yml | 2 +- .github/workflows/rpc-tests.yml | 4 ++-- .github/workflows/rust.yml | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 3757f94f3..42ddb786a 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -38,7 +38,7 @@ jobs: ext: .exe runs-on: ${{ matrix.sys.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: rustup update - run: rustup target add ${{ matrix.sys.target }} - if: matrix.sys.target == 'aarch64-unknown-linux-gnu' @@ -67,13 +67,13 @@ jobs: cd target/${{ matrix.sys.target }}/release tar czvf $NAME.tar.gz ${{ matrix.crate.binary }}${{ matrix.sys.ext }} - name: Upload to Artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ env.NAME }} path: 'target/${{ matrix.sys.target }}/release/${{ env.NAME }}.tar.gz' - name: Upload to Release (release only) if: github.event_name == 'release' - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | const fs = require('fs'); diff --git a/.github/workflows/bindings-ts.yml b/.github/workflows/bindings-ts.yml index 30858aa4a..957661c4c 100644 --- a/.github/workflows/bindings-ts.yml +++ b/.github/workflows/bindings-ts.yml @@ -24,8 +24,8 @@ jobs: --health-timeout 5s --health-retries 50 steps: - - uses: actions/checkout@v3 - - uses: actions/cache@v3 + - uses: actions/checkout@v4 + - uses: actions/cache@v4 with: path: | ~/.cargo/bin/ diff --git a/.github/workflows/dependency-check.yml b/.github/workflows/dependency-check.yml index 4abad18a0..cef838a7c 100644 --- a/.github/workflows/dependency-check.yml +++ b/.github/workflows/dependency-check.yml @@ -17,11 +17,11 @@ jobs: dependency-sanity-checker: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: rustup update - run: scripts/check-dependencies.bash validate-rust-git-rev-deps: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: stellar/actions/rust-check-git-rev-deps@main diff --git a/.github/workflows/full-help-docs.yml b/.github/workflows/full-help-docs.yml index 4be8cad00..f564ae9ae 100644 --- a/.github/workflows/full-help-docs.yml +++ b/.github/workflows/full-help-docs.yml @@ -26,7 +26,7 @@ jobs: doc-check: runs-on: ubuntu-latest-16-cores steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: stellar/actions/rust-cache@main - run: rustup update - name: Generate help doc @@ -41,7 +41,7 @@ jobs: - name: Check diff if: steps.doc-gen.outputs.diff != '' - uses: actions/github-script@v3 + uses: actions/github-script@v7 with: script: | core.setFailed('Expected `doc-gen` to generate no diffs, but got diff shown in previous step.\n\nUpdate the full help docs:\n\n cargo md-gen\n\nDo this automatically on every commit by installing the pre-commit hook as explained in the README.') diff --git a/.github/workflows/ledger-emulator.yml b/.github/workflows/ledger-emulator.yml index 2bf371b63..7a3068d2e 100644 --- a/.github/workflows/ledger-emulator.yml +++ b/.github/workflows/ledger-emulator.yml @@ -19,7 +19,7 @@ jobs: env: CI_TESTS: true steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: stellar/actions/rust-cache@main - name: install libudev-dev run: | diff --git a/.github/workflows/rpc-tests.yml b/.github/workflows/rpc-tests.yml index a6faba83b..769cd21f7 100644 --- a/.github/workflows/rpc-tests.yml +++ b/.github/workflows/rpc-tests.yml @@ -28,8 +28,8 @@ jobs: --health-timeout 5s --health-retries 50 steps: - - uses: actions/checkout@v3 - - uses: actions/cache@v3 + - uses: actions/checkout@v4 + - uses: actions/cache@v4 with: path: | ~/.cargo/bin/ diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c51d18373..eb4e4c414 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -26,7 +26,7 @@ jobs: fmt: runs-on: ubuntu-latest-16-cores steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: rustup update - run: cargo fmt --all --check @@ -58,7 +58,7 @@ jobs: env: CI_TESTS: true steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: stellar/actions/rust-cache@main - name: Use the minimum supported Rust version if: matrix.rust == 'msrv' From 4137b4129110c21c4a54d329abf1fdcadebf7cea Mon Sep 17 00:00:00 2001 From: Elliot Voris Date: Wed, 25 Sep 2024 14:35:58 -0500 Subject: [PATCH 11/55] reformat link to stellar lab, remove surrounding angle brackets (#1622) --- FULL_HELP_DOCS.md | 2 +- cmd/soroban-cli/src/config/sign_with.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index b3645c850..a2d5644f6 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -1338,7 +1338,7 @@ Sign a transaction envelope appending the signature to the envelope * `--sign-with-key ` — Sign with a local key. Can be an identity (--sign-with-key alice), a secret key (--sign-with-key SC36…), or a seed phrase (--sign-with-key "kite urban…"). If using seed phrase, `--hd-path` defaults to the `0` path * `--hd-path ` — If using a seed phrase to sign, sets which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` -* `--sign-with-lab` — Sign with +* `--sign-with-lab` — Sign with https://lab.stellar.org * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config diff --git a/cmd/soroban-cli/src/config/sign_with.rs b/cmd/soroban-cli/src/config/sign_with.rs index db1c44d14..475013bc8 100644 --- a/cmd/soroban-cli/src/config/sign_with.rs +++ b/cmd/soroban-cli/src/config/sign_with.rs @@ -42,7 +42,8 @@ pub struct Args { /// If using a seed phrase to sign, sets which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` pub hd_path: Option, - /// Sign with + #[allow(clippy::doc_markdown)] + /// Sign with https://lab.stellar.org #[arg(long, conflicts_with = "sign_with_key", env = "STELLAR_SIGN_WITH_LAB")] pub sign_with_lab: bool, } From c8c3871ef3a409328d9b0561f87d55c00bd41071 Mon Sep 17 00:00:00 2001 From: Gleb Date: Wed, 25 Sep 2024 15:00:19 -0700 Subject: [PATCH 12/55] Update nix lockfile (#1626) --- flake.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/flake.lock b/flake.lock index 836d0a12b..fef18714e 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "lastModified": 1726560853, + "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", "type": "github" }, "original": { @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1722813957, - "narHash": "sha256-IAoYyYnED7P8zrBFMnmp7ydaJfwTnwcnqxUElC1I26Y=", + "lastModified": 1726937504, + "narHash": "sha256-bvGoiQBvponpZh8ClUcmJ6QnsNKw0EMrCQJARK3bI1c=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "cb9a96f23c491c081b38eab96d22fa958043c9fa", + "rev": "9357f4f23713673f310988025d9dc261c20e70c6", "type": "github" }, "original": { @@ -62,11 +62,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1722997267, - "narHash": "sha256-8Pncp8IKd0f0N711CRrCGTC4iLfBE+/5kaMqyWxnYic=", + "lastModified": 1727144949, + "narHash": "sha256-uMZMjoCS2nf40TAE1686SJl3OXWfdfM+BDEfRdr+uLc=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "d720bf3cebac38c2426d77ee2e59943012854cb8", + "rev": "2e19799819104b46019d339e78d21c14372d3666", "type": "github" }, "original": { From c3aebe0c62c19d616e87ce2ea4b126326511121e Mon Sep 17 00:00:00 2001 From: Gleb Date: Fri, 27 Sep 2024 04:19:32 -0700 Subject: [PATCH 13/55] Fix source account bug (#1631) --- FULL_HELP_DOCS.md | 22 +++++++++---------- .../tests/it/integration/hello_world.rs | 11 ++++++++++ cmd/soroban-cli/src/config/mod.rs | 8 +++++-- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index a2d5644f6..479ac6720 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -119,7 +119,7 @@ Get Id of builtin Soroban Asset Contract. Deprecated, use `stellar contract id a * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that signs the final transaction. Alias `source`. Can be an identity (--source alice), a secret key (--source SC36…), or a seed phrase (--source "kite urban…") +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail * `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." @@ -138,7 +138,7 @@ Deploy builtin Soroban Asset Contract * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that signs the final transaction. Alias `source`. Can be an identity (--source alice), a secret key (--source SC36…), or a seed phrase (--source "kite urban…") +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail * `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." @@ -363,7 +363,7 @@ If no keys are specified the contract itself is extended. * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that signs the final transaction. Alias `source`. Can be an identity (--source alice), a secret key (--source SC36…), or a seed phrase (--source "kite urban…") +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail * `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." @@ -391,7 +391,7 @@ Deploy a wasm contract * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that signs the final transaction. Alias `source`. Can be an identity (--source alice), a secret key (--source SC36…), or a seed phrase (--source "kite urban…") +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail * `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." @@ -452,7 +452,7 @@ Deploy builtin Soroban Asset Contract * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that signs the final transaction. Alias `source`. Can be an identity (--source alice), a secret key (--source SC36…), or a seed phrase (--source "kite urban…") +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail * `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." @@ -471,7 +471,7 @@ Deploy normal Wasm Contract * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that signs the final transaction. Alias `source`. Can be an identity (--source alice), a secret key (--source SC36…), or a seed phrase (--source "kite urban…") +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail * `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." @@ -669,7 +669,7 @@ Install a WASM file to the ledger without creating a contract instance * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that signs the final transaction. Alias `source`. Can be an identity (--source alice), a secret key (--source SC36…), or a seed phrase (--source "kite urban…") +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail * `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." @@ -708,7 +708,7 @@ stellar contract invoke ... -- --help * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that signs the final transaction. Alias `source`. Can be an identity (--source alice), a secret key (--source SC36…), or a seed phrase (--source "kite urban…") +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail * `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." @@ -785,7 +785,7 @@ Print the current value of a contract-data ledger entry * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that signs the final transaction. Alias `source`. Can be an identity (--source alice), a secret key (--source SC36…), or a seed phrase (--source "kite urban…") +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail * `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." @@ -822,7 +822,7 @@ If no keys are specificed the contract itself is restored. * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that signs the final transaction. Alias `source`. Can be an identity (--source alice), a secret key (--source SC36…), or a seed phrase (--source "kite urban…") +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail * `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." @@ -1307,7 +1307,7 @@ Simulate a transaction envelope from stdin * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that signs the final transaction. Alias `source`. Can be an identity (--source alice), a secret key (--source SC36…), or a seed phrase (--source "kite urban…") +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail * `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." diff --git a/cmd/crates/soroban-test/tests/it/integration/hello_world.rs b/cmd/crates/soroban-test/tests/it/integration/hello_world.rs index e523efb8f..c982b0c40 100644 --- a/cmd/crates/soroban-test/tests/it/integration/hello_world.rs +++ b/cmd/crates/soroban-test/tests/it/integration/hello_world.rs @@ -60,6 +60,12 @@ async fn invoke() { .arg("test") .assert() .stdout_as_str(); + let public_key = sandbox + .new_assert_cmd("keys") + .arg("address") + .arg("test") + .assert() + .stdout_as_str(); let secret_key_1 = sandbox .new_assert_cmd("keys") .arg("show") @@ -129,6 +135,7 @@ async fn invoke() { contract_data_read_failure(sandbox, id); invoke_with_seed(sandbox, id, &seed_phrase).await; invoke_with_sk(sandbox, id, &secret_key).await; + invoke_with_pk(sandbox, id, &public_key).await; // This does add an identity to local config invoke_with_id(sandbox, id).await; handles_kebab_case(sandbox, id).await; @@ -334,6 +341,10 @@ async fn invoke_with_sk(sandbox: &TestEnv, id: &str, sk: &str) { invoke_with_source(sandbox, sk, id).await; } +async fn invoke_with_pk(sandbox: &TestEnv, id: &str, pk: &str) { + invoke_with_source(sandbox, pk, id).await; +} + async fn invoke_with_id(sandbox: &TestEnv, id: &str) { invoke_with_source(sandbox, "test", id).await; } diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index 511c5a53c..e6cf8fa26 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -45,7 +45,11 @@ pub struct Args { pub network: network::Args, #[arg(long, visible_alias = "source", env = "STELLAR_ACCOUNT")] - /// Account that signs the final transaction. Alias `source`. Can be an identity (--source alice), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). + /// Account that where transaction originates from. Alias `source`. + /// Can be an identity (--source alice), a public key (--source GDKW...), + /// a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). + /// If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to + /// sign the final transaction. In that case, trying to sign with public key will fail. pub source_account: String, #[arg(long)] @@ -59,7 +63,7 @@ pub struct Args { impl Args { // TODO: Replace PublicKey with MuxedAccount once https://github.com/stellar/rs-stellar-xdr/pull/396 is merged. pub fn source_account(&self) -> Result { - if let Ok(secret) = self.locator.read_identity(&self.source_account) { + if let Ok(secret) = self.account(&self.source_account) { Ok(stellar_strkey::ed25519::PublicKey( secret.key_pair(self.hd_path)?.verifying_key().to_bytes(), )) From aad9be9708037e122e8e9ff09a67a09c26168513 Mon Sep 17 00:00:00 2001 From: Gleb Date: Sat, 28 Sep 2024 01:55:49 -0700 Subject: [PATCH 14/55] Code cleanup (#1635) --- cmd/crates/soroban-spec-tools/src/utils.rs | 264 --------------------- 1 file changed, 264 deletions(-) diff --git a/cmd/crates/soroban-spec-tools/src/utils.rs b/cmd/crates/soroban-spec-tools/src/utils.rs index 66b153a11..f030cc0f8 100644 --- a/cmd/crates/soroban-spec-tools/src/utils.rs +++ b/cmd/crates/soroban-spec-tools/src/utils.rs @@ -1,268 +1,4 @@ -use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; use hex::FromHexError; -use std::{ - fmt::Display, - io::{self, Cursor}, -}; - -use stellar_xdr::curr::{ - Limited, Limits, ReadXdr, ScEnvMetaEntry, ScMetaEntry, ScMetaV0, ScSpecEntry, ScSpecFunctionV0, - ScSpecUdtEnumV0, ScSpecUdtErrorEnumV0, ScSpecUdtStructV0, ScSpecUdtUnionV0, StringM, -}; - -pub struct ContractSpec { - pub env_meta_base64: Option, - pub env_meta: Vec, - pub meta_base64: Option, - pub meta: Vec, - pub spec_base64: Option, - pub spec: Vec, -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("reading file {filepath}: {error}")] - CannotReadContractFile { - filepath: std::path::PathBuf, - error: io::Error, - }, - #[error("cannot parse wasm file {file}: {error}")] - CannotParseWasm { - file: std::path::PathBuf, - error: wasmparser::BinaryReaderError, - }, - #[error("xdr processing error: {0}")] - Xdr(#[from] stellar_xdr::curr::Error), - - #[error(transparent)] - Parser(#[from] wasmparser::BinaryReaderError), -} - -impl ContractSpec { - pub fn new(bytes: &[u8]) -> Result { - let mut env_meta: Option<&[u8]> = None; - let mut meta: Option<&[u8]> = None; - let mut spec: Option<&[u8]> = None; - for payload in wasmparser::Parser::new(0).parse_all(bytes) { - let payload = payload?; - if let wasmparser::Payload::CustomSection(section) = payload { - let out = match section.name() { - "contractenvmetav0" => &mut env_meta, - "contractmetav0" => &mut meta, - "contractspecv0" => &mut spec, - _ => continue, - }; - *out = Some(section.data()); - }; - } - - let mut env_meta_base64 = None; - let env_meta = if let Some(env_meta) = env_meta { - env_meta_base64 = Some(base64.encode(env_meta)); - let cursor = Cursor::new(env_meta); - let mut read = Limited::new(cursor, Limits::none()); - ScEnvMetaEntry::read_xdr_iter(&mut read).collect::, _>>()? - } else { - vec![] - }; - - let mut meta_base64 = None; - let meta = if let Some(meta) = meta { - meta_base64 = Some(base64.encode(meta)); - let cursor = Cursor::new(meta); - let mut read = Limited::new(cursor, Limits::none()); - ScMetaEntry::read_xdr_iter(&mut read).collect::, _>>()? - } else { - vec![] - }; - - let mut spec_base64 = None; - let spec = if let Some(spec) = spec { - spec_base64 = Some(base64.encode(spec)); - let cursor = Cursor::new(spec); - let mut read = Limited::new(cursor, Limits::none()); - ScSpecEntry::read_xdr_iter(&mut read).collect::, _>>()? - } else { - vec![] - }; - - Ok(ContractSpec { - env_meta_base64, - env_meta, - meta_base64, - meta, - spec_base64, - spec, - }) - } -} - -impl Display for ContractSpec { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if let Some(env_meta) = &self.env_meta_base64 { - writeln!(f, "Env Meta: {env_meta}")?; - for env_meta_entry in &self.env_meta { - match env_meta_entry { - ScEnvMetaEntry::ScEnvMetaKindInterfaceVersion(v) => { - writeln!(f, " • Interface Version: {v}")?; - } - } - } - writeln!(f)?; - } else { - writeln!(f, "Env Meta: None\n")?; - } - - if let Some(_meta) = &self.meta_base64 { - writeln!(f, "Contract Meta:")?; - for meta_entry in &self.meta { - match meta_entry { - ScMetaEntry::ScMetaV0(ScMetaV0 { key, val }) => { - writeln!(f, " • {key}: {val}")?; - } - } - } - writeln!(f)?; - } else { - writeln!(f, "Contract Meta: None\n")?; - } - - if let Some(_spec_base64) = &self.spec_base64 { - writeln!(f, "Contract Spec:")?; - for spec_entry in &self.spec { - match spec_entry { - ScSpecEntry::FunctionV0(func) => write_func(f, func)?, - ScSpecEntry::UdtUnionV0(udt) => write_union(f, udt)?, - ScSpecEntry::UdtStructV0(udt) => write_struct(f, udt)?, - ScSpecEntry::UdtEnumV0(udt) => write_enum(f, udt)?, - ScSpecEntry::UdtErrorEnumV0(udt) => write_error(f, udt)?, - } - } - } else { - writeln!(f, "Contract Spec: None")?; - } - Ok(()) - } -} - -fn write_func(f: &mut std::fmt::Formatter<'_>, func: &ScSpecFunctionV0) -> std::fmt::Result { - writeln!(f, " • Function: {}", func.name.to_utf8_string_lossy())?; - if func.doc.len() > 0 { - writeln!( - f, - " Docs: {}", - &indent(&func.doc.to_utf8_string_lossy(), 11).trim() - )?; - } - writeln!( - f, - " Inputs: {}", - indent(&format!("{:#?}", func.inputs), 5).trim() - )?; - writeln!( - f, - " Output: {}", - indent(&format!("{:#?}", func.outputs), 5).trim() - )?; - writeln!(f)?; - Ok(()) -} - -fn write_union(f: &mut std::fmt::Formatter<'_>, udt: &ScSpecUdtUnionV0) -> std::fmt::Result { - writeln!(f, " • Union: {}", format_name(&udt.lib, &udt.name))?; - if udt.doc.len() > 0 { - writeln!( - f, - " Docs: {}", - indent(&udt.doc.to_utf8_string_lossy(), 10).trim() - )?; - } - writeln!(f, " Cases:")?; - for case in udt.cases.iter() { - writeln!(f, " • {}", indent(&format!("{case:#?}"), 8).trim())?; - } - writeln!(f)?; - Ok(()) -} - -fn write_struct(f: &mut std::fmt::Formatter<'_>, udt: &ScSpecUdtStructV0) -> std::fmt::Result { - writeln!(f, " • Struct: {}", format_name(&udt.lib, &udt.name))?; - if udt.doc.len() > 0 { - writeln!( - f, - " Docs: {}", - indent(&udt.doc.to_utf8_string_lossy(), 10).trim() - )?; - } - writeln!(f, " Fields:")?; - for field in udt.fields.iter() { - writeln!( - f, - " • {}: {}", - field.name.to_utf8_string_lossy(), - indent(&format!("{:#?}", field.type_), 8).trim() - )?; - if field.doc.len() > 0 { - writeln!(f, "{}", indent(&format!("{:#?}", field.doc), 8))?; - } - } - writeln!(f)?; - Ok(()) -} - -fn write_enum(f: &mut std::fmt::Formatter<'_>, udt: &ScSpecUdtEnumV0) -> std::fmt::Result { - writeln!(f, " • Enum: {}", format_name(&udt.lib, &udt.name))?; - if udt.doc.len() > 0 { - writeln!( - f, - " Docs: {}", - indent(&udt.doc.to_utf8_string_lossy(), 10).trim() - )?; - } - writeln!(f, " Cases:")?; - for case in udt.cases.iter() { - writeln!(f, " • {}", indent(&format!("{case:#?}"), 8).trim())?; - } - writeln!(f)?; - Ok(()) -} - -fn write_error(f: &mut std::fmt::Formatter<'_>, udt: &ScSpecUdtErrorEnumV0) -> std::fmt::Result { - writeln!(f, " • Error: {}", format_name(&udt.lib, &udt.name))?; - if udt.doc.len() > 0 { - writeln!( - f, - " Docs: {}", - indent(&udt.doc.to_utf8_string_lossy(), 10).trim() - )?; - } - writeln!(f, " Cases:")?; - for case in udt.cases.iter() { - writeln!(f, " • {}", indent(&format!("{case:#?}"), 8).trim())?; - } - writeln!(f)?; - Ok(()) -} - -fn indent(s: &str, n: usize) -> String { - let pad = " ".repeat(n); - s.lines() - .map(|line| format!("{pad}{line}")) - .collect::>() - .join("\n") -} - -fn format_name(lib: &StringM<80>, name: &StringM<60>) -> String { - if lib.len() > 0 { - format!( - "{}::{}", - lib.to_utf8_string_lossy(), - name.to_utf8_string_lossy() - ) - } else { - name.to_utf8_string_lossy() - } -} /// # Errors /// From 1f20dcdd7ed3dd0325d1d51238eda72173c38570 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Wed, 2 Oct 2024 02:07:15 -0400 Subject: [PATCH 15/55] feat: add `tx::builder` module and initial classic commands (#1551) --- Cargo.lock | 8 + FULL_HELP_DOCS.md | 283 +++++++- cmd/crates/soroban-test/Cargo.toml | 1 + cmd/crates/soroban-test/src/lib.rs | 19 +- .../tests/it/integration/cookbook.rs | 29 +- .../tests/it/integration/hello_world.rs | 10 +- .../soroban-test/tests/it/integration/tx.rs | 10 +- .../tests/it/integration/tx/operations.rs | 616 ++++++++++++++++++ .../soroban-test/tests/it/integration/util.rs | 39 +- .../soroban-test/tests/it/integration/wrap.rs | 4 +- cmd/crates/soroban-test/tests/it/util.rs | 3 +- cmd/soroban-cli/Cargo.toml | 1 + .../src/commands/contract/alias/add.rs | 2 +- .../src/commands/contract/deploy/asset.rs | 38 +- .../src/commands/contract/deploy/wasm.rs | 42 +- .../src/commands/contract/extend.rs | 12 +- .../src/commands/contract/id/asset.rs | 9 +- .../src/commands/contract/id/wasm.rs | 27 +- .../src/commands/contract/install.rs | 30 +- .../src/commands/contract/restore.rs | 12 +- .../src/commands/snapshot/create.rs | 18 +- cmd/soroban-cli/src/commands/tx/args.rs | 107 +++ cmd/soroban-cli/src/commands/tx/mod.rs | 18 +- .../src/commands/tx/new/account_merge.rs | 19 + .../src/commands/tx/new/bump_sequence.rs | 21 + .../src/commands/tx/new/change_trust.rs | 29 + .../src/commands/tx/new/create_account.rs | 25 + .../src/commands/tx/new/manage_data.rs | 29 + cmd/soroban-cli/src/commands/tx/new/mod.rs | 69 ++ .../src/commands/tx/new/payment.rs | 29 + .../src/commands/tx/new/set_options.rs | 123 ++++ .../commands/tx/new/set_trustline_flags.rs | 71 ++ cmd/soroban-cli/src/config/address.rs | 63 ++ cmd/soroban-cli/src/config/locator.rs | 8 +- cmd/soroban-cli/src/config/mod.rs | 45 +- cmd/soroban-cli/src/lib.rs | 1 + cmd/soroban-cli/src/tx.rs | 4 + cmd/soroban-cli/src/tx/builder.rs | 11 + cmd/soroban-cli/src/tx/builder/asset.rs | 50 ++ cmd/soroban-cli/src/tx/builder/transaction.rs | 53 ++ cmd/soroban-cli/src/utils.rs | 111 +--- 41 files changed, 1830 insertions(+), 269 deletions(-) create mode 100644 cmd/crates/soroban-test/tests/it/integration/tx/operations.rs create mode 100644 cmd/soroban-cli/src/commands/tx/args.rs create mode 100644 cmd/soroban-cli/src/commands/tx/new/account_merge.rs create mode 100644 cmd/soroban-cli/src/commands/tx/new/bump_sequence.rs create mode 100644 cmd/soroban-cli/src/commands/tx/new/change_trust.rs create mode 100644 cmd/soroban-cli/src/commands/tx/new/create_account.rs create mode 100644 cmd/soroban-cli/src/commands/tx/new/manage_data.rs create mode 100644 cmd/soroban-cli/src/commands/tx/new/mod.rs create mode 100644 cmd/soroban-cli/src/commands/tx/new/payment.rs create mode 100644 cmd/soroban-cli/src/commands/tx/new/set_options.rs create mode 100644 cmd/soroban-cli/src/commands/tx/new/set_trustline_flags.rs create mode 100644 cmd/soroban-cli/src/config/address.rs create mode 100644 cmd/soroban-cli/src/tx.rs create mode 100644 cmd/soroban-cli/src/tx/builder.rs create mode 100644 cmd/soroban-cli/src/tx/builder/asset.rs create mode 100644 cmd/soroban-cli/src/tx/builder/transaction.rs diff --git a/Cargo.lock b/Cargo.lock index cd3761b51..21df05973 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1560,6 +1560,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fqdn" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb540cf7bc4fe6df9d8f7f0c974cfd0dce8ed4e9e8884e73433b503ee78b4e7d" + [[package]] name = "fs_extra" version = "1.3.0" @@ -5119,6 +5125,7 @@ dependencies = [ "ed25519-dalek 2.1.1", "ethnum", "flate2", + "fqdn", "futures", "futures-util", "gix", @@ -5409,6 +5416,7 @@ dependencies = [ "assert_fs", "ed25519-dalek 2.1.1", "fs_extra", + "hex", "predicates 2.1.5", "sep5", "serde_json", diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index 479ac6720..be20ae24a 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -119,7 +119,7 @@ Get Id of builtin Soroban Asset Contract. Deprecated, use `stellar contract id a * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail * `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." @@ -138,7 +138,7 @@ Deploy builtin Soroban Asset Contract * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail * `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." @@ -363,7 +363,7 @@ If no keys are specified the contract itself is extended. * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail * `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." @@ -391,7 +391,7 @@ Deploy a wasm contract * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail * `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." @@ -452,7 +452,7 @@ Deploy builtin Soroban Asset Contract * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail * `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." @@ -471,7 +471,7 @@ Deploy normal Wasm Contract * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail * `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." @@ -669,7 +669,7 @@ Install a WASM file to the ledger without creating a contract instance * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail * `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." @@ -708,7 +708,7 @@ stellar contract invoke ... -- --help * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail * `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." @@ -785,7 +785,7 @@ Print the current value of a contract-data ledger entry * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail * `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." @@ -822,7 +822,7 @@ If no keys are specificed the contract itself is restored. * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail * `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." @@ -1293,6 +1293,7 @@ Sign, Simulate, and Send transactions * `hash` — Calculate the hash of a transaction envelope from stdin * `sign` — Sign a transaction envelope appending the signature to the envelope * `send` — Send a transaction envelope to the network +* `new` — Create a new transaction @@ -1307,7 +1308,7 @@ Simulate a transaction envelope from stdin * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail * `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." @@ -1363,6 +1364,266 @@ Send a transaction envelope to the network +## `stellar tx new` + +Create a new transaction + +**Usage:** `stellar tx new ` + +###### **Subcommands:** + +* `account-merge` — Transfers the XLM balance of an account to another account and removes the source account from the ledger +* `bump-sequence` — Bumps forward the sequence number of the source account to the given sequence number, invalidating any transaction with a smaller sequence number +* `change-trust` — Creates, updates, or deletes a trustline Learn more about trustlines https://developers.stellar.org/docs/learn/fundamentals/stellar-data-structures/accounts#trustlines +* `create-account` — Creates and funds a new account with the specified starting balance +* `manage-data` — Sets, modifies, or deletes a data entry (name/value pair) that is attached to an account Learn more about entries and subentries: https://developers.stellar.org/docs/learn/fundamentals/stellar-data-structures/accounts#subentries +* `payment` — Sends an amount in a specific asset to a destination account +* `set-options` — Set option for an account such as flags, inflation destination, signers, home domain, and master key weight Learn more about flags: https://developers.stellar.org/docs/learn/glossary#flags Learn more about the home domain: https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0001.md Learn more about signers operations and key weight: https://developers.stellar.org/docs/learn/encyclopedia/security/signatures-multisig#multisig +* `set-trustline-flags` — Allows issuing account to configure authorization and trustline flags to an asset The Asset parameter is of the `TrustLineAsset` type. If you are modifying a trustline to a regular asset (i.e. one in a Code:Issuer format), this is equivalent to the Asset type. If you are modifying a trustline to a pool share, however, this is composed of the liquidity pool's unique ID. Learn more about flags: https://developers.stellar.org/docs/learn/glossary#flags + + + +## `stellar tx new account-merge` + +Transfers the XLM balance of an account to another account and removes the source account from the ledger + +**Usage:** `stellar tx new account-merge [OPTIONS] --source-account --account ` + +###### **Options:** + +* `--fee ` — fee amount for transaction, in stroops. 1 stroop = 0.0000001 xlm + + Default value: `100` +* `--cost` — Output the cost execution to stderr +* `--instructions ` — Number of instructions to simulate +* `--build-only` — Build the transaction and only write the base64 xdr to stdout +* `--sim-only` — Simulate the transaction and only write the base64 xdr to stdout +* `--rpc-url ` — RPC server endpoint +* `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server +* `--network ` — Name of network to use from config +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail +* `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` +* `--global` — Use global config +* `--config-dir ` — Location of config directory, default is "." +* `--account ` — Muxed Account to merge with, e.g. `GBX...`, 'MBX...' + + + +## `stellar tx new bump-sequence` + +Bumps forward the sequence number of the source account to the given sequence number, invalidating any transaction with a smaller sequence number + +**Usage:** `stellar tx new bump-sequence [OPTIONS] --source-account --bump-to ` + +###### **Options:** + +* `--fee ` — fee amount for transaction, in stroops. 1 stroop = 0.0000001 xlm + + Default value: `100` +* `--cost` — Output the cost execution to stderr +* `--instructions ` — Number of instructions to simulate +* `--build-only` — Build the transaction and only write the base64 xdr to stdout +* `--sim-only` — Simulate the transaction and only write the base64 xdr to stdout +* `--rpc-url ` — RPC server endpoint +* `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server +* `--network ` — Name of network to use from config +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail +* `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` +* `--global` — Use global config +* `--config-dir ` — Location of config directory, default is "." +* `--bump-to ` — Sequence number to bump to + + + +## `stellar tx new change-trust` + +Creates, updates, or deletes a trustline Learn more about trustlines https://developers.stellar.org/docs/learn/fundamentals/stellar-data-structures/accounts#trustlines + +**Usage:** `stellar tx new change-trust [OPTIONS] --source-account --line ` + +###### **Options:** + +* `--fee ` — fee amount for transaction, in stroops. 1 stroop = 0.0000001 xlm + + Default value: `100` +* `--cost` — Output the cost execution to stderr +* `--instructions ` — Number of instructions to simulate +* `--build-only` — Build the transaction and only write the base64 xdr to stdout +* `--sim-only` — Simulate the transaction and only write the base64 xdr to stdout +* `--rpc-url ` — RPC server endpoint +* `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server +* `--network ` — Name of network to use from config +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail +* `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` +* `--global` — Use global config +* `--config-dir ` — Location of config directory, default is "." +* `--line ` +* `--limit ` — Limit for the trust line, 0 to remove the trust line + + Default value: `18446744073709551615` + + + +## `stellar tx new create-account` + +Creates and funds a new account with the specified starting balance + +**Usage:** `stellar tx new create-account [OPTIONS] --source-account --destination ` + +###### **Options:** + +* `--fee ` — fee amount for transaction, in stroops. 1 stroop = 0.0000001 xlm + + Default value: `100` +* `--cost` — Output the cost execution to stderr +* `--instructions ` — Number of instructions to simulate +* `--build-only` — Build the transaction and only write the base64 xdr to stdout +* `--sim-only` — Simulate the transaction and only write the base64 xdr to stdout +* `--rpc-url ` — RPC server endpoint +* `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server +* `--network ` — Name of network to use from config +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail +* `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` +* `--global` — Use global config +* `--config-dir ` — Location of config directory, default is "." +* `--destination ` — Account Id to create, e.g. `GBX...` +* `--starting-balance ` — Initial balance in stroops of the account, default 1 XLM + + Default value: `10_000_000` + + + +## `stellar tx new manage-data` + +Sets, modifies, or deletes a data entry (name/value pair) that is attached to an account Learn more about entries and subentries: https://developers.stellar.org/docs/learn/fundamentals/stellar-data-structures/accounts#subentries + +**Usage:** `stellar tx new manage-data [OPTIONS] --source-account --data-name ` + +###### **Options:** + +* `--fee ` — fee amount for transaction, in stroops. 1 stroop = 0.0000001 xlm + + Default value: `100` +* `--cost` — Output the cost execution to stderr +* `--instructions ` — Number of instructions to simulate +* `--build-only` — Build the transaction and only write the base64 xdr to stdout +* `--sim-only` — Simulate the transaction and only write the base64 xdr to stdout +* `--rpc-url ` — RPC server endpoint +* `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server +* `--network ` — Name of network to use from config +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail +* `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` +* `--global` — Use global config +* `--config-dir ` — Location of config directory, default is "." +* `--data-name ` — Line to change, either 4 or 12 alphanumeric characters, or "native" if not specified +* `--data-value ` — Up to 64 bytes long hex string If not present then the existing Name will be deleted. If present then this value will be set in the `DataEntry` + + + +## `stellar tx new payment` + +Sends an amount in a specific asset to a destination account + +**Usage:** `stellar tx new payment [OPTIONS] --source-account --destination --amount ` + +###### **Options:** + +* `--fee ` — fee amount for transaction, in stroops. 1 stroop = 0.0000001 xlm + + Default value: `100` +* `--cost` — Output the cost execution to stderr +* `--instructions ` — Number of instructions to simulate +* `--build-only` — Build the transaction and only write the base64 xdr to stdout +* `--sim-only` — Simulate the transaction and only write the base64 xdr to stdout +* `--rpc-url ` — RPC server endpoint +* `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server +* `--network ` — Name of network to use from config +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail +* `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` +* `--global` — Use global config +* `--config-dir ` — Location of config directory, default is "." +* `--destination ` — Account to send to, e.g. `GBX...` +* `--asset ` — Asset to send, default native, e.i. XLM + + Default value: `native` +* `--amount ` — Amount of the aforementioned asset to send + + + +## `stellar tx new set-options` + +Set option for an account such as flags, inflation destination, signers, home domain, and master key weight Learn more about flags: https://developers.stellar.org/docs/learn/glossary#flags Learn more about the home domain: https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0001.md Learn more about signers operations and key weight: https://developers.stellar.org/docs/learn/encyclopedia/security/signatures-multisig#multisig + +**Usage:** `stellar tx new set-options [OPTIONS] --source-account ` + +###### **Options:** + +* `--fee ` — fee amount for transaction, in stroops. 1 stroop = 0.0000001 xlm + + Default value: `100` +* `--cost` — Output the cost execution to stderr +* `--instructions ` — Number of instructions to simulate +* `--build-only` — Build the transaction and only write the base64 xdr to stdout +* `--sim-only` — Simulate the transaction and only write the base64 xdr to stdout +* `--rpc-url ` — RPC server endpoint +* `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server +* `--network ` — Name of network to use from config +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail +* `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` +* `--global` — Use global config +* `--config-dir ` — Location of config directory, default is "." +* `--inflation-dest ` — Account of the inflation destination +* `--master-weight ` — A number from 0-255 (inclusive) representing the weight of the master key. If the weight of the master key is updated to 0, it is effectively disabled +* `--low-threshold ` — A number from 0-255 (inclusive) representing the threshold this account sets on all operations it performs that have a low threshold. https://developers.stellar.org/docs/learn/encyclopedia/security/signatures-multisig#multisig +* `--med-threshold ` — A number from 0-255 (inclusive) representing the threshold this account sets on all operations it performs that have a medium threshold. https://developers.stellar.org/docs/learn/encyclopedia/security/signatures-multisig#multisig +* `--high-threshold ` — A number from 0-255 (inclusive) representing the threshold this account sets on all operations it performs that have a high threshold. https://developers.stellar.org/docs/learn/encyclopedia/security/signatures-multisig#multisig +* `--home-domain ` — Sets the home domain of an account. See https://developers.stellar.org/docs/learn/encyclopedia/network-configuration/federation +* `--signer ` — Add, update, or remove a signer from an account +* `--signer-weight ` — Signer weight is a number from 0-255 (inclusive). The signer is deleted if the weight is 0 +* `--set-required` — When enabled, an issuer must approve an account before that account can hold its asset. https://developers.stellar.org/docs/tokens/control-asset-access#authorization-required-0x1 +* `--set-revocable` — When enabled, an issuer can revoke an existing trustline’s authorization, thereby freezing the asset held by an account. https://developers.stellar.org/docs/tokens/control-asset-access#authorization-revocable-0x2 +* `--set-clawback-enabled` — Enables the issuing account to take back (burning) all of the asset. https://developers.stellar.org/docs/tokens/control-asset-access#clawback-enabled-0x8 +* `--set-immutable` — With this setting, none of the other authorization flags (`AUTH_REQUIRED_FLAG`, `AUTH_REVOCABLE_FLAG`) can be set, and the issuing account can’t be merged. https://developers.stellar.org/docs/tokens/control-asset-access#authorization-immutable-0x4 +* `--clear-required` +* `--clear-revocable` +* `--clear-immutable` +* `--clear-clawback-enabled` + + + +## `stellar tx new set-trustline-flags` + +Allows issuing account to configure authorization and trustline flags to an asset The Asset parameter is of the `TrustLineAsset` type. If you are modifying a trustline to a regular asset (i.e. one in a Code:Issuer format), this is equivalent to the Asset type. If you are modifying a trustline to a pool share, however, this is composed of the liquidity pool's unique ID. Learn more about flags: https://developers.stellar.org/docs/learn/glossary#flags + +**Usage:** `stellar tx new set-trustline-flags [OPTIONS] --source-account --trustor --asset ` + +###### **Options:** + +* `--fee ` — fee amount for transaction, in stroops. 1 stroop = 0.0000001 xlm + + Default value: `100` +* `--cost` — Output the cost execution to stderr +* `--instructions ` — Number of instructions to simulate +* `--build-only` — Build the transaction and only write the base64 xdr to stdout +* `--sim-only` — Simulate the transaction and only write the base64 xdr to stdout +* `--rpc-url ` — RPC server endpoint +* `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server +* `--network ` — Name of network to use from config +* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail +* `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` +* `--global` — Use global config +* `--config-dir ` — Location of config directory, default is "." +* `--trustor ` — Account to set trustline flags for +* `--asset ` — Asset to set trustline flags for +* `--set-authorize` — Signifies complete authorization allowing an account to transact freely with the asset to make and receive payments and place orders +* `--set-authorize-to-maintain-liabilities` — Denotes limited authorization that allows an account to maintain current orders but not to otherwise transact with the asset +* `--set-trustline-clawback-enabled` — Enables the issuing account to take back (burning) all of the asset. See our section on Clawbacks: https://developers.stellar.org/docs/learn/encyclopedia/transactions-specialized/clawbacks +* `--clear-authorize` +* `--clear-authorize-to-maintain-liabilities` +* `--clear-trustline-clawback-enabled` + + + ## `stellar xdr` Decode and encode XDR diff --git a/cmd/crates/soroban-test/Cargo.toml b/cmd/crates/soroban-test/Cargo.toml index 09126adcf..a34b7c65c 100644 --- a/cmd/crates/soroban-test/Cargo.toml +++ b/cmd/crates/soroban-test/Cargo.toml @@ -43,6 +43,7 @@ tokio = "1.28.1" walkdir = "2.4.0" ulid.workspace = true ed25519-dalek = { workspace = true } +hex = { workspace = true } [features] it = [] diff --git a/cmd/crates/soroban-test/src/lib.rs b/cmd/crates/soroban-test/src/lib.rs index 544e2d59e..849b78796 100644 --- a/cmd/crates/soroban-test/src/lib.rs +++ b/cmd/crates/soroban-test/src/lib.rs @@ -59,6 +59,7 @@ pub enum Error { pub struct TestEnv { pub temp_dir: TempDir, pub rpc_url: String, + pub network_passphrase: String, } impl Default for TestEnv { @@ -67,6 +68,7 @@ impl Default for TestEnv { Self { temp_dir, rpc_url: "http://localhost:8889/soroban/rpc".to_string(), + network_passphrase: LOCAL_NETWORK_PASSPHRASE.to_string(), } } } @@ -100,10 +102,13 @@ impl TestEnv { } pub fn with_rpc_url(rpc_url: &str) -> TestEnv { - let env = TestEnv { + let mut env = TestEnv { rpc_url: rpc_url.to_string(), ..Default::default() }; + if let Ok(network_passphrase) = std::env::var("STELLAR_NETWORK_PASSPHRASE") { + env.network_passphrase = network_passphrase; + }; env.generate_account("test", None).assert().success(); env } @@ -112,6 +117,9 @@ impl TestEnv { if let Ok(rpc_url) = std::env::var("SOROBAN_RPC_URL") { return Self::with_rpc_url(&rpc_url); } + if let Ok(rpc_url) = std::env::var("STELLAR_RPC_URL") { + return Self::with_rpc_url(&rpc_url); + } let host_port = std::env::var("SOROBAN_PORT") .as_deref() .ok() @@ -193,7 +201,7 @@ impl TestEnv { command_str: &[I], source: &str, ) -> Result { - let cmd = self.cmd_with_config::(command_str); + let cmd = self.cmd_with_config::(command_str, None); self.run_cmd_with(cmd, source) .await .map(|r| r.into_result().unwrap()) @@ -203,12 +211,15 @@ impl TestEnv { pub fn cmd_with_config, T: CommandParser + NetworkRunnable>( &self, command_str: &[I], + source_account: Option<&str>, ) -> T { + let source = source_account.unwrap_or("test"); + let source_str = format!("--source-account={source}"); let mut arg = vec![ "--network=local", "--rpc-url=http", "--network-passphrase=AA", - "--source-account=test", + source_str.as_str(), ]; let input = command_str .iter() @@ -227,7 +238,7 @@ impl TestEnv { network_passphrase: Some(LOCAL_NETWORK_PASSPHRASE.to_string()), network: None, }, - source_account: account.to_string(), + source_account: account.parse().unwrap(), locator: config::locator::Args { global: false, config_dir, diff --git a/cmd/crates/soroban-test/tests/it/integration/cookbook.rs b/cmd/crates/soroban-test/tests/it/integration/cookbook.rs index 65855d775..4c0ad5a63 100644 --- a/cmd/crates/soroban-test/tests/it/integration/cookbook.rs +++ b/cmd/crates/soroban-test/tests/it/integration/cookbook.rs @@ -12,7 +12,8 @@ fn parse_command(command: &str) -> Vec { .collect() } -async fn run_command( +#[allow(clippy::too_many_lines, clippy::too_many_arguments)] +fn run_command( sandbox: &TestEnv, command: &str, wasm_path: &str, @@ -133,7 +134,8 @@ async fn run_command( Ok(()) } -async fn test_mdx_file( +#[allow(clippy::too_many_arguments)] +fn test_mdx_file( sandbox: &TestEnv, file_path: &str, wasm_path: &str, @@ -145,7 +147,7 @@ async fn test_mdx_file( key_xdr: &str, ) -> Result<(), String> { let content = fs::read_to_string(file_path) - .map_err(|e| format!("Failed to read file {}: {}", file_path, e))?; + .map_err(|e| format!("Failed to read file {file_path}: {e}"))?; let commands: Vec<&str> = content .split("```bash") @@ -153,7 +155,7 @@ async fn test_mdx_file( .filter_map(|block| block.split("```").next()) .collect(); - println!("Testing commands from file: {}", file_path); + println!("Testing commands from file: {file_path}"); for (i, command) in commands.iter().enumerate() { println!("Running command {}: {}", i + 1, command); @@ -167,8 +169,7 @@ async fn test_mdx_file( bob_id, native_id, key_xdr, - ) - .await?; + )?; } Ok(()) @@ -260,9 +261,7 @@ mod tests { let key_xdr = read_xdr.split(',').next().unwrap_or("").trim(); let repo_root = get_repo_root(); let docs_dir = repo_root.join("cookbook"); - if !docs_dir.is_dir() { - panic!("docs directory not found"); - } + assert!(docs_dir.is_dir(), "docs directory not found"); for entry in fs::read_dir(docs_dir).expect("Failed to read docs directory") { let entry = entry.expect("Failed to read directory entry"); @@ -273,18 +272,16 @@ mod tests { match test_mdx_file( &sandbox, file_path, - &wasm_path.to_str().unwrap(), + wasm_path.to_str().unwrap(), &wasm_hash, source, &contract_id, &bob_id, &native_id, - &key_xdr, - ) - .await - { - Ok(_) => println!("Successfully tested all commands in {}", file_path), - Err(e) => panic!("Error testing {}: {}", file_path, e), + key_xdr, + ) { + Ok(()) => println!("Successfully tested all commands in {file_path}"), + Err(e) => panic!("Error testing {file_path}: {e}"), } } } diff --git a/cmd/crates/soroban-test/tests/it/integration/hello_world.rs b/cmd/crates/soroban-test/tests/it/integration/hello_world.rs index c982b0c40..767c9a07c 100644 --- a/cmd/crates/soroban-test/tests/it/integration/hello_world.rs +++ b/cmd/crates/soroban-test/tests/it/integration/hello_world.rs @@ -4,7 +4,7 @@ use soroban_cli::{ contract::{self, fetch}, txn_result::TxnResult, }, - config::{locator, secret}, + config::{address::Address, locator, secret}, }; use soroban_rpc::GetLatestLedgerResponse; use soroban_test::{AssertExt, TestEnv, LOCAL_NETWORK_PASSPHRASE}; @@ -20,7 +20,7 @@ async fn invoke_view_with_non_existent_source_account() { let id = deploy_hello(sandbox).await; let world = "world"; let mut cmd = hello_world_cmd(&id, world); - cmd.config.source_account = String::new(); + cmd.config.source_account = Address::default(); cmd.is_view = true; let res = sandbox.run_cmd_with(cmd, "test").await.unwrap(); assert_eq!(res, TxnResult::Res(format!(r#"["Hello",{world:?}]"#))); @@ -94,7 +94,7 @@ async fn invoke() { sandbox .new_assert_cmd("events") .arg("--start-ledger") - .arg(&sequence.to_string()) + .arg(sequence.to_string()) .arg("--id") .arg(id) .assert() @@ -144,7 +144,7 @@ async fn invoke() { invoke_log(sandbox, id); } -fn invoke_hello_world(sandbox: &TestEnv, id: &str) { +pub(crate) fn invoke_hello_world(sandbox: &TestEnv, id: &str) { sandbox .new_assert_cmd("contract") .arg("invoke") @@ -324,7 +324,7 @@ async fn half_max_instructions() { .arg("--fee") .arg("1000000") .arg("--instructions") - .arg(&(u32::MAX / 2).to_string()) + .arg((u32::MAX / 2).to_string()) .arg("--wasm") .arg(wasm.path()) .arg("--ignore-checks") diff --git a/cmd/crates/soroban-test/tests/it/integration/tx.rs b/cmd/crates/soroban-test/tests/it/integration/tx.rs index ef2d1e26c..66c4b69fe 100644 --- a/cmd/crates/soroban-test/tests/it/integration/tx.rs +++ b/cmd/crates/soroban-test/tests/it/integration/tx.rs @@ -3,11 +3,15 @@ use soroban_test::{AssertExt, TestEnv}; use crate::integration::util::{deploy_contract, DeployKind, HELLO_WORLD}; +mod operations; + #[tokio::test] async fn simulate() { let sandbox = &TestEnv::new(); - let xdr_base64_build_only = deploy_contract(sandbox, HELLO_WORLD, DeployKind::BuildOnly).await; - let xdr_base64_sim_only = deploy_contract(sandbox, HELLO_WORLD, DeployKind::SimOnly).await; + let xdr_base64_build_only = + deploy_contract(sandbox, HELLO_WORLD, DeployKind::BuildOnly, None).await; + let xdr_base64_sim_only = + deploy_contract(sandbox, HELLO_WORLD, DeployKind::SimOnly, None).await; let tx_env = TransactionEnvelope::from_xdr_base64(&xdr_base64_build_only, Limits::none()).unwrap(); let tx = soroban_cli::commands::tx::xdr::unwrap_envelope_v1(tx_env).unwrap(); @@ -60,7 +64,7 @@ async fn build_simulate_sign_send() { .assert() .success(); - let tx_simulated = deploy_contract(sandbox, HELLO_WORLD, DeployKind::SimOnly).await; + let tx_simulated = deploy_contract(sandbox, HELLO_WORLD, DeployKind::SimOnly, None).await; dbg!("{tx_simulated}"); let tx_signed = sandbox diff --git a/cmd/crates/soroban-test/tests/it/integration/tx/operations.rs b/cmd/crates/soroban-test/tests/it/integration/tx/operations.rs new file mode 100644 index 000000000..9df808b34 --- /dev/null +++ b/cmd/crates/soroban-test/tests/it/integration/tx/operations.rs @@ -0,0 +1,616 @@ +use soroban_cli::{ + tx::{builder, ONE_XLM}, + utils::contract_id_hash_from_asset, +}; +use soroban_sdk::xdr::{self, ReadXdr, SequenceNumber}; +use soroban_test::{AssertExt, TestEnv}; + +use crate::integration::{ + hello_world::invoke_hello_world, + util::{deploy_contract, DeployKind, HELLO_WORLD}, +}; + +fn test_address(sandbox: &TestEnv) -> String { + sandbox + .new_assert_cmd("keys") + .arg("address") + .arg("test") + .assert() + .success() + .stdout_as_str() +} + +fn new_account(sandbox: &TestEnv, name: &str) -> String { + sandbox.generate_account(name, None).assert().success(); + sandbox + .new_assert_cmd("keys") + .args(["address", name]) + .assert() + .success() + .stdout_as_str() +} + +// returns test and test1 addresses +fn setup_accounts(sandbox: &TestEnv) -> (String, String) { + (test_address(sandbox), new_account(sandbox, "test1")) +} + +#[tokio::test] +async fn create_account() { + let sandbox = &TestEnv::new(); + sandbox + .new_assert_cmd("keys") + .args(["generate", "--no-fund", "new"]) + .assert() + .success(); + + let address = sandbox + .new_assert_cmd("keys") + .args(["address", "new"]) + .assert() + .success() + .stdout_as_str(); + let test = test_address(sandbox); + let client = soroban_rpc::Client::new(&sandbox.rpc_url).unwrap(); + let test_account = client.get_account(&test).await.unwrap(); + println!("test account has a balance of {}", test_account.balance); + let starting_balance = ONE_XLM * 100; + sandbox + .new_assert_cmd("tx") + .args([ + "new", + "create-account", + "--destination", + address.as_str(), + "--starting-balance", + starting_balance.to_string().as_str(), + ]) + .assert() + .success(); + let test_account_after = client.get_account(&test).await.unwrap(); + assert!(test_account_after.balance < test_account.balance); + let id = deploy_contract(sandbox, HELLO_WORLD, DeployKind::Normal, Some("new")).await; + println!("{id}"); + invoke_hello_world(sandbox, &id); +} + +#[tokio::test] +async fn payment() { + let sandbox = &TestEnv::new(); + let client = soroban_rpc::Client::new(&sandbox.rpc_url).unwrap(); + let (test, test1) = setup_accounts(sandbox); + let test_account = client.get_account(&test).await.unwrap(); + println!("test account has a balance of {}", test_account.balance); + + let before = client.get_account(&test).await.unwrap(); + let test1_account_entry_before = client.get_account(&test1).await.unwrap(); + + sandbox + .new_assert_cmd("tx") + .args([ + "new", + "payment", + "--destination", + test1.as_str(), + "--amount", + ONE_XLM.to_string().as_str(), + ]) + .assert() + .success(); + let test1_account_entry = client.get_account(&test1).await.unwrap(); + assert_eq!( + ONE_XLM, + test1_account_entry.balance - test1_account_entry_before.balance, + "Should have One XLM more" + ); + let after = client.get_account(&test).await.unwrap(); + assert_eq!(before.balance - 10_000_100, after.balance); +} + +#[tokio::test] +async fn bump_sequence() { + let sandbox = &TestEnv::new(); + let client = soroban_rpc::Client::new(&sandbox.rpc_url).unwrap(); + let test = test_address(sandbox); + let before = client.get_account(&test).await.unwrap(); + let amount = 50; + let seq = SequenceNumber(before.seq_num.0 + amount); + // bump sequence tx new + sandbox + .new_assert_cmd("tx") + .args([ + "new", + "bump-sequence", + "--bump-to", + seq.0.to_string().as_str(), + ]) + .assert() + .success(); + let after = client.get_account(&test).await.unwrap(); + assert_eq!(seq, after.seq_num); +} + +#[tokio::test] +async fn account_merge() { + let sandbox = &TestEnv::new(); + let client = soroban_rpc::Client::new(&sandbox.rpc_url).unwrap(); + let (test, test1) = setup_accounts(sandbox); + let before = client.get_account(&test).await.unwrap(); + let before1 = client.get_account(&test1).await.unwrap(); + let fee = 100; + sandbox + .new_assert_cmd("tx") + .args([ + "new", + "account-merge", + "--source", + "test1", + "--account", + test.as_str(), + "--fee", + fee.to_string().as_str(), + ]) + .assert() + .success(); + let after = client.get_account(&test).await.unwrap(); + assert!(client.get_account(&test1).await.is_err()); + assert_eq!(before.balance + before1.balance - fee, after.balance); +} + +#[tokio::test] +async fn set_trustline_flags() { + let sandbox = &TestEnv::new(); + let (test, issuer) = setup_accounts(sandbox); + let asset = format!("usdc:{issuer}"); + issue_asset(sandbox, &test, &asset, 100_000, 100).await; + sandbox + .new_assert_cmd("contract") + .arg("asset") + .arg("deploy") + .arg("--asset") + .arg(&asset) + .assert() + .success(); + let id = contract_id_hash_from_asset( + asset.parse::().unwrap(), + &sandbox.network_passphrase, + ); + // sandbox + // .new_assert_cmd("contract") + // .args([ + // "invoke", + // "--id", + // &id.to_string(), + // "--", + // "authorized", + // "--id", + // &test, + // ]) + // .assert() + // .success() + // .stdout("false\n"); + + sandbox + .new_assert_cmd("contract") + .args([ + "invoke", + "--id", + &id.to_string(), + "--", + "authorized", + "--id", + &test, + ]) + .assert() + .success() + .stdout("true\n"); +} + +#[tokio::test] +async fn set_options_add_signer() { + let sandbox = &TestEnv::new(); + let client = soroban_rpc::Client::new(&sandbox.rpc_url).unwrap(); + let (test, test1) = setup_accounts(sandbox); + let before = client.get_account(&test).await.unwrap(); + sandbox + .new_assert_cmd("tx") + .args([ + "new", + "set-options", + "--signer", + test1.as_str(), + "--signer-weight", + "1", + ]) + .assert() + .success(); + let after = client.get_account(&test).await.unwrap(); + assert_eq!(before.signers.len() + 1, after.signers.len()); + assert_eq!(after.signers.first().unwrap().key, test1.parse().unwrap()); + let key = xdr::LedgerKey::Account(xdr::LedgerKeyAccount { + account_id: test.parse().unwrap(), + }); + let res = client.get_ledger_entries(&[key]).await.unwrap(); + let xdr_str = res.entries.unwrap().clone().first().unwrap().clone().xdr; + let entry = xdr::LedgerEntryData::from_xdr_base64(&xdr_str, xdr::Limits::none()).unwrap(); + let xdr::LedgerEntryData::Account(xdr::AccountEntry { signers, .. }) = entry else { + panic!(); + }; + assert_eq!(signers.first().unwrap().key, test1.parse().unwrap()); + + // Now remove signer with a weight of 0 + sandbox + .new_assert_cmd("tx") + .args([ + "new", + "set-options", + "--signer", + test1.as_str(), + "--signer-weight", + "0", + ]) + .assert() + .success(); + let after = client.get_account(&test).await.unwrap(); + assert_eq!(before.signers.len(), after.signers.len()); +} + +fn build_and_run(sandbox: &TestEnv, cmd: &str, args: &[&str]) -> String { + let mut args_2 = args.to_vec(); + args_2.push("--build-only"); + let res = sandbox + .new_assert_cmd(cmd) + .args(args_2) + .assert() + .success() + .stdout_as_str(); + sandbox.new_assert_cmd(cmd).args(args).assert().success(); + res +} + +#[tokio::test] +async fn set_options() { + let sandbox = &TestEnv::new(); + let client = soroban_rpc::Client::new(&sandbox.rpc_url).unwrap(); + let (test, alice) = setup_accounts(sandbox); + let before = client.get_account(&test).await.unwrap(); + assert!(before.inflation_dest.is_none()); + let tx_xdr = build_and_run( + sandbox, + "tx", + &[ + "new", + "set-options", + "--inflation-dest", + alice.as_str(), + "--home-domain", + "test.com", + "--master-weight=100", + "--med-threshold=100", + "--low-threshold=100", + "--high-threshold=100", + "--signer", + alice.as_str(), + "--signer-weight=100", + "--set-required", + "--set-revocable", + "--set-clawback-enabled", + "--set-immutable", + ], + ); + println!("{tx_xdr}"); + let after = client.get_account(&test).await.unwrap(); + println!("{before:#?}\n{after:#?}"); + assert_eq!( + after.flags, + xdr::AccountFlags::ClawbackEnabledFlag as u32 + | xdr::AccountFlags::ImmutableFlag as u32 + | xdr::AccountFlags::RevocableFlag as u32 + | xdr::AccountFlags::RequiredFlag as u32 + ); + assert_eq!([100, 100, 100, 100], after.thresholds.0); + assert_eq!(100, after.signers[0].weight); + assert_eq!(alice, after.signers[0].key.to_string()); + let xdr::PublicKey::PublicKeyTypeEd25519(xdr::Uint256(key)) = after.inflation_dest.unwrap().0; + assert_eq!(alice, stellar_strkey::ed25519::PublicKey(key).to_string()); + assert_eq!("test.com", after.home_domain.to_string()); + sandbox + .new_assert_cmd("tx") + .args([ + "new", + "set-options", + "--inflation-dest", + test.as_str(), + "--home-domain", + "test.com", + "--master-weight=100", + "--med-threshold=100", + "--low-threshold=100", + "--high-threshold=100", + "--signer", + alice.as_str(), + "--signer-weight=100", + "--set-required", + "--set-revocable", + "--set-clawback-enabled", + ]) + .assert() + .failure(); +} + +#[tokio::test] +async fn set_some_options() { + let sandbox = &TestEnv::new(); + let client = soroban_rpc::Client::new(&sandbox.rpc_url).unwrap(); + let test = test_address(sandbox); + let before = client.get_account(&test).await.unwrap(); + assert!(before.inflation_dest.is_none()); + sandbox + .new_assert_cmd("tx") + .args([ + "new", + "set-options", + "--set-clawback-enabled", + "--master-weight=100", + ]) + .assert() + .failure() + .stderr(predicates::str::contains("AuthRevocableRequired")); + sandbox + .new_assert_cmd("tx") + .args([ + "new", + "set-options", + "--set-revocable", + "--master-weight=100", + ]) + .assert() + .success(); + let after = client.get_account(&test).await.unwrap(); + assert_eq!(after.flags, xdr::AccountFlags::RevocableFlag as u32); + assert_eq!([100, 0, 0, 0], after.thresholds.0); + assert!(after.inflation_dest.is_none()); + assert_eq!( + after.home_domain, + "".parse::>().unwrap().into() + ); + assert!(after.signers.is_empty()); + sandbox + .new_assert_cmd("tx") + .args(["new", "set-options", "--set-clawback-enabled"]) + .assert() + .success(); + let after = client.get_account(&test).await.unwrap(); + assert_eq!( + after.flags, + xdr::AccountFlags::RevocableFlag as u32 | xdr::AccountFlags::ClawbackEnabledFlag as u32 + ); + sandbox + .new_assert_cmd("tx") + .args(["new", "set-options", "--clear-clawback-enabled"]) + .assert() + .success(); + let after = client.get_account(&test).await.unwrap(); + assert_eq!(after.flags, xdr::AccountFlags::RevocableFlag as u32); + sandbox + .new_assert_cmd("tx") + .args(["new", "set-options", "--clear-revocable"]) + .assert() + .success(); + let after = client.get_account(&test).await.unwrap(); + assert_eq!(after.flags, 0); + sandbox + .new_assert_cmd("tx") + .args(["new", "set-options", "--set-required"]) + .assert() + .success(); + let after = client.get_account(&test).await.unwrap(); + assert_eq!(after.flags, xdr::AccountFlags::RequiredFlag as u32); + sandbox + .new_assert_cmd("tx") + .args(["new", "set-options", "--clear-required"]) + .assert() + .success(); + let after = client.get_account(&test).await.unwrap(); + assert_eq!(after.flags, 0); +} + +#[tokio::test] +async fn change_trust() { + let sandbox = &TestEnv::new(); + let (test, issuer) = setup_accounts(sandbox); + let asset = &format!("usdc:{issuer}"); + + let limit = 100_000_000; + let half_limit = limit / 2; + issue_asset(sandbox, &test, asset, limit, half_limit).await; + sandbox + .new_assert_cmd("contract") + .arg("asset") + .arg("deploy") + .arg("--asset") + .arg(asset) + .assert() + .success(); + + // wrap_cmd(&asset).run().await.unwrap(); + let id = contract_id_hash_from_asset( + asset.parse::().unwrap(), + &sandbox.network_passphrase, + ); + sandbox + .new_assert_cmd("contract") + .args([ + "invoke", + "--id", + &id.to_string(), + "--", + "balance", + "--id", + &test, + ]) + .assert() + .stdout(format!("\"{half_limit}\"\n")); + + let bob = new_account(sandbox, "bob"); + let bobs_limit = half_limit / 2; + sandbox + .new_assert_cmd("tx") + .args([ + "new", + "change-trust", + "--source=bob", + "--line", + asset, + "--limit", + bobs_limit.to_string().as_str(), + ]) + .assert() + .success(); + sandbox + .new_assert_cmd("tx") + .args([ + "new", + "payment", + "--destination", + &bob, + "--asset", + asset, + "--amount", + half_limit.to_string().as_str(), + ]) + .assert() + .failure(); + sandbox + .new_assert_cmd("tx") + .args([ + "new", + "payment", + "--destination", + &bob, + "--asset", + asset, + "--amount", + bobs_limit.to_string().as_str(), + ]) + .assert() + .success(); + sandbox + .new_assert_cmd("contract") + .args([ + "invoke", + "--id", + &id.to_string(), + "--", + "balance", + "--id", + &bob, + ]) + .assert() + .stdout(format!("\"{bobs_limit}\"\n")); +} + +#[tokio::test] +async fn manage_data() { + let sandbox = &TestEnv::new(); + let (test, _) = setup_accounts(sandbox); + let client = soroban_rpc::Client::new(&sandbox.rpc_url).unwrap(); + let key = "test"; + let value = "beefface"; + sandbox + .new_assert_cmd("tx") + .args([ + "new", + "manage-data", + "--data-name", + key, + "--data-value", + value, + ]) + .assert() + .success(); + let account_id = xdr::AccountId(xdr::PublicKey::PublicKeyTypeEd25519(xdr::Uint256( + stellar_strkey::ed25519::PublicKey::from_string(&test) + .unwrap() + .0, + ))); + let orig_data_name: xdr::StringM<64> = key.parse().unwrap(); + let res = client + .get_ledger_entries(&[xdr::LedgerKey::Data(xdr::LedgerKeyData { + account_id, + data_name: orig_data_name.clone().into(), + })]) + .await + .unwrap(); + let value_res = res.entries.as_ref().unwrap().first().unwrap(); + let ledeger_entry_data = + xdr::LedgerEntryData::from_xdr_base64(&value_res.xdr, xdr::Limits::none()).unwrap(); + let xdr::LedgerEntryData::Data(xdr::DataEntry { + data_value, + data_name, + .. + }) = ledeger_entry_data + else { + panic!("Expected DataEntry"); + }; + assert_eq!(data_name, orig_data_name.into()); + assert_eq!(hex::encode(data_value.0.to_vec()), value); +} + +async fn issue_asset(sandbox: &TestEnv, test: &str, asset: &str, limit: u64, initial_balance: u64) { + let client = soroban_rpc::Client::new(&sandbox.rpc_url).unwrap(); + let test_before = client.get_account(test).await.unwrap(); + sandbox + .new_assert_cmd("tx") + .args([ + "new", + "change-trust", + "--line", + asset, + "--limit", + limit.to_string().as_str(), + ]) + .assert() + .success(); + + sandbox + .new_assert_cmd("tx") + .args(["new", "set-options", "--set-required"]) + .assert() + .success(); + sandbox + .new_assert_cmd("tx") + .args([ + "new", + "set-trustline-flags", + "--asset", + asset, + "--trustor", + test, + "--set-authorize", + "--source", + "test1", + ]) + .assert() + .success(); + + let after = client.get_account(test).await.unwrap(); + assert_eq!(test_before.num_sub_entries + 1, after.num_sub_entries); + println!("aa"); + // Send a payment to the issuer + sandbox + .new_assert_cmd("tx") + .args([ + "new", + "payment", + "--destination", + test, + "--asset", + asset, + "--amount", + initial_balance.to_string().as_str(), + "--source=test1", + ]) + .assert() + .success(); +} diff --git a/cmd/crates/soroban-test/tests/it/integration/util.rs b/cmd/crates/soroban-test/tests/it/integration/util.rs index dec7941f6..22ef4fa82 100644 --- a/cmd/crates/soroban-test/tests/it/integration/util.rs +++ b/cmd/crates/soroban-test/tests/it/integration/util.rs @@ -40,37 +40,44 @@ impl Display for DeployKind { } pub async fn deploy_hello(sandbox: &TestEnv) -> String { - deploy_contract(sandbox, HELLO_WORLD, DeployKind::Normal).await + deploy_contract(sandbox, HELLO_WORLD, DeployKind::Normal, None).await } pub async fn deploy_custom(sandbox: &TestEnv) -> String { - deploy_contract(sandbox, CUSTOM_TYPES, DeployKind::Normal).await + deploy_contract(sandbox, CUSTOM_TYPES, DeployKind::Normal, None).await } pub async fn deploy_swap(sandbox: &TestEnv) -> String { - deploy_contract(sandbox, SWAP, DeployKind::Normal).await + deploy_contract(sandbox, SWAP, DeployKind::Normal, None).await } pub async fn deploy_custom_account(sandbox: &TestEnv) -> String { - deploy_contract(sandbox, CUSTOM_ACCOUNT, DeployKind::Normal).await + deploy_contract(sandbox, CUSTOM_ACCOUNT, DeployKind::Normal, None).await } pub async fn deploy_contract( sandbox: &TestEnv, wasm: &Wasm<'static>, deploy: DeployKind, + deployer: Option<&str>, ) -> String { - let cmd = sandbox.cmd_with_config::<_, commands::contract::deploy::wasm::Cmd>(&[ - "--fee", - "1000000", - "--wasm", - &wasm.path().to_string_lossy(), - "--salt", - TEST_SALT, - "--ignore-checks", - deploy.to_string().as_str(), - ]); - let res = sandbox.run_cmd_with(cmd, "test").await.unwrap(); + let cmd = sandbox.cmd_with_config::<_, commands::contract::deploy::wasm::Cmd>( + &[ + "--fee", + "1000000", + "--wasm", + &wasm.path().to_string_lossy(), + "--salt", + TEST_SALT, + "--ignore-checks", + &deploy.to_string(), + ], + None, + ); + let res = sandbox + .run_cmd_with(cmd, deployer.unwrap_or("test")) + .await + .unwrap(); match deploy { DeployKind::BuildOnly | DeployKind::SimOnly => match res.to_envelope() { commands::txn_result::TxnEnvelopeResult::TxnEnvelope(e) => { @@ -80,7 +87,7 @@ pub async fn deploy_contract( }, DeployKind::Normal => (), } - res.into_result().unwrap() + res.into_result().unwrap().to_string() } pub async fn extend_contract(sandbox: &TestEnv, id: &str) { diff --git a/cmd/crates/soroban-test/tests/it/integration/wrap.rs b/cmd/crates/soroban-test/tests/it/integration/wrap.rs index c43f5b007..3fef56ef9 100644 --- a/cmd/crates/soroban-test/tests/it/integration/wrap.rs +++ b/cmd/crates/soroban-test/tests/it/integration/wrap.rs @@ -1,4 +1,4 @@ -use soroban_cli::utils::contract_id_hash_from_asset; +use soroban_cli::{tx::builder, utils::contract_id_hash_from_asset}; use soroban_test::{AssertExt, TestEnv, LOCAL_NETWORK_PASSPHRASE}; #[tokio::test] @@ -23,7 +23,7 @@ async fn burn() { .assert() .success(); // wrap_cmd(&asset).run().await.unwrap(); - let asset = soroban_cli::utils::parsing::parse_asset(&asset).unwrap(); + let asset: builder::Asset = asset.parse().unwrap(); let hash = contract_id_hash_from_asset(&asset, &network_passphrase); let id = stellar_strkey::Contract(hash.0).to_string(); println!("{id}, {address}"); diff --git a/cmd/crates/soroban-test/tests/it/util.rs b/cmd/crates/soroban-test/tests/it/util.rs index f424ea1ae..a74428666 100644 --- a/cmd/crates/soroban-test/tests/it/util.rs +++ b/cmd/crates/soroban-test/tests/it/util.rs @@ -51,7 +51,8 @@ pub async fn invoke_custom( arg: &str, wasm: &Path, ) -> Result { - let mut i: contract::invoke::Cmd = sandbox.cmd_with_config(&["--id", id, "--", func, arg]); + let mut i: contract::invoke::Cmd = + sandbox.cmd_with_config(&["--id", id, "--", func, arg], None); i.wasm = Some(wasm.to_path_buf()); sandbox .run_cmd_with(i, TEST_ACCOUNT) diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index 75b562bb6..5f4cc7c5a 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -123,6 +123,7 @@ humantime = "2.1.0" phf = { version = "0.11.2", features = ["macros"] } semver = "1.0.0" glob = "0.3.1" +fqdn = "0.3.12" open = "5.3.0" url = "2.5.2" diff --git a/cmd/soroban-cli/src/commands/contract/alias/add.rs b/cmd/soroban-cli/src/commands/contract/alias/add.rs index d5c304452..5a7a17ddb 100644 --- a/cmd/soroban-cli/src/commands/contract/alias/add.rs +++ b/cmd/soroban-cli/src/commands/contract/alias/add.rs @@ -73,7 +73,7 @@ impl Cmd { self.config_locator.save_contract_id( &network.network_passphrase, - &self.contract_id.to_string(), + &self.contract_id, alias, )?; diff --git a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs index da7b6b805..d2fcb62c4 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs @@ -4,7 +4,7 @@ use soroban_env_host::{ Asset, ContractDataDurability, ContractExecutable, ContractIdPreimage, CreateContractArgs, Error as XdrError, Hash, HostFunction, InvokeHostFunctionOp, LedgerKey::ContractData, LedgerKeyContractData, Limits, Memo, MuxedAccount, Operation, OperationBody, Preconditions, - ScAddress, ScVal, SequenceNumber, Transaction, TransactionExt, Uint256, VecM, WriteXdr, + ScAddress, ScVal, SequenceNumber, Transaction, TransactionExt, VecM, WriteXdr, }, HostError, }; @@ -19,7 +19,8 @@ use crate::{ }, config::{self, data, network}, rpc::{Client, Error as SorobanRpcError}, - utils::{contract_id_hash_from_asset, parsing::parse_asset}, + tx::builder, + utils::contract_id_hash_from_asset, }; #[derive(thiserror::Error, Debug)] @@ -39,11 +40,11 @@ pub enum Error { #[error(transparent)] Config(#[from] config::Error), #[error(transparent)] - ParseAssetError(#[from] crate::utils::parsing::Error), - #[error(transparent)] Data(#[from] data::Error), #[error(transparent)] Network(#[from] network::Error), + #[error(transparent)] + Builder(#[from] builder::Error), } impl From for Error { @@ -57,7 +58,7 @@ impl From for Error { pub struct Cmd { /// ID of the Stellar classic asset to wrap, e.g. "USDC:G...5" #[arg(long)] - pub asset: String, + pub asset: builder::Asset, #[command(flatten)] pub config: config::Args, @@ -90,29 +91,29 @@ impl NetworkRunnable for Cmd { ) -> Result { let config = config.unwrap_or(&self.config); // Parse asset - let asset = parse_asset(&self.asset)?; + let asset = &self.asset; let network = config.get_network()?; let client = Client::new(&network.rpc_url)?; client .verify_network_passphrase(Some(&network.network_passphrase)) .await?; - let key = config.source_account()?; + let source_account = config.source_account()?; // Get the account sequence number - let public_strkey = key.to_string(); + let public_strkey = source_account.to_string(); // TODO: use symbols for the method names (both here and in serve) let account_details = client.get_account(&public_strkey).await?; let sequence: i64 = account_details.seq_num.into(); let network_passphrase = &network.network_passphrase; - let contract_id = contract_id_hash_from_asset(&asset, network_passphrase); + let contract_id = contract_id_hash_from_asset(asset, network_passphrase); let tx = build_wrap_token_tx( - &asset, + asset, &contract_id, sequence + 1, self.fee.fee, network_passphrase, - &key, + source_account, )?; if self.fee.build_only { return Ok(TxnResult::Txn(tx)); @@ -135,14 +136,14 @@ impl NetworkRunnable for Cmd { } fn build_wrap_token_tx( - asset: &Asset, - contract_id: &Hash, + asset: impl Into, + contract_id: &stellar_strkey::Contract, sequence: i64, fee: u32, _network_passphrase: &str, - key: &stellar_strkey::ed25519::PublicKey, + source_account: MuxedAccount, ) -> Result { - let contract = ScAddress::Contract(contract_id.clone()); + let contract = ScAddress::Contract(Hash(contract_id.0)); let mut read_write = vec![ ContractData(LedgerKeyContractData { contract: contract.clone(), @@ -157,7 +158,8 @@ fn build_wrap_token_tx( durability: ContractDataDurability::Persistent, }), ]; - if asset != &Asset::Native { + let asset = asset.into(); + if asset != Asset::Native { read_write.push(ContractData(LedgerKeyContractData { contract, key: ScVal::Vec(Some( @@ -171,7 +173,7 @@ fn build_wrap_token_tx( source_account: None, body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { host_function: HostFunction::CreateContract(CreateContractArgs { - contract_id_preimage: ContractIdPreimage::Asset(asset.clone()), + contract_id_preimage: ContractIdPreimage::Asset(asset), executable: ContractExecutable::StellarAsset, }), auth: VecM::default(), @@ -179,7 +181,7 @@ fn build_wrap_token_tx( }; Ok(Transaction { - source_account: MuxedAccount::Ed25519(Uint256(key.0)), + source_account, fee, seq_num: SequenceNumber(sequence), cond: Preconditions::None, diff --git a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs index 84138d432..42b31dd7d 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs @@ -115,6 +115,8 @@ pub enum Error { InvalidAliasFormat { alias: String }, #[error(transparent)] Locator(#[from] locator::Error), + #[error("Only ed25519 accounts are allowed")] + OnlyEd25519AccountsAllowed, } impl Cmd { @@ -158,13 +160,13 @@ fn alias_validator(alias: &str) -> Result { #[async_trait::async_trait] impl NetworkRunnable for Cmd { type Error = Error; - type Result = TxnResult; + type Result = TxnResult; async fn run_against_rpc_server( &self, global_args: Option<&global::Args>, config: Option<&config::Args>, - ) -> Result, Error> { + ) -> Result, Error> { let print = Print::new(global_args.map_or(false, |a| a.quiet)); let config = config.unwrap_or(&self.config); let wasm_hash = if let Some(wasm) = &self.wasm { @@ -190,12 +192,14 @@ impl NetworkRunnable for Cmd { .to_string() }; - let wasm_hash = Hash(utils::contract_id_from_str(&wasm_hash).map_err(|e| { - Error::CannotParseWasmHash { - wasm_hash: wasm_hash.clone(), - error: e, - } - })?); + let wasm_hash = Hash( + utils::contract_id_from_str(&wasm_hash) + .map_err(|e| Error::CannotParseWasmHash { + wasm_hash: wasm_hash.clone(), + error: e, + })? + .0, + ); print.infoln(format!("Using wasm hash {wasm_hash}").as_str()); @@ -212,11 +216,13 @@ impl NetworkRunnable for Cmd { client .verify_network_passphrase(Some(&network.network_passphrase)) .await?; - let key = config.source_account()?; + let MuxedAccount::Ed25519(bytes) = config.source_account()? else { + return Err(Error::OnlyEd25519AccountsAllowed); + }; + let key = stellar_strkey::ed25519::PublicKey(bytes.into()); // Get the account sequence number - let public_strkey = key.to_string(); - let account_details = client.get_account(&public_strkey).await?; + let account_details = client.get_account(&key.to_string()).await?; let sequence: i64 = account_details.seq_num.into(); let (txn, contract_id) = build_create_contract_tx( wasm_hash, @@ -224,7 +230,7 @@ impl NetworkRunnable for Cmd { self.fee.fee, &network.network_passphrase, salt, - &key, + key, )?; if self.fee.build_only { @@ -254,8 +260,6 @@ impl NetworkRunnable for Cmd { data::write(get_txn_resp, &network.rpc_uri()?)?; } - let contract_id = stellar_strkey::Contract(contract_id.0).to_string(); - if let Some(url) = utils::explorer_url_for_contract(&network, &contract_id) { print.linkln(url); } @@ -272,8 +276,8 @@ fn build_create_contract_tx( fee: u32, network_passphrase: &str, salt: [u8; 32], - key: &stellar_strkey::ed25519::PublicKey, -) -> Result<(Transaction, Hash), Error> { + key: stellar_strkey::ed25519::PublicKey, +) -> Result<(Transaction, stellar_strkey::Contract), Error> { let source_account = AccountId(PublicKey::PublicKeyTypeEd25519(key.0.into())); let contract_id_preimage = ContractIdPreimage::Address(ContractIdPreimageFromAddress { @@ -293,7 +297,7 @@ fn build_create_contract_tx( }), }; let tx = Transaction { - source_account: MuxedAccount::Ed25519(Uint256(key.0)), + source_account: MuxedAccount::Ed25519(key.0.into()), fee, seq_num: SequenceNumber(sequence), cond: Preconditions::None, @@ -302,7 +306,7 @@ fn build_create_contract_tx( ext: TransactionExt::V0, }; - Ok((tx, Hash(contract_id.into()))) + Ok((tx, contract_id)) } #[cfg(test)] @@ -321,7 +325,7 @@ mod tests { 1, "Public Global Stellar Network ; September 2015", [0u8; 32], - &stellar_strkey::ed25519::PublicKey( + stellar_strkey::ed25519::PublicKey( utils::parse_secret_key("SBFGFF27Y64ZUGFAIG5AMJGQODZZKV2YQKAVUUN4HNE24XZXD2OEUVUP") .unwrap() .verifying_key() diff --git a/cmd/soroban-cli/src/commands/contract/extend.rs b/cmd/soroban-cli/src/commands/contract/extend.rs index 6666fcce7..b06cacf3e 100644 --- a/cmd/soroban-cli/src/commands/contract/extend.rs +++ b/cmd/soroban-cli/src/commands/contract/extend.rs @@ -3,9 +3,9 @@ use std::{fmt::Debug, path::Path, str::FromStr}; use clap::{command, Parser}; use soroban_env_host::xdr::{ Error as XdrError, ExtendFootprintTtlOp, ExtensionPoint, LedgerEntry, LedgerEntryChange, - LedgerEntryData, LedgerFootprint, Limits, Memo, MuxedAccount, Operation, OperationBody, - Preconditions, SequenceNumber, SorobanResources, SorobanTransactionData, Transaction, - TransactionExt, TransactionMeta, TransactionMetaV3, TtlEntry, Uint256, WriteXdr, + LedgerEntryData, LedgerFootprint, Limits, Memo, Operation, OperationBody, Preconditions, + SequenceNumber, SorobanResources, SorobanTransactionData, Transaction, TransactionExt, + TransactionMeta, TransactionMetaV3, TtlEntry, WriteXdr, }; use crate::{ @@ -132,15 +132,15 @@ impl NetworkRunnable for Cmd { tracing::trace!(?network); let keys = self.key.parse_keys(&config.locator, &network)?; let client = Client::new(&network.rpc_url)?; - let key = config.source_account()?; + let source_account = config.source_account()?; let extend_to = self.ledgers_to_extend(); // Get the account sequence number - let account_details = client.get_account(&key.to_string()).await?; + let account_details = client.get_account(&source_account.to_string()).await?; let sequence: i64 = account_details.seq_num.into(); let tx = Transaction { - source_account: MuxedAccount::Ed25519(Uint256(key.0)), + source_account, fee: self.fee.fee, seq_num: SequenceNumber(sequence + 1), cond: Preconditions::None, diff --git a/cmd/soroban-cli/src/commands/contract/id/asset.rs b/cmd/soroban-cli/src/commands/contract/id/asset.rs index e57385859..63b3017a1 100644 --- a/cmd/soroban-cli/src/commands/contract/id/asset.rs +++ b/cmd/soroban-cli/src/commands/contract/id/asset.rs @@ -2,23 +2,21 @@ use clap::{arg, command, Parser}; use crate::config; +use crate::tx::builder; use crate::utils::contract_id_hash_from_asset; -use crate::utils::parsing::parse_asset; #[derive(Parser, Debug, Clone)] #[group(skip)] pub struct Cmd { /// ID of the Stellar classic asset to wrap, e.g. "USDC:G...5" #[arg(long)] - pub asset: String, + pub asset: builder::Asset, #[command(flatten)] pub config: config::Args, } #[derive(thiserror::Error, Debug)] pub enum Error { - #[error(transparent)] - ParseError(#[from] crate::utils::parsing::Error), #[error(transparent)] ConfigError(#[from] config::Error), #[error(transparent)] @@ -31,9 +29,8 @@ impl Cmd { } pub fn contract_address(&self) -> Result { - let asset = parse_asset(&self.asset)?; let network = self.config.get_network()?; - let contract_id = contract_id_hash_from_asset(&asset, &network.network_passphrase); + let contract_id = contract_id_hash_from_asset(&self.asset, &network.network_passphrase); Ok(stellar_strkey::Contract(contract_id.0)) } } diff --git a/cmd/soroban-cli/src/commands/contract/id/wasm.rs b/cmd/soroban-cli/src/commands/contract/id/wasm.rs index 14824b145..c020e94b2 100644 --- a/cmd/soroban-cli/src/commands/contract/id/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/id/wasm.rs @@ -19,14 +19,14 @@ pub struct Cmd { } #[derive(thiserror::Error, Debug)] pub enum Error { - #[error(transparent)] - ParseError(#[from] crate::utils::parsing::Error), #[error(transparent)] ConfigError(#[from] config::Error), #[error(transparent)] Xdr(#[from] xdr::Error), #[error("cannot parse salt {0}")] CannotParseSalt(String), + #[error("only Ed25519 accounts are allowed")] + OnlyEd25519AccountsAllowed, } impl Cmd { pub fn run(&self) -> Result<(), Error> { @@ -34,20 +34,25 @@ impl Cmd { .map_err(|_| Error::CannotParseSalt(self.salt.clone()))? .try_into() .map_err(|_| Error::CannotParseSalt(self.salt.clone()))?; - let contract_id_preimage = - contract_preimage(&self.config.key_pair()?.verifying_key(), salt); + let source_account = match self.config.source_account()? { + xdr::MuxedAccount::Ed25519(uint256) => stellar_strkey::ed25519::PublicKey(uint256.0), + xdr::MuxedAccount::MuxedEd25519(_) => return Err(Error::OnlyEd25519AccountsAllowed), + }; + let contract_id_preimage = contract_preimage(&source_account, salt); let contract_id = get_contract_id( contract_id_preimage.clone(), &self.config.get_network()?.network_passphrase, )?; - let strkey_contract_id = stellar_strkey::Contract(contract_id.0).to_string(); - println!("{strkey_contract_id}"); + println!("{contract_id}"); Ok(()) } } -pub fn contract_preimage(key: &ed25519_dalek::VerifyingKey, salt: [u8; 32]) -> ContractIdPreimage { - let source_account = AccountId(PublicKey::PublicKeyTypeEd25519(key.to_bytes().into())); +pub fn contract_preimage( + key: &stellar_strkey::ed25519::PublicKey, + salt: [u8; 32], +) -> ContractIdPreimage { + let source_account = AccountId(PublicKey::PublicKeyTypeEd25519(key.0.into())); ContractIdPreimage::Address(ContractIdPreimageFromAddress { address: ScAddress::Account(source_account), salt: Uint256(salt), @@ -57,12 +62,14 @@ pub fn contract_preimage(key: &ed25519_dalek::VerifyingKey, salt: [u8; 32]) -> C pub fn get_contract_id( contract_id_preimage: ContractIdPreimage, network_passphrase: &str, -) -> Result { +) -> Result { let network_id = Hash(Sha256::digest(network_passphrase.as_bytes()).into()); let preimage = HashIdPreimage::ContractId(HashIdPreimageContractId { network_id, contract_id_preimage, }); let preimage_xdr = preimage.to_xdr(Limits::none())?; - Ok(Hash(Sha256::digest(preimage_xdr).into())) + Ok(stellar_strkey::Contract( + Sha256::digest(preimage_xdr).into(), + )) } diff --git a/cmd/soroban-cli/src/commands/contract/install.rs b/cmd/soroban-cli/src/commands/contract/install.rs index 5a1842b7b..9e8f6b098 100644 --- a/cmd/soroban-cli/src/commands/contract/install.rs +++ b/cmd/soroban-cli/src/commands/contract/install.rs @@ -5,9 +5,8 @@ use std::num::ParseIntError; use clap::{command, Parser}; use soroban_env_host::xdr::{ self, ContractCodeEntryExt, Error as XdrError, Hash, HostFunction, InvokeHostFunctionOp, - LedgerEntryData, Limits, Memo, MuxedAccount, Operation, OperationBody, Preconditions, ReadXdr, - ScMetaEntry, ScMetaV0, SequenceNumber, Transaction, TransactionExt, TransactionResult, - TransactionResultResult, Uint256, VecM, WriteXdr, + LedgerEntryData, Limits, OperationBody, ReadXdr, ScMetaEntry, ScMetaV0, Transaction, + TransactionResult, TransactionResultResult, VecM, WriteXdr, }; use super::restore; @@ -17,6 +16,7 @@ use crate::config::{self, data, network}; use crate::key; use crate::print::Print; use crate::rpc::{self, Client}; +use crate::tx::builder::{self, TxExt}; use crate::{utils, wasm}; const CONTRACT_META_SDK_KEY: &str = "rssdkver"; @@ -70,6 +70,8 @@ pub enum Error { Network(#[from] network::Error), #[error(transparent)] Data(#[from] data::Error), + #[error(transparent)] + Builder(#[from] builder::Error), } impl Cmd { @@ -253,27 +255,18 @@ pub(crate) fn build_install_contract_code_tx( source_code: &[u8], sequence: i64, fee: u32, - key: &stellar_strkey::ed25519::PublicKey, -) -> Result<(Transaction, Hash), XdrError> { + source: &xdr::MuxedAccount, +) -> Result<(Transaction, Hash), Error> { let hash = utils::contract_hash(source_code)?; - let op = Operation { - source_account: Some(MuxedAccount::Ed25519(Uint256(key.0))), + let op = xdr::Operation { + source_account: None, body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { host_function: HostFunction::UploadContractWasm(source_code.try_into()?), auth: VecM::default(), }), }; - - let tx = Transaction { - source_account: MuxedAccount::Ed25519(Uint256(key.0)), - fee, - seq_num: SequenceNumber(sequence), - cond: Preconditions::None, - memo: Memo::None, - operations: vec![op].try_into()?, - ext: TransactionExt::V0, - }; + let tx = Transaction::new_tx(source.clone(), fee, sequence, op); Ok((tx, hash)) } @@ -294,6 +287,9 @@ mod tests { .verifying_key() .as_bytes(), ) + .unwrap() + .to_string() + .parse() .unwrap(), ); diff --git a/cmd/soroban-cli/src/commands/contract/restore.rs b/cmd/soroban-cli/src/commands/contract/restore.rs index cf6a33248..05fa90695 100644 --- a/cmd/soroban-cli/src/commands/contract/restore.rs +++ b/cmd/soroban-cli/src/commands/contract/restore.rs @@ -3,9 +3,9 @@ use std::{fmt::Debug, path::Path, str::FromStr}; use clap::{command, Parser}; use soroban_env_host::xdr::{ Error as XdrError, ExtensionPoint, LedgerEntry, LedgerEntryChange, LedgerEntryData, - LedgerFootprint, Limits, Memo, MuxedAccount, Operation, OperationBody, OperationMeta, - Preconditions, RestoreFootprintOp, SequenceNumber, SorobanResources, SorobanTransactionData, - Transaction, TransactionExt, TransactionMeta, TransactionMetaV3, TtlEntry, Uint256, WriteXdr, + LedgerFootprint, Limits, Memo, Operation, OperationBody, OperationMeta, Preconditions, + RestoreFootprintOp, SequenceNumber, SorobanResources, SorobanTransactionData, Transaction, + TransactionExt, TransactionMeta, TransactionMetaV3, TtlEntry, WriteXdr, }; use stellar_strkey::DecodeError; @@ -135,15 +135,15 @@ impl NetworkRunnable for Cmd { tracing::trace!(?network); let entry_keys = self.key.parse_keys(&config.locator, &network)?; let client = Client::new(&network.rpc_url)?; - let key = config.source_account()?; + let source_account = config.source_account()?; // Get the account sequence number - let public_strkey = key.to_string(); + let public_strkey = source_account.to_string(); let account_details = client.get_account(&public_strkey).await?; let sequence: i64 = account_details.seq_num.into(); let tx = Transaction { - source_account: MuxedAccount::Ed25519(Uint256(key.0)), + source_account, fee: self.fee.fee, seq_num: SequenceNumber(sequence + 1), cond: Preconditions::None, diff --git a/cmd/soroban-cli/src/commands/snapshot/create.rs b/cmd/soroban-cli/src/commands/snapshot/create.rs index ee0f5f520..8a587e243 100644 --- a/cmd/soroban-cli/src/commands/snapshot/create.rs +++ b/cmd/soroban-cli/src/commands/snapshot/create.rs @@ -29,7 +29,8 @@ use crate::{ commands::{config::data, global, HEADING_RPC}, config::{self, locator, network::passphrase}, print, - utils::{get_name_from_stellar_asset_contract_storage, parsing::parse_asset}, + tx::builder, + utils::get_name_from_stellar_asset_contract_storage, }; #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)] @@ -131,6 +132,8 @@ pub enum Error { ArchiveUrlNotConfigured, #[error("parsing asset name: {0}")] ParseAssetName(String), + #[error(transparent)] + Asset(#[from] builder::asset::Error), } /// Checkpoint frequency is usually 64 ledgers, but in local test nets it'll @@ -310,16 +313,11 @@ impl Cmd { if let Some(name) = get_name_from_stellar_asset_contract_storage(storage) { - let asset = parse_asset(&name) - .map_err(|_| Error::ParseAssetName(name))?; - if let Some(issuer) = match &asset { + let asset: builder::Asset = name.parse()?; + if let Some(issuer) = match asset.into() { Asset::Native => None, - Asset::CreditAlphanum4(a4) => { - Some(a4.issuer.clone()) - } - Asset::CreditAlphanum12(a12) => { - Some(a12.issuer.clone()) - } + Asset::CreditAlphanum4(a4) => Some(a4.issuer), + Asset::CreditAlphanum12(a12) => Some(a12.issuer), } { print.infoln(format!( "Adding asset issuer {issuer} to search" diff --git a/cmd/soroban-cli/src/commands/tx/args.rs b/cmd/soroban-cli/src/commands/tx/args.rs new file mode 100644 index 000000000..7e032fd53 --- /dev/null +++ b/cmd/soroban-cli/src/commands/tx/args.rs @@ -0,0 +1,107 @@ +use crate::{ + commands::{global, txn_result::TxnEnvelopeResult}, + config::{self, data, network, secret}, + fee, + rpc::{self, Client, GetTransactionResponse}, + tx::builder::{self, TxExt}, + xdr::{self, Limits, WriteXdr}, +}; + +#[derive(Debug, clap::Args, Clone)] +#[group(skip)] +pub struct Args { + #[clap(flatten)] + pub fee: fee::Args, + #[clap(flatten)] + pub config: config::Args, +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Rpc(#[from] rpc::Error), + #[error(transparent)] + Config(#[from] config::Error), + #[error(transparent)] + Network(#[from] network::Error), + #[error(transparent)] + Secret(#[from] secret::Error), + #[error(transparent)] + Tx(#[from] builder::Error), + #[error(transparent)] + Data(#[from] data::Error), + #[error(transparent)] + Xdr(#[from] xdr::Error), +} + +impl Args { + pub async fn tx(&self, body: impl Into) -> Result { + let source_account = self.source_account()?; + let seq_num = self + .config + .next_sequence_number(&source_account.to_string()) + .await?; + // Once we have a way to add operations this will be updated to allow for a different source account + let operation = xdr::Operation { + source_account: None, + body: body.into(), + }; + Ok(xdr::Transaction::new_tx( + source_account, + self.fee.fee, + seq_num, + operation, + )) + } + + pub fn client(&self) -> Result { + let network = self.config.get_network()?; + Ok(Client::new(&network.rpc_url)?) + } + + pub async fn handle( + &self, + op: impl Into, + global_args: &global::Args, + ) -> Result, Error> { + let tx = self.tx(op.into()).await?; + self.handle_tx(tx, global_args).await + } + pub async fn handle_and_print( + &self, + op: impl Into, + global_args: &global::Args, + ) -> Result<(), Error> { + let res = self.handle(op, global_args).await?; + if let TxnEnvelopeResult::TxnEnvelope(tx) = res { + println!("{}", tx.to_xdr_base64(Limits::none())?); + }; + Ok(()) + } + + pub async fn handle_tx( + &self, + tx: xdr::Transaction, + args: &global::Args, + ) -> Result, Error> { + let network = self.config.get_network()?; + let client = Client::new(&network.rpc_url)?; + if self.fee.build_only { + return Ok(TxnEnvelopeResult::TxnEnvelope(tx.into())); + } + + let txn_resp = client + .send_transaction_polling(&self.config.sign_with_local_key(tx).await?) + .await?; + + if !args.no_cache { + data::write(txn_resp.clone().try_into().unwrap(), &network.rpc_uri()?)?; + } + + Ok(TxnEnvelopeResult::Res(txn_resp)) + } + + pub fn source_account(&self) -> Result { + Ok(self.config.source_account()?) + } +} diff --git a/cmd/soroban-cli/src/commands/tx/mod.rs b/cmd/soroban-cli/src/commands/tx/mod.rs index 587a75fda..c0390f92e 100644 --- a/cmd/soroban-cli/src/commands/tx/mod.rs +++ b/cmd/soroban-cli/src/commands/tx/mod.rs @@ -1,14 +1,16 @@ -use clap::Parser; - use super::global; +pub mod args; pub mod hash; +pub mod new; pub mod send; pub mod sign; pub mod simulate; pub mod xdr; -#[derive(Debug, Parser)] +pub use args::Args; + +#[derive(Debug, clap::Subcommand)] pub enum Cmd { /// Simulate a transaction envelope from stdin Simulate(simulate::Cmd), @@ -18,15 +20,20 @@ pub enum Cmd { Sign(sign::Cmd), /// Send a transaction envelope to the network Send(send::Cmd), + /// Create a new transaction + #[command(subcommand)] + New(new::Cmd), } #[derive(thiserror::Error, Debug)] pub enum Error { - #[error(transparent)] - Simulate(#[from] simulate::Error), #[error(transparent)] Hash(#[from] hash::Error), #[error(transparent)] + New(#[from] new::Error), + #[error(transparent)] + Simulate(#[from] simulate::Error), + #[error(transparent)] Sign(#[from] sign::Error), #[error(transparent)] Send(#[from] send::Error), @@ -37,6 +44,7 @@ impl Cmd { match self { Cmd::Simulate(cmd) => cmd.run(global_args).await?, Cmd::Hash(cmd) => cmd.run(global_args)?, + Cmd::New(cmd) => cmd.run(global_args).await?, Cmd::Sign(cmd) => cmd.run(global_args).await?, Cmd::Send(cmd) => cmd.run(global_args).await?, }; diff --git a/cmd/soroban-cli/src/commands/tx/new/account_merge.rs b/cmd/soroban-cli/src/commands/tx/new/account_merge.rs new file mode 100644 index 000000000..ce01f5e1f --- /dev/null +++ b/cmd/soroban-cli/src/commands/tx/new/account_merge.rs @@ -0,0 +1,19 @@ +use clap::{command, Parser}; + +use crate::{commands::tx, xdr}; + +#[derive(Parser, Debug, Clone)] +#[group(skip)] +pub struct Cmd { + #[command(flatten)] + pub tx: tx::Args, + /// Muxed Account to merge with, e.g. `GBX...`, 'MBX...' + #[arg(long)] + pub account: xdr::MuxedAccount, +} + +impl From<&Cmd> for xdr::OperationBody { + fn from(cmd: &Cmd) -> Self { + xdr::OperationBody::AccountMerge(cmd.account.clone()) + } +} diff --git a/cmd/soroban-cli/src/commands/tx/new/bump_sequence.rs b/cmd/soroban-cli/src/commands/tx/new/bump_sequence.rs new file mode 100644 index 000000000..dfb521f23 --- /dev/null +++ b/cmd/soroban-cli/src/commands/tx/new/bump_sequence.rs @@ -0,0 +1,21 @@ +use clap::{command, Parser}; + +use crate::{commands::tx, xdr}; + +#[derive(Parser, Debug, Clone)] +#[group(skip)] +pub struct Cmd { + #[command(flatten)] + pub tx: tx::Args, + /// Sequence number to bump to + #[arg(long)] + pub bump_to: i64, +} + +impl From<&Cmd> for xdr::OperationBody { + fn from(cmd: &Cmd) -> Self { + xdr::OperationBody::BumpSequence(xdr::BumpSequenceOp { + bump_to: cmd.bump_to.into(), + }) + } +} diff --git a/cmd/soroban-cli/src/commands/tx/new/change_trust.rs b/cmd/soroban-cli/src/commands/tx/new/change_trust.rs new file mode 100644 index 000000000..1ea4e737e --- /dev/null +++ b/cmd/soroban-cli/src/commands/tx/new/change_trust.rs @@ -0,0 +1,29 @@ +use clap::{command, Parser}; + +use crate::{commands::tx, tx::builder, xdr}; + +#[derive(Parser, Debug, Clone)] +#[group(skip)] +pub struct Cmd { + #[command(flatten)] + pub tx: tx::Args, + #[arg(long)] + pub line: builder::Asset, + /// Limit for the trust line, 0 to remove the trust line + #[arg(long, default_value = u64::MAX.to_string())] + pub limit: i64, +} + +impl From<&Cmd> for xdr::OperationBody { + fn from(cmd: &Cmd) -> Self { + let line = match cmd.line.0.clone() { + xdr::Asset::CreditAlphanum4(asset) => xdr::ChangeTrustAsset::CreditAlphanum4(asset), + xdr::Asset::CreditAlphanum12(asset) => xdr::ChangeTrustAsset::CreditAlphanum12(asset), + xdr::Asset::Native => xdr::ChangeTrustAsset::Native, + }; + xdr::OperationBody::ChangeTrust(xdr::ChangeTrustOp { + line, + limit: cmd.limit, + }) + } +} diff --git a/cmd/soroban-cli/src/commands/tx/new/create_account.rs b/cmd/soroban-cli/src/commands/tx/new/create_account.rs new file mode 100644 index 000000000..967c0cf43 --- /dev/null +++ b/cmd/soroban-cli/src/commands/tx/new/create_account.rs @@ -0,0 +1,25 @@ +use clap::{command, Parser}; + +use crate::{commands::tx, xdr}; + +#[derive(Parser, Debug, Clone)] +#[group(skip)] +pub struct Cmd { + #[command(flatten)] + pub tx: tx::Args, + /// Account Id to create, e.g. `GBX...` + #[arg(long)] + pub destination: xdr::AccountId, + /// Initial balance in stroops of the account, default 1 XLM + #[arg(long, default_value = "10_000_000")] + pub starting_balance: i64, +} + +impl From<&Cmd> for xdr::OperationBody { + fn from(cmd: &Cmd) -> Self { + xdr::OperationBody::CreateAccount(xdr::CreateAccountOp { + destination: cmd.destination.clone(), + starting_balance: cmd.starting_balance, + }) + } +} diff --git a/cmd/soroban-cli/src/commands/tx/new/manage_data.rs b/cmd/soroban-cli/src/commands/tx/new/manage_data.rs new file mode 100644 index 000000000..e0a029f02 --- /dev/null +++ b/cmd/soroban-cli/src/commands/tx/new/manage_data.rs @@ -0,0 +1,29 @@ +use clap::{command, Parser}; + +use crate::{commands::tx, xdr}; + +#[derive(Parser, Debug, Clone)] +#[group(skip)] +pub struct Cmd { + #[command(flatten)] + pub tx: tx::Args, + /// Line to change, either 4 or 12 alphanumeric characters, or "native" if not specified + #[arg(long)] + pub data_name: xdr::StringM<64>, + /// Up to 64 bytes long hex string + /// If not present then the existing Name will be deleted. + /// If present then this value will be set in the `DataEntry`. + #[arg(long)] + pub data_value: Option>, +} + +impl From<&Cmd> for xdr::OperationBody { + fn from(cmd: &Cmd) -> Self { + let data_value = cmd.data_value.clone().map(Into::into); + let data_name = cmd.data_name.clone().into(); + xdr::OperationBody::ManageData(xdr::ManageDataOp { + data_name, + data_value, + }) + } +} diff --git a/cmd/soroban-cli/src/commands/tx/new/mod.rs b/cmd/soroban-cli/src/commands/tx/new/mod.rs new file mode 100644 index 000000000..e5923f4ec --- /dev/null +++ b/cmd/soroban-cli/src/commands/tx/new/mod.rs @@ -0,0 +1,69 @@ +use clap::Parser; + +use super::global; + +mod account_merge; +mod bump_sequence; +mod change_trust; +mod create_account; +mod manage_data; +mod payment; +mod set_options; +mod set_trustline_flags; + +#[derive(Debug, Parser)] +#[allow(clippy::doc_markdown)] +pub enum Cmd { + /// Transfers the XLM balance of an account to another account and removes the source account from the ledger + AccountMerge(account_merge::Cmd), + /// Bumps forward the sequence number of the source account to the given sequence number, invalidating any transaction with a smaller sequence number + BumpSequence(bump_sequence::Cmd), + /// Creates, updates, or deletes a trustline + /// Learn more about trustlines + /// https://developers.stellar.org/docs/learn/fundamentals/stellar-data-structures/accounts#trustlines + ChangeTrust(change_trust::Cmd), + /// Creates and funds a new account with the specified starting balance + CreateAccount(create_account::Cmd), + /// Sets, modifies, or deletes a data entry (name/value pair) that is attached to an account + /// Learn more about entries and subentries: + /// https://developers.stellar.org/docs/learn/fundamentals/stellar-data-structures/accounts#subentries + ManageData(manage_data::Cmd), + /// Sends an amount in a specific asset to a destination account + Payment(payment::Cmd), + /// Set option for an account such as flags, inflation destination, signers, home domain, and master key weight + /// Learn more about flags: + /// https://developers.stellar.org/docs/learn/glossary#flags + /// Learn more about the home domain: + /// https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0001.md + /// Learn more about signers operations and key weight: + /// https://developers.stellar.org/docs/learn/encyclopedia/security/signatures-multisig#multisig + SetOptions(set_options::Cmd), + /// Allows issuing account to configure authorization and trustline flags to an asset + /// The Asset parameter is of the `TrustLineAsset` type. If you are modifying a trustline to a regular asset (i.e. one in a Code:Issuer format), this is equivalent to the Asset type. + /// If you are modifying a trustline to a pool share, however, this is composed of the liquidity pool's unique ID. + /// Learn more about flags: + /// https://developers.stellar.org/docs/learn/glossary#flags + SetTrustlineFlags(set_trustline_flags::Cmd), +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Tx(#[from] super::args::Error), +} + +impl Cmd { + pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { + match self { + Cmd::AccountMerge(cmd) => cmd.tx.handle_and_print(cmd, global_args).await, + Cmd::BumpSequence(cmd) => cmd.tx.handle_and_print(cmd, global_args).await, + Cmd::ChangeTrust(cmd) => cmd.tx.handle_and_print(cmd, global_args).await, + Cmd::CreateAccount(cmd) => cmd.tx.handle_and_print(cmd, global_args).await, + Cmd::ManageData(cmd) => cmd.tx.handle_and_print(cmd, global_args).await, + Cmd::Payment(cmd) => cmd.tx.handle_and_print(cmd, global_args).await, + Cmd::SetOptions(cmd) => cmd.tx.handle_and_print(cmd, global_args).await, + Cmd::SetTrustlineFlags(cmd) => cmd.tx.handle_and_print(cmd, global_args).await, + }?; + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/tx/new/payment.rs b/cmd/soroban-cli/src/commands/tx/new/payment.rs new file mode 100644 index 000000000..c626d9ca8 --- /dev/null +++ b/cmd/soroban-cli/src/commands/tx/new/payment.rs @@ -0,0 +1,29 @@ +use clap::{command, Parser}; + +use crate::{commands::tx, tx::builder, xdr}; + +#[derive(Parser, Debug, Clone)] +#[group(skip)] +pub struct Cmd { + #[command(flatten)] + pub tx: tx::Args, + /// Account to send to, e.g. `GBX...` + #[arg(long)] + pub destination: xdr::MuxedAccount, + /// Asset to send, default native, e.i. XLM + #[arg(long, default_value = "native")] + pub asset: builder::Asset, + /// Amount of the aforementioned asset to send. + #[arg(long)] + pub amount: i64, +} + +impl From<&Cmd> for xdr::OperationBody { + fn from(cmd: &Cmd) -> Self { + xdr::OperationBody::Payment(xdr::PaymentOp { + destination: cmd.destination.clone(), + asset: cmd.asset.clone().into(), + amount: cmd.amount, + }) + } +} diff --git a/cmd/soroban-cli/src/commands/tx/new/set_options.rs b/cmd/soroban-cli/src/commands/tx/new/set_options.rs new file mode 100644 index 000000000..3410b69e8 --- /dev/null +++ b/cmd/soroban-cli/src/commands/tx/new/set_options.rs @@ -0,0 +1,123 @@ +use clap::{command, Parser}; + +use crate::{commands::tx, xdr}; + +#[derive(Parser, Debug, Clone)] +#[allow(clippy::struct_excessive_bools, clippy::doc_markdown)] +#[group(skip)] +pub struct Cmd { + #[command(flatten)] + pub tx: tx::Args, + #[arg(long)] + /// Account of the inflation destination. + pub inflation_dest: Option, + #[arg(long)] + /// A number from 0-255 (inclusive) representing the weight of the master key. If the weight of the master key is updated to 0, it is effectively disabled. + pub master_weight: Option, + #[arg(long)] + /// A number from 0-255 (inclusive) representing the threshold this account sets on all operations it performs that have a low threshold. + /// https://developers.stellar.org/docs/learn/encyclopedia/security/signatures-multisig#multisig + pub low_threshold: Option, + #[arg(long)] + /// A number from 0-255 (inclusive) representing the threshold this account sets on all operations it performs that have a medium threshold. + /// https://developers.stellar.org/docs/learn/encyclopedia/security/signatures-multisig#multisig + pub med_threshold: Option, + #[arg(long)] + /// A number from 0-255 (inclusive) representing the threshold this account sets on all operations it performs that have a high threshold. + /// https://developers.stellar.org/docs/learn/encyclopedia/security/signatures-multisig#multisig + pub high_threshold: Option, + #[arg(long)] + /// Sets the home domain of an account. See https://developers.stellar.org/docs/learn/encyclopedia/network-configuration/federation. + pub home_domain: Option>, + #[arg(long, requires = "signer_weight")] + /// Add, update, or remove a signer from an account. + pub signer: Option, + #[arg(long = "signer-weight", requires = "signer")] + /// Signer weight is a number from 0-255 (inclusive). The signer is deleted if the weight is 0. + pub signer_weight: Option, + #[arg(long, conflicts_with = "clear_required")] + /// When enabled, an issuer must approve an account before that account can hold its asset. + /// https://developers.stellar.org/docs/tokens/control-asset-access#authorization-required-0x1 + pub set_required: bool, + #[arg(long, conflicts_with = "clear_revocable")] + /// When enabled, an issuer can revoke an existing trustline’s authorization, thereby freezing the asset held by an account. + /// https://developers.stellar.org/docs/tokens/control-asset-access#authorization-revocable-0x2 + pub set_revocable: bool, + #[arg(long, conflicts_with = "clear_clawback_enabled")] + /// Enables the issuing account to take back (burning) all of the asset. + /// https://developers.stellar.org/docs/tokens/control-asset-access#clawback-enabled-0x8 + pub set_clawback_enabled: bool, + #[arg(long, conflicts_with = "clear_immutable")] + /// With this setting, none of the other authorization flags (`AUTH_REQUIRED_FLAG`, `AUTH_REVOCABLE_FLAG`) can be set, and the issuing account can’t be merged. + /// https://developers.stellar.org/docs/tokens/control-asset-access#authorization-immutable-0x4 + pub set_immutable: bool, + #[arg(long)] + pub clear_required: bool, + #[arg(long)] + pub clear_revocable: bool, + #[arg(long)] + pub clear_immutable: bool, + #[arg(long)] + pub clear_clawback_enabled: bool, +} + +impl From<&Cmd> for xdr::OperationBody { + fn from(cmd: &Cmd) -> Self { + let mut set_flags = None; + let mut set_flag = |flag: xdr::AccountFlags| { + *set_flags.get_or_insert(0) |= flag as u32; + }; + + if cmd.set_required { + set_flag(xdr::AccountFlags::RequiredFlag); + }; + if cmd.set_revocable { + set_flag(xdr::AccountFlags::RevocableFlag); + }; + if cmd.set_immutable { + set_flag(xdr::AccountFlags::ImmutableFlag); + }; + if cmd.set_clawback_enabled { + set_flag(xdr::AccountFlags::ClawbackEnabledFlag); + }; + + let mut clear_flags = None; + let mut clear_flag = |flag: xdr::AccountFlags| { + *clear_flags.get_or_insert(0) |= flag as u32; + }; + if cmd.clear_required { + clear_flag(xdr::AccountFlags::RequiredFlag); + }; + if cmd.clear_revocable { + clear_flag(xdr::AccountFlags::RevocableFlag); + }; + if cmd.clear_immutable { + clear_flag(xdr::AccountFlags::ImmutableFlag); + }; + if cmd.clear_clawback_enabled { + clear_flag(xdr::AccountFlags::ClawbackEnabledFlag); + }; + + let signer = if let (Some(key), Some(signer_weight)) = + (cmd.signer.clone(), cmd.signer_weight.as_ref()) + { + Some(xdr::Signer { + key, + weight: u32::from(*signer_weight), + }) + } else { + None + }; + xdr::OperationBody::SetOptions(xdr::SetOptionsOp { + inflation_dest: cmd.inflation_dest.clone().map(Into::into), + clear_flags, + set_flags, + master_weight: cmd.master_weight.map(Into::into), + low_threshold: cmd.low_threshold.map(Into::into), + med_threshold: cmd.med_threshold.map(Into::into), + high_threshold: cmd.high_threshold.map(Into::into), + home_domain: cmd.home_domain.clone().map(Into::into), + signer, + }) + } +} diff --git a/cmd/soroban-cli/src/commands/tx/new/set_trustline_flags.rs b/cmd/soroban-cli/src/commands/tx/new/set_trustline_flags.rs new file mode 100644 index 000000000..2955fe5b0 --- /dev/null +++ b/cmd/soroban-cli/src/commands/tx/new/set_trustline_flags.rs @@ -0,0 +1,71 @@ +use clap::{command, Parser}; + +use soroban_sdk::xdr::{self}; + +use crate::{commands::tx, tx::builder}; + +#[allow(clippy::struct_excessive_bools, clippy::doc_markdown)] +#[derive(Parser, Debug, Clone)] +#[group(skip)] +pub struct Cmd { + #[command(flatten)] + pub tx: tx::Args, + /// Account to set trustline flags for + #[arg(long)] + pub trustor: xdr::AccountId, + /// Asset to set trustline flags for + #[arg(long)] + pub asset: builder::Asset, + #[arg(long, conflicts_with = "clear_authorize")] + /// Signifies complete authorization allowing an account to transact freely with the asset to make and receive payments and place orders. + pub set_authorize: bool, + #[arg(long, conflicts_with = "clear_authorize_to_maintain_liabilities")] + /// Denotes limited authorization that allows an account to maintain current orders but not to otherwise transact with the asset. + pub set_authorize_to_maintain_liabilities: bool, + #[arg(long, conflicts_with = "clear_trustline_clawback_enabled")] + /// Enables the issuing account to take back (burning) all of the asset. See our section on Clawbacks: + /// https://developers.stellar.org/docs/learn/encyclopedia/transactions-specialized/clawbacks + pub set_trustline_clawback_enabled: bool, + #[arg(long)] + pub clear_authorize: bool, + #[arg(long)] + pub clear_authorize_to_maintain_liabilities: bool, + #[arg(long)] + pub clear_trustline_clawback_enabled: bool, +} + +impl From<&Cmd> for xdr::OperationBody { + fn from(cmd: &Cmd) -> Self { + let mut set_flags = 0; + let mut set_flag = |flag: xdr::TrustLineFlags| set_flags |= flag as u32; + + if cmd.set_authorize { + set_flag(xdr::TrustLineFlags::AuthorizedFlag); + }; + if cmd.set_authorize_to_maintain_liabilities { + set_flag(xdr::TrustLineFlags::AuthorizedToMaintainLiabilitiesFlag); + }; + if cmd.set_trustline_clawback_enabled { + set_flag(xdr::TrustLineFlags::TrustlineClawbackEnabledFlag); + }; + + let mut clear_flags = 0; + let mut clear_flag = |flag: xdr::TrustLineFlags| clear_flags |= flag as u32; + if cmd.clear_authorize { + clear_flag(xdr::TrustLineFlags::AuthorizedFlag); + }; + if cmd.clear_authorize_to_maintain_liabilities { + clear_flag(xdr::TrustLineFlags::AuthorizedToMaintainLiabilitiesFlag); + }; + if cmd.clear_trustline_clawback_enabled { + clear_flag(xdr::TrustLineFlags::TrustlineClawbackEnabledFlag); + }; + + xdr::OperationBody::SetTrustLineFlags(xdr::SetTrustLineFlagsOp { + trustor: cmd.trustor.clone(), + asset: cmd.asset.clone().into(), + clear_flags, + set_flags, + }) + } +} diff --git a/cmd/soroban-cli/src/config/address.rs b/cmd/soroban-cli/src/config/address.rs new file mode 100644 index 000000000..066bc8d91 --- /dev/null +++ b/cmd/soroban-cli/src/config/address.rs @@ -0,0 +1,63 @@ +use std::str::FromStr; + +use crate::xdr; + +use super::{locator, secret}; + +/// Address can be either a public key or eventually an alias of a address. +#[derive(Clone, Debug)] +pub enum Address { + MuxedAccount(xdr::MuxedAccount), + AliasOrSecret(String), +} + +impl Default for Address { + fn default() -> Self { + Address::AliasOrSecret(String::default()) + } +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Locator(#[from] locator::Error), + #[error(transparent)] + Secret(#[from] secret::Error), + #[error("Address cannot be used to sign {0}")] + CannotSign(xdr::MuxedAccount), +} + +impl FromStr for Address { + type Err = Error; + + fn from_str(value: &str) -> Result { + Ok(xdr::MuxedAccount::from_str(value).map_or_else( + |_| Address::AliasOrSecret(value.to_string()), + Address::MuxedAccount, + )) + } +} + +impl Address { + pub fn resolve_muxed_account( + &self, + locator: &locator::Args, + hd_path: Option, + ) -> Result { + 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(), + )) + }), + } + } + + pub fn resolve_secret(&self, locator: &locator::Args) -> Result { + match &self { + Address::MuxedAccount(muxed_account) => Err(Error::CannotSign(muxed_account.clone())), + Address::AliasOrSecret(alias) => Ok(locator.read_identity(alias)?), + } + } +} diff --git a/cmd/soroban-cli/src/config/locator.rs b/cmd/soroban-cli/src/config/locator.rs index bc167c977..2fad2bb62 100644 --- a/cmd/soroban-cli/src/config/locator.rs +++ b/cmd/soroban-cli/src/config/locator.rs @@ -214,7 +214,9 @@ impl Args { } pub fn read_identity(&self, name: &str) -> Result { - KeyType::Identity.read_with_global(name, &self.local_config()?) + Ok(KeyType::Identity + .read_with_global(name, &self.local_config()?) + .or_else(|_| name.parse())?) } pub fn key(&self, key_or_name: &str) -> Result { @@ -266,7 +268,7 @@ impl Args { pub fn save_contract_id( &self, network_passphrase: &str, - contract_id: &str, + contract_id: &stellar_strkey::Contract, alias: &str, ) -> Result<(), Error> { let path = self.alias_path(alias)?; @@ -284,7 +286,7 @@ impl Args { .open(path)?; data.ids - .insert(network_passphrase.into(), contract_id.into()); + .insert(network_passphrase.into(), contract_id.to_string()); let content = serde_json::to_string(&data)?; diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index e6cf8fa26..5b64a2697 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -1,5 +1,4 @@ -use std::path::PathBuf; - +use address::Address; use clap::{arg, command}; use serde::{Deserialize, Serialize}; @@ -8,12 +7,13 @@ use soroban_rpc::Client; use crate::{ print::Print, signer::{self, LocalKey, Signer, SignerKind}, - xdr::{Transaction, TransactionEnvelope}, + xdr::{self, SequenceNumber, Transaction, TransactionEnvelope}, Pwd, }; -use self::{network::Network, secret::Secret}; +use network::Network; +pub mod address; pub mod alias; pub mod data; pub mod locator; @@ -36,6 +36,8 @@ pub enum Error { Signer(#[from] signer::Error), #[error(transparent)] StellarStrkey(#[from] stellar_strkey::DecodeError), + #[error(transparent)] + Address(#[from] address::Error), } #[derive(Debug, clap::Args, Clone, Default)] @@ -47,10 +49,11 @@ pub struct Args { #[arg(long, visible_alias = "source", env = "STELLAR_ACCOUNT")] /// Account that where transaction originates from. Alias `source`. /// Can be an identity (--source alice), a public key (--source GDKW...), - /// a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). + /// a muxed account (--source MDA…), a secret key (--source SC36…), + /// or a seed phrase (--source "kite urban…"). /// If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to /// sign the final transaction. In that case, trying to sign with public key will fail. - pub source_account: String, + pub source_account: Address, #[arg(long)] /// If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` @@ -62,20 +65,14 @@ pub struct Args { impl Args { // TODO: Replace PublicKey with MuxedAccount once https://github.com/stellar/rs-stellar-xdr/pull/396 is merged. - pub fn source_account(&self) -> Result { - if let Ok(secret) = self.account(&self.source_account) { - Ok(stellar_strkey::ed25519::PublicKey( - secret.key_pair(self.hd_path)?.verifying_key().to_bytes(), - )) - } else { - Ok(stellar_strkey::ed25519::PublicKey::from_string( - &self.source_account, - )?) - } + pub fn source_account(&self) -> Result { + Ok(self + .source_account + .resolve_muxed_account(&self.locator, self.hd_path)?) } pub fn key_pair(&self) -> Result { - let key = self.account(&self.source_account)?; + let key = &self.source_account.resolve_secret(&self.locator)?; Ok(key.key_pair(self.hd_path)?) } @@ -113,20 +110,14 @@ impl Args { )?) } - pub fn account(&self, account_str: &str) -> Result { - if let Ok(secret) = self.locator.read_identity(account_str) { - Ok(secret) - } else { - Ok(account_str.parse::()?) - } - } - pub fn get_network(&self) -> Result { Ok(self.network.get(&self.locator)?) } - pub fn config_dir(&self) -> Result { - Ok(self.locator.config_dir()?) + pub async fn next_sequence_number(&self, account_str: &str) -> Result { + let network = self.get_network()?; + let client = Client::new(&network.rpc_url)?; + Ok((client.get_account(account_str).await?.seq_num.0 + 1).into()) } } diff --git a/cmd/soroban-cli/src/lib.rs b/cmd/soroban-cli/src/lib.rs index 2a6a591ca..f5ea21884 100644 --- a/cmd/soroban-cli/src/lib.rs +++ b/cmd/soroban-cli/src/lib.rs @@ -20,6 +20,7 @@ pub mod log; pub mod print; pub mod signer; pub mod toid; +pub mod tx; pub mod upgrade_check; pub mod utils; pub mod wasm; diff --git a/cmd/soroban-cli/src/tx.rs b/cmd/soroban-cli/src/tx.rs new file mode 100644 index 000000000..940b67305 --- /dev/null +++ b/cmd/soroban-cli/src/tx.rs @@ -0,0 +1,4 @@ +pub mod builder; + +/// 10,000,000 stroops in 1 XLM +pub const ONE_XLM: i64 = 10_000_000; diff --git a/cmd/soroban-cli/src/tx/builder.rs b/cmd/soroban-cli/src/tx/builder.rs new file mode 100644 index 000000000..ad22737ea --- /dev/null +++ b/cmd/soroban-cli/src/tx/builder.rs @@ -0,0 +1,11 @@ +pub mod asset; +pub mod transaction; + +pub use asset::Asset; +pub use transaction::TxExt; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Transaction contains too many operations")] + TooManyOperations, +} diff --git a/cmd/soroban-cli/src/tx/builder/asset.rs b/cmd/soroban-cli/src/tx/builder/asset.rs new file mode 100644 index 000000000..bba39804e --- /dev/null +++ b/cmd/soroban-cli/src/tx/builder/asset.rs @@ -0,0 +1,50 @@ +use std::str::FromStr; + +use crate::xdr::{self, AlphaNum12, AlphaNum4, AssetCode}; + +#[derive(Clone, Debug)] +pub struct Asset(pub xdr::Asset); + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("cannot parse asset: {0}, expected format: 'native' or 'code:issuer'")] + CannotParseAsset(String), + + #[error(transparent)] + Xdr(#[from] xdr::Error), +} + +impl FromStr for Asset { + type Err = Error; + + fn from_str(value: &str) -> Result { + if value == "native" { + return Ok(Asset(xdr::Asset::Native)); + } + let mut iter = value.splitn(2, ':'); + let (Some(code), Some(issuer), None) = (iter.next(), iter.next(), iter.next()) else { + return Err(Error::CannotParseAsset(value.to_string())); + }; + let issuer = issuer.parse()?; + Ok(Asset(match code.parse()? { + AssetCode::CreditAlphanum4(asset_code) => { + xdr::Asset::CreditAlphanum4(AlphaNum4 { asset_code, issuer }) + } + AssetCode::CreditAlphanum12(asset_code) => { + xdr::Asset::CreditAlphanum12(AlphaNum12 { asset_code, issuer }) + } + })) + } +} + +impl From for xdr::Asset { + fn from(builder: Asset) -> Self { + builder.0 + } +} + +impl From<&Asset> for xdr::Asset { + fn from(builder: &Asset) -> Self { + builder.clone().into() + } +} diff --git a/cmd/soroban-cli/src/tx/builder/transaction.rs b/cmd/soroban-cli/src/tx/builder/transaction.rs new file mode 100644 index 000000000..51232d5c5 --- /dev/null +++ b/cmd/soroban-cli/src/tx/builder/transaction.rs @@ -0,0 +1,53 @@ +use crate::xdr::{self, Memo, SequenceNumber, TransactionExt}; + +use super::Error; + +pub trait TxExt { + fn new_tx( + source: xdr::MuxedAccount, + fee: u32, + seq_num: impl Into, + operation: xdr::Operation, + ) -> xdr::Transaction; + + fn add_operation(self, operation: xdr::Operation) -> Result; + + fn add_memo(self, memo: Memo) -> xdr::Transaction; + + fn add_cond(self, cond: xdr::Preconditions) -> xdr::Transaction; +} + +impl TxExt for xdr::Transaction { + fn new_tx( + source_account: xdr::MuxedAccount, + fee: u32, + seq_num: impl Into, + operation: xdr::Operation, + ) -> xdr::Transaction { + xdr::Transaction { + source_account, + fee, + seq_num: seq_num.into(), + cond: soroban_env_host::xdr::Preconditions::None, + memo: Memo::None, + operations: [operation].try_into().unwrap(), + ext: TransactionExt::V0, + } + } + + fn add_operation(mut self, operation: xdr::Operation) -> Result { + let mut ops = self.operations.to_vec(); + ops.push(operation); + self.operations = ops.try_into().map_err(|_| Error::TooManyOperations)?; + Ok(self) + } + + fn add_memo(mut self, memo: Memo) -> Self { + self.memo = memo; + self + } + + fn add_cond(self, cond: xdr::Preconditions) -> xdr::Transaction { + xdr::Transaction { cond, ..self } + } +} diff --git a/cmd/soroban-cli/src/utils.rs b/cmd/soroban-cli/src/utils.rs index f5827f75b..8d0090042 100644 --- a/cmd/soroban-cli/src/utils.rs +++ b/cmd/soroban-cli/src/utils.rs @@ -3,8 +3,8 @@ use sha2::{Digest, Sha256}; use stellar_strkey::ed25519::PrivateKey; use soroban_env_host::xdr::{ - Asset, ContractIdPreimage, Error as XdrError, Hash, HashIdPreimage, HashIdPreimageContractId, - Limits, ScMap, ScMapEntry, ScVal, Transaction, TransactionSignaturePayload, + self, Asset, ContractIdPreimage, Hash, HashIdPreimage, HashIdPreimageContractId, Limits, ScMap, + ScMapEntry, ScVal, Transaction, TransactionSignaturePayload, TransactionSignaturePayloadTaggedTransaction, WriteXdr, }; @@ -15,14 +15,17 @@ use crate::config::network::Network; /// # Errors /// /// Might return an error -pub fn contract_hash(contract: &[u8]) -> Result { +pub fn contract_hash(contract: &[u8]) -> Result { Ok(Hash(Sha256::digest(contract).into())) } /// # Errors /// /// Might return an error -pub fn transaction_hash(tx: &Transaction, network_passphrase: &str) -> Result<[u8; 32], XdrError> { +pub fn transaction_hash( + tx: &Transaction, + network_passphrase: &str, +) -> Result<[u8; 32], xdr::Error> { let signature_payload = TransactionSignaturePayload { network_id: Hash(Sha256::digest(network_passphrase).into()), tagged_transaction: TransactionSignaturePayloadTaggedTransaction::Tx(tx.clone()), @@ -41,7 +44,10 @@ pub fn explorer_url_for_transaction(network: &Network, tx_hash: &str) -> Option< .map(|base_url| format!("{base_url}/tx/{tx_hash}")) } -pub fn explorer_url_for_contract(network: &Network, contract_id: &str) -> Option { +pub fn explorer_url_for_contract( + network: &Network, + contract_id: &stellar_strkey::Contract, +) -> Option { EXPLORERS .get(&network.network_passphrase) .map(|base_url| format!("{base_url}/contract/{contract_id}")) @@ -50,16 +56,20 @@ pub fn explorer_url_for_contract(network: &Network, contract_id: &str) -> Option /// # Errors /// /// Might return an error -pub fn contract_id_from_str(contract_id: &str) -> Result<[u8; 32], stellar_strkey::DecodeError> { +pub fn contract_id_from_str( + contract_id: &str, +) -> Result { Ok( if let Ok(strkey) = stellar_strkey::Contract::from_string(contract_id) { - strkey.0 + strkey } else { // strkey failed, try to parse it as a hex string, for backwards compatibility. - soroban_spec_tools::utils::padded_hex_from_str(contract_id, 32) - .map_err(|_| stellar_strkey::DecodeError::Invalid)? - .try_into() - .map_err(|_| stellar_strkey::DecodeError::Invalid)? + stellar_strkey::Contract( + soroban_spec_tools::utils::padded_hex_from_str(contract_id, 32) + .map_err(|_| stellar_strkey::DecodeError::Invalid)? + .try_into() + .map_err(|_| stellar_strkey::DecodeError::Invalid)?, + ) }, ) } @@ -112,16 +122,19 @@ pub fn is_hex_string(s: &str) -> bool { s.chars().all(|s| s.is_ascii_hexdigit()) } -pub fn contract_id_hash_from_asset(asset: &Asset, network_passphrase: &str) -> Hash { +pub fn contract_id_hash_from_asset( + asset: impl Into, + network_passphrase: &str, +) -> stellar_strkey::Contract { let network_id = Hash(Sha256::digest(network_passphrase.as_bytes()).into()); let preimage = HashIdPreimage::ContractId(HashIdPreimageContractId { network_id, - contract_id_preimage: ContractIdPreimage::Asset(asset.clone()), + contract_id_preimage: ContractIdPreimage::Asset(asset.into()), }); let preimage_xdr = preimage .to_xdr(Limits::none()) .expect("HashIdPreimage should not fail encoding to xdr"); - Hash(Sha256::digest(preimage_xdr).into()) + stellar_strkey::Contract(Sha256::digest(preimage_xdr).into()) } pub fn get_name_from_stellar_asset_contract_storage(storage: &ScMap) -> Option { @@ -171,74 +184,6 @@ pub mod rpc { } } -pub mod parsing { - - use regex::Regex; - use soroban_env_host::xdr::{ - AccountId, AlphaNum12, AlphaNum4, Asset, AssetCode12, AssetCode4, PublicKey, - }; - - #[derive(thiserror::Error, Debug)] - pub enum Error { - #[error("invalid asset code: {asset}")] - InvalidAssetCode { asset: String }, - #[error("cannot parse account id: {account_id}")] - CannotParseAccountId { account_id: String }, - #[error("cannot parse asset: {asset}")] - CannotParseAsset { asset: String }, - #[error(transparent)] - Regex(#[from] regex::Error), - } - - pub fn parse_asset(str: &str) -> Result { - if str == "native" { - return Ok(Asset::Native); - } - let split: Vec<&str> = str.splitn(2, ':').collect(); - if split.len() != 2 { - return Err(Error::CannotParseAsset { - asset: str.to_string(), - }); - } - let code = split[0]; - let issuer = split[1]; - let re = Regex::new("^[[:alnum:]]{1,12}$")?; - if !re.is_match(code) { - return Err(Error::InvalidAssetCode { - asset: str.to_string(), - }); - } - if code.len() <= 4 { - let mut asset_code: [u8; 4] = [0; 4]; - for (i, b) in code.as_bytes().iter().enumerate() { - asset_code[i] = *b; - } - Ok(Asset::CreditAlphanum4(AlphaNum4 { - asset_code: AssetCode4(asset_code), - issuer: parse_account_id(issuer)?, - })) - } else { - let mut asset_code: [u8; 12] = [0; 12]; - for (i, b) in code.as_bytes().iter().enumerate() { - asset_code[i] = *b; - } - Ok(Asset::CreditAlphanum12(AlphaNum12 { - asset_code: AssetCode12(asset_code), - issuer: parse_account_id(issuer)?, - })) - } - } - - pub fn parse_account_id(str: &str) -> Result { - let pk_bytes = stellar_strkey::ed25519::PublicKey::from_string(str) - .map_err(|_| Error::CannotParseAccountId { - account_id: str.to_string(), - })? - .0; - Ok(AccountId(PublicKey::PublicKeyTypeEd25519(pk_bytes.into()))) - } -} - #[cfg(test)] mod tests { use super::*; @@ -248,7 +193,7 @@ mod tests { // strkey match contract_id_from_str("CA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAXE") { Ok(contract_id) => assert_eq!( - contract_id, + contract_id.0, [ 0x36, 0x3e, 0xaa, 0x38, 0x67, 0x84, 0x1f, 0xba, 0xd0, 0xf4, 0xed, 0x88, 0xc7, 0x79, 0xe4, 0xfe, 0x66, 0xe5, 0x6a, 0x24, 0x70, 0xdc, 0x98, 0xc0, 0xec, 0x9c, From bb9c712004c3819506442c97c6872ec4f8c28a95 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Wed, 2 Oct 2024 16:14:34 -0400 Subject: [PATCH 16/55] fix: add test to check if default is used for durability (#1644) --- cmd/crates/soroban-test/tests/it/integration/util.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/cmd/crates/soroban-test/tests/it/integration/util.rs b/cmd/crates/soroban-test/tests/it/integration/util.rs index 22ef4fa82..6feae7860 100644 --- a/cmd/crates/soroban-test/tests/it/integration/util.rs +++ b/cmd/crates/soroban-test/tests/it/integration/util.rs @@ -95,14 +95,7 @@ pub async fn extend_contract(sandbox: &TestEnv, id: &str) { } pub async fn extend(sandbox: &TestEnv, id: &str, value: Option<&str>) { - let mut args = vec![ - "--id", - id, - "--durability", - "persistent", - "--ledgers-to-extend", - "100001", - ]; + let mut args = vec!["--id", id, "--ledgers-to-extend", "100001"]; if let Some(value) = value { args.push("--key"); args.push(value); From ddad905341042867b32f02cbe005bcedd5a06f78 Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Wed, 2 Oct 2024 17:12:44 -0700 Subject: [PATCH 17/55] Do not require source account when fetching an asset's contract id. (#1647) --- FULL_HELP_DOCS.md | 8 ++------ .../tests/it/integration/cookbook.rs | 2 -- .../src/commands/contract/id/asset.rs | 2 +- cmd/soroban-cli/src/config/mod.rs | 16 ++++++++++++++++ cookbook/deploy-stellar-asset-contract.mdx | 1 - cookbook/payments-and-assets.mdx | 2 +- 6 files changed, 20 insertions(+), 11 deletions(-) diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index be20ae24a..b8b03c580 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -111,7 +111,7 @@ Utilities to deploy a Stellar Asset Contract or get its id Get Id of builtin Soroban Asset Contract. Deprecated, use `stellar contract id asset` instead -**Usage:** `stellar contract asset id [OPTIONS] --asset --source-account ` +**Usage:** `stellar contract asset id [OPTIONS] --asset ` ###### **Options:** @@ -119,8 +119,6 @@ Get Id of builtin Soroban Asset Contract. Deprecated, use `stellar contract id a * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail -* `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." @@ -444,7 +442,7 @@ Generate the contract id for a given contract or asset Deploy builtin Soroban Asset Contract -**Usage:** `stellar contract id asset [OPTIONS] --asset --source-account ` +**Usage:** `stellar contract id asset [OPTIONS] --asset ` ###### **Options:** @@ -452,8 +450,6 @@ Deploy builtin Soroban Asset Contract * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail -* `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." diff --git a/cmd/crates/soroban-test/tests/it/integration/cookbook.rs b/cmd/crates/soroban-test/tests/it/integration/cookbook.rs index 4c0ad5a63..82b9c5f43 100644 --- a/cmd/crates/soroban-test/tests/it/integration/cookbook.rs +++ b/cmd/crates/soroban-test/tests/it/integration/cookbook.rs @@ -236,8 +236,6 @@ mod tests { .arg("asset") .arg("--asset") .arg("native") - .arg("--source-account") - .arg(source) .assert() .stdout_as_str(); let contract_id = deploy_hello(&sandbox).await; diff --git a/cmd/soroban-cli/src/commands/contract/id/asset.rs b/cmd/soroban-cli/src/commands/contract/id/asset.rs index 63b3017a1..cdf826015 100644 --- a/cmd/soroban-cli/src/commands/contract/id/asset.rs +++ b/cmd/soroban-cli/src/commands/contract/id/asset.rs @@ -13,7 +13,7 @@ pub struct Cmd { pub asset: builder::Asset, #[command(flatten)] - pub config: config::Args, + pub config: config::ArgsLocatorAndNetwork, } #[derive(thiserror::Error, Debug)] pub enum Error { diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index 5b64a2697..12f571a50 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -129,3 +129,19 @@ impl Pwd for Args { #[derive(Default, Serialize, Deserialize)] pub struct Config {} + +#[derive(Debug, clap::Args, Clone, Default)] +#[group(skip)] +pub struct ArgsLocatorAndNetwork { + #[command(flatten)] + pub network: network::Args, + + #[command(flatten)] + pub locator: locator::Args, +} + +impl ArgsLocatorAndNetwork { + pub fn get_network(&self) -> Result { + Ok(self.network.get(&self.locator)?) + } +} diff --git a/cookbook/deploy-stellar-asset-contract.mdx b/cookbook/deploy-stellar-asset-contract.mdx index 7233acc60..8fca51c48 100644 --- a/cookbook/deploy-stellar-asset-contract.mdx +++ b/cookbook/deploy-stellar-asset-contract.mdx @@ -42,7 +42,6 @@ For any asset, the contract address can be fetched with: ```bash stellar contract id asset \ - --source S... \ --network testnet \ --asset native ``` diff --git a/cookbook/payments-and-assets.mdx b/cookbook/payments-and-assets.mdx index 9849bf3d9..e3be8dafc 100644 --- a/cookbook/payments-and-assets.mdx +++ b/cookbook/payments-and-assets.mdx @@ -35,7 +35,7 @@ stellar keys fund bob 3. Obtain the stellar asset contract ID: ```bash -stellar contract id asset --asset native --source-account alice +stellar contract id asset --asset native ``` 4. Get Bob's public key: From 2cb77d27df85c645cf7d5fab14e3586875ef581a Mon Sep 17 00:00:00 2001 From: Jun Luo <4catcode@gmail.com> Date: Thu, 3 Oct 2024 18:58:19 +0800 Subject: [PATCH 18/55] Use reqwest, remove http, ureq and hyper (#1632) --- Cargo.lock | 87 ++++++---- Cargo.toml | 1 - cmd/soroban-cli/Cargo.toml | 23 ++- cmd/soroban-cli/src/cli.rs | 5 +- cmd/soroban-cli/src/commands/contract/init.rs | 4 +- cmd/soroban-cli/src/commands/network/mod.rs | 4 +- .../src/commands/snapshot/create.rs | 81 +++++----- cmd/soroban-cli/src/config/data.rs | 14 +- cmd/soroban-cli/src/config/network.rs | 149 +++++++++++++----- cmd/soroban-cli/src/upgrade_check.rs | 24 +-- cmd/soroban-cli/src/utils.rs | 35 ++++ 11 files changed, 275 insertions(+), 152 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 21df05973..7ce514d54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -779,6 +779,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -2859,6 +2869,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.1", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -3528,6 +3539,30 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "mockito" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b34bd91b9e5c5b06338d392463e1318d683cf82ec3d3af4014609be6e2108d" +dependencies = [ + "assert-json-diff", + "bytes", + "colored", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "log", + "rand", + "regex", + "serde_json", + "serde_urlencoded", + "similar", + "tokio", +] + [[package]] name = "native-tls" version = "0.2.12" @@ -3693,15 +3728,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" -[[package]] -name = "openssl-src" -version = "300.3.1+3.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7259953d42a81bf137fbbd73bd30a8e1914d6dce43c2b90ed575783a22608b91" -dependencies = [ - "cc", -] - [[package]] name = "openssl-sys" version = "0.9.103" @@ -3710,7 +3736,6 @@ checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", - "openssl-src", "pkg-config", "vcpkg", ] @@ -4308,6 +4333,7 @@ dependencies = [ "base64 0.22.1", "bytes", "encoding_rs", + "futures-channel", "futures-core", "futures-util", "h2 0.4.6", @@ -4336,10 +4362,12 @@ dependencies = [ "sync_wrapper 1.0.1", "tokio", "tokio-rustls 0.26.0", + "tokio-util", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", "webpki-roots 0.26.3", "windows-registry", @@ -4521,7 +4549,6 @@ version = "0.23.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" dependencies = [ - "log", "once_cell", "ring", "rustls-pki-types", @@ -5133,21 +5160,19 @@ dependencies = [ "heck 0.5.0", "hex", "home", - "http 0.2.12", "humantime", - "hyper 0.14.30", - "hyper-tls", "itertools 0.10.5", "jsonrpsee-core", "jsonrpsee-http-client", + "mockito", "num-bigint", "open", - "openssl", "pathdiff", "phf", "predicates 2.1.5", "rand", "regex", + "reqwest 0.12.7", "rpassword", "rust-embed", "semver", @@ -5183,7 +5208,6 @@ dependencies = [ "tracing-appender", "tracing-subscriber", "ulid", - "ureq", "url", "walkdir", "wasm-opt", @@ -6341,24 +6365,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" -[[package]] -name = "ureq" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a" -dependencies = [ - "base64 0.22.1", - "flate2", - "log", - "once_cell", - "rustls 0.23.12", - "rustls-pki-types", - "serde", - "serde_json", - "url", - "webpki-roots 0.26.3", -] - [[package]] name = "url" version = "2.5.2" @@ -6547,6 +6553,19 @@ dependencies = [ "cxx-build", ] +[[package]] +name = "wasm-streams" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "wasmi_arena" version = "0.4.1" diff --git a/Cargo.toml b/Cargo.toml index 4cf12b4d9..7282ff497 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,7 +94,6 @@ termcolor_output = "1.0.1" ed25519-dalek = ">= 2.1.1" # networking -http = "1.0.0" jsonrpsee-http-client = "0.20.1" jsonrpsee-core = "0.20.1" tokio = "1.28.1" diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index 5f4cc7c5a..646e8a92e 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -74,14 +74,18 @@ wasmparser = { workspace = true } sha2 = { workspace = true } csv = "1.1.6" ed25519-dalek = { workspace = true } +reqwest = { version = "0.12.7", default-features = false, features = [ + "rustls-tls", + "http2", + "json", + "blocking", + "stream", +] } jsonrpsee-http-client = "0.20.1" jsonrpsee-core = "0.20.1" -hyper = "0.14.27" -hyper-tls = "0.5" -http = "0.2.9" regex = "1.6.0" wasm-opt = { version = "0.114.0", optional = true } -chrono = { version = "0.4.27", features = ["serde"]} +chrono = { version = "0.4.27", features = ["serde"] } rpassword = "7.2.0" dirs = "4.0.0" toml = "0.5.9" @@ -107,13 +111,12 @@ gix = { version = "0.58.0", default-features = false, features = [ "blocking-http-transport-reqwest-rust-tls", "worktree-mutation", ] } -ureq = { version = "2.9.1", features = ["json"] } -async-compression = { version = "0.4.12", features = [ "tokio", "gzip" ] } +async-compression = { version = "0.4.12", features = ["tokio", "gzip"] } tempfile = "3.8.1" toml_edit = "0.21.0" rust-embed = { version = "8.2.0", features = ["debug-embed"] } -bollard = { workspace=true } +bollard = { workspace = true } futures-util = "0.3.30" futures = "0.3.30" home = "0.5.9" @@ -127,15 +130,10 @@ fqdn = "0.3.12" open = "5.3.0" url = "2.5.2" -# For hyper-tls -[target.'cfg(unix)'.dependencies] -openssl = { version = "=0.10.55", features = ["vendored"] } - [build-dependencies] crate-git-revision = "0.0.4" serde.workspace = true thiserror.workspace = true -ureq = { version = "2.9.1", features = ["json"] } [dev-dependencies] @@ -143,3 +141,4 @@ assert_cmd = "2.0.4" assert_fs = "1.0.7" predicates = "2.1.5" walkdir = "2.5.0" +mockito = "1.5.0" diff --git a/cmd/soroban-cli/src/cli.rs b/cmd/soroban-cli/src/cli.rs index efed1b63b..5470562db 100644 --- a/cmd/soroban-cli/src/cli.rs +++ b/cmd/soroban-cli/src/cli.rs @@ -1,6 +1,5 @@ use clap::CommandFactory; use dotenvy::dotenv; -use std::thread; use tracing_subscriber::{fmt, EnvFilter}; use crate::upgrade_check::upgrade_check; @@ -75,8 +74,8 @@ pub async fn main() { // Spawn a thread to check if a new version exists. // It depends on logger, so we need to place it after // the code block that initializes the logger. - thread::spawn(move || { - upgrade_check(root.global_args.quiet); + tokio::spawn(async move { + upgrade_check(root.global_args.quiet).await; }); let printer = print::Print::new(root.global_args.quiet); diff --git a/cmd/soroban-cli/src/commands/contract/init.rs b/cmd/soroban-cli/src/commands/contract/init.rs index 18938d001..fd4cf483a 100644 --- a/cmd/soroban-cli/src/commands/contract/init.rs +++ b/cmd/soroban-cli/src/commands/contract/init.rs @@ -19,8 +19,8 @@ use std::{ sync::atomic::AtomicBool, }; use toml_edit::{Document, TomlError}; -use ureq::get; +use crate::utils::http; use crate::{commands::global, print}; const SOROBAN_EXAMPLES_URL: &str = "https://github.com/stellar/soroban-examples.git"; @@ -261,7 +261,7 @@ impl Runner { } fn check_internet_connection() -> bool { - if let Ok(_req) = get(GITHUB_URL).call() { + if let Ok(_req) = http::blocking_client().get(GITHUB_URL).send() { return true; } diff --git a/cmd/soroban-cli/src/commands/network/mod.rs b/cmd/soroban-cli/src/commands/network/mod.rs index 772d0cbe8..8dd61b394 100644 --- a/cmd/soroban-cli/src/commands/network/mod.rs +++ b/cmd/soroban-cli/src/commands/network/mod.rs @@ -67,11 +67,9 @@ pub enum Error { #[error("network arg or rpc url and network passphrase are required if using the network")] Network, #[error(transparent)] - Http(#[from] http::Error), - #[error(transparent)] Rpc(#[from] rpc::Error), #[error(transparent)] - Hyper(#[from] hyper::Error), + HttpClient(#[from] reqwest::Error), #[error("Failed to parse JSON from {0}, {1}")] FailedToParseJSON(String, serde_json::Error), #[error("Invalid URL {0}")] diff --git a/cmd/soroban-cli/src/commands/snapshot/create.rs b/cmd/soroban-cli/src/commands/snapshot/create.rs index 8a587e243..13bef9465 100644 --- a/cmd/soroban-cli/src/commands/snapshot/create.rs +++ b/cmd/soroban-cli/src/commands/snapshot/create.rs @@ -1,8 +1,7 @@ use async_compression::tokio::bufread::GzipDecoder; use bytesize::ByteSize; use clap::{arg, Parser, ValueEnum}; -use futures::{StreamExt, TryStreamExt}; -use http::Uri; +use futures::StreamExt; use humantime::format_duration; use itertools::{Either, Itertools}; use sha2::{Digest, Sha256}; @@ -24,7 +23,11 @@ use stellar_xdr::curr::{ ScVal, }; use tokio::fs::OpenOptions; +use tokio::io::BufReader; +use tokio_util::io::StreamReader; +use url::Url; +use crate::utils::http; use crate::{ commands::{config::data, global, HEADING_RPC}, config::{self, locator, network::passphrase}, @@ -85,7 +88,7 @@ pub struct Cmd { network: config::network::Args, /// Archive URL #[arg(long, help_heading = HEADING_RPC, env = "STELLAR_ARCHIVE_URL")] - archive_url: Option, + archive_url: Option, } #[derive(thiserror::Error, Debug)] @@ -93,19 +96,19 @@ pub enum Error { #[error("wasm hash invalid: {0}")] WasmHashInvalid(String), #[error("downloading history: {0}")] - DownloadingHistory(hyper::Error), + DownloadingHistory(reqwest::Error), #[error("downloading history: got status code {0}")] - DownloadingHistoryGotStatusCode(hyper::StatusCode), + DownloadingHistoryGotStatusCode(reqwest::StatusCode), #[error("json decoding history: {0}")] JsonDecodingHistory(serde_json::Error), #[error("opening cached bucket to read: {0}")] ReadOpeningCachedBucket(io::Error), #[error("parsing bucket url: {0}")] - ParsingBucketUrl(http::uri::InvalidUri), + ParsingBucketUrl(url::ParseError), #[error("getting bucket: {0}")] - GettingBucket(hyper::Error), + GettingBucket(reqwest::Error), #[error("getting bucket: got status code {0}")] - GettingBucketGotStatusCode(hyper::StatusCode), + GettingBucketGotStatusCode(reqwest::StatusCode), #[error("opening cached bucket to write: {0}")] WriteOpeningCachedBucket(io::Error), #[error("streaming bucket: {0}")] @@ -117,7 +120,7 @@ pub enum Error { #[error("getting bucket directory: {0}")] GetBucketDir(data::Error), #[error("reading history http stream: {0}")] - ReadHistoryHttpStream(hyper::Error), + ReadHistoryHttpStream(reqwest::Error), #[error("writing ledger snapshot: {0}")] WriteLedgerSnapshot(soroban_ledger_snapshot::Error), #[error(transparent)] @@ -362,7 +365,7 @@ impl Cmd { Ok(()) } - fn archive_url(&self) -> Result { + fn archive_url(&self) -> Result { // Return the configured archive URL, or if one is not configured, guess // at an appropriate archive URL given the network passphrase. self.archive_url @@ -380,7 +383,7 @@ impl Cmd { passphrase::LOCAL => Some("http://localhost:8000/archive"), _ => None, } - .map(|s| Uri::from_str(s).expect("archive url valid")) + .map(|s| Url::from_str(s).expect("archive url valid")) }) }) .ok_or(Error::ArchiveUrlNotConfigured) @@ -389,7 +392,7 @@ impl Cmd { async fn get_history( print: &print::Print, - archive_url: &Uri, + archive_url: &Url, ledger: Option, ) -> Result { let archive_url = archive_url.to_string(); @@ -403,14 +406,13 @@ async fn get_history( } else { format!("{archive_url}/.well-known/stellar-history.json") }; - let history_url = Uri::from_str(&history_url).unwrap(); + let history_url = Url::from_str(&history_url).unwrap(); print.globe(format!("Downloading history {history_url}")); - let https = hyper_tls::HttpsConnector::new(); - let response = hyper::Client::builder() - .build::<_, hyper::Body>(https) - .get(history_url.clone()) + let response = http::client() + .get(history_url.as_str()) + .send() .await .map_err(Error::DownloadingHistory)?; @@ -431,7 +433,8 @@ async fn get_history( return Err(Error::DownloadingHistoryGotStatusCode(response.status())); } - let body = hyper::body::to_bytes(response.into_body()) + let body = response + .bytes() .await .map_err(Error::ReadHistoryHttpStream)?; @@ -443,7 +446,7 @@ async fn get_history( async fn cache_bucket( print: &print::Print, - archive_url: &Uri, + archive_url: &Url, bucket_index: usize, bucket: &str, ) -> Result { @@ -458,11 +461,11 @@ async fn cache_bucket( print.globe(format!("Downloading bucket {bucket_index} {bucket}…")); - let bucket_url = Uri::from_str(&bucket_url).map_err(Error::ParsingBucketUrl)?; - let https = hyper_tls::HttpsConnector::new(); - let response = hyper::Client::builder() - .build::<_, hyper::Body>(https) - .get(bucket_url) + let bucket_url = Url::from_str(&bucket_url).map_err(Error::ParsingBucketUrl)?; + + let response = http::client() + .get(bucket_url.as_str()) + .send() .await .map_err(Error::GettingBucket)?; @@ -471,26 +474,22 @@ async fn cache_bucket( return Err(Error::GettingBucketGotStatusCode(response.status())); } - if let Some(val) = response.headers().get("Content-Length") { - if let Ok(str) = val.to_str() { - if let Ok(len) = str.parse::() { - print.clear_line(); - print.globe(format!( - "Downloaded bucket {bucket_index} {bucket} ({})", - ByteSize(len) - )); - } - } + if let Some(len) = response.content_length() { + print.clear_line(); + print.globe(format!( + "Downloaded bucket {bucket_index} {bucket} ({})", + ByteSize(len) + )); } print.println(""); - let read = response - .into_body() - .map(|result| result.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))) - .into_async_read(); - let read = tokio_util::compat::FuturesAsyncReadCompatExt::compat(read); - let mut read = GzipDecoder::new(read); + let stream = response + .bytes_stream() + .map(|result| result.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))); + let stream_reader = StreamReader::new(stream); + let buf_reader = BufReader::new(stream_reader); + let mut decoder = GzipDecoder::new(buf_reader); let dl_path = cache_path.with_extension("dl"); let mut file = OpenOptions::new() .create(true) @@ -499,7 +498,7 @@ async fn cache_bucket( .open(&dl_path) .await .map_err(Error::WriteOpeningCachedBucket)?; - tokio::io::copy(&mut read, &mut file) + tokio::io::copy(&mut decoder, &mut file) .await .map_err(Error::StreamingBucket)?; fs::rename(&dl_path, &cache_path).map_err(Error::RenameDownloadFile)?; diff --git a/cmd/soroban-cli/src/config/data.rs b/cmd/soroban-cli/src/config/data.rs index 23dedc619..bbfc6994e 100644 --- a/cmd/soroban-cli/src/config/data.rs +++ b/cmd/soroban-cli/src/config/data.rs @@ -1,8 +1,8 @@ use crate::rpc::{GetTransactionResponse, GetTransactionResponseRaw, SimulateTransactionResponse}; use directories::ProjectDirs; -use http::Uri; use serde::{Deserialize, Serialize}; use std::str::FromStr; +use url::Url; use crate::xdr::{self, WriteXdr}; @@ -15,7 +15,7 @@ pub enum Error { #[error(transparent)] SerdeJson(#[from] serde_json::Error), #[error(transparent)] - Http(#[from] http::uri::InvalidUri), + InvalidUrl(#[from] url::ParseError), #[error(transparent)] Ulid(#[from] ulid::DecodeError), #[error(transparent)] @@ -56,7 +56,7 @@ pub fn bucket_dir() -> Result { Ok(dir) } -pub fn write(action: Action, rpc_url: &Uri) -> Result { +pub fn write(action: Action, rpc_url: &Url) -> Result { let data = Data { action, rpc_url: rpc_url.to_string(), @@ -67,10 +67,10 @@ pub fn write(action: Action, rpc_url: &Uri) -> Result { Ok(id) } -pub fn read(id: &ulid::Ulid) -> Result<(Action, Uri), Error> { +pub fn read(id: &ulid::Ulid) -> Result<(Action, Url), Error> { let file = actions_dir()?.join(id.to_string()).with_extension("json"); let data: Data = serde_json::from_str(&std::fs::read_to_string(file)?)?; - Ok((data.action, http::Uri::from_str(&data.rpc_url)?)) + Ok((data.action, Url::from_str(&data.rpc_url)?)) } pub fn write_spec(hash: &str, spec_entries: &[xdr::ScSpecEntry]) -> Result<(), Error> { @@ -117,7 +117,7 @@ pub fn list_actions() -> Result, Error> { .collect::, Error>>() } -pub struct DatedAction(ulid::Ulid, Action, Uri); +pub struct DatedAction(ulid::Ulid, Action, Url); impl std::fmt::Display for DatedAction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -200,7 +200,7 @@ mod test { fn test_write_read() { let t = assert_fs::TempDir::new().unwrap(); std::env::set_var(XDG_DATA_HOME, t.path().to_str().unwrap()); - let rpc_uri = http::uri::Uri::from_str("http://localhost:8000").unwrap(); + let rpc_uri = Url::from_str("http://localhost:8000").unwrap(); let sim = SimulateTransactionResponse::default(); let original_action: Action = sim.into(); diff --git a/cmd/soroban-cli/src/config/network.rs b/cmd/soroban-cli/src/config/network.rs index 9e1eabee3..b6f6d8c1d 100644 --- a/cmd/soroban-cli/src/config/network.rs +++ b/cmd/soroban-cli/src/config/network.rs @@ -1,32 +1,29 @@ -use std::str::FromStr; - use clap::arg; use phf::phf_map; use serde::{Deserialize, Serialize}; use serde_json::Value; +use std::str::FromStr; use stellar_strkey::ed25519::PublicKey; +use url::Url; +use super::locator; +use crate::utils::http; use crate::{ commands::HEADING_RPC, rpc::{self, Client}, }; - -use super::locator; pub mod passphrase; #[derive(thiserror::Error, Debug)] pub enum Error { #[error(transparent)] Config(#[from] locator::Error), - #[error("network arg or rpc url and network passphrase are required if using the network")] Network, #[error(transparent)] - Http(#[from] http::Error), - #[error(transparent)] Rpc(#[from] rpc::Error), #[error(transparent)] - Hyper(#[from] hyper::Error), + HttpClient(#[from] reqwest::Error), #[error("Failed to parse JSON from {0}, {1}")] FailedToParseJSON(String, serde_json::Error), #[error("Invalid URL {0}")] @@ -107,29 +104,27 @@ pub struct Network { } impl Network { - pub async fn helper_url(&self, addr: &str) -> Result { - use http::Uri; + pub async fn helper_url(&self, addr: &str) -> Result { tracing::debug!("address {addr:?}"); - let rpc_uri = Uri::from_str(&self.rpc_url) + let rpc_url = Url::from_str(&self.rpc_url) .map_err(|_| Error::InvalidUrl(self.rpc_url.to_string()))?; if self.network_passphrase.as_str() == passphrase::LOCAL { - let auth = rpc_uri.authority().unwrap().clone(); - let scheme = rpc_uri.scheme_str().unwrap(); - Ok(Uri::builder() - .authority(auth) - .scheme(scheme) - .path_and_query(format!("/friendbot?addr={addr}")) - .build()?) + let mut local_url = rpc_url; + local_url.set_path("/friendbot"); + local_url.set_query(Some(&format!("addr={addr}"))); + Ok(local_url) } else { let client = Client::new(&self.rpc_url)?; let network = client.get_network().await?; tracing::debug!("network {network:?}"); - let uri = client.friendbot_url().await?; - tracing::debug!("URI {uri:?}"); - Uri::from_str(&format!("{uri}?addr={addr}")).map_err(|e| { + let url = client.friendbot_url().await?; + tracing::debug!("URL {url:?}"); + let mut url = Url::from_str(&url).map_err(|e| { tracing::error!("{e}"); - Error::InvalidUrl(uri.to_string()) - }) + Error::InvalidUrl(url.to_string()) + })?; + url.query_pairs_mut().append_pair("addr", addr); + Ok(url) } } @@ -137,21 +132,10 @@ impl Network { pub async fn fund_address(&self, addr: &PublicKey) -> Result<(), Error> { let uri = self.helper_url(&addr.to_string()).await?; tracing::debug!("URL {uri:?}"); - let response = match uri.scheme_str() { - Some("http") => hyper::Client::new().get(uri.clone()).await?, - Some("https") => { - let https = hyper_tls::HttpsConnector::new(); - hyper::Client::builder() - .build::<_, hyper::Body>(https) - .get(uri.clone()) - .await? - } - _ => { - return Err(Error::InvalidUrl(uri.to_string())); - } - }; + let response = http::client().get(uri.as_str()).send().await?; + let request_successful = response.status().is_success(); - let body = hyper::body::to_bytes(response.into_body()).await?; + let body = response.bytes().await?; let res = serde_json::from_slice::(&body) .map_err(|e| Error::FailedToParseJSON(uri.to_string(), e))?; tracing::debug!("{res:#?}"); @@ -173,8 +157,8 @@ impl Network { Ok(()) } - pub fn rpc_uri(&self) -> Result { - http::Uri::from_str(&self.rpc_url).map_err(|_| Error::InvalidUrl(self.rpc_url.to_string())) + pub fn rpc_uri(&self) -> Result { + Url::from_str(&self.rpc_url).map_err(|_| Error::InvalidUrl(self.rpc_url.to_string())) } } @@ -206,3 +190,90 @@ impl From<&(&str, &str)> for Network { } } } + +#[cfg(test)] +mod tests { + use super::*; + use mockito::Server; + use serde_json::json; + + #[tokio::test] + async fn test_helper_url_local_network() { + let network = Network { + rpc_url: "http://localhost:8000".to_string(), + network_passphrase: passphrase::LOCAL.to_string(), + }; + + let result = network + .helper_url("GBZXN7PIRZGNMHGA7MUUUF4GWPY5AYPV6LY4UV2GL6VJGIQRXFDNMADI") + .await; + + assert!(result.is_ok()); + let url = result.unwrap(); + assert_eq!(url.as_str(), "http://localhost:8000/friendbot?addr=GBZXN7PIRZGNMHGA7MUUUF4GWPY5AYPV6LY4UV2GL6VJGIQRXFDNMADI"); + } + + #[tokio::test] + async fn test_helper_url_test_network() { + let mut server = Server::new_async().await; + let _mock = server + .mock("POST", "/") + .with_body_from_request(|req| { + let body: Value = serde_json::from_slice(req.body().unwrap()).unwrap(); + let id = body["id"].clone(); + json!({ + "jsonrpc": "2.0", + "id": id, + "result": { + "friendbotUrl": "https://friendbot.stellar.org/", + "passphrase": passphrase::TESTNET.to_string(), + "protocolVersion": 21 + } + }) + .to_string() + .into() + }) + .create_async() + .await; + + let network = Network { + rpc_url: server.url(), + network_passphrase: passphrase::TESTNET.to_string(), + }; + let url = network + .helper_url("GBZXN7PIRZGNMHGA7MUUUF4GWPY5AYPV6LY4UV2GL6VJGIQRXFDNMADI") + .await + .unwrap(); + assert_eq!(url.as_str(), "https://friendbot.stellar.org/?addr=GBZXN7PIRZGNMHGA7MUUUF4GWPY5AYPV6LY4UV2GL6VJGIQRXFDNMADI"); + } + + #[tokio::test] + async fn test_helper_url_test_network_with_path_and_params() { + let mut server = Server::new_async().await; + let _mock = server.mock("POST", "/") + .with_body_from_request(|req| { + let body: Value = serde_json::from_slice(req.body().unwrap()).unwrap(); + let id = body["id"].clone(); + json!({ + "jsonrpc": "2.0", + "id": id, + "result": { + "friendbotUrl": "https://friendbot.stellar.org/secret?api_key=123456&user=demo", + "passphrase": passphrase::TESTNET.to_string(), + "protocolVersion": 21 + } + }).to_string().into() + }) + .create_async().await; + + let network = Network { + rpc_url: server.url(), + network_passphrase: passphrase::TESTNET.to_string(), + }; + let url = network + .helper_url("GBZXN7PIRZGNMHGA7MUUUF4GWPY5AYPV6LY4UV2GL6VJGIQRXFDNMADI") + .await + .unwrap(); + assert_eq!(url.as_str(), "https://friendbot.stellar.org/secret?api_key=123456&user=demo&addr=GBZXN7PIRZGNMHGA7MUUUF4GWPY5AYPV6LY4UV2GL6VJGIQRXFDNMADI"); + } +} diff --git a/cmd/soroban-cli/src/upgrade_check.rs b/cmd/soroban-cli/src/upgrade_check.rs index ecd1a4adc..294056dd1 100644 --- a/cmd/soroban-cli/src/upgrade_check.rs +++ b/cmd/soroban-cli/src/upgrade_check.rs @@ -1,5 +1,6 @@ use crate::config::upgrade_check::UpgradeCheck; use crate::print::Print; +use crate::utils::http; use semver::Version; use serde::Deserialize; use std::error::Error; @@ -8,7 +9,6 @@ use std::time::Duration; const MINIMUM_CHECK_INTERVAL: Duration = Duration::from_secs(60 * 60 * 24); // 1 day const CRATES_IO_API_URL: &str = "https://crates.io/api/v1/crates/"; -const REQUEST_TIMEOUT: Duration = Duration::from_secs(30); const NO_UPDATE_CHECK_ENV_VAR: &str = "STELLAR_NO_UPDATE_CHECK"; #[derive(Deserialize)] @@ -26,16 +26,20 @@ struct Crate { } /// Fetch the latest stable version of the crate from crates.io -fn fetch_latest_crate_info() -> Result> { +async fn fetch_latest_crate_info() -> Result> { let crate_name = env!("CARGO_PKG_NAME"); let url = format!("{CRATES_IO_API_URL}{crate_name}"); - let response = ureq::get(&url).timeout(REQUEST_TIMEOUT).call()?; - let crate_data: CrateResponse = response.into_json()?; - Ok(crate_data.crate_) + let resp = http::client() + .get(url) + .send() + .await? + .json::() + .await?; + Ok(resp.crate_) } /// Print a warning if a new version of the CLI is available -pub fn upgrade_check(quiet: bool) { +pub async fn upgrade_check(quiet: bool) { // We should skip the upgrade check if we're not in a tty environment. if !std::io::stderr().is_terminal() { return; @@ -59,7 +63,7 @@ pub fn upgrade_check(quiet: bool) { let now = chrono::Utc::now(); // Skip fetch from crates.io if we've checked recently if now - MINIMUM_CHECK_INTERVAL >= stats.latest_check_time { - match fetch_latest_crate_info() { + match fetch_latest_crate_info().await { Ok(c) => { stats = UpgradeCheck { latest_check_time: now, @@ -112,9 +116,9 @@ fn get_latest_version<'a>(current_version: &Version, stats: &'a UpgradeCheck) -> mod tests { use super::*; - #[test] - fn test_fetch_latest_stable_version() { - let _ = fetch_latest_crate_info().unwrap(); + #[tokio::test] + async fn test_fetch_latest_stable_version() { + let _ = fetch_latest_crate_info().await.unwrap(); } #[test] diff --git a/cmd/soroban-cli/src/utils.rs b/cmd/soroban-cli/src/utils.rs index 8d0090042..ee8bb1ece 100644 --- a/cmd/soroban-cli/src/utils.rs +++ b/cmd/soroban-cli/src/utils.rs @@ -161,6 +161,41 @@ pub fn get_name_from_stellar_asset_contract_storage(storage: &ScMap) -> Option String { + format!("{}/{}", env!("CARGO_PKG_NAME"), version::pkg()) + } + + /// Creates and returns a configured `reqwest::Client`. + /// + /// # Panics + /// + /// Panics if the Client initialization fails. + pub fn client() -> reqwest::Client { + // Why we panic here: + // 1. Client initialization failures are rare and usually indicate serious issues. + // 2. The application cannot function properly without a working HTTP client. + // 3. This simplifies error handling for callers, as they can assume a valid client. + reqwest::Client::builder() + .user_agent(user_agent()) + .build() + .expect("Failed to build reqwest client") + } + + /// Creates and returns a configured `reqwest::blocking::Client`. + /// + /// # Panics + /// + /// Panics if the Client initialization fails. + pub fn blocking_client() -> reqwest::blocking::Client { + reqwest::blocking::Client::builder() + .user_agent(user_agent()) + .build() + .expect("Failed to build reqwest blocking client") + } +} + pub mod rpc { use soroban_env_host::xdr; use soroban_rpc::{Client, Error}; From 88a8ca91a28afe0da63debb448b2d0a8060f99e5 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Thu, 3 Oct 2024 10:33:13 -0400 Subject: [PATCH 19/55] Chore/use rich printer for network container (#1638) * Use rich printer in network container shared * Cleanup container retry logging --------- Co-authored-by: Nando Vieira --- .../src/commands/network/container.rs | 2 +- .../src/commands/network/container/logs.rs | 10 ++-- .../src/commands/network/container/shared.rs | 50 ++++++++++--------- .../src/commands/network/container/start.rs | 6 ++- .../src/commands/network/container/stop.rs | 2 +- 5 files changed, 41 insertions(+), 29 deletions(-) diff --git a/cmd/soroban-cli/src/commands/network/container.rs b/cmd/soroban-cli/src/commands/network/container.rs index 463dbbdc8..d14dc72e4 100644 --- a/cmd/soroban-cli/src/commands/network/container.rs +++ b/cmd/soroban-cli/src/commands/network/container.rs @@ -41,7 +41,7 @@ pub enum Error { impl Cmd { pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { match &self { - Cmd::Logs(cmd) => cmd.run().await?, + Cmd::Logs(cmd) => cmd.run(global_args).await?, Cmd::Start(cmd) => cmd.run(global_args).await?, Cmd::Stop(cmd) => cmd.run(global_args).await?, } diff --git a/cmd/soroban-cli/src/commands/network/container/logs.rs b/cmd/soroban-cli/src/commands/network/container/logs.rs index 99b36af9b..aaccffdde 100644 --- a/cmd/soroban-cli/src/commands/network/container/logs.rs +++ b/cmd/soroban-cli/src/commands/network/container/logs.rs @@ -1,6 +1,9 @@ use futures_util::TryStreamExt; -use crate::commands::network::container::shared::Error as ConnectionError; +use crate::{ + commands::{global, network::container::shared::Error as ConnectionError}, + print, +}; use super::shared::{Args, Name}; @@ -23,9 +26,10 @@ pub struct Cmd { } impl Cmd { - pub async fn run(&self) -> Result<(), Error> { + pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { + let print = print::Print::new(global_args.quiet); let container_name = Name(self.name.clone()).get_internal_container_name(); - let docker = self.container_args.connect_to_docker().await?; + let docker = self.container_args.connect_to_docker(&print).await?; let logs_stream = &mut docker.logs( &container_name, Some(bollard::container::LogsOptions { diff --git a/cmd/soroban-cli/src/commands/network/container/shared.rs b/cmd/soroban-cli/src/commands/network/container/shared.rs index f819f3ed3..38cb17af2 100644 --- a/cmd/soroban-cli/src/commands/network/container/shared.rs +++ b/cmd/soroban-cli/src/commands/network/container/shared.rs @@ -6,6 +6,8 @@ use clap::ValueEnum; // Need to add this for windows, since we are only using this crate for the unix fn try_docker_desktop_socket use home::home_dir; +use crate::print; + pub const DOCKER_HOST_HELP: &str = "Optional argument to override the default docker host. This is useful when you are using a non-standard docker host path for your Docker-compatible container runtime, e.g. Docker Desktop defaults to $HOME/.docker/run/docker.sock instead of /var/run/docker.sock"; // DEFAULT_DOCKER_HOST is from the bollard crate on the main branch, which has not been released yet: https://github.com/fussybeaver/bollard/blob/0972b1aac0ad5c08798e100319ddd0d2ee010365/src/docker.rs#L64 @@ -46,7 +48,8 @@ impl Args { .unwrap_or_default() } - pub(crate) async fn connect_to_docker(&self) -> Result { + #[allow(unused_variables)] + pub(crate) async fn connect_to_docker(&self, print: &print::Print) -> Result { // if no docker_host is provided, use the default docker host: // "unix:///var/run/docker.sock" on unix machines // "npipe:////./pipe/docker_engine" on windows machines @@ -89,7 +92,7 @@ impl Args { // if on unix, try to connect to the default docker desktop socket #[cfg(unix)] { - let docker_desktop_connection = try_docker_desktop_socket(&host)?; + let docker_desktop_connection = try_docker_desktop_socket(&host, print)?; match check_docker_connection(&docker_desktop_connection).await { Ok(()) => Ok(docker_desktop_connection), Err(err) => Err(err)?, @@ -138,40 +141,41 @@ impl Name { } #[cfg(unix)] -fn try_docker_desktop_socket(host: &str) -> Result { +fn try_docker_desktop_socket( + host: &str, + print: &print::Print, +) -> Result { let default_docker_desktop_host = format!("{}/.docker/run/docker.sock", home_dir().unwrap().display()); - println!("Failed to connect to DOCKER_HOST: {host}.\nTrying to connect to the default Docker Desktop socket at {default_docker_desktop_host}."); + print.warnln(format!("Failed to connect to Docker daemon at {host}.")); + + print.infoln(format!( + "Attempting to connect to the default Docker Desktop socket at {default_docker_desktop_host} instead." + )); Docker::connect_with_unix( &default_docker_desktop_host, DEFAULT_TIMEOUT, API_DEFAULT_VERSION, - ) + ).map_err(|e| { + print.errorln(format!( + "Failed to connect to the Docker daemon at {host:?}. Is the docker daemon running?" + )); + print.infoln( + "Running a local Stellar network requires a Docker-compatible container runtime." + ); + print.infoln( + "Please note that if you are using Docker Desktop, you may need to utilize the `--docker-host` flag to pass in the location of the docker socket on your machine." + ); + e + }) } // When bollard is not able to connect to the docker daemon, it returns a generic ConnectionRefused error // This method attempts to connect to the docker daemon and returns a more specific error message async fn check_docker_connection(docker: &Docker) -> Result<(), bollard::errors::Error> { - // This is a bit hacky, but the `client_addr` field is not directly accessible from the `Docker` struct, but we can access it from the debug string representation of the `Docker` struct - let docker_debug_string = format!("{docker:#?}"); - let start_of_client_addr = docker_debug_string.find("client_addr: ").unwrap(); - let end_of_client_addr = docker_debug_string[start_of_client_addr..] - .find(',') - .unwrap(); - // Extract the substring containing the value of client_addr - let client_addr = &docker_debug_string - [start_of_client_addr + "client_addr: ".len()..start_of_client_addr + end_of_client_addr] - .trim() - .trim_matches('"'); - match docker.version().await { Ok(_version) => Ok(()), - Err(err) => { - println!( - "⛔️ Failed to connect to the Docker daemon at {client_addr:?}. Is the docker daemon running?\nℹ️ Running a local Stellar network requires a Docker-compatible container runtime.\nℹ️ Please note that if you are using Docker Desktop, you may need to utilize the `--docker-host` flag to pass in the location of the docker socket on your machine.\n" - ); - Err(err) - } + Err(err) => Err(err), } } diff --git a/cmd/soroban-cli/src/commands/network/container/start.rs b/cmd/soroban-cli/src/commands/network/container/start.rs index a4d840f0f..2f16d3c1e 100644 --- a/cmd/soroban-cli/src/commands/network/container/start.rs +++ b/cmd/soroban-cli/src/commands/network/container/start.rs @@ -79,7 +79,11 @@ impl Runner { self.print .infoln(format!("Starting {} network", &self.args.network)); - let docker = self.args.container_args.connect_to_docker().await?; + let docker = self + .args + .container_args + .connect_to_docker(&self.print) + .await?; let image = self.get_image_name(); let mut stream = docker.create_image( diff --git a/cmd/soroban-cli/src/commands/network/container/stop.rs b/cmd/soroban-cli/src/commands/network/container/stop.rs index 87d6ba495..77f674c46 100644 --- a/cmd/soroban-cli/src/commands/network/container/stop.rs +++ b/cmd/soroban-cli/src/commands/network/container/stop.rs @@ -34,7 +34,7 @@ impl Cmd { pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { let print = print::Print::new(global_args.quiet); let container_name = Name(self.name.clone()); - let docker = self.container_args.connect_to_docker().await?; + let docker = self.container_args.connect_to_docker(&print).await?; print.infoln(format!( "Stopping {} container", From d37632aea26b68cfce22bb2d31e16727c56629d4 Mon Sep 17 00:00:00 2001 From: Abeeujah <100226788+Abeeujah@users.noreply.github.com> Date: Fri, 4 Oct 2024 13:28:56 +0100 Subject: [PATCH 20/55] feat: Bring colors to help message (#1650) Co-authored-by: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> --- cmd/soroban-cli/src/commands/global.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/cmd/soroban-cli/src/commands/global.rs b/cmd/soroban-cli/src/commands/global.rs index 9148e30cc..be883b6fd 100644 --- a/cmd/soroban-cli/src/commands/global.rs +++ b/cmd/soroban-cli/src/commands/global.rs @@ -1,11 +1,24 @@ -use clap::arg; +use clap::{ + arg, + builder::styling::{AnsiColor, Effects, Styles}, +}; use std::path::PathBuf; use super::config; +const USAGE_STYLES: Styles = Styles::styled() + .header(AnsiColor::Green.on_default().effects(Effects::BOLD)) + .usage(AnsiColor::Green.on_default().effects(Effects::BOLD)) + .literal(AnsiColor::Cyan.on_default().effects(Effects::BOLD)) + .placeholder(AnsiColor::Cyan.on_default().effects(Effects::BOLD)) + .error(AnsiColor::Red.on_default().effects(Effects::BOLD)) + .valid(AnsiColor::Cyan.on_default().effects(Effects::BOLD)) + .invalid(AnsiColor::Yellow.on_default().effects(Effects::BOLD)); + #[derive(Debug, clap::Args, Clone, Default)] #[group(skip)] #[allow(clippy::struct_excessive_bools)] +#[command(styles = USAGE_STYLES)] pub struct Args { #[clap(flatten)] pub locator: config::locator::Args, From b940978a5a61fa7ddfcb75235cc2e5796f26c22e Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Sat, 5 Oct 2024 01:29:02 +1000 Subject: [PATCH 21/55] Add Assembled from stellar-rpc-client@9f74833 (#1648) --- .../soroban-test/tests/it/integration/tx.rs | 5 +- cmd/soroban-cli/src/assembled.rs | 541 ++++++++++++++++++ .../src/commands/contract/deploy/asset.rs | 3 +- .../src/commands/contract/deploy/wasm.rs | 3 +- .../src/commands/contract/extend.rs | 4 +- .../src/commands/contract/install.rs | 5 +- .../src/commands/contract/invoke.rs | 3 +- cmd/soroban-cli/src/commands/tx/simulate.rs | 9 +- cmd/soroban-cli/src/fee.rs | 2 +- cmd/soroban-cli/src/lib.rs | 1 + 10 files changed, 561 insertions(+), 15 deletions(-) create mode 100644 cmd/soroban-cli/src/assembled.rs diff --git a/cmd/crates/soroban-test/tests/it/integration/tx.rs b/cmd/crates/soroban-test/tests/it/integration/tx.rs index 66c4b69fe..5cc6bdb9b 100644 --- a/cmd/crates/soroban-test/tests/it/integration/tx.rs +++ b/cmd/crates/soroban-test/tests/it/integration/tx.rs @@ -1,3 +1,4 @@ +use soroban_cli::assembled::simulate_and_assemble_transaction; use soroban_sdk::xdr::{Limits, ReadXdr, TransactionEnvelope, WriteXdr}; use soroban_test::{AssertExt, TestEnv}; @@ -23,9 +24,7 @@ async fn simulate() { .success() .stdout_as_str(); assert_eq!(xdr_base64_sim_only, assembled_str); - let assembled = sandbox - .client() - .simulate_and_assemble_transaction(&tx) + let assembled = simulate_and_assemble_transaction(&sandbox.client(), &tx) .await .unwrap(); let txn_env: TransactionEnvelope = assembled.transaction().clone().into(); diff --git a/cmd/soroban-cli/src/assembled.rs b/cmd/soroban-cli/src/assembled.rs new file mode 100644 index 000000000..312863d52 --- /dev/null +++ b/cmd/soroban-cli/src/assembled.rs @@ -0,0 +1,541 @@ +use sha2::{Digest, Sha256}; +use stellar_xdr::curr::{ + self as xdr, ExtensionPoint, Hash, InvokeHostFunctionOp, LedgerFootprint, Limits, Memo, + Operation, OperationBody, Preconditions, ReadXdr, RestoreFootprintOp, + SorobanAuthorizationEntry, SorobanAuthorizedFunction, SorobanResources, SorobanTransactionData, + Transaction, TransactionEnvelope, TransactionExt, TransactionSignaturePayload, + TransactionSignaturePayloadTaggedTransaction, TransactionV1Envelope, VecM, WriteXdr, +}; + +use soroban_rpc::{Error, RestorePreamble, SimulateTransactionResponse}; + +use soroban_rpc::{LogEvents, LogResources}; + +pub(crate) const DEFAULT_TRANSACTION_FEES: u32 = 100; + +pub async fn simulate_and_assemble_transaction( + client: &soroban_rpc::Client, + tx: &Transaction, +) -> Result { + let sim_res = client + .simulate_transaction_envelope(&TransactionEnvelope::Tx(TransactionV1Envelope { + tx: tx.clone(), + signatures: VecM::default(), + })) + .await?; + match sim_res.error { + None => Ok(Assembled::new(tx, sim_res)?), + Some(e) => { + diagnostic_events(&sim_res.events, tracing::Level::ERROR); + Err(Error::TransactionSimulationFailed(e)) + } + } +} + +pub struct Assembled { + pub(crate) txn: Transaction, + pub(crate) sim_res: SimulateTransactionResponse, +} + +/// Represents an assembled transaction ready to be signed and submitted to the network. +impl Assembled { + /// + /// Creates a new `Assembled` transaction. + /// + /// # Arguments + /// + /// * `txn` - The original transaction. + /// * `client` - The client used for simulation and submission. + /// + /// # Errors + /// + /// Returns an error if simulation fails or if assembling the transaction fails. + pub fn new(txn: &Transaction, sim_res: SimulateTransactionResponse) -> Result { + let txn = assemble(txn, &sim_res)?; + Ok(Self { txn, sim_res }) + } + + /// + /// Calculates the hash of the assembled transaction. + /// + /// # Arguments + /// + /// * `network_passphrase` - The network passphrase. + /// + /// # Errors + /// + /// Returns an error if generating the hash fails. + pub fn hash(&self, network_passphrase: &str) -> Result<[u8; 32], xdr::Error> { + let signature_payload = TransactionSignaturePayload { + network_id: Hash(Sha256::digest(network_passphrase).into()), + tagged_transaction: TransactionSignaturePayloadTaggedTransaction::Tx(self.txn.clone()), + }; + Ok(Sha256::digest(signature_payload.to_xdr(Limits::none())?).into()) + } + + /// Create a transaction for restoring any data in the `restore_preamble` field of the `SimulateTransactionResponse`. + /// + /// # Errors + pub fn restore_txn(&self) -> Result, Error> { + if let Some(restore_preamble) = &self.sim_res.restore_preamble { + restore(self.transaction(), restore_preamble).map(Option::Some) + } else { + Ok(None) + } + } + + /// Returns a reference to the original transaction. + #[must_use] + pub fn transaction(&self) -> &Transaction { + &self.txn + } + + /// Returns a reference to the simulation response. + #[must_use] + pub fn sim_response(&self) -> &SimulateTransactionResponse { + &self.sim_res + } + + #[must_use] + pub fn bump_seq_num(mut self) -> Self { + self.txn.seq_num.0 += 1; + self + } + + /// + /// # Errors + #[must_use] + pub fn auth_entries(&self) -> VecM { + self.txn + .operations + .first() + .and_then(|op| match op.body { + OperationBody::InvokeHostFunction(ref body) => (matches!( + body.auth.first().map(|x| &x.root_invocation.function), + Some(&SorobanAuthorizedFunction::ContractFn(_)) + )) + .then_some(body.auth.clone()), + _ => None, + }) + .unwrap_or_default() + } + + /// + /// # Errors + pub fn log( + &self, + log_events: Option, + log_resources: Option, + ) -> Result<(), Error> { + if let TransactionExt::V1(SorobanTransactionData { + resources: resources @ SorobanResources { footprint, .. }, + .. + }) = &self.txn.ext + { + if let Some(log) = log_resources { + log(resources); + } + if let Some(log) = log_events { + log(footprint, &[self.auth_entries()], &self.sim_res.events()?); + }; + } + Ok(()) + } + + #[must_use] + pub fn requires_auth(&self) -> bool { + requires_auth(&self.txn).is_some() + } + + #[must_use] + pub fn is_view(&self) -> bool { + let TransactionExt::V1(SorobanTransactionData { + resources: + SorobanResources { + footprint: LedgerFootprint { read_write, .. }, + .. + }, + .. + }) = &self.txn.ext + else { + return false; + }; + read_write.is_empty() + } + + #[must_use] + pub fn set_max_instructions(mut self, instructions: u32) -> Self { + if let TransactionExt::V1(SorobanTransactionData { + resources: + SorobanResources { + instructions: ref mut i, + .. + }, + .. + }) = &mut self.txn.ext + { + tracing::trace!("setting max instructions to {instructions} from {i}"); + *i = instructions; + } + self + } +} + +// Apply the result of a simulateTransaction onto a transaction envelope, preparing it for +// submission to the network. +/// +/// # Errors +fn assemble( + raw: &Transaction, + simulation: &SimulateTransactionResponse, +) -> Result { + let mut tx = raw.clone(); + + // Right now simulate.results is one-result-per-function, and assumes there is only one + // operation in the txn, so we need to enforce that here. I (Paul) think that is a bug + // in soroban-rpc.simulateTransaction design, and we should fix it there. + // TODO: We should to better handling so non-soroban txns can be a passthrough here. + if tx.operations.len() != 1 { + return Err(Error::UnexpectedOperationCount { + count: tx.operations.len(), + }); + } + + let transaction_data = simulation.transaction_data()?; + + let mut op = tx.operations[0].clone(); + if let OperationBody::InvokeHostFunction(ref mut body) = &mut op.body { + if body.auth.is_empty() { + if simulation.results.len() != 1 { + return Err(Error::UnexpectedSimulateTransactionResultSize { + length: simulation.results.len(), + }); + } + + let auths = simulation + .results + .iter() + .map(|r| { + VecM::try_from( + r.auth + .iter() + .map(|v| SorobanAuthorizationEntry::from_xdr_base64(v, Limits::none())) + .collect::, _>>()?, + ) + }) + .collect::, _>>()?; + if !auths.is_empty() { + body.auth = auths[0].clone(); + } + } + } + + // Update transaction fees to meet the minimum resource fees. + let classic_tx_fee: u64 = DEFAULT_TRANSACTION_FEES.into(); + + // Choose larger of existing fee or inclusion + resource fee. + tx.fee = tx.fee.max( + u32::try_from(classic_tx_fee + simulation.min_resource_fee) + .map_err(|_| Error::LargeFee(simulation.min_resource_fee + classic_tx_fee))?, + ); + + tx.operations = vec![op].try_into()?; + tx.ext = TransactionExt::V1(transaction_data); + Ok(tx) +} + +fn requires_auth(txn: &Transaction) -> Option { + let [op @ Operation { + body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { auth, .. }), + .. + }] = txn.operations.as_slice() + else { + return None; + }; + matches!( + auth.first().map(|x| &x.root_invocation.function), + Some(&SorobanAuthorizedFunction::ContractFn(_)) + ) + .then(move || op.clone()) +} + +fn restore(parent: &Transaction, restore: &RestorePreamble) -> Result { + let transaction_data = + SorobanTransactionData::from_xdr_base64(&restore.transaction_data, Limits::none())?; + let fee = u32::try_from(restore.min_resource_fee) + .map_err(|_| Error::LargeFee(restore.min_resource_fee))?; + Ok(Transaction { + source_account: parent.source_account.clone(), + fee: parent + .fee + .checked_add(fee) + .ok_or(Error::LargeFee(restore.min_resource_fee))?, + seq_num: parent.seq_num.clone(), + cond: Preconditions::None, + memo: Memo::None, + operations: vec![Operation { + source_account: None, + body: OperationBody::RestoreFootprint(RestoreFootprintOp { + ext: ExtensionPoint::V0, + }), + }] + .try_into()?, + ext: TransactionExt::V1(transaction_data), + }) +} + +fn diagnostic_events(events: &[impl std::fmt::Debug], level: tracing::Level) { + for (i, event) in events.iter().enumerate() { + if level == tracing::Level::TRACE { + tracing::trace!("{i}: {event:#?}"); + } else if level == tracing::Level::INFO { + tracing::info!("{i}: {event:#?}"); + } else if level == tracing::Level::ERROR { + tracing::error!("{i}: {event:#?}"); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use soroban_rpc::SimulateHostFunctionResultRaw; + use stellar_strkey::ed25519::PublicKey as Ed25519PublicKey; + use stellar_xdr::curr::{ + AccountId, ChangeTrustAsset, ChangeTrustOp, ExtensionPoint, Hash, HostFunction, + InvokeContractArgs, InvokeHostFunctionOp, LedgerFootprint, Memo, MuxedAccount, Operation, + Preconditions, PublicKey, ScAddress, ScSymbol, ScVal, SequenceNumber, + SorobanAuthorizedFunction, SorobanAuthorizedInvocation, SorobanResources, + SorobanTransactionData, Uint256, WriteXdr, + }; + + const SOURCE: &str = "GBZXN7PIRZGNMHGA7MUUUF4GWPY5AYPV6LY4UV2GL6VJGIQRXFDNMADI"; + + fn transaction_data() -> SorobanTransactionData { + SorobanTransactionData { + resources: SorobanResources { + footprint: LedgerFootprint { + read_only: VecM::default(), + read_write: VecM::default(), + }, + instructions: 0, + read_bytes: 5, + write_bytes: 0, + }, + resource_fee: 0, + ext: ExtensionPoint::V0, + } + } + + fn simulation_response() -> SimulateTransactionResponse { + let source_bytes = Ed25519PublicKey::from_string(SOURCE).unwrap().0; + let fn_auth = &SorobanAuthorizationEntry { + credentials: xdr::SorobanCredentials::Address(xdr::SorobanAddressCredentials { + address: ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519(Uint256( + source_bytes, + )))), + nonce: 0, + signature_expiration_ledger: 0, + signature: ScVal::Void, + }), + root_invocation: SorobanAuthorizedInvocation { + function: SorobanAuthorizedFunction::ContractFn(InvokeContractArgs { + contract_address: ScAddress::Contract(Hash([0; 32])), + function_name: ScSymbol("fn".try_into().unwrap()), + args: VecM::default(), + }), + sub_invocations: VecM::default(), + }, + }; + + SimulateTransactionResponse { + min_resource_fee: 115, + latest_ledger: 3, + results: vec![SimulateHostFunctionResultRaw { + auth: vec![fn_auth.to_xdr_base64(Limits::none()).unwrap()], + xdr: ScVal::U32(0).to_xdr_base64(Limits::none()).unwrap(), + }], + transaction_data: transaction_data().to_xdr_base64(Limits::none()).unwrap(), + ..Default::default() + } + } + + fn single_contract_fn_transaction() -> Transaction { + let source_bytes = Ed25519PublicKey::from_string(SOURCE).unwrap().0; + Transaction { + source_account: MuxedAccount::Ed25519(Uint256(source_bytes)), + fee: 100, + seq_num: SequenceNumber(0), + cond: Preconditions::None, + memo: Memo::None, + operations: vec![Operation { + source_account: None, + body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { + host_function: HostFunction::InvokeContract(InvokeContractArgs { + contract_address: ScAddress::Contract(Hash([0x0; 32])), + function_name: ScSymbol::default(), + args: VecM::default(), + }), + auth: VecM::default(), + }), + }] + .try_into() + .unwrap(), + ext: TransactionExt::V0, + } + } + + #[test] + fn test_assemble_transaction_updates_tx_data_from_simulation_response() { + let sim = simulation_response(); + let txn = single_contract_fn_transaction(); + let Ok(result) = assemble(&txn, &sim) else { + panic!("assemble failed"); + }; + + // validate it auto updated the tx fees from sim response fees + // since it was greater than tx.fee + assert_eq!(215, result.fee); + + // validate it updated sorobantransactiondata block in the tx ext + assert_eq!(TransactionExt::V1(transaction_data()), result.ext); + } + + #[test] + fn test_assemble_transaction_adds_the_auth_to_the_host_function() { + let sim = simulation_response(); + let txn = single_contract_fn_transaction(); + let Ok(result) = assemble(&txn, &sim) else { + panic!("assemble failed"); + }; + + assert_eq!(1, result.operations.len()); + let OperationBody::InvokeHostFunction(ref op) = result.operations[0].body else { + panic!("unexpected operation type: {:#?}", result.operations[0]); + }; + + assert_eq!(1, op.auth.len()); + let auth = &op.auth[0]; + + let xdr::SorobanAuthorizedFunction::ContractFn(xdr::InvokeContractArgs { + ref function_name, + .. + }) = auth.root_invocation.function + else { + panic!("unexpected function type"); + }; + assert_eq!("fn".to_string(), format!("{}", function_name.0)); + + let xdr::SorobanCredentials::Address(xdr::SorobanAddressCredentials { + address: + xdr::ScAddress::Account(xdr::AccountId(xdr::PublicKey::PublicKeyTypeEd25519(address))), + .. + }) = &auth.credentials + else { + panic!("unexpected credentials type"); + }; + assert_eq!( + SOURCE.to_string(), + stellar_strkey::ed25519::PublicKey(address.0).to_string() + ); + } + + #[test] + fn test_assemble_transaction_errors_for_non_invokehostfn_ops() { + let source_bytes = Ed25519PublicKey::from_string(SOURCE).unwrap().0; + let txn = Transaction { + source_account: MuxedAccount::Ed25519(Uint256(source_bytes)), + fee: 100, + seq_num: SequenceNumber(0), + cond: Preconditions::None, + memo: Memo::None, + operations: vec![Operation { + source_account: None, + body: OperationBody::ChangeTrust(ChangeTrustOp { + line: ChangeTrustAsset::Native, + limit: 0, + }), + }] + .try_into() + .unwrap(), + ext: TransactionExt::V0, + }; + + let result = assemble( + &txn, + &SimulateTransactionResponse { + min_resource_fee: 115, + transaction_data: transaction_data().to_xdr_base64(Limits::none()).unwrap(), + latest_ledger: 3, + ..Default::default() + }, + ); + + match result { + Ok(_) => {} + Err(e) => panic!("expected assembled operation, got: {e:#?}"), + } + } + + #[test] + fn test_assemble_transaction_errors_for_errors_for_mismatched_simulation() { + let txn = single_contract_fn_transaction(); + + let result = assemble( + &txn, + &SimulateTransactionResponse { + min_resource_fee: 115, + transaction_data: transaction_data().to_xdr_base64(Limits::none()).unwrap(), + latest_ledger: 3, + ..Default::default() + }, + ); + + match result { + Err(Error::UnexpectedSimulateTransactionResultSize { length }) => { + assert_eq!(0, length); + } + r => panic!("expected UnexpectedSimulateTransactionResultSize error, got: {r:#?}"), + } + } + + #[test] + fn test_assemble_transaction_overflow_behavior() { + // + // Test two separate cases: + // + // 1. Given a near-max (u32::MAX - 100) resource fee make sure the tx + // fee does not overflow after adding the base inclusion fee (100). + // 2. Given a large resource fee that WILL exceed u32::MAX with the + // base inclusion fee, ensure the overflow is caught with an error + // rather than silently ignored. + let txn = single_contract_fn_transaction(); + let mut response = simulation_response(); + + // sanity check so these can be adjusted if the above helper changes + assert_eq!(txn.fee, 100, "modified txn.fee: update the math below"); + + // 1: wiggle room math overflows but result fits + response.min_resource_fee = (u32::MAX - 100).into(); + + match assemble(&txn, &response) { + Ok(asstxn) => { + let expected = u32::MAX; + assert_eq!(asstxn.fee, expected); + } + r => panic!("expected success, got: {r:#?}"), + } + + // 2: combo overflows, should throw + response.min_resource_fee = (u32::MAX - 99).into(); + + match assemble(&txn, &response) { + Err(Error::LargeFee(fee)) => { + let expected = u64::from(u32::MAX) + 1; + assert_eq!(expected, fee, "expected {expected} != {fee} actual"); + } + r => panic!("expected LargeFee error, got: {r:#?}"), + } + } +} diff --git a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs index d2fcb62c4..8ff8e08b3 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs @@ -12,6 +12,7 @@ use std::convert::Infallible; use std::{array::TryFromSliceError, fmt::Debug, num::ParseIntError}; use crate::{ + assembled::simulate_and_assemble_transaction, commands::{ global, txn_result::{TxnEnvelopeResult, TxnResult}, @@ -118,7 +119,7 @@ impl NetworkRunnable for Cmd { if self.fee.build_only { return Ok(TxnResult::Txn(tx)); } - let txn = client.simulate_and_assemble_transaction(&tx).await?; + let txn = simulate_and_assemble_transaction(&client, &tx).await?; let txn = self.fee.apply_to_assembled_txn(txn).transaction().clone(); if self.fee.sim_only { return Ok(TxnResult::Txn(txn)); diff --git a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs index 42b31dd7d..36ce2e9f7 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs @@ -16,6 +16,7 @@ use soroban_env_host::{ }; use crate::{ + assembled::simulate_and_assemble_transaction, commands::{contract::install, HEADING_RPC}, config::{self, data, locator, network}, rpc::{self, Client}, @@ -240,7 +241,7 @@ impl NetworkRunnable for Cmd { print.infoln("Simulating deploy transaction…"); - let txn = client.simulate_and_assemble_transaction(&txn).await?; + let txn = simulate_and_assemble_transaction(&client, &txn).await?; let txn = self.fee.apply_to_assembled_txn(txn).transaction().clone(); if self.fee.sim_only { diff --git a/cmd/soroban-cli/src/commands/contract/extend.rs b/cmd/soroban-cli/src/commands/contract/extend.rs index b06cacf3e..3f7477254 100644 --- a/cmd/soroban-cli/src/commands/contract/extend.rs +++ b/cmd/soroban-cli/src/commands/contract/extend.rs @@ -9,6 +9,7 @@ use soroban_env_host::xdr::{ }; use crate::{ + assembled::simulate_and_assemble_transaction, commands::{ global, txn_result::{TxnEnvelopeResult, TxnResult}, @@ -170,8 +171,7 @@ impl NetworkRunnable for Cmd { if self.fee.build_only { return Ok(TxnResult::Txn(tx)); } - let tx = client - .simulate_and_assemble_transaction(&tx) + let tx = simulate_and_assemble_transaction(&client, &tx) .await? .transaction() .clone(); diff --git a/cmd/soroban-cli/src/commands/contract/install.rs b/cmd/soroban-cli/src/commands/contract/install.rs index 9e8f6b098..b70cadb78 100644 --- a/cmd/soroban-cli/src/commands/contract/install.rs +++ b/cmd/soroban-cli/src/commands/contract/install.rs @@ -10,6 +10,7 @@ use soroban_env_host::xdr::{ }; use super::restore; +use crate::assembled::simulate_and_assemble_transaction; use crate::commands::txn_result::{TxnEnvelopeResult, TxnResult}; use crate::commands::{global, NetworkRunnable}; use crate::config::{self, data, network}; @@ -177,9 +178,7 @@ impl NetworkRunnable for Cmd { print.infoln("Simulating install transaction…"); - let txn = client - .simulate_and_assemble_transaction(&tx_without_preflight) - .await?; + let txn = simulate_and_assemble_transaction(&client, &tx_without_preflight).await?; let txn = self.fee.apply_to_assembled_txn(txn).transaction().clone(); if self.fee.sim_only { diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index fa90ef9b3..55e2554bf 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -23,6 +23,7 @@ use soroban_spec::read::FromWasmError; use super::super::events; use super::arg_parsing; +use crate::assembled::simulate_and_assemble_transaction; use crate::commands::contract::arg_parsing::{build_host_function_parameters, output_to_string}; use crate::commands::txn_result::{TxnEnvelopeResult, TxnResult}; use crate::commands::NetworkRunnable; @@ -246,7 +247,7 @@ impl NetworkRunnable for Cmd { if self.fee.build_only { return Ok(TxnResult::Txn(tx)); } - let txn = client.simulate_and_assemble_transaction(&tx).await?; + let txn = simulate_and_assemble_transaction(&client, &tx).await?; let txn = self.fee.apply_to_assembled_txn(txn); if self.fee.sim_only { return Ok(TxnResult::Txn(txn.transaction().clone())); diff --git a/cmd/soroban-cli/src/commands/tx/simulate.rs b/cmd/soroban-cli/src/commands/tx/simulate.rs index 58be37deb..1f534884d 100644 --- a/cmd/soroban-cli/src/commands/tx/simulate.rs +++ b/cmd/soroban-cli/src/commands/tx/simulate.rs @@ -1,6 +1,8 @@ -use crate::xdr::{self, TransactionEnvelope, WriteXdr}; +use crate::{ + assembled::{simulate_and_assemble_transaction, Assembled}, + xdr::{self, TransactionEnvelope, WriteXdr}, +}; use async_trait::async_trait; -use soroban_rpc::Assembled; use crate::commands::{config, global, NetworkRunnable}; @@ -50,6 +52,7 @@ impl NetworkRunnable for Cmd { let network = config.get_network()?; let client = crate::rpc::Client::new(&network.rpc_url)?; let tx = super::xdr::unwrap_envelope_v1(super::xdr::tx_envelope_from_stdin()?)?; - Ok(client.simulate_and_assemble_transaction(&tx).await?) + let tx = simulate_and_assemble_transaction(&client, &tx).await?; + Ok(tx) } } diff --git a/cmd/soroban-cli/src/fee.rs b/cmd/soroban-cli/src/fee.rs index 698d66007..c2bc32bdb 100644 --- a/cmd/soroban-cli/src/fee.rs +++ b/cmd/soroban-cli/src/fee.rs @@ -1,7 +1,7 @@ use clap::arg; +use crate::assembled::Assembled; use soroban_env_host::xdr; -use soroban_rpc::Assembled; use crate::commands::HEADING_RPC; diff --git a/cmd/soroban-cli/src/lib.rs b/cmd/soroban-cli/src/lib.rs index f5ea21884..4c904855c 100644 --- a/cmd/soroban-cli/src/lib.rs +++ b/cmd/soroban-cli/src/lib.rs @@ -11,6 +11,7 @@ pub(crate) use soroban_rpc as rpc; mod cli; pub use cli::main; +pub mod assembled; pub mod commands; pub mod config; pub mod fee; From fb4e6bd9352251a243801ad189362ca2abc9d7c0 Mon Sep 17 00:00:00 2001 From: Gleb Date: Fri, 4 Oct 2024 16:23:43 -0700 Subject: [PATCH 22/55] Deprecate contract inspect (#1636) --- FULL_HELP_DOCS.md | 4 ++-- cmd/soroban-cli/src/commands/contract/inspect.rs | 8 +++++++- cmd/soroban-cli/src/commands/contract/mod.rs | 5 +++-- cmd/soroban-cli/src/commands/network/container/shared.rs | 3 +-- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index b8b03c580..5128ce396 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -85,7 +85,7 @@ Tools for smart contract developers * `id` — Generate the contract id for a given contract or asset * `info` — Access info about contracts * `init` — Initialize a Soroban project with an example contract -* `inspect` — Inspect a WASM file listing contract functions, meta, etc +* `inspect` — (Deprecated in favor of `contract info` subcommands) Inspect a WASM file listing contract functions, meta, etc * `install` — Install a WASM file to the ledger without creating a contract instance * `invoke` — Invoke a contract function * `optimize` — Optimize a WASM file @@ -630,7 +630,7 @@ Initialize a Soroban project with an example contract ## `stellar contract inspect` -Inspect a WASM file listing contract functions, meta, etc +(Deprecated in favor of `contract info` subcommands) Inspect a WASM file listing contract functions, meta, etc **Usage:** `stellar contract inspect [OPTIONS] --wasm ` diff --git a/cmd/soroban-cli/src/commands/contract/inspect.rs b/cmd/soroban-cli/src/commands/contract/inspect.rs index 36a1a2302..46fdc33a7 100644 --- a/cmd/soroban-cli/src/commands/contract/inspect.rs +++ b/cmd/soroban-cli/src/commands/contract/inspect.rs @@ -5,6 +5,8 @@ use std::{fmt::Debug, path::PathBuf}; use tracing::debug; use super::SpecOutput; +use crate::commands::global::Args; +use crate::print::Print; use crate::{config::locator, wasm}; #[derive(Parser, Debug, Clone)] @@ -33,7 +35,11 @@ pub enum Error { } impl Cmd { - pub fn run(&self) -> Result<(), Error> { + pub fn run(&self, global_args: &Args) -> Result<(), Error> { + Print::new(global_args.quiet).warnln( + "`contract inspect` has been deprecated in favor of `contract info`. \ + Please use `contract info` instead.", + ); let wasm = self.wasm.parse()?; debug!("File: {}", self.wasm.wasm.to_string_lossy()); let output = match self.output { diff --git a/cmd/soroban-cli/src/commands/contract/mod.rs b/cmd/soroban-cli/src/commands/contract/mod.rs index 761784de9..9ac6b8527 100644 --- a/cmd/soroban-cli/src/commands/contract/mod.rs +++ b/cmd/soroban-cli/src/commands/contract/mod.rs @@ -56,7 +56,8 @@ pub enum Cmd { /// Initialize a Soroban project with an example contract Init(init::Cmd), - /// Inspect a WASM file listing contract functions, meta, etc + /// (Deprecated in favor of `contract info` subcommands) Inspect a WASM file listing contract functions, meta, etc + #[command(display_order = 100)] Inspect(inspect::Cmd), /// Install a WASM file to the ledger without creating a contract instance @@ -147,7 +148,7 @@ impl Cmd { Cmd::Id(id) => id.run()?, Cmd::Info(info) => info.run().await?, Cmd::Init(init) => init.run(global_args)?, - Cmd::Inspect(inspect) => inspect.run()?, + Cmd::Inspect(inspect) => inspect.run(global_args)?, Cmd::Install(install) => install.run(global_args).await?, Cmd::Invoke(invoke) => invoke.run(global_args).await?, Cmd::Optimize(optimize) => optimize.run()?, diff --git a/cmd/soroban-cli/src/commands/network/container/shared.rs b/cmd/soroban-cli/src/commands/network/container/shared.rs index 38cb17af2..c7964abb0 100644 --- a/cmd/soroban-cli/src/commands/network/container/shared.rs +++ b/cmd/soroban-cli/src/commands/network/container/shared.rs @@ -157,7 +157,7 @@ fn try_docker_desktop_socket( &default_docker_desktop_host, DEFAULT_TIMEOUT, API_DEFAULT_VERSION, - ).map_err(|e| { + ).inspect_err(|_| { print.errorln(format!( "Failed to connect to the Docker daemon at {host:?}. Is the docker daemon running?" )); @@ -167,7 +167,6 @@ fn try_docker_desktop_socket( print.infoln( "Please note that if you are using Docker Desktop, you may need to utilize the `--docker-host` flag to pass in the location of the docker socket on your machine." ); - e }) } From 46c534ee4223120b3ee9ca29aef3d4dda6603600 Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Tue, 8 Oct 2024 00:00:49 +1000 Subject: [PATCH 23/55] Make all logs in upgrade check debug (#1655) --- cmd/soroban-cli/src/upgrade_check.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/soroban-cli/src/upgrade_check.rs b/cmd/soroban-cli/src/upgrade_check.rs index 294056dd1..81ad7a0ee 100644 --- a/cmd/soroban-cli/src/upgrade_check.rs +++ b/cmd/soroban-cli/src/upgrade_check.rs @@ -56,7 +56,7 @@ pub async fn upgrade_check(quiet: bool) { let current_version = crate::commands::version::pkg(); let mut stats = UpgradeCheck::load().unwrap_or_else(|e| { - tracing::error!("Failed to load upgrade check data: {e}"); + tracing::debug!("Failed to load upgrade check data: {e}"); UpgradeCheck::default() }); @@ -72,7 +72,7 @@ pub async fn upgrade_check(quiet: bool) { }; } Err(e) => { - tracing::error!("Failed to fetch stellar-cli info from crates.io: {e}"); + tracing::debug!("Failed to fetch stellar-cli info from crates.io: {e}"); // Only update the latest check time if the fetch failed // This way we don't spam the user with errors stats.latest_check_time = now; @@ -80,7 +80,7 @@ pub async fn upgrade_check(quiet: bool) { } if let Err(e) = stats.save() { - tracing::error!("Failed to save upgrade check data: {e}"); + tracing::debug!("Failed to save upgrade check data: {e}"); } } From ba23e00b9a0be2262f2bfe2fbe38ae0e67cbcf06 Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Mon, 7 Oct 2024 17:04:36 -0700 Subject: [PATCH 24/55] Do not require source account when reading contract. (#1658) --- FULL_HELP_DOCS.md | 4 +--- .../soroban-test/tests/it/integration/cookbook.rs | 2 -- cmd/soroban-cli/src/commands/contract/read.rs | 15 +++++++++------ cookbook/contract-lifecycle.mdx | 2 +- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index 5128ce396..e393818ce 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -747,7 +747,7 @@ Optimize a WASM file Print the current value of a contract-data ledger entry -**Usage:** `stellar contract read [OPTIONS] --source-account ` +**Usage:** `stellar contract read [OPTIONS]` ###### **Options:** @@ -781,8 +781,6 @@ Print the current value of a contract-data ledger entry * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config -* `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail -* `--hd-path ` — If using a seed phrase, which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." diff --git a/cmd/crates/soroban-test/tests/it/integration/cookbook.rs b/cmd/crates/soroban-test/tests/it/integration/cookbook.rs index 82b9c5f43..5e9a30eb2 100644 --- a/cmd/crates/soroban-test/tests/it/integration/cookbook.rs +++ b/cmd/crates/soroban-test/tests/it/integration/cookbook.rs @@ -252,8 +252,6 @@ mod tests { .arg("xdr") .arg("--key") .arg("COUNTER") - .arg("--source-account") - .arg(source) .assert() .stdout_as_str(); let key_xdr = read_xdr.split(',').next().unwrap_or("").trim(); diff --git a/cmd/soroban-cli/src/commands/contract/read.rs b/cmd/soroban-cli/src/commands/contract/read.rs index 3cb253bb1..dae5f3cdf 100644 --- a/cmd/soroban-cli/src/commands/contract/read.rs +++ b/cmd/soroban-cli/src/commands/contract/read.rs @@ -28,7 +28,7 @@ pub struct Cmd { #[command(flatten)] pub key: key::Args, #[command(flatten)] - config: config::Args, + config: config::ArgsLocatorAndNetwork, } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)] @@ -89,6 +89,8 @@ pub enum Error { OnlyDataAllowed, #[error(transparent)] Locator(#[from] locator::Error), + #[error(transparent)] + Network(#[from] config::network::Error), } impl Cmd { @@ -179,14 +181,15 @@ impl NetworkRunnable for Cmd { async fn run_against_rpc_server( &self, - _: Option<&global::Args>, - config: Option<&config::Args>, + _global_args: Option<&global::Args>, + _config: Option<&config::Args>, ) -> Result { - let config = config.unwrap_or(&self.config); - let network = config.get_network()?; + let locator = self.config.locator.clone(); + let network = self.config.network.get(&locator)?; + tracing::trace!(?network); let client = Client::new(&network.rpc_url)?; - let keys = self.key.parse_keys(&config.locator, &network)?; + let keys = self.key.parse_keys(&locator, &network)?; Ok(client.get_full_ledger_entries(&keys).await?) } } diff --git a/cookbook/contract-lifecycle.mdx b/cookbook/contract-lifecycle.mdx index b64262b34..e38a233de 100644 --- a/cookbook/contract-lifecycle.mdx +++ b/cookbook/contract-lifecycle.mdx @@ -47,7 +47,7 @@ stellar contract invoke --id --source alice --network testnet -- f 6. View the contract's state: ```bash -stellar contract read --id --network testnet --source alice --durability --key +stellar contract read --id --network testnet --durability --key ``` Note: `` is either `persistent` or `temporary`. `KEY` provides the key of the storage entry being read. From 8ebf6eaf370311a8218eeacad7a3da35b13b56e7 Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Thu, 10 Oct 2024 00:47:41 +1000 Subject: [PATCH 25/55] Update xdr, env, sdk, strkey deps to compat with 22.0.0-rc.1 stream (#1646) --- .github/workflows/dependency-check.yml | 6 - Cargo.lock | 547 +++++++++++++++--- Cargo.toml | 50 +- FULL_HELP_DOCS.md | 26 + cmd/crates/soroban-spec-tools/Cargo.toml | 7 +- cmd/crates/soroban-spec-tools/src/contract.rs | 22 +- cmd/crates/soroban-test/Cargo.toml | 2 - cmd/crates/soroban-test/src/wasm.rs | 2 +- .../test-wasms/custom_account/Cargo.toml | 4 +- .../test-wasms/custom_type/Cargo.toml | 4 +- .../test-wasms/hello_world/Cargo.toml | 4 +- .../test-wasms/hello_world/src/lib.rs | 2 +- .../tests/fixtures/test-wasms/swap/Cargo.toml | 4 +- .../fixtures/test-wasms/token/Cargo.toml | 4 +- .../tests/fixtures/test-wasms/udt/Cargo.toml | 4 +- .../contracts/add/Cargo.toml | 4 +- .../contracts/call/Cargo.toml | 4 +- .../workspace/contracts/add/Cargo.toml | 4 +- .../workspace/contracts/add/add2/Cargo.toml | 4 +- .../workspace/contracts/call/Cargo.toml | 4 +- .../soroban-test/tests/it/arg_parsing.rs | 2 +- .../soroban-test/tests/it/integration/tx.rs | 2 +- .../soroban-test/tests/it/integration/util.rs | 6 +- cmd/crates/stellar-ledger/Cargo.toml | 1 - cmd/crates/stellar-ledger/src/lib.rs | 8 +- .../tests/test/emulator_tests.rs | 6 +- cmd/soroban-cli/Cargo.toml | 1 - .../src/commands/contract/arg_parsing.rs | 2 +- .../src/commands/contract/deploy/asset.rs | 19 +- .../src/commands/contract/deploy/wasm.rs | 17 +- .../src/commands/contract/extend.rs | 4 +- .../src/commands/contract/id/asset.rs | 2 +- .../src/commands/contract/id/wasm.rs | 6 +- .../src/commands/contract/info/env_meta.rs | 25 +- .../src/commands/contract/info/shared.rs | 2 +- .../src/commands/contract/inspect.rs | 2 +- .../src/commands/contract/install.rs | 4 +- .../src/commands/contract/invoke.rs | 19 +- cmd/soroban-cli/src/commands/contract/mod.rs | 6 +- cmd/soroban-cli/src/commands/contract/read.rs | 15 +- .../src/commands/contract/restore.rs | 4 +- cmd/soroban-cli/src/commands/events.rs | 2 +- cmd/soroban-cli/src/commands/tx/hash.rs | 2 +- .../commands/tx/new/set_trustline_flags.rs | 4 +- cmd/soroban-cli/src/commands/tx/xdr.rs | 3 +- cmd/soroban-cli/src/commands/txn_result.rs | 2 +- cmd/soroban-cli/src/commands/version.rs | 6 +- cmd/soroban-cli/src/fee.rs | 2 +- cmd/soroban-cli/src/get_spec.rs | 6 +- cmd/soroban-cli/src/key.rs | 8 +- cmd/soroban-cli/src/lib.rs | 2 +- cmd/soroban-cli/src/log.rs | 5 +- cmd/soroban-cli/src/log/auth.rs | 2 +- cmd/soroban-cli/src/log/budget.rs | 5 - cmd/soroban-cli/src/log/cost.rs | 2 +- cmd/soroban-cli/src/log/footprint.rs | 2 +- cmd/soroban-cli/src/log/host_event.rs | 7 - cmd/soroban-cli/src/print.rs | 2 +- cmd/soroban-cli/src/signer.rs | 2 +- cmd/soroban-cli/src/tx/builder/transaction.rs | 2 +- cmd/soroban-cli/src/utils.rs | 4 +- cmd/soroban-cli/src/wasm.rs | 2 +- scripts/check-dependencies.bash | 61 -- 63 files changed, 656 insertions(+), 338 deletions(-) delete mode 100644 cmd/soroban-cli/src/log/budget.rs delete mode 100644 cmd/soroban-cli/src/log/host_event.rs delete mode 100755 scripts/check-dependencies.bash diff --git a/.github/workflows/dependency-check.yml b/.github/workflows/dependency-check.yml index cef838a7c..fe8c0e8bc 100644 --- a/.github/workflows/dependency-check.yml +++ b/.github/workflows/dependency-check.yml @@ -14,12 +14,6 @@ defaults: shell: bash jobs: - dependency-sanity-checker: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - run: rustup update - - run: scripts/check-dependencies.bash validate-rust-git-rev-deps: runs-on: ubuntu-latest steps: diff --git a/Cargo.lock b/Cargo.lock index 7ce514d54..33f0962d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,18 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -111,12 +123,136 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools 0.10.5", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + [[package]] name = "arrayref" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "ascii-canvas" version = "3.0.0" @@ -1108,6 +1244,17 @@ dependencies = [ "serde", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "derive_arbitrary" version = "1.3.2" @@ -1266,9 +1413,9 @@ checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] name = "ecdsa" -version = "0.16.7" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0997c976637b606099b9985693efa3581e84e41f5c11ba5255f88711058ad428" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", "digest 0.10.7", @@ -2580,11 +2727,23 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] [[package]] name = "heck" @@ -3277,9 +3436,9 @@ dependencies = [ [[package]] name = "k256" -version = "0.13.1" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if", "ecdsa", @@ -3563,6 +3722,12 @@ dependencies = [ "tokio", ] +[[package]] +name = "multi-stash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685a9ac4b61f4e728e1d2c6a7844609c16527aeb5e6c865915c08e619c16410f" + [[package]] name = "native-tls" version = "0.2.12" @@ -5128,6 +5293,18 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "soroban-builtin-sdk-macros" +version = "22.0.0-rc.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4c8668199d95e3061cd42e1b96a91451c656a238a607fa53f96f0a3fdcf5f3" +dependencies = [ + "itertools 0.10.5", + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "soroban-cli" version = "21.5.0" @@ -5182,17 +5359,16 @@ dependencies = [ "serde_json", "sha2 0.10.8", "shlex", - "soroban-env-host", - "soroban-ledger-snapshot", - "soroban-sdk", - "soroban-spec", + "soroban-ledger-snapshot 22.0.0-rc.1.1", + "soroban-sdk 22.0.0-rc.1.1", + "soroban-spec 22.0.0-rc.1.1", "soroban-spec-json", - "soroban-spec-rust", + "soroban-spec-rust 22.0.0-rc.1.1", "soroban-spec-tools", "soroban-spec-typescript", "stellar-rpc-client", "stellar-strkey 0.0.11", - "stellar-xdr", + "stellar-xdr 22.0.0-rc.1.1", "strsim 0.10.0", "strum 0.17.1", "strum_macros 0.17.1", @@ -5227,10 +5403,28 @@ dependencies = [ "num-derive", "num-traits", "serde", - "soroban-env-macros", - "soroban-wasmi", + "soroban-env-macros 21.2.1", + "soroban-wasmi 0.31.1-soroban.20.0.1", + "static_assertions", + "stellar-xdr 21.2.0", + "wasmparser 0.116.1", +] + +[[package]] +name = "soroban-env-common" +version = "22.0.0-rc.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdf1d66133d6b29e2834acea79decb57c47c71aa01885cae2b9ad621d67525c" +dependencies = [ + "crate-git-revision 0.0.6", + "ethnum", + "num-derive", + "num-traits", + "serde", + "soroban-env-macros 22.0.0-rc.1.1", + "soroban-wasmi 0.36.1-soroban.22.0.0", "static_assertions", - "stellar-xdr", + "stellar-xdr 22.0.0-rc.1.1", "wasmparser 0.116.1", ] @@ -5240,7 +5434,17 @@ version = "21.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bfb2536811045d5cd0c656a324cbe9ce4467eb734c7946b74410d90dea5d0ce" dependencies = [ - "soroban-env-common", + "soroban-env-common 21.2.1", + "static_assertions", +] + +[[package]] +name = "soroban-env-guest" +version = "22.0.0-rc.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa9610ac8a4a900e6f35b2ed171bc325c7e9883929f5e9da758e85f1226dd284" +dependencies = [ + "soroban-env-common 22.0.0-rc.1.1", "static_assertions", ] @@ -5269,14 +5473,50 @@ dependencies = [ "sec1", "sha2 0.10.8", "sha3", - "soroban-builtin-sdk-macros", - "soroban-env-common", - "soroban-wasmi", + "soroban-builtin-sdk-macros 21.2.1", + "soroban-env-common 21.2.1", + "soroban-wasmi 0.31.1-soroban.20.0.1", "static_assertions", "stellar-strkey 0.0.8", "wasmparser 0.116.1", ] +[[package]] +name = "soroban-env-host" +version = "22.0.0-rc.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c695d22888ede1f98c016a4a690be307817d133be0e0f32a25fd6e53bb6c929" +dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-serialize", + "curve25519-dalek 4.1.3", + "ecdsa", + "ed25519-dalek 2.1.1", + "elliptic-curve", + "generic-array", + "getrandom", + "hex-literal", + "hmac 0.12.1", + "k256", + "num-derive", + "num-integer", + "num-traits", + "p256", + "rand", + "rand_chacha", + "sec1", + "sha2 0.10.8", + "sha3", + "soroban-builtin-sdk-macros 22.0.0-rc.1.1", + "soroban-env-common 22.0.0-rc.1.1", + "soroban-wasmi 0.36.1-soroban.22.0.0", + "static_assertions", + "stellar-strkey 0.0.9", + "wasmparser 0.116.1", +] + [[package]] name = "soroban-env-macros" version = "21.2.1" @@ -5288,7 +5528,22 @@ dependencies = [ "quote", "serde", "serde_json", - "stellar-xdr", + "stellar-xdr 21.2.0", + "syn 2.0.77", +] + +[[package]] +name = "soroban-env-macros" +version = "22.0.0-rc.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d926d0daa3ba798cd70ce962ea10012e630d75088352369a6d248b2644dd7a2c" +dependencies = [ + "itertools 0.10.5", + "proc-macro2", + "quote", + "serde", + "serde_json", + "stellar-xdr 22.0.0-rc.1.1", "syn 2.0.77", ] @@ -5298,15 +5553,29 @@ version = "21.5.0" [[package]] name = "soroban-ledger-snapshot" -version = "21.7.2" +version = "21.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84589856911dfd6731695c9b51c858aed6d4540118c0a1e5c4c858ea13bc744c" +dependencies = [ + "serde", + "serde_json", + "serde_with", + "soroban-env-common 21.2.1", + "soroban-env-host 21.2.1", + "thiserror", +] + +[[package]] +name = "soroban-ledger-snapshot" +version = "22.0.0-rc.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf596b2083946a95914a55d7d29cee6a8095b515fd06211851f45bf6af5a496" +checksum = "c07ca63290730b803f0637e00994f803d2f24cc6383ac8680724050d07690a89" dependencies = [ "serde", "serde_json", "serde_with", - "soroban-env-common", - "soroban-env-host", + "soroban-env-common 22.0.0-rc.1.1", + "soroban-env-host 22.0.0-rc.1.1", "thiserror", ] @@ -5323,18 +5592,35 @@ dependencies = [ "rand", "serde", "serde_json", - "soroban-env-guest", - "soroban-env-host", - "soroban-ledger-snapshot", - "soroban-sdk-macros", + "soroban-env-guest 21.2.1", + "soroban-env-host 21.2.1", + "soroban-ledger-snapshot 21.7.3", + "soroban-sdk-macros 21.7.3", "stellar-strkey 0.0.8", ] +[[package]] +name = "soroban-sdk" +version = "22.0.0-rc.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b715e15357dbdda2fe0626005b75c2253bb3786deec3001d4077462a9dadd3" +dependencies = [ + "bytes-lit", + "rand", + "serde", + "serde_json", + "soroban-env-guest 22.0.0-rc.1.1", + "soroban-env-host 22.0.0-rc.1.1", + "soroban-ledger-snapshot 22.0.0-rc.1.1", + "soroban-sdk-macros 22.0.0-rc.1.1", + "stellar-strkey 0.0.9", +] + [[package]] name = "soroban-sdk-macros" -version = "21.7.2" +version = "21.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da03fa00b8ca6e392f013359c06d790d2d379f9c8d6f8a6dfe563ec64311e5d3" +checksum = "63c2173f1aacd56b4405eed71cb2a9694dff99d51ba72d4f0cbc5e4961fdabf4" dependencies = [ "crate-git-revision 0.0.6", "darling", @@ -5343,21 +5629,53 @@ dependencies = [ "quote", "rustc_version", "sha2 0.10.8", - "soroban-env-common", - "soroban-spec", - "soroban-spec-rust", - "stellar-xdr", + "soroban-env-common 21.2.1", + "soroban-spec 21.7.3", + "soroban-spec-rust 21.7.3", + "stellar-xdr 21.2.0", + "syn 2.0.77", +] + +[[package]] +name = "soroban-sdk-macros" +version = "22.0.0-rc.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00d1b8d96ccf53ea9c30d86352756cace630ca5c7e1200fff32ffc793bedd593" +dependencies = [ + "crate-git-revision 0.0.6", + "darling", + "itertools 0.10.5", + "proc-macro2", + "quote", + "rustc_version", + "sha2 0.10.8", + "soroban-env-common 22.0.0-rc.1.1", + "soroban-spec 22.0.0-rc.1.1", + "soroban-spec-rust 22.0.0-rc.1.1", + "stellar-xdr 22.0.0-rc.1.1", "syn 2.0.77", ] [[package]] name = "soroban-spec" -version = "21.7.2" +version = "21.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c723195463d8742bcb481520bd8b8325da66c39ea236ad46261e6af992e8a8" +checksum = "7705bffbcc747c08e81698b87b4a787f8b268c25d88f777160091dc1ee8121cb" dependencies = [ "base64 0.13.1", - "stellar-xdr", + "stellar-xdr 21.2.0", + "thiserror", + "wasmparser 0.116.1", +] + +[[package]] +name = "soroban-spec" +version = "22.0.0-rc.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4603430fd36848da7189e758d7f3fe1d7bbfef024e8aad2230a8be8252a583c1" +dependencies = [ + "base64 0.13.1", + "stellar-xdr 22.0.0-rc.1.1", "thiserror", "wasmparser 0.116.1", ] @@ -5371,23 +5689,39 @@ dependencies = [ "serde_derive", "serde_json", "sha2 0.9.9", - "soroban-spec", - "stellar-xdr", + "soroban-spec 22.0.0-rc.1.1", + "stellar-xdr 22.0.0-rc.1.1", "thiserror", ] [[package]] name = "soroban-spec-rust" -version = "21.7.2" +version = "21.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f1b0ec2af54e38f138910f09e101b100130efe625f69ece51c76dd4f06f8b2" +checksum = "48207ebc8616c2804a17203d1d86c53c3d3c804b682cbab011a135893db1cf78" dependencies = [ "prettyplease", "proc-macro2", "quote", "sha2 0.10.8", - "soroban-spec", - "stellar-xdr", + "soroban-spec 21.7.3", + "stellar-xdr 21.2.0", + "syn 2.0.77", + "thiserror", +] + +[[package]] +name = "soroban-spec-rust" +version = "22.0.0-rc.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d5f3fe5b27e0b1c1d9ecdff04c4e31f5cd4d12cdc052d54f0e4995cf637b9c" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "sha2 0.10.8", + "soroban-spec 22.0.0-rc.1.1", + "stellar-xdr 22.0.0-rc.1.1", "syn 2.0.77", "thiserror", ] @@ -5401,10 +5735,9 @@ dependencies = [ "hex", "itertools 0.10.5", "serde_json", - "soroban-env-host", - "soroban-spec", + "soroban-spec 22.0.0-rc.1.1", "stellar-strkey 0.0.11", - "stellar-xdr", + "stellar-xdr 22.0.0-rc.1.1", "thiserror", "tokio", "wasmparser 0.90.0", @@ -5425,8 +5758,8 @@ dependencies = [ "serde_derive", "serde_json", "sha2 0.9.9", - "soroban-spec", - "stellar-xdr", + "soroban-spec 22.0.0-rc.1.1", + "stellar-xdr 22.0.0-rc.1.1", "temp-dir", "thiserror", "walkdir", @@ -5446,10 +5779,8 @@ dependencies = [ "serde_json", "sha2 0.10.8", "soroban-cli", - "soroban-env-host", - "soroban-ledger-snapshot", - "soroban-sdk", - "soroban-spec", + "soroban-ledger-snapshot 22.0.0-rc.1.1", + "soroban-spec 22.0.0-rc.1.1", "soroban-spec-tools", "stellar-rpc-client", "stellar-strkey 0.0.11", @@ -5463,11 +5794,11 @@ dependencies = [ [[package]] name = "soroban-token-sdk" -version = "21.7.2" +version = "22.0.0-rc.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6ede0da76646037f3ea5db9ccd37830865444bb24f137cb8f0af8282c784f5" +checksum = "7baa9e15d0838973cfa0aa0eee6e0fa062bec2e4a115c82396f452fa6148d731" dependencies = [ - "soroban-sdk", + "soroban-sdk 22.0.0-rc.1.1", ] [[package]] @@ -5479,7 +5810,24 @@ dependencies = [ "smallvec", "spin", "wasmi_arena", - "wasmi_core", + "wasmi_core 0.13.0", + "wasmparser-nostd", +] + +[[package]] +name = "soroban-wasmi" +version = "0.36.1-soroban.22.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7044ea0ee6ff67039df1f232f0d3d98121f69a0409e944774912fc5f043c280f" +dependencies = [ + "arrayvec", + "multi-stash", + "num-derive", + "num-traits", + "smallvec", + "spin", + "wasmi_collections", + "wasmi_core 0.36.3", "wasmparser-nostd", ] @@ -5539,11 +5887,10 @@ dependencies = [ "serial_test", "sha2 0.9.9", "slip10", - "soroban-env-host", - "soroban-spec", + "soroban-spec 22.0.0-rc.1.1", "stellar-rpc-client", "stellar-strkey 0.0.11", - "stellar-xdr", + "stellar-xdr 22.0.0-rc.1.1", "test-case", "testcontainers", "thiserror", @@ -5554,8 +5901,7 @@ dependencies = [ [[package]] name = "stellar-rpc-client" version = "21.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c735c8e456fa7c37f72dd2a6abc022a111b3bf9b0520d50eef091eb1336ad370" +source = "git+https://github.com/stellar/rs-stellar-rpc-client?branch=main#9f748334fffb2762db01b566dee455eea3095c79" dependencies = [ "clap", "hex", @@ -5566,9 +5912,10 @@ dependencies = [ "serde", "serde-aux", "serde_json", + "serde_with", "sha2 0.10.8", - "stellar-strkey 0.0.8", - "stellar-xdr", + "stellar-strkey 0.0.9", + "stellar-xdr 22.0.0-rc.1.1", "termcolor", "termcolor_output", "thiserror", @@ -5587,6 +5934,17 @@ dependencies = [ "thiserror", ] +[[package]] +name = "stellar-strkey" +version = "0.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e3aa3ed00e70082cb43febc1c2afa5056b9bb3e348bbb43d0cd0aa88a611144" +dependencies = [ + "crate-git-revision 0.0.6", + "data-encoding", + "thiserror", +] + [[package]] name = "stellar-strkey" version = "0.0.11" @@ -5604,6 +5962,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2675a71212ed39a806e415b0dbf4702879ff288ec7f5ee996dda42a135512b50" dependencies = [ "arbitrary", + "base64 0.13.1", + "crate-git-revision 0.0.6", + "escape-bytes", + "hex", + "serde", + "serde_with", + "stellar-strkey 0.0.8", +] + +[[package]] +name = "stellar-xdr" +version = "22.0.0-rc.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c88dc0e928b9cb65ea43836b52560bb4ead3e32895f5019ca223dc7cd1966cbf" +dependencies = [ "base64 0.13.1", "clap", "crate-git-revision 0.0.6", @@ -5613,10 +5986,21 @@ dependencies = [ "serde", "serde_json", "serde_with", - "stellar-strkey 0.0.8", + "stellar-strkey 0.0.9", "thiserror", ] +[[package]] +name = "string-interner" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c6a0d765f5807e98a091107bae0a56ea3799f66a5de47b2c84c94a39c09974e" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "serde", +] + [[package]] name = "string_cache" version = "0.8.7" @@ -5864,35 +6248,35 @@ dependencies = [ name = "test_custom_account" version = "21.5.0" dependencies = [ - "soroban-sdk", + "soroban-sdk 21.7.2", ] [[package]] name = "test_custom_types" version = "21.5.0" dependencies = [ - "soroban-sdk", + "soroban-sdk 21.7.2", ] [[package]] name = "test_hello_world" version = "21.5.0" dependencies = [ - "soroban-sdk", + "soroban-sdk 21.7.2", ] [[package]] name = "test_swap" version = "21.5.0" dependencies = [ - "soroban-sdk", + "soroban-sdk 21.7.2", ] [[package]] name = "test_token" version = "21.5.0" dependencies = [ - "soroban-sdk", + "soroban-sdk 21.7.2", "soroban-token-sdk", ] @@ -5900,7 +6284,7 @@ dependencies = [ name = "test_udt" version = "21.5.0" dependencies = [ - "soroban-sdk", + "soroban-sdk 21.7.2", ] [[package]] @@ -6555,9 +6939,9 @@ dependencies = [ [[package]] name = "wasm-streams" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" dependencies = [ "futures-util", "js-sys", @@ -6572,6 +6956,17 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" +[[package]] +name = "wasmi_collections" +version = "0.36.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ee1cf2328e7fbb8654fda8449395c64c36ef5a30511e79fae0265a96e1a446" +dependencies = [ + "ahash", + "hashbrown 0.14.5", + "string-interner", +] + [[package]] name = "wasmi_core" version = "0.13.0" @@ -6584,6 +6979,18 @@ dependencies = [ "paste", ] +[[package]] +name = "wasmi_core" +version = "0.36.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e77c1e701d21edfd263e5c6c940861202c6b840c715040cfdca6211bf8857aa" +dependencies = [ + "downcast-rs", + "libm", + "num-traits", + "paste", +] + [[package]] name = "wasmparser" version = "0.90.0" diff --git a/Cargo.toml b/Cargo.toml index 7282ff497..ef052c569 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,17 +14,10 @@ exclude = ["cmd/crates/soroban-test/tests/fixtures/hello"] version = "21.5.0" rust-version = "1.81.0" -[workspace.dependencies.soroban-env-host] -version = "=21.2.1" - -[workspace.dependencies.soroban-simulation] -version = "=21.2.1" - -[workspace.dependencies.soroban-spec] -version = "=21.7.2" - -[workspace.dependencies.soroban-spec-rust] -version = "=21.7.2" +# Dependencies located in this repo: +[workspace.dependencies.soroban-cli] +version = "=21.5.0" +path = "cmd/soroban-cli" [workspace.dependencies.soroban-spec-json] version = "=21.5.0" @@ -38,27 +31,35 @@ path = "./cmd/crates/soroban-spec-typescript" version = "21.5.0" path = "./cmd/crates/soroban-spec-tools" +# Dependencies from the rs-stellar-xdr repo: +[workspace.dependencies.stellar-xdr] +version = "=22.0.0-rc.1.1" +default-features = true + +# Dependencies from the rs-soroban-sdk repo: +[workspace.dependencies.soroban-spec] +version = "=22.0.0-rc.1.1" + +[workspace.dependencies.soroban-spec-rust] +version = "=22.0.0-rc.1.1" + [workspace.dependencies.soroban-sdk] -version = "=21.7.2" +version = "=22.0.0-rc.1.1" [workspace.dependencies.soroban-token-sdk] -version = "=21.7.2" +version = "=22.0.0-rc.1.1" [workspace.dependencies.soroban-ledger-snapshot] -version = "=21.7.2" - -[workspace.dependencies.soroban-cli] -version = "=21.5.0" -path = "cmd/soroban-cli" +version = "=22.0.0-rc.1.1" +# Dependencies from the rs-stellar-rpc-client repo: [workspace.dependencies.soroban-rpc] package = "stellar-rpc-client" version = "21.4.0" +git = "https://github.com/stellar/rs-stellar-rpc-client" +branch = "main" -[workspace.dependencies.stellar-xdr] -version = "21.2.0" -default-features = true - +# Dependencies from elsewhere shared by crates: [workspace.dependencies] stellar-strkey = "0.0.11" sep5 = "0.0.4" @@ -70,11 +71,9 @@ hex = "0.4.3" itertools = "0.10.0" async-trait = "0.1.76" bollard = "0.16.0" - serde-aux = "4.1.2" serde_json = "1.0.82" serde = "1.0.82" - clap = { version = "4.1.8", features = [ "derive", "env", @@ -92,8 +91,7 @@ ulid = "1.1" termcolor = "1.1.3" termcolor_output = "1.0.1" ed25519-dalek = ">= 2.1.1" - -# networking +http = "1.0.0" jsonrpsee-http-client = "0.20.1" jsonrpsee-core = "0.20.1" tokio = "1.28.1" diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index e393818ce..2fd02cafb 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -1630,6 +1630,7 @@ Decode and encode XDR * `guess` — Guess the XDR type * `decode` — Decode XDR * `encode` — Encode XDR +* `compare` — Compare two XDR values with each other * `version` — Print version information ###### **Arguments:** @@ -1768,6 +1769,31 @@ Encode XDR Default value: `single-base64` + Possible values: `single`, `single-base64`, `stream` + + + + +## `stellar xdr compare` + +Compare two XDR values with each other + +Outputs: `-1` when the left XDR value is less than the right XDR value, `0` when the left XDR value is equal to the right XDR value, `1` when the left XDR value is greater than the right XDR value + +**Usage:** `stellar xdr compare [OPTIONS] --type ` + +###### **Arguments:** + +* `` — XDR file to decode and compare with the right value +* `` — XDR file to decode and compare with the left value + +###### **Options:** + +* `--type ` — XDR type of both inputs +* `--input ` + + Default value: `single-base64` + Possible values: `single`, `single-base64` diff --git a/cmd/crates/soroban-spec-tools/Cargo.toml b/cmd/crates/soroban-spec-tools/Cargo.toml index 514b82924..a17a5d31f 100644 --- a/cmd/crates/soroban-spec-tools/Cargo.toml +++ b/cmd/crates/soroban-spec-tools/Cargo.toml @@ -19,9 +19,7 @@ crate-type = ["rlib"] [dependencies] soroban-spec = { workspace = true } stellar-strkey = { workspace = true } -stellar-xdr = { workspace = true, features = ["curr", "std", "serde"] } -soroban-env-host = { workspace = true } - +stellar-xdr = { workspace = true, features = ["curr", "std", "serde", "base64"] } serde_json = { workspace = true } itertools = { workspace = true } ethnum = { workspace = true } @@ -29,9 +27,6 @@ hex = { workspace = true } wasmparser = { workspace = true } base64 = { workspace = true } thiserror = "1.0.31" -# soroban-ledger-snapshot = { workspace = true } -# soroban-sdk = { workspace = true } -# sep5 = { workspace = true } [dev-dependencies] diff --git a/cmd/crates/soroban-spec-tools/src/contract.rs b/cmd/crates/soroban-spec-tools/src/contract.rs index 334b2425f..9984a19d1 100644 --- a/cmd/crates/soroban-spec-tools/src/contract.rs +++ b/cmd/crates/soroban-spec-tools/src/contract.rs @@ -4,10 +4,10 @@ use std::{ io::{self, Cursor}, }; -use soroban_env_host::xdr::{ - self, Limited, Limits, ReadXdr, ScEnvMetaEntry, ScMetaEntry, ScMetaV0, ScSpecEntry, - ScSpecFunctionV0, ScSpecUdtEnumV0, ScSpecUdtErrorEnumV0, ScSpecUdtStructV0, ScSpecUdtUnionV0, - StringM, WriteXdr, +use stellar_xdr::curr::{ + self as xdr, Limited, Limits, ReadXdr, ScEnvMetaEntry, ScEnvMetaEntryInterfaceVersion, + ScMetaEntry, ScMetaV0, ScSpecEntry, ScSpecFunctionV0, ScSpecUdtEnumV0, ScSpecUdtErrorEnumV0, + ScSpecUdtStructV0, ScSpecUdtUnionV0, StringM, WriteXdr, }; pub struct Spec { @@ -121,10 +121,16 @@ impl Display for Spec { writeln!(f, "Env Meta: {env_meta}")?; for env_meta_entry in &self.env_meta { match env_meta_entry { - ScEnvMetaEntry::ScEnvMetaKindInterfaceVersion(v) => { - let protocol = v >> 32; - let interface = v & 0xffff_ffff; - writeln!(f, " • Interface Version: {v} (protocol: {protocol}, interface: {interface})")?; + ScEnvMetaEntry::ScEnvMetaKindInterfaceVersion( + ScEnvMetaEntryInterfaceVersion { + protocol, + pre_release, + }, + ) => { + writeln!(f, " • Protocol Version: {protocol}")?; + if pre_release != &0 { + writeln!(f, " • Pre-release Version: {pre_release})")?; + } } } } diff --git a/cmd/crates/soroban-test/Cargo.toml b/cmd/crates/soroban-test/Cargo.toml index a34b7c65c..b240494e4 100644 --- a/cmd/crates/soroban-test/Cargo.toml +++ b/cmd/crates/soroban-test/Cargo.toml @@ -17,12 +17,10 @@ crate-type = ["rlib", "cdylib"] [dependencies] -soroban-env-host = { workspace = true } soroban-spec = { workspace = true } soroban-spec-tools = { workspace = true } soroban-ledger-snapshot = { workspace = true } stellar-strkey = { workspace = true } -soroban-sdk = { workspace = true } sep5 = { workspace = true } soroban-cli = { workspace = true } soroban-rpc = { workspace = true } diff --git a/cmd/crates/soroban-test/src/wasm.rs b/cmd/crates/soroban-test/src/wasm.rs index d03114b93..3e473ed46 100644 --- a/cmd/crates/soroban-test/src/wasm.rs +++ b/cmd/crates/soroban-test/src/wasm.rs @@ -1,7 +1,7 @@ use std::{fmt::Display, fs, path::PathBuf}; use sha2::{Digest, Sha256}; -use soroban_env_host::xdr; +use soroban_cli::xdr; #[derive(thiserror::Error, Debug)] pub enum Error { diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_account/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_account/Cargo.toml index d4405c512..792925edb 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_account/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_account/Cargo.toml @@ -12,7 +12,7 @@ crate-type = ["cdylib"] doctest = false [dependencies] -soroban-sdk = { workspace = true } +soroban-sdk = { version = "=21.7.2" } [dev-dependencies] -soroban-sdk = { workspace = true, features = ["testutils"]} +soroban-sdk = { version = "=21.7.2", features = ["testutils"]} diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_type/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_type/Cargo.toml index 17e618805..398520104 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_type/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_type/Cargo.toml @@ -12,7 +12,7 @@ crate-type = ["cdylib", "rlib"] doctest = false [dependencies] -soroban-sdk = { workspace = true } +soroban-sdk = { version = "=21.7.2" } [dev-dependencies] -soroban-sdk = { workspace = true, features = ["testutils"]} +soroban-sdk = { version = "=21.7.2", features = ["testutils"]} diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/Cargo.toml index 9ad83fd1c..5f8b1256f 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/Cargo.toml @@ -12,7 +12,7 @@ crate-type = ["cdylib", "rlib"] doctest = false [dependencies] -soroban-sdk = { workspace = true } +soroban-sdk = { version = "=21.7.2" } [dev-dependencies] -soroban-sdk = { workspace = true, features = ["testutils"]} +soroban-sdk = { version = "=21.7.2", features = ["testutils"]} diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/src/lib.rs b/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/src/lib.rs index 40006a1b7..b4b53c237 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/src/lib.rs +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/src/lib.rs @@ -77,7 +77,7 @@ mod test { #[test] fn test_hello() { let env = Env::default(); - let contract_id = env.register_contract(None, Contract); + let contract_id = env.register(Contract, ()); let client = ContractClient::new(&env, &contract_id); let world = symbol_short!("world"); let res = client.hello(&world); diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/Cargo.toml index f74201a31..509483408 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/Cargo.toml @@ -12,7 +12,7 @@ crate-type = ["cdylib"] doctest = false [dependencies] -soroban-sdk = { workspace = true } +soroban-sdk = { version = "=21.7.2" } [dev-dependencies] -soroban-sdk = { workspace = true, features = ["testutils"] } +soroban-sdk = { version = "=21.7.2", features = ["testutils"] } diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/Cargo.toml index 880c39d52..8875b87e7 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/Cargo.toml @@ -12,8 +12,8 @@ rust-version.workspace = true crate-type = ["cdylib"] [dependencies] -soroban-sdk = { workspace = true } +soroban-sdk = { version = "=21.7.2" } soroban-token-sdk = { workspace = true } [dev-dependencies] -soroban-sdk = { workspace = true, features = ["testutils"] } +soroban-sdk = { version = "=21.7.2", features = ["testutils"] } diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/udt/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/test-wasms/udt/Cargo.toml index 206284802..aea356447 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/udt/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/udt/Cargo.toml @@ -12,7 +12,7 @@ crate-type = ["cdylib"] doctest = false [dependencies] -soroban-sdk = { workspace = true } +soroban-sdk = { version = "=21.7.2" } [dev-dependencies] -soroban-sdk = { workspace = true , features = ["testutils"]} +soroban-sdk = { version = "=21.7.2" , features = ["testutils"]} diff --git a/cmd/crates/soroban-test/tests/fixtures/workspace-with-default-members/contracts/add/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/workspace-with-default-members/contracts/add/Cargo.toml index 660b7eb11..ca38cca49 100644 --- a/cmd/crates/soroban-test/tests/fixtures/workspace-with-default-members/contracts/add/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/workspace-with-default-members/contracts/add/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["cdylib"] doctest = false [dependencies] -soroban-sdk = { workspace = true } +soroban-sdk = { version = "=21.7.2" } [dev-dependencies] -soroban-sdk = { workspace = true, features = ["testutils"] } +soroban-sdk = { version = "=21.7.2", features = ["testutils"] } diff --git a/cmd/crates/soroban-test/tests/fixtures/workspace-with-default-members/contracts/call/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/workspace-with-default-members/contracts/call/Cargo.toml index 48e32b14d..a1ba4ed43 100644 --- a/cmd/crates/soroban-test/tests/fixtures/workspace-with-default-members/contracts/call/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/workspace-with-default-members/contracts/call/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["cdylib"] doctest = false [dependencies] -soroban-sdk = { workspace = true } +soroban-sdk = { version = "=21.7.2" } [dev-dependencies] -soroban-sdk = { workspace = true, features = ["testutils"] } +soroban-sdk = { version = "=21.7.2", features = ["testutils"] } diff --git a/cmd/crates/soroban-test/tests/fixtures/workspace/contracts/add/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/workspace/contracts/add/Cargo.toml index 660b7eb11..ca38cca49 100644 --- a/cmd/crates/soroban-test/tests/fixtures/workspace/contracts/add/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/workspace/contracts/add/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["cdylib"] doctest = false [dependencies] -soroban-sdk = { workspace = true } +soroban-sdk = { version = "=21.7.2" } [dev-dependencies] -soroban-sdk = { workspace = true, features = ["testutils"] } +soroban-sdk = { version = "=21.7.2", features = ["testutils"] } diff --git a/cmd/crates/soroban-test/tests/fixtures/workspace/contracts/add/add2/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/workspace/contracts/add/add2/Cargo.toml index bae834e02..b6b570327 100644 --- a/cmd/crates/soroban-test/tests/fixtures/workspace/contracts/add/add2/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/workspace/contracts/add/add2/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["cdylib"] doctest = false [dependencies] -soroban-sdk = { workspace = true } +soroban-sdk = { version = "=21.7.2" } [dev-dependencies] -soroban-sdk = { workspace = true, features = ["testutils"] } +soroban-sdk = { version = "=21.7.2", features = ["testutils"] } diff --git a/cmd/crates/soroban-test/tests/fixtures/workspace/contracts/call/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/workspace/contracts/call/Cargo.toml index 48e32b14d..a1ba4ed43 100644 --- a/cmd/crates/soroban-test/tests/fixtures/workspace/contracts/call/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/workspace/contracts/call/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["cdylib"] doctest = false [dependencies] -soroban-sdk = { workspace = true } +soroban-sdk = { version = "=21.7.2" } [dev-dependencies] -soroban-sdk = { workspace = true, features = ["testutils"] } +soroban-sdk = { version = "=21.7.2", features = ["testutils"] } diff --git a/cmd/crates/soroban-test/tests/it/arg_parsing.rs b/cmd/crates/soroban-test/tests/it/arg_parsing.rs index c245fd8c9..d25d22354 100644 --- a/cmd/crates/soroban-test/tests/it/arg_parsing.rs +++ b/cmd/crates/soroban-test/tests/it/arg_parsing.rs @@ -1,6 +1,6 @@ use crate::util::CUSTOM_TYPES; use serde_json::json; -use soroban_env_host::xdr::{ +use soroban_cli::xdr::{ ScBytes, ScSpecTypeBytesN, ScSpecTypeDef, ScSpecTypeOption, ScSpecTypeUdt, ScVal, }; use soroban_spec_tools::{from_string_primitive, Spec}; diff --git a/cmd/crates/soroban-test/tests/it/integration/tx.rs b/cmd/crates/soroban-test/tests/it/integration/tx.rs index 5cc6bdb9b..c3cd2693b 100644 --- a/cmd/crates/soroban-test/tests/it/integration/tx.rs +++ b/cmd/crates/soroban-test/tests/it/integration/tx.rs @@ -1,5 +1,5 @@ use soroban_cli::assembled::simulate_and_assemble_transaction; -use soroban_sdk::xdr::{Limits, ReadXdr, TransactionEnvelope, WriteXdr}; +use soroban_cli::xdr::{Limits, ReadXdr, TransactionEnvelope, WriteXdr}; use soroban_test::{AssertExt, TestEnv}; use crate::integration::util::{deploy_contract, DeployKind, HELLO_WORLD}; diff --git a/cmd/crates/soroban-test/tests/it/integration/util.rs b/cmd/crates/soroban-test/tests/it/integration/util.rs index 6feae7860..438428e38 100644 --- a/cmd/crates/soroban-test/tests/it/integration/util.rs +++ b/cmd/crates/soroban-test/tests/it/integration/util.rs @@ -1,5 +1,7 @@ -use soroban_cli::commands; -use soroban_sdk::xdr::{Limits, WriteXdr}; +use soroban_cli::{ + commands, + xdr::{Limits, WriteXdr}, +}; use soroban_test::{TestEnv, Wasm}; use std::fmt::Display; diff --git a/cmd/crates/stellar-ledger/Cargo.toml b/cmd/crates/stellar-ledger/Cargo.toml index b8894b71d..abf73c375 100644 --- a/cmd/crates/stellar-ledger/Cargo.toml +++ b/cmd/crates/stellar-ledger/Cargo.toml @@ -17,7 +17,6 @@ serde = "1.0.82" serde_derive = "1.0.82" serde_json = "1.0.82" sha2 = "0.9.9" -soroban-env-host = { workspace = true } ed25519-dalek = { workspace = true } stellar-strkey = { workspace = true } ledger-transport-hid = "0.10.0" diff --git a/cmd/crates/stellar-ledger/src/lib.rs b/cmd/crates/stellar-ledger/src/lib.rs index c74365af2..e90c31bb9 100644 --- a/cmd/crates/stellar-ledger/src/lib.rs +++ b/cmd/crates/stellar-ledger/src/lib.rs @@ -5,12 +5,11 @@ use ledger_transport_hid::{ LedgerHIDError, TransportNativeHID, }; -use soroban_env_host::xdr::{Hash, Transaction}; use std::vec; use stellar_strkey::DecodeError; use stellar_xdr::curr::{ - self as xdr, Limits, TransactionSignaturePayload, TransactionSignaturePayloadTaggedTransaction, - WriteXdr, + self as xdr, Hash, Limits, Transaction, TransactionSignaturePayload, + TransactionSignaturePayloadTaggedTransaction, WriteXdr, }; pub use crate::signer::Blob; @@ -312,10 +311,9 @@ mod test { use crate::Blob; use test_helpers::test::emulator_http_transport::EmulatorHttpTransport; - use soroban_env_host::xdr::Transaction; use std::vec; - use soroban_env_host::xdr::{self, Operation, OperationBody, Uint256}; + use super::xdr::{self, Operation, OperationBody, Transaction, Uint256}; use crate::{test_network_hash, Error, LedgerSigner}; diff --git a/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs b/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs index d45981eef..1b0c09bab 100644 --- a/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs +++ b/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs @@ -1,8 +1,6 @@ use ledger_transport::Exchange; use once_cell::sync::Lazy; use serde::Deserialize; -use soroban_env_host::xdr::{self, Operation, OperationBody, Uint256}; -use soroban_env_host::xdr::{Hash, Transaction}; use std::ops::Range; use std::sync::Mutex; use std::vec; @@ -15,8 +13,10 @@ use std::sync::Arc; use std::{collections::HashMap, time::Duration}; use stellar_xdr::curr::{ - Memo, MuxedAccount, PaymentOp, Preconditions, SequenceNumber, TransactionExt, + self as xdr, Hash, Memo, MuxedAccount, Operation, OperationBody, PaymentOp, Preconditions, + SequenceNumber, Transaction, TransactionExt, Uint256, }; + use testcontainers::{core::ContainerPort, runners::AsyncRunner, ContainerAsync, ImageExt}; use tokio::time::sleep; diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index 646e8a92e..da142ad57 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -40,7 +40,6 @@ opt = ["dep:wasm-opt"] [dependencies] stellar-xdr = { workspace = true, features = ["cli"] } -soroban-env-host = { workspace = true } soroban-spec = { workspace = true } soroban-spec-json = { workspace = true } soroban-spec-rust = { workspace = true } diff --git a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs index 4a8af47e2..2eb3c2696 100644 --- a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs +++ b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs @@ -8,7 +8,7 @@ use clap::value_parser; use ed25519_dalek::SigningKey; use heck::ToKebabCase; -use soroban_env_host::xdr::{ +use crate::xdr::{ self, Hash, InvokeContractArgs, ScAddress, ScSpecEntry, ScSpecFunctionV0, ScSpecTypeDef, ScVal, ScVec, }; diff --git a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs index 8ff8e08b3..ad0afce5e 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs @@ -1,13 +1,10 @@ -use clap::{arg, command, Parser}; -use soroban_env_host::{ - xdr::{ - Asset, ContractDataDurability, ContractExecutable, ContractIdPreimage, CreateContractArgs, - Error as XdrError, Hash, HostFunction, InvokeHostFunctionOp, LedgerKey::ContractData, - LedgerKeyContractData, Limits, Memo, MuxedAccount, Operation, OperationBody, Preconditions, - ScAddress, ScVal, SequenceNumber, Transaction, TransactionExt, VecM, WriteXdr, - }, - HostError, +use crate::xdr::{ + Asset, ContractDataDurability, ContractExecutable, ContractIdPreimage, CreateContractArgs, + Error as XdrError, Hash, HostFunction, InvokeHostFunctionOp, LedgerKey::ContractData, + LedgerKeyContractData, Limits, Memo, MuxedAccount, Operation, OperationBody, Preconditions, + ScAddress, ScVal, SequenceNumber, Transaction, TransactionExt, VecM, WriteXdr, }; +use clap::{arg, command, Parser}; use std::convert::Infallible; use std::{array::TryFromSliceError, fmt::Debug, num::ParseIntError}; @@ -26,10 +23,6 @@ use crate::{ #[derive(thiserror::Error, Debug)] pub enum Error { - #[error(transparent)] - // TODO: the Display impl of host errors is pretty user-unfriendly - // (it just calls Debug). I think we can do better than that - Host(#[from] HostError), #[error("error parsing int: {0}")] ParseIntError(#[from] ParseIntError), #[error(transparent)] diff --git a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs index 36ce2e9f7..03ed3f438 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs @@ -2,18 +2,15 @@ use std::array::TryFromSliceError; use std::fmt::Debug; use std::num::ParseIntError; +use crate::xdr::{ + AccountId, ContractExecutable, ContractIdPreimage, ContractIdPreimageFromAddress, + CreateContractArgs, Error as XdrError, Hash, HostFunction, InvokeHostFunctionOp, Limits, Memo, + MuxedAccount, Operation, OperationBody, Preconditions, PublicKey, ScAddress, SequenceNumber, + Transaction, TransactionExt, Uint256, VecM, WriteXdr, +}; use clap::{arg, command, Parser}; use rand::Rng; use regex::Regex; -use soroban_env_host::{ - xdr::{ - AccountId, ContractExecutable, ContractIdPreimage, ContractIdPreimageFromAddress, - CreateContractArgs, Error as XdrError, Hash, HostFunction, InvokeHostFunctionOp, Limits, - Memo, MuxedAccount, Operation, OperationBody, Preconditions, PublicKey, ScAddress, - SequenceNumber, Transaction, TransactionExt, Uint256, VecM, WriteXdr, - }, - HostError, -}; use crate::{ assembled::simulate_and_assemble_transaction, @@ -70,8 +67,6 @@ pub struct Cmd { pub enum Error { #[error(transparent)] Install(#[from] install::Error), - #[error(transparent)] - Host(#[from] HostError), #[error("error parsing int: {0}")] ParseIntError(#[from] ParseIntError), #[error("internal conversion error: {0}")] diff --git a/cmd/soroban-cli/src/commands/contract/extend.rs b/cmd/soroban-cli/src/commands/contract/extend.rs index 3f7477254..92b986a22 100644 --- a/cmd/soroban-cli/src/commands/contract/extend.rs +++ b/cmd/soroban-cli/src/commands/contract/extend.rs @@ -1,12 +1,12 @@ use std::{fmt::Debug, path::Path, str::FromStr}; -use clap::{command, Parser}; -use soroban_env_host::xdr::{ +use crate::xdr::{ Error as XdrError, ExtendFootprintTtlOp, ExtensionPoint, LedgerEntry, LedgerEntryChange, LedgerEntryData, LedgerFootprint, Limits, Memo, Operation, OperationBody, Preconditions, SequenceNumber, SorobanResources, SorobanTransactionData, Transaction, TransactionExt, TransactionMeta, TransactionMetaV3, TtlEntry, WriteXdr, }; +use clap::{command, Parser}; use crate::{ assembled::simulate_and_assemble_transaction, diff --git a/cmd/soroban-cli/src/commands/contract/id/asset.rs b/cmd/soroban-cli/src/commands/contract/id/asset.rs index cdf826015..921592cf8 100644 --- a/cmd/soroban-cli/src/commands/contract/id/asset.rs +++ b/cmd/soroban-cli/src/commands/contract/id/asset.rs @@ -20,7 +20,7 @@ pub enum Error { #[error(transparent)] ConfigError(#[from] config::Error), #[error(transparent)] - Xdr(#[from] soroban_env_host::xdr::Error), + Xdr(#[from] crate::xdr::Error), } impl Cmd { pub fn run(&self) -> Result<(), Error> { diff --git a/cmd/soroban-cli/src/commands/contract/id/wasm.rs b/cmd/soroban-cli/src/commands/contract/id/wasm.rs index c020e94b2..349dd167b 100644 --- a/cmd/soroban-cli/src/commands/contract/id/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/id/wasm.rs @@ -1,9 +1,9 @@ -use clap::{arg, command, Parser}; -use sha2::{Digest, Sha256}; -use soroban_env_host::xdr::{ +use crate::xdr::{ self, AccountId, ContractIdPreimage, ContractIdPreimageFromAddress, Hash, HashIdPreimage, HashIdPreimageContractId, Limits, PublicKey, ScAddress, Uint256, WriteXdr, }; +use clap::{arg, command, Parser}; +use sha2::{Digest, Sha256}; use crate::config; diff --git a/cmd/soroban-cli/src/commands/contract/info/env_meta.rs b/cmd/soroban-cli/src/commands/contract/info/env_meta.rs index 03225c328..bc2d03bc6 100644 --- a/cmd/soroban-cli/src/commands/contract/info/env_meta.rs +++ b/cmd/soroban-cli/src/commands/contract/info/env_meta.rs @@ -1,14 +1,17 @@ use std::fmt::Debug; use clap::{command, Parser}; -use stellar_xdr::curr::ScEnvMetaEntry; use soroban_spec_tools::contract; use soroban_spec_tools::contract::Spec; -use crate::commands::contract::info::env_meta::Error::{NoEnvMetaPresent, NoSACEnvMeta}; -use crate::commands::contract::info::shared; -use crate::commands::contract::info::shared::{fetch_wasm, MetasInfoOutput}; +use crate::{ + commands::contract::info::{ + env_meta::Error::{NoEnvMetaPresent, NoSACEnvMeta}, + shared::{self, fetch_wasm, MetasInfoOutput}, + }, + xdr::{ScEnvMetaEntry, ScEnvMetaEntryInterfaceVersion}, +}; #[derive(Parser, Debug, Clone)] pub struct Cmd { @@ -54,12 +57,16 @@ impl Cmd { let mut meta_str = "Contract env-meta:\n".to_string(); for env_meta_entry in &spec.env_meta { match env_meta_entry { - ScEnvMetaEntry::ScEnvMetaKindInterfaceVersion(v) => { - let protocol = v >> 32; - let interface = v & 0xffff_ffff; + ScEnvMetaEntry::ScEnvMetaKindInterfaceVersion( + ScEnvMetaEntryInterfaceVersion { + protocol, + pre_release, + }, + ) => { meta_str.push_str(&format!(" • Protocol: v{protocol}\n")); - meta_str.push_str(&format!(" • Interface: v{interface}\n")); - meta_str.push_str(&format!(" • Interface Version: {v}\n")); + if pre_release != &0 { + meta_str.push_str(&format!(" • Pre-release: v{pre_release}\n")); + } } } } diff --git a/cmd/soroban-cli/src/commands/contract/info/shared.rs b/cmd/soroban-cli/src/commands/contract/info/shared.rs index 25cbc9258..4113f7d5b 100644 --- a/cmd/soroban-cli/src/commands/contract/info/shared.rs +++ b/cmd/soroban-cli/src/commands/contract/info/shared.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; +use crate::xdr; use clap::arg; -use soroban_env_host::xdr; use soroban_rpc::Client; use crate::commands::contract::info::shared::Error::InvalidWasmHash; diff --git a/cmd/soroban-cli/src/commands/contract/inspect.rs b/cmd/soroban-cli/src/commands/contract/inspect.rs index 46fdc33a7..86559641b 100644 --- a/cmd/soroban-cli/src/commands/contract/inspect.rs +++ b/cmd/soroban-cli/src/commands/contract/inspect.rs @@ -1,5 +1,5 @@ +use crate::xdr; use clap::{command, Parser}; -use soroban_env_host::xdr; use soroban_spec_tools::contract; use std::{fmt::Debug, path::PathBuf}; use tracing::debug; diff --git a/cmd/soroban-cli/src/commands/contract/install.rs b/cmd/soroban-cli/src/commands/contract/install.rs index b70cadb78..d51158ec3 100644 --- a/cmd/soroban-cli/src/commands/contract/install.rs +++ b/cmd/soroban-cli/src/commands/contract/install.rs @@ -2,12 +2,12 @@ use std::array::TryFromSliceError; use std::fmt::Debug; use std::num::ParseIntError; -use clap::{command, Parser}; -use soroban_env_host::xdr::{ +use crate::xdr::{ self, ContractCodeEntryExt, Error as XdrError, Hash, HostFunction, InvokeHostFunctionOp, LedgerEntryData, Limits, OperationBody, ReadXdr, ScMetaEntry, ScMetaV0, Transaction, TransactionResult, TransactionResultResult, VecM, WriteXdr, }; +use clap::{command, Parser}; use super::restore; use crate::assembled::simulate_and_assemble_transaction; diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index 55e2554bf..e32d5d5a6 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -7,15 +7,12 @@ use std::{fmt::Debug, fs, io}; use clap::{arg, command, Parser, ValueEnum}; -use soroban_env_host::{ - xdr::{ - self, AccountEntry, AccountEntryExt, AccountId, ContractEvent, ContractEventType, - DiagnosticEvent, HostFunction, InvokeContractArgs, InvokeHostFunctionOp, LedgerEntryData, - Limits, Memo, MuxedAccount, Operation, OperationBody, Preconditions, PublicKey, - ScSpecEntry, SequenceNumber, String32, StringM, Thresholds, Transaction, TransactionExt, - Uint256, VecM, WriteXdr, - }, - HostError, +use crate::xdr::{ + self, AccountEntry, AccountEntryExt, AccountId, ContractEvent, ContractEventType, + DiagnosticEvent, HostFunction, InvokeContractArgs, InvokeHostFunctionOp, LedgerEntryData, + Limits, Memo, MuxedAccount, Operation, OperationBody, Preconditions, PublicKey, ScSpecEntry, + SequenceNumber, String32, StringM, Thresholds, Transaction, TransactionExt, Uint256, VecM, + WriteXdr, }; use soroban_rpc::{SimulateHostFunctionResult, SimulateTransactionResponse}; @@ -80,10 +77,6 @@ impl Pwd for Cmd { pub enum Error { #[error("cannot add contract to ledger entries: {0}")] CannotAddContractToLedgerEntries(xdr::Error), - #[error(transparent)] - // TODO: the Display impl of host errors is pretty user-unfriendly - // (it just calls Debug). I think we can do better than that - Host(#[from] HostError), #[error("reading file {0:?}: {1}")] CannotReadContractFile(PathBuf, io::Error), #[error("committing file {filepath}: {error}")] diff --git a/cmd/soroban-cli/src/commands/contract/mod.rs b/cmd/soroban-cli/src/commands/contract/mod.rs index 9ac6b8527..4f3c6f7f4 100644 --- a/cmd/soroban-cli/src/commands/contract/mod.rs +++ b/cmd/soroban-cli/src/commands/contract/mod.rs @@ -168,11 +168,11 @@ pub enum Durability { Temporary, } -impl From<&Durability> for soroban_env_host::xdr::ContractDataDurability { +impl From<&Durability> for crate::xdr::ContractDataDurability { fn from(d: &Durability) -> Self { match d { - Durability::Persistent => soroban_env_host::xdr::ContractDataDurability::Persistent, - Durability::Temporary => soroban_env_host::xdr::ContractDataDurability::Temporary, + Durability::Persistent => crate::xdr::ContractDataDurability::Persistent, + Durability::Temporary => crate::xdr::ContractDataDurability::Temporary, } } } diff --git a/cmd/soroban-cli/src/commands/contract/read.rs b/cmd/soroban-cli/src/commands/contract/read.rs index dae5f3cdf..4e013a1f4 100644 --- a/cmd/soroban-cli/src/commands/contract/read.rs +++ b/cmd/soroban-cli/src/commands/contract/read.rs @@ -3,14 +3,11 @@ use std::{ io::{self, stdout}, }; -use clap::{command, Parser, ValueEnum}; -use soroban_env_host::{ - xdr::{ - ContractDataEntry, Error as XdrError, LedgerEntryData, LedgerKey, LedgerKeyContractData, - Limits, ScVal, WriteXdr, - }, - HostError, +use crate::xdr::{ + ContractDataEntry, Error as XdrError, LedgerEntryData, LedgerKey, LedgerKeyContractData, + Limits, ScVal, WriteXdr, }; +use clap::{command, Parser, ValueEnum}; use crate::{ commands::{global, NetworkRunnable}, @@ -77,10 +74,6 @@ pub enum Error { Rpc(#[from] rpc::Error), #[error(transparent)] Xdr(#[from] XdrError), - #[error(transparent)] - // TODO: the Display impl of host errors is pretty user-unfriendly - // (it just calls Debug). I think we can do better than that - Host(#[from] HostError), #[error("no matching contract data entries were found for the specified contract id")] NoContractDataEntryFoundForContractID, #[error(transparent)] diff --git a/cmd/soroban-cli/src/commands/contract/restore.rs b/cmd/soroban-cli/src/commands/contract/restore.rs index 05fa90695..627fd3eee 100644 --- a/cmd/soroban-cli/src/commands/contract/restore.rs +++ b/cmd/soroban-cli/src/commands/contract/restore.rs @@ -1,12 +1,12 @@ use std::{fmt::Debug, path::Path, str::FromStr}; -use clap::{command, Parser}; -use soroban_env_host::xdr::{ +use crate::xdr::{ Error as XdrError, ExtensionPoint, LedgerEntry, LedgerEntryChange, LedgerEntryData, LedgerFootprint, Limits, Memo, Operation, OperationBody, OperationMeta, Preconditions, RestoreFootprintOp, SequenceNumber, SorobanResources, SorobanTransactionData, Transaction, TransactionExt, TransactionMeta, TransactionMetaV3, TtlEntry, WriteXdr, }; +use clap::{command, Parser}; use stellar_strkey::DecodeError; use crate::{ diff --git a/cmd/soroban-cli/src/commands/events.rs b/cmd/soroban-cli/src/commands/events.rs index 5e2bda66f..0c4cfcc0e 100644 --- a/cmd/soroban-cli/src/commands/events.rs +++ b/cmd/soroban-cli/src/commands/events.rs @@ -1,7 +1,7 @@ use clap::{arg, command, Parser}; use std::io; -use soroban_env_host::xdr::{self, Limits, ReadXdr}; +use crate::xdr::{self, Limits, ReadXdr}; use super::{global, NetworkRunnable}; use crate::config::{self, locator, network}; diff --git a/cmd/soroban-cli/src/commands/tx/hash.rs b/cmd/soroban-cli/src/commands/tx/hash.rs index 8d8ec6d82..c422302f6 100644 --- a/cmd/soroban-cli/src/commands/tx/hash.rs +++ b/cmd/soroban-cli/src/commands/tx/hash.rs @@ -7,7 +7,7 @@ pub enum Error { #[error(transparent)] TxEnvelopeFromStdin(#[from] super::xdr::Error), #[error(transparent)] - XdrToBase64(#[from] soroban_env_host::xdr::Error), + XdrToBase64(#[from] crate::xdr::Error), #[error(transparent)] Config(#[from] network::Error), } diff --git a/cmd/soroban-cli/src/commands/tx/new/set_trustline_flags.rs b/cmd/soroban-cli/src/commands/tx/new/set_trustline_flags.rs index 2955fe5b0..d9b70ecbd 100644 --- a/cmd/soroban-cli/src/commands/tx/new/set_trustline_flags.rs +++ b/cmd/soroban-cli/src/commands/tx/new/set_trustline_flags.rs @@ -1,8 +1,6 @@ use clap::{command, Parser}; -use soroban_sdk::xdr::{self}; - -use crate::{commands::tx, tx::builder}; +use crate::{commands::tx, tx::builder, xdr}; #[allow(clippy::struct_excessive_bools, clippy::doc_markdown)] #[derive(Parser, Debug, Clone)] diff --git a/cmd/soroban-cli/src/commands/tx/xdr.rs b/cmd/soroban-cli/src/commands/tx/xdr.rs index 71bd59af6..fcacce9ac 100644 --- a/cmd/soroban-cli/src/commands/tx/xdr.rs +++ b/cmd/soroban-cli/src/commands/tx/xdr.rs @@ -3,8 +3,7 @@ use std::{ path::PathBuf, }; -use soroban_env_host::xdr::ReadXdr; -use soroban_sdk::xdr::{Limits, Transaction, TransactionEnvelope, TransactionV1Envelope}; +use crate::xdr::{Limits, ReadXdr, Transaction, TransactionEnvelope, TransactionV1Envelope}; #[derive(Debug, thiserror::Error)] pub enum Error { diff --git a/cmd/soroban-cli/src/commands/txn_result.rs b/cmd/soroban-cli/src/commands/txn_result.rs index 08482e94f..d6b308b3a 100644 --- a/cmd/soroban-cli/src/commands/txn_result.rs +++ b/cmd/soroban-cli/src/commands/txn_result.rs @@ -1,4 +1,4 @@ -use soroban_env_host::xdr::{Transaction, TransactionEnvelope, TransactionV1Envelope, VecM}; +use crate::xdr::{Transaction, TransactionEnvelope, TransactionV1Envelope, VecM}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum TxnResult { diff --git a/cmd/soroban-cli/src/commands/version.rs b/cmd/soroban-cli/src/commands/version.rs index 5e6d0e4dd..f785399e9 100644 --- a/cmd/soroban-cli/src/commands/version.rs +++ b/cmd/soroban-cli/src/commands/version.rs @@ -1,5 +1,4 @@ use clap::Parser; -use soroban_env_host::meta; use std::fmt::Debug; #[derive(Parser, Debug, Clone)] @@ -22,12 +21,9 @@ pub fn git() -> &'static str { } pub fn long() -> String { - let env = soroban_env_host::VERSION; - let xdr = soroban_env_host::VERSION.xdr; + let xdr = stellar_xdr::VERSION; [ format!("{} ({})", pkg(), git()), - format!("soroban-env {} ({})", env.pkg, env.rev), - format!("soroban-env interface version {}", meta::INTERFACE_VERSION), format!( "stellar-xdr {} ({}) xdr curr ({})", diff --git a/cmd/soroban-cli/src/fee.rs b/cmd/soroban-cli/src/fee.rs index c2bc32bdb..f6afdc500 100644 --- a/cmd/soroban-cli/src/fee.rs +++ b/cmd/soroban-cli/src/fee.rs @@ -1,7 +1,7 @@ use clap::arg; use crate::assembled::Assembled; -use soroban_env_host::xdr; +use crate::xdr; use crate::commands::HEADING_RPC; diff --git a/cmd/soroban-cli/src/get_spec.rs b/cmd/soroban-cli/src/get_spec.rs index 125fa984c..26e609543 100644 --- a/cmd/soroban-cli/src/get_spec.rs +++ b/cmd/soroban-cli/src/get_spec.rs @@ -1,8 +1,6 @@ -use soroban_env_host::xdr; +use crate::xdr; -use soroban_env_host::xdr::{ - ContractDataEntry, ContractExecutable, ScContractInstance, ScSpecEntry, ScVal, -}; +use crate::xdr::{ContractDataEntry, ContractExecutable, ScContractInstance, ScSpecEntry, ScVal}; use soroban_spec::read::FromWasmError; pub use soroban_spec_tools::contract as contract_spec; diff --git a/cmd/soroban-cli/src/key.rs b/cmd/soroban-cli/src/key.rs index b4fd358aa..3f4dcaf7c 100644 --- a/cmd/soroban-cli/src/key.rs +++ b/cmd/soroban-cli/src/key.rs @@ -1,13 +1,13 @@ +use crate::xdr::{ + self, LedgerKey, LedgerKeyContractCode, LedgerKeyContractData, Limits, ReadXdr, ScAddress, + ScVal, +}; use crate::{ commands::contract::Durability, config::{locator, network::Network}, wasm, }; use clap::arg; -use soroban_env_host::xdr::{ - self, LedgerKey, LedgerKeyContractCode, LedgerKeyContractData, Limits, ReadXdr, ScAddress, - ScVal, -}; use std::path::PathBuf; #[derive(thiserror::Error, Debug)] diff --git a/cmd/soroban-cli/src/lib.rs b/cmd/soroban-cli/src/lib.rs index 4c904855c..302ecab00 100644 --- a/cmd/soroban-cli/src/lib.rs +++ b/cmd/soroban-cli/src/lib.rs @@ -5,8 +5,8 @@ )] use std::path::Path; -pub(crate) use soroban_env_host::xdr; pub(crate) use soroban_rpc as rpc; +pub use stellar_xdr::curr as xdr; mod cli; pub use cli::main; diff --git a/cmd/soroban-cli/src/log.rs b/cmd/soroban-cli/src/log.rs index 28ed4041b..1a1c27035 100644 --- a/cmd/soroban-cli/src/log.rs +++ b/cmd/soroban-cli/src/log.rs @@ -1,17 +1,14 @@ use crate::xdr; + pub mod auth; -pub mod budget; pub mod cost; pub mod event; pub mod footprint; -pub mod host_event; pub use auth::*; -pub use budget::*; pub use cost::*; pub use event::*; pub use footprint::*; -pub use host_event::*; pub fn extract_events(tx_meta: &xdr::TransactionMeta) -> Vec { match tx_meta { diff --git a/cmd/soroban-cli/src/log/auth.rs b/cmd/soroban-cli/src/log/auth.rs index c37e7ed3e..4a6b4bea8 100644 --- a/cmd/soroban-cli/src/log/auth.rs +++ b/cmd/soroban-cli/src/log/auth.rs @@ -1,4 +1,4 @@ -use soroban_env_host::xdr::{SorobanAuthorizationEntry, VecM}; +use crate::xdr::{SorobanAuthorizationEntry, VecM}; pub fn auth(auth: &[VecM]) { if !auth.is_empty() { diff --git a/cmd/soroban-cli/src/log/budget.rs b/cmd/soroban-cli/src/log/budget.rs deleted file mode 100644 index 59ff4aad4..000000000 --- a/cmd/soroban-cli/src/log/budget.rs +++ /dev/null @@ -1,5 +0,0 @@ -use soroban_env_host::budget::Budget; - -pub fn budget(budget: &Budget) { - tracing::debug!("{budget:#?}"); -} diff --git a/cmd/soroban-cli/src/log/cost.rs b/cmd/soroban-cli/src/log/cost.rs index 3e049a6cd..8dfc18df6 100644 --- a/cmd/soroban-cli/src/log/cost.rs +++ b/cmd/soroban-cli/src/log/cost.rs @@ -1,4 +1,4 @@ -use soroban_env_host::xdr::SorobanResources; +use crate::xdr::SorobanResources; use std::fmt::{Debug, Display}; struct Cost<'a>(&'a SorobanResources); diff --git a/cmd/soroban-cli/src/log/footprint.rs b/cmd/soroban-cli/src/log/footprint.rs index bfbc9f7a3..7c351de1d 100644 --- a/cmd/soroban-cli/src/log/footprint.rs +++ b/cmd/soroban-cli/src/log/footprint.rs @@ -1,4 +1,4 @@ -use soroban_env_host::xdr::LedgerFootprint; +use crate::xdr::LedgerFootprint; pub fn footprint(footprint: &LedgerFootprint) { tracing::debug!("{footprint:#?}"); diff --git a/cmd/soroban-cli/src/log/host_event.rs b/cmd/soroban-cli/src/log/host_event.rs deleted file mode 100644 index 4238a74c3..000000000 --- a/cmd/soroban-cli/src/log/host_event.rs +++ /dev/null @@ -1,7 +0,0 @@ -use soroban_env_host::events::HostEvent; - -pub fn host_events(events: &[HostEvent]) { - for (i, event) in events.iter().enumerate() { - tracing::info!("{i}: {event:#?}"); - } -} diff --git a/cmd/soroban-cli/src/print.rs b/cmd/soroban-cli/src/print.rs index 5b98687bd..5b8ca2bd2 100644 --- a/cmd/soroban-cli/src/print.rs +++ b/cmd/soroban-cli/src/print.rs @@ -1,6 +1,6 @@ use std::{env, fmt::Display}; -use soroban_env_host::xdr::{Error as XdrError, Transaction}; +use crate::xdr::{Error as XdrError, Transaction}; use crate::{ config::network::Network, utils::explorer_url_for_transaction, utils::transaction_hash, diff --git a/cmd/soroban-cli/src/signer.rs b/cmd/soroban-cli/src/signer.rs index bf02fdb17..5bf22499c 100644 --- a/cmd/soroban-cli/src/signer.rs +++ b/cmd/soroban-cli/src/signer.rs @@ -1,7 +1,7 @@ use ed25519_dalek::ed25519::signature::Signer as _; use sha2::{Digest, Sha256}; -use soroban_env_host::xdr::{ +use crate::xdr::{ self, AccountId, DecoratedSignature, Hash, HashIdPreimage, HashIdPreimageSorobanAuthorization, InvokeHostFunctionOp, Limits, Operation, OperationBody, PublicKey, ScAddress, ScMap, ScSymbol, ScVal, Signature, SignatureHint, SorobanAddressCredentials, SorobanAuthorizationEntry, diff --git a/cmd/soroban-cli/src/tx/builder/transaction.rs b/cmd/soroban-cli/src/tx/builder/transaction.rs index 51232d5c5..7cd922679 100644 --- a/cmd/soroban-cli/src/tx/builder/transaction.rs +++ b/cmd/soroban-cli/src/tx/builder/transaction.rs @@ -28,7 +28,7 @@ impl TxExt for xdr::Transaction { source_account, fee, seq_num: seq_num.into(), - cond: soroban_env_host::xdr::Preconditions::None, + cond: crate::xdr::Preconditions::None, memo: Memo::None, operations: [operation].try_into().unwrap(), ext: TransactionExt::V0, diff --git a/cmd/soroban-cli/src/utils.rs b/cmd/soroban-cli/src/utils.rs index ee8bb1ece..0729edb27 100644 --- a/cmd/soroban-cli/src/utils.rs +++ b/cmd/soroban-cli/src/utils.rs @@ -2,7 +2,7 @@ use phf::phf_map; use sha2::{Digest, Sha256}; use stellar_strkey::ed25519::PrivateKey; -use soroban_env_host::xdr::{ +use crate::xdr::{ self, Asset, ContractIdPreimage, Hash, HashIdPreimage, HashIdPreimageContractId, Limits, ScMap, ScMapEntry, ScVal, Transaction, TransactionSignaturePayload, TransactionSignaturePayloadTaggedTransaction, WriteXdr, @@ -197,7 +197,7 @@ pub mod http { } pub mod rpc { - use soroban_env_host::xdr; + use crate::xdr; use soroban_rpc::{Client, Error}; use stellar_xdr::curr::{Hash, LedgerEntryData, LedgerKey, Limits, ReadXdr}; diff --git a/cmd/soroban-cli/src/wasm.rs b/cmd/soroban-cli/src/wasm.rs index 7ab2c243d..bc202a4b3 100644 --- a/cmd/soroban-cli/src/wasm.rs +++ b/cmd/soroban-cli/src/wasm.rs @@ -1,8 +1,8 @@ use crate::config::locator; use crate::config::network::Network; +use crate::xdr::{self, Hash, LedgerKey, LedgerKeyContractCode}; use clap::arg; use sha2::{Digest, Sha256}; -use soroban_env_host::xdr::{self, Hash, LedgerKey, LedgerKeyContractCode}; use soroban_rpc::Client; use soroban_spec_tools::contract::{self, Spec}; use std::{ diff --git a/scripts/check-dependencies.bash b/scripts/check-dependencies.bash deleted file mode 100755 index c82d2f2b1..000000000 --- a/scripts/check-dependencies.bash +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash - -set -e - -SED=sed -if [ -z "$(sed --version 2>&1 | grep GNU)" ]; then - SED=gsed -fi - -CURL="curl -sL --fail-with-body" -if ! CARGO_OUTPUT=$(cargo tree -p soroban-env-host 2>&1); then - echo "The project depends on multiple versions of the soroban-env-host Rust library, please unify them." - echo "Make sure the soroban-sdk dependency indirectly points to the same soroban-env-host dependency imported explicitly." - echo - echo "This is soroban-env-host version imported by soroban-sdk:" - cargo tree --depth 1 -p soroban-sdk | grep env-host - echo - echo - echo - echo "Full error:" - echo $CARGO_OUTPUT - exit 1 -fi - -# revision of the https://github.com/stellar/rs-stellar-xdr library used by the Rust code -RS_STELLAR_XDR_REVISION="" - -# revision of https://github.com/stellar/stellar-xdr/ used by the Rust code -STELLAR_XDR_REVISION_FROM_RUST="" - -function stellar_xdr_version_from_rust_dep_tree { - LINE=$(grep stellar-xdr | head -n 1) - # try to obtain a commit - COMMIT=$(echo $LINE | $SED -n 's/.*rev=\(.*\)#.*/\1/p') - if [ -n "$COMMIT" ]; then - echo "$COMMIT" - return - fi - # obtain a crate version - echo $LINE | $SED -n 's/.*stellar-xdr \(v\)\{0,1\}\([^ ]*\).*/\2/p' -} - -if CARGO_OUTPUT=$(cargo tree --depth 0 -p stellar-xdr 2>&1); then - RS_STELLAR_XDR_REVISION=$(echo "$CARGO_OUTPUT" | stellar_xdr_version_from_rust_dep_tree) - if [ ${#RS_STELLAR_XDR_REVISION} -eq 40 ]; then - # revision is a git hash - STELLAR_XDR_REVISION_FROM_RUST=$($CURL https://raw.githubusercontent.com/stellar/rs-stellar-xdr/${RS_STELLAR_XDR_REVISION}/xdr/curr-version) - else - # revision is a crate version - CARGO_SRC_BASE_DIR=$(realpath ${CARGO_HOME:-$HOME/.cargo}/registry/src/index*) - STELLAR_XDR_REVISION_FROM_RUST=$(cat "${CARGO_SRC_BASE_DIR}/stellar-xdr-${RS_STELLAR_XDR_REVISION}/xdr/curr-version") - fi -else - echo "The project depends on multiple versions of the Rust rs-stellar-xdr library" - echo "Make sure a single version of stellar-xdr is used" - echo - echo - echo - echo "Full error:" - echo $CARGO_OUTPUT -fi From 440b75d784bf7e095ebc65334625cd3561deeaac Mon Sep 17 00:00:00 2001 From: "Thomas M. DuBuisson" Date: Wed, 9 Oct 2024 13:51:48 -0700 Subject: [PATCH 26/55] Fix flake for nixpkgs on darwin (#1665) * Do not use SHELL (default shell) but $0 (current shell) * Cleanup shell hook --------- Co-authored-by: Gleb --- flake.nix | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/flake.nix b/flake.nix index 3b826ee77..1923d75bb 100644 --- a/flake.nix +++ b/flake.nix @@ -18,25 +18,18 @@ with pkgs; { devShells.default = mkShell { + nativeBuildInputs = lib.optionals (stdenv.isDarwin) [ + pkgs.darwin.apple_sdk.frameworks.SystemConfiguration + ]; buildInputs = [ openssl pkg-config - libudev-zero jq (rust-bin.stable.latest.default.override { extensions = [ "rust-src" ]; targets = [ "wasm32-unknown-unknown" ]; }) - ]; - shellHook = - '' - echo "Using `nix --version`" - alias stellar="cargo run --bin stellar --" - [ -f ./local.sh ] && source ./local.sh - shell=$0 - shell=`basename $SHELL` - source <(stellar completion --shell $shell) - ''; + ] ++ lib.optionals (stdenv.isLinux) [libudev-zero]; }; } ); From 09a63abd6e87accd5b874171138e8adfe2c9c8db Mon Sep 17 00:00:00 2001 From: Gleb Date: Wed, 9 Oct 2024 18:23:07 -0700 Subject: [PATCH 27/55] Add warning on no fund flag (#1637) --- FULL_HELP_DOCS.md | 3 +++ cmd/soroban-cli/src/commands/keys/generate.rs | 16 +++++++++++++++- cmd/soroban-cli/src/commands/keys/mod.rs | 5 +++-- cmd/soroban-cli/src/commands/mod.rs | 2 +- cookbook/contract-lifecycle.mdx | 2 +- cookbook/payments-and-assets.mdx | 4 ++-- 6 files changed, 25 insertions(+), 7 deletions(-) diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index 2fd02cafb..5d83daeb2 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -977,6 +977,9 @@ Generate a new identity with a seed phrase, currently 12 words * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config +* `--fund` — Fund generated key pair + + Default value: `false` diff --git a/cmd/soroban-cli/src/commands/keys/generate.rs b/cmd/soroban-cli/src/commands/keys/generate.rs index 16e6a7fdb..b59e227fc 100644 --- a/cmd/soroban-cli/src/commands/keys/generate.rs +++ b/cmd/soroban-cli/src/commands/keys/generate.rs @@ -4,6 +4,7 @@ use super::super::config::{ locator, network, secret::{self, Secret}, }; +use crate::{commands::global, print::Print}; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -17,6 +18,7 @@ pub enum Error { #[derive(Debug, clap::Parser, Clone)] #[group(skip)] +#[allow(clippy::struct_excessive_bools)] pub struct Cmd { /// Name of identity pub name: String, @@ -46,10 +48,22 @@ pub struct Cmd { #[command(flatten)] pub network: network::Args, + + /// Fund generated key pair + #[arg(long, default_value = "false")] + pub fund: bool, } impl Cmd { - pub async fn run(&self) -> Result<(), Error> { + pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { + if !self.fund { + Print::new(global_args.quiet).warnln( + "Behavior of `generate` will change in the \ + future, and it will no longer fund by default. If you want to fund please \ + provide `--fund` flag. If you don't need to fund your keys in the future, ignore this \ + warning. It can be suppressed with -q flag.", + ); + } let seed_phrase = if self.default_seed { Secret::test_seed_phrase() } else { diff --git a/cmd/soroban-cli/src/commands/keys/mod.rs b/cmd/soroban-cli/src/commands/keys/mod.rs index 42814092f..30df5ccee 100644 --- a/cmd/soroban-cli/src/commands/keys/mod.rs +++ b/cmd/soroban-cli/src/commands/keys/mod.rs @@ -1,3 +1,4 @@ +use crate::commands::global; use clap::Parser; pub mod add; @@ -48,12 +49,12 @@ pub enum Error { } impl Cmd { - pub async fn run(&self) -> Result<(), Error> { + pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { match self { Cmd::Add(cmd) => cmd.run()?, Cmd::Address(cmd) => cmd.run()?, Cmd::Fund(cmd) => cmd.run().await?, - Cmd::Generate(cmd) => cmd.run().await?, + Cmd::Generate(cmd) => cmd.run(global_args).await?, Cmd::Ls(cmd) => cmd.run()?, Cmd::Rm(cmd) => cmd.run()?, Cmd::Show(cmd) => cmd.run()?, diff --git a/cmd/soroban-cli/src/commands/mod.rs b/cmd/soroban-cli/src/commands/mod.rs index b2e431e6e..0a1d4629b 100644 --- a/cmd/soroban-cli/src/commands/mod.rs +++ b/cmd/soroban-cli/src/commands/mod.rs @@ -113,7 +113,7 @@ impl Root { Cmd::Network(network) => network.run(&self.global_args).await?, Cmd::Snapshot(snapshot) => snapshot.run(&self.global_args).await?, Cmd::Version(version) => version.run(), - Cmd::Keys(id) => id.run().await?, + Cmd::Keys(id) => id.run(&self.global_args).await?, Cmd::Tx(tx) => tx.run(&self.global_args).await?, Cmd::Cache(data) => data.run()?, }; diff --git a/cookbook/contract-lifecycle.mdx b/cookbook/contract-lifecycle.mdx index e38a233de..2be63c23f 100644 --- a/cookbook/contract-lifecycle.mdx +++ b/cookbook/contract-lifecycle.mdx @@ -9,7 +9,7 @@ To manage the lifecycle of a Stellar smart contract using the CLI, follow these 1. Create an identity for Alice: ```bash -stellar keys generate alice +stellar keys generate alice -q ``` 2. Fund the identity: diff --git a/cookbook/payments-and-assets.mdx b/cookbook/payments-and-assets.mdx index e3be8dafc..a198973e1 100644 --- a/cookbook/payments-and-assets.mdx +++ b/cookbook/payments-and-assets.mdx @@ -17,11 +17,11 @@ By setting the `STELLAR_NETWORK` environment variable, we will not have to set t 2. Fund the accounts: ```bash -stellar keys generate alice +stellar keys generate alice -q ``` ```bash -stellar keys generate bob +stellar keys generate bob -q ``` ```bash From e8aba2531ff2e59cd912a9759384e99a344cea34 Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Thu, 10 Oct 2024 14:55:02 +1000 Subject: [PATCH 28/55] Fix tests (#1666) --- .github/workflows/full-help-docs.yml | 47 ------------------- .github/workflows/rust.yml | 11 ++++- Cargo.lock | 6 +-- .../test-wasms/hello_world/src/lib.rs | 2 +- .../fixtures/test-wasms/token/Cargo.toml | 2 +- .../tests/it/integration/tx/operations.rs | 2 +- 6 files changed, 16 insertions(+), 54 deletions(-) delete mode 100644 .github/workflows/full-help-docs.yml diff --git a/.github/workflows/full-help-docs.yml b/.github/workflows/full-help-docs.yml deleted file mode 100644 index f564ae9ae..000000000 --- a/.github/workflows/full-help-docs.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: CLI Help Doc - -on: - push: - branches: [main, release/**] - pull_request: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref_protected == 'true' && github.sha || github.ref }} - cancel-in-progress: true - -permissions: - contents: read - # Optional: allow read access to pull request. Use with `only-new-issues` option. - pull-requests: read - -jobs: - complete: - if: always() - needs: [doc-check] - runs-on: ubuntu-latest - steps: - - if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') - run: exit 1 - - doc-check: - runs-on: ubuntu-latest-16-cores - steps: - - uses: actions/checkout@v4 - - uses: stellar/actions/rust-cache@main - - run: rustup update - - name: Generate help doc - # this looks goofy to get GITHUB_OUTPUT to work with multi-line return values; - # see https://stackoverflow.com/a/74266196/249801 - run: | - make generate-full-help-doc - raw_diff=$(git diff FULL_HELP_DOCS.md) - if [ "$raw_diff" != "" ]; then echo ""; echo "Unexpected docs change:"; echo "======================="; echo ""; echo "$raw_diff"; echo ""; echo "======================="; echo ""; fi - echo diff=$raw_diff >> $GITHUB_OUTPUT - id: doc-gen - - - name: Check diff - if: steps.doc-gen.outputs.diff != '' - uses: actions/github-script@v7 - with: - script: | - core.setFailed('Expected `doc-gen` to generate no diffs, but got diff shown in previous step.\n\nUpdate the full help docs:\n\n cargo md-gen\n\nDo this automatically on every commit by installing the pre-commit hook as explained in the README.') diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index eb4e4c414..d7c49389b 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -17,7 +17,7 @@ jobs: complete: if: always() - needs: [fmt, build-and-test, publish-dry-run] + needs: [fmt, check-generated-full-help-docs, build-and-test, publish-dry-run] runs-on: ubuntu-latest steps: - if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') @@ -38,6 +38,15 @@ jobs: - name: Check no diffs exist run: git add -N . && git diff HEAD --exit-code + check-generated-full-help-docs: + runs-on: ubuntu-latest-16-cores + steps: + - uses: actions/checkout@v4 + - uses: stellar/actions/rust-cache@main + - run: rustup update + - run: make generate-full-help-doc + - run: git add -N . && git diff HEAD --exit-code + build-and-test: strategy: fail-fast: false diff --git a/Cargo.lock b/Cargo.lock index 33f0962d8..81e2e4c7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5794,11 +5794,11 @@ dependencies = [ [[package]] name = "soroban-token-sdk" -version = "22.0.0-rc.1.1" +version = "21.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7baa9e15d0838973cfa0aa0eee6e0fa062bec2e4a115c82396f452fa6148d731" +checksum = "0c6ede0da76646037f3ea5db9ccd37830865444bb24f137cb8f0af8282c784f5" dependencies = [ - "soroban-sdk 22.0.0-rc.1.1", + "soroban-sdk 21.7.2", ] [[package]] diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/src/lib.rs b/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/src/lib.rs index b4b53c237..40006a1b7 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/src/lib.rs +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/src/lib.rs @@ -77,7 +77,7 @@ mod test { #[test] fn test_hello() { let env = Env::default(); - let contract_id = env.register(Contract, ()); + let contract_id = env.register_contract(None, Contract); let client = ContractClient::new(&env, &contract_id); let world = symbol_short!("world"); let res = client.hello(&world); diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/Cargo.toml index 8875b87e7..03055f2b7 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/Cargo.toml @@ -13,7 +13,7 @@ crate-type = ["cdylib"] [dependencies] soroban-sdk = { version = "=21.7.2" } -soroban-token-sdk = { workspace = true } +soroban-token-sdk = { version = "=21.7.2" } [dev-dependencies] soroban-sdk = { version = "=21.7.2", features = ["testutils"] } diff --git a/cmd/crates/soroban-test/tests/it/integration/tx/operations.rs b/cmd/crates/soroban-test/tests/it/integration/tx/operations.rs index 9df808b34..0a0a103e8 100644 --- a/cmd/crates/soroban-test/tests/it/integration/tx/operations.rs +++ b/cmd/crates/soroban-test/tests/it/integration/tx/operations.rs @@ -1,8 +1,8 @@ use soroban_cli::{ tx::{builder, ONE_XLM}, utils::contract_id_hash_from_asset, + xdr::{self, ReadXdr, SequenceNumber}, }; -use soroban_sdk::xdr::{self, ReadXdr, SequenceNumber}; use soroban_test::{AssertExt, TestEnv}; use crate::integration::{ From 9880ad603765effecc1048da990b580a1b14623e Mon Sep 17 00:00:00 2001 From: Gleb Date: Thu, 10 Oct 2024 07:19:53 -0700 Subject: [PATCH 29/55] Add sim-only deprecation notice (#1634) * Add sim-only deprecation notice * Update display order * After-merge fixes --------- Co-authored-by: Willem Wyndham --- FULL_HELP_DOCS.md | 28 ++++++++++++++-------------- cmd/soroban-cli/src/fee.rs | 16 +++++++++++++--- cmd/soroban-cli/src/utils.rs | 18 ++++++++++++++++++ 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index 5d83daeb2..3efb16dda 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -146,7 +146,7 @@ Deploy builtin Soroban Asset Contract * `--cost` — Output the cost execution to stderr * `--instructions ` — Number of instructions to simulate * `--build-only` — Build the transaction and only write the base64 xdr to stdout -* `--sim-only` — Simulate the transaction and only write the base64 xdr to stdout +* `--sim-only` — (Deprecated) simulate the transaction and only write the base64 xdr to stdout @@ -371,7 +371,7 @@ If no keys are specified the contract itself is extended. * `--cost` — Output the cost execution to stderr * `--instructions ` — Number of instructions to simulate * `--build-only` — Build the transaction and only write the base64 xdr to stdout -* `--sim-only` — Simulate the transaction and only write the base64 xdr to stdout +* `--sim-only` — (Deprecated) simulate the transaction and only write the base64 xdr to stdout @@ -399,7 +399,7 @@ Deploy a wasm contract * `--cost` — Output the cost execution to stderr * `--instructions ` — Number of instructions to simulate * `--build-only` — Build the transaction and only write the base64 xdr to stdout -* `--sim-only` — Simulate the transaction and only write the base64 xdr to stdout +* `--sim-only` — (Deprecated) simulate the transaction and only write the base64 xdr to stdout * `-i`, `--ignore-checks` — Whether to ignore safety checks when deploying contracts Default value: `false` @@ -675,7 +675,7 @@ Install a WASM file to the ledger without creating a contract instance * `--cost` — Output the cost execution to stderr * `--instructions ` — Number of instructions to simulate * `--build-only` — Build the transaction and only write the base64 xdr to stdout -* `--sim-only` — Simulate the transaction and only write the base64 xdr to stdout +* `--sim-only` — (Deprecated) simulate the transaction and only write the base64 xdr to stdout * `--wasm ` — Path to wasm binary * `-i`, `--ignore-checks` — Whether to ignore safety checks when deploying contracts @@ -714,7 +714,7 @@ stellar contract invoke ... -- --help * `--cost` — Output the cost execution to stderr * `--instructions ` — Number of instructions to simulate * `--build-only` — Build the transaction and only write the base64 xdr to stdout -* `--sim-only` — Simulate the transaction and only write the base64 xdr to stdout +* `--sim-only` — (Deprecated) simulate the transaction and only write the base64 xdr to stdout * `--send ` — Whether or not to send a transaction Default value: `default` @@ -826,7 +826,7 @@ If no keys are specificed the contract itself is restored. * `--cost` — Output the cost execution to stderr * `--instructions ` — Number of instructions to simulate * `--build-only` — Build the transaction and only write the base64 xdr to stdout -* `--sim-only` — Simulate the transaction and only write the base64 xdr to stdout +* `--sim-only` — (Deprecated) simulate the transaction and only write the base64 xdr to stdout @@ -1394,7 +1394,7 @@ Transfers the XLM balance of an account to another account and removes the sourc * `--cost` — Output the cost execution to stderr * `--instructions ` — Number of instructions to simulate * `--build-only` — Build the transaction and only write the base64 xdr to stdout -* `--sim-only` — Simulate the transaction and only write the base64 xdr to stdout +* `--sim-only` — (Deprecated) simulate the transaction and only write the base64 xdr to stdout * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config @@ -1420,7 +1420,7 @@ Bumps forward the sequence number of the source account to the given sequence nu * `--cost` — Output the cost execution to stderr * `--instructions ` — Number of instructions to simulate * `--build-only` — Build the transaction and only write the base64 xdr to stdout -* `--sim-only` — Simulate the transaction and only write the base64 xdr to stdout +* `--sim-only` — (Deprecated) simulate the transaction and only write the base64 xdr to stdout * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config @@ -1446,7 +1446,7 @@ Creates, updates, or deletes a trustline Learn more about trustlines https://dev * `--cost` — Output the cost execution to stderr * `--instructions ` — Number of instructions to simulate * `--build-only` — Build the transaction and only write the base64 xdr to stdout -* `--sim-only` — Simulate the transaction and only write the base64 xdr to stdout +* `--sim-only` — (Deprecated) simulate the transaction and only write the base64 xdr to stdout * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config @@ -1475,7 +1475,7 @@ Creates and funds a new account with the specified starting balance * `--cost` — Output the cost execution to stderr * `--instructions ` — Number of instructions to simulate * `--build-only` — Build the transaction and only write the base64 xdr to stdout -* `--sim-only` — Simulate the transaction and only write the base64 xdr to stdout +* `--sim-only` — (Deprecated) simulate the transaction and only write the base64 xdr to stdout * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config @@ -1504,7 +1504,7 @@ Sets, modifies, or deletes a data entry (name/value pair) that is attached to an * `--cost` — Output the cost execution to stderr * `--instructions ` — Number of instructions to simulate * `--build-only` — Build the transaction and only write the base64 xdr to stdout -* `--sim-only` — Simulate the transaction and only write the base64 xdr to stdout +* `--sim-only` — (Deprecated) simulate the transaction and only write the base64 xdr to stdout * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config @@ -1531,7 +1531,7 @@ Sends an amount in a specific asset to a destination account * `--cost` — Output the cost execution to stderr * `--instructions ` — Number of instructions to simulate * `--build-only` — Build the transaction and only write the base64 xdr to stdout -* `--sim-only` — Simulate the transaction and only write the base64 xdr to stdout +* `--sim-only` — (Deprecated) simulate the transaction and only write the base64 xdr to stdout * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config @@ -1561,7 +1561,7 @@ Set option for an account such as flags, inflation destination, signers, home do * `--cost` — Output the cost execution to stderr * `--instructions ` — Number of instructions to simulate * `--build-only` — Build the transaction and only write the base64 xdr to stdout -* `--sim-only` — Simulate the transaction and only write the base64 xdr to stdout +* `--sim-only` — (Deprecated) simulate the transaction and only write the base64 xdr to stdout * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config @@ -1602,7 +1602,7 @@ Allows issuing account to configure authorization and trustline flags to an asse * `--cost` — Output the cost execution to stderr * `--instructions ` — Number of instructions to simulate * `--build-only` — Build the transaction and only write the base64 xdr to stdout -* `--sim-only` — Simulate the transaction and only write the base64 xdr to stdout +* `--sim-only` — (Deprecated) simulate the transaction and only write the base64 xdr to stdout * `--rpc-url ` — RPC server endpoint * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config diff --git a/cmd/soroban-cli/src/fee.rs b/cmd/soroban-cli/src/fee.rs index f6afdc500..ea968870f 100644 --- a/cmd/soroban-cli/src/fee.rs +++ b/cmd/soroban-cli/src/fee.rs @@ -3,7 +3,11 @@ use clap::arg; use crate::assembled::Assembled; use crate::xdr; -use crate::commands::HEADING_RPC; +use crate::{commands::HEADING_RPC, deprecated_arg}; + +const DEPRECATION_MESSAGE: &str = "--sim-only is deprecated and will be removed \ +in the future versions of CLI. The same functionality is offered by `tx simulate` command. To \ +replicate the behaviour, run `stellar --build only | stellar tx simulate`"; #[derive(Debug, clap::Args, Clone)] #[group(skip)] @@ -20,8 +24,14 @@ pub struct Args { /// Build the transaction and only write the base64 xdr to stdout #[arg(long, help_heading = HEADING_RPC)] pub build_only: bool, - /// Simulate the transaction and only write the base64 xdr to stdout - #[arg(long, help_heading = HEADING_RPC, conflicts_with = "build_only")] + /// (Deprecated) simulate the transaction and only write the base64 xdr to stdout + #[arg( + long, + help_heading = HEADING_RPC, + conflicts_with = "build_only", + display_order = 100, + value_parser = deprecated_arg!(bool, DEPRECATION_MESSAGE)) + ] pub sim_only: bool, } diff --git a/cmd/soroban-cli/src/utils.rs b/cmd/soroban-cli/src/utils.rs index 0729edb27..b52a7b5fe 100644 --- a/cmd/soroban-cli/src/utils.rs +++ b/cmd/soroban-cli/src/utils.rs @@ -196,6 +196,24 @@ pub mod http { } } +pub mod args { + /// Mark argument as deprecated with warning to be printed when it's used. + #[macro_export] + macro_rules! deprecated_arg { + (bool, $message: expr) => { + <_ as clap::builder::TypedValueParser>::map( + clap::builder::BoolValueParser::new(), + |x| { + if (x) { + $crate::print::Print::new(false).warnln($message); + } + x + }, + ) + }; + } +} + pub mod rpc { use crate::xdr; use soroban_rpc::{Client, Error}; From 18fd1ff2b4d120e93a506278e7888c625fe13cbf Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Thu, 10 Oct 2024 11:07:47 -0700 Subject: [PATCH 30/55] Add support for contract id alias and key name to `stellar snapshot create`. (#1662) --- FULL_HELP_DOCS.md | 6 +- .../src/commands/snapshot/create.rs | 61 ++++++++++++++++--- 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index 3efb16dda..d0e7cd9dd 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -1249,18 +1249,20 @@ Create a ledger snapshot using a history archive. Filters (address, wasm-hash) specify what ledger entries to include. -Account addresses include the account, and trust lines. +Account addresses include the account, and trustlines. Contract addresses include the related wasm, contract data. If a contract is a Stellar asset contract, it includes the asset issuer's account and trust lines, but does not include all the trust lines of other accounts holding the asset. To include them specify the addresses of relevant accounts. +Any invalid contract id passed as `--address` will be ignored. + **Usage:** `stellar snapshot create [OPTIONS] --output ` ###### **Options:** * `--ledger ` — The ledger sequence number to snapshot. Defaults to latest history archived ledger -* `--address
` — Account or contract address to include in the snapshot +* `--address
` — Account or contract address/alias to include in the snapshot * `--wasm-hash ` — WASM hashes to include in the snapshot * `--output ` — Format of the out file diff --git a/cmd/soroban-cli/src/commands/snapshot/create.rs b/cmd/soroban-cli/src/commands/snapshot/create.rs index 13bef9465..6ce48d3f2 100644 --- a/cmd/soroban-cli/src/commands/snapshot/create.rs +++ b/cmd/soroban-cli/src/commands/snapshot/create.rs @@ -27,7 +27,6 @@ use tokio::io::BufReader; use tokio_util::io::StreamReader; use url::Url; -use crate::utils::http; use crate::{ commands::{config::data, global, HEADING_RPC}, config::{self, locator, network::passphrase}, @@ -35,6 +34,7 @@ use crate::{ tx::builder, utils::get_name_from_stellar_asset_contract_storage, }; +use crate::{config::address::Address, utils::http}; #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, ValueEnum)] pub enum Output { @@ -55,7 +55,7 @@ fn default_out_path() -> PathBuf { /// /// Filters (address, wasm-hash) specify what ledger entries to include. /// -/// Account addresses include the account, and trust lines. +/// Account addresses include the account, and trustlines. /// /// Contract addresses include the related wasm, contract data. /// @@ -63,6 +63,9 @@ fn default_out_path() -> PathBuf { /// account and trust lines, but does not include all the trust lines of other /// accounts holding the asset. To include them specify the addresses of /// relevant accounts. +/// +/// Any invalid contract id passed as `--address` will be ignored. +/// #[derive(Parser, Debug, Clone)] #[group(skip)] #[command(arg_required_else_help = true)] @@ -70,9 +73,9 @@ pub struct Cmd { /// The ledger sequence number to snapshot. Defaults to latest history archived ledger. #[arg(long)] ledger: Option, - /// Account or contract address to include in the snapshot. + /// Account or contract address/alias to include in the snapshot. #[arg(long = "address", help_heading = "Filter Options")] - address: Vec, + address: Vec, /// WASM hashes to include in the snapshot. #[arg(long = "wasm-hash", help_heading = "Filter Options")] wasm_hashes: Vec, @@ -213,11 +216,13 @@ impl Cmd { } // Search the buckets using the user inputs as the starting inputs. - let (account_ids, contract_ids): (HashSet, HashSet) = - self.address.iter().cloned().partition_map(|a| match a { - ScAddress::Account(account_id) => Either::Left(account_id), - ScAddress::Contract(_) => Either::Right(a), - }); + let (account_ids, contract_ids): (HashSet, HashSet) = self + .address + .iter() + .cloned() + .filter_map(|a| self.resolve_address(&a, network_passphrase)) + .partition_map(|a| a); + let mut current = SearchInputs { account_ids, contract_ids, @@ -388,6 +393,44 @@ impl Cmd { }) .ok_or(Error::ArchiveUrlNotConfigured) } + + fn resolve_address( + &self, + address: &str, + network_passphrase: &str, + ) -> Option> { + self.resolve_contract(address, network_passphrase) + .map(Either::Right) + .or_else(|| self.resolve_account(address).map(Either::Left)) + } + + // 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 { + let address: Address = address.parse().ok()?; + + Some(AccountId(xdr::PublicKey::PublicKeyTypeEd25519( + match address.resolve_muxed_account(&self.locator, None).ok()? { + xdr::MuxedAccount::Ed25519(uint256) => uint256, + xdr::MuxedAccount::MuxedEd25519(xdr::MuxedAccountMed25519 { ed25519, .. }) => { + ed25519 + } + }, + ))) + } + // Resolve a contract address to a contract id. The contract can be a + // C-address or a contract alias. + fn resolve_contract(&self, address: &str, network_passphrase: &str) -> Option { + address.parse().ok().or_else(|| { + Some(ScAddress::Contract( + self.locator + .resolve_contract_id(address, network_passphrase) + .ok()? + .0 + .into(), + )) + }) + } } async fn get_history( From a1ee9449428d2fae6938089cdf1a68bca84f66ca Mon Sep 17 00:00:00 2001 From: Gleb Date: Thu, 10 Oct 2024 11:49:02 -0700 Subject: [PATCH 31/55] Contract init: add flag removal message (#1628) * contract init: add removal memo * Rename macro to removed_arg * Refactoring * Clippy * Remove gix from dependencies * revert Cargo.lock * minimal updates to lock file * Address comments --------- Co-authored-by: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> --- Cargo.lock | 899 +----------------- FULL_HELP_DOCS.md | 7 - cmd/soroban-cli/Cargo.toml | 4 - cmd/soroban-cli/src/commands/contract/init.rs | 674 +------------ cmd/soroban-cli/src/utils.rs | 19 + 5 files changed, 64 insertions(+), 1539 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 81e2e4c7a..71ca2b35f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,12 +117,6 @@ dependencies = [ "derive_arbitrary", ] -[[package]] -name = "arc-swap" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" - [[package]] name = "ark-bls12-381" version = "0.4.0" @@ -727,15 +721,6 @@ dependencies = [ "serde", ] -[[package]] -name = "btoi" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd6407f73a9b8b6162d8a2ef999fe6afd7cc15902ebf42c5cd296addf17e0ad" -dependencies = [ - "num-traits", -] - [[package]] name = "bumpalo" version = "3.16.0" @@ -893,12 +878,6 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" -[[package]] -name = "clru" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbd0f76e066e64fdc5631e3bb46381254deab9ef1158292f27c8c57e3bf3fe59" - [[package]] name = "codespan-reporting" version = "0.11.1" @@ -1399,12 +1378,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - [[package]] name = "dyn-clone" version = "1.0.17" @@ -1613,12 +1586,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "faster-hex" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183" - [[package]] name = "fastrand" version = "1.9.0" @@ -1650,18 +1617,6 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" -[[package]] -name = "filetime" -version = "0.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.4.1", - "windows-sys 0.52.0", -] - [[package]] name = "fixedbitset" version = "0.4.2" @@ -1876,760 +1831,6 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" -[[package]] -name = "gix" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31887c304d9a935f3e5494fb5d6a0106c34e965168ec0db9b457424eedd0c741" -dependencies = [ - "gix-actor", - "gix-attributes", - "gix-command", - "gix-commitgraph", - "gix-config", - "gix-credentials", - "gix-date", - "gix-diff", - "gix-discover", - "gix-features", - "gix-filter", - "gix-fs", - "gix-glob", - "gix-hash", - "gix-hashtable", - "gix-ignore", - "gix-index", - "gix-lock", - "gix-macros", - "gix-negotiate", - "gix-object", - "gix-odb", - "gix-pack", - "gix-path", - "gix-pathspec", - "gix-prompt", - "gix-protocol", - "gix-ref", - "gix-refspec", - "gix-revision", - "gix-revwalk", - "gix-sec", - "gix-submodule", - "gix-tempfile", - "gix-trace", - "gix-transport", - "gix-traverse", - "gix-url", - "gix-utils", - "gix-validate", - "gix-worktree", - "gix-worktree-state", - "once_cell", - "parking_lot", - "reqwest 0.11.27", - "smallvec", - "thiserror", -] - -[[package]] -name = "gix-actor" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a7bb9fad6125c81372987c06469601d37e1a2d421511adb69971b9083517a8a" -dependencies = [ - "bstr", - "btoi", - "gix-date", - "itoa", - "thiserror", - "winnow 0.5.40", -] - -[[package]] -name = "gix-attributes" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e37ce99c7e81288c28b703641b6d5d119aacc45c1a6b247156e6249afa486257" -dependencies = [ - "bstr", - "gix-glob", - "gix-path", - "gix-quote", - "gix-trace", - "kstring", - "smallvec", - "thiserror", - "unicode-bom", -] - -[[package]] -name = "gix-bitmap" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a371db66cbd4e13f0ed9dc4c0fea712d7276805fccc877f77e96374d317e87ae" -dependencies = [ - "thiserror", -] - -[[package]] -name = "gix-chunk" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c8751169961ba7640b513c3b24af61aa962c967aaf04116734975cd5af0c52" -dependencies = [ - "thiserror", -] - -[[package]] -name = "gix-command" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d76867867da891cbe32021ad454e8cae90242f6afb06762e4dd0d357afd1d7b" -dependencies = [ - "bstr", - "gix-path", - "gix-trace", - "shell-words", -] - -[[package]] -name = "gix-commitgraph" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "133b06f67f565836ec0c473e2116a60fb74f80b6435e21d88013ac0e3c60fc78" -dependencies = [ - "bstr", - "gix-chunk", - "gix-features", - "gix-hash", - "memmap2", - "thiserror", -] - -[[package]] -name = "gix-config" -version = "0.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e62bf2073b6ce3921ffa6d8326f645f30eec5fc4a8e8a4bc0fcb721a2f3f69dc" -dependencies = [ - "bstr", - "gix-config-value", - "gix-features", - "gix-glob", - "gix-path", - "gix-ref", - "gix-sec", - "memchr", - "once_cell", - "smallvec", - "thiserror", - "unicode-bom", - "winnow 0.5.40", -] - -[[package]] -name = "gix-config-value" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b328997d74dd15dc71b2773b162cb4af9a25c424105e4876e6d0686ab41c383e" -dependencies = [ - "bitflags 2.6.0", - "bstr", - "gix-path", - "libc", - "thiserror", -] - -[[package]] -name = "gix-credentials" -version = "0.24.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "198588f532e4d1202e04e6c3f50e4d7c060dffc66801c6f53cc246f1d234739e" -dependencies = [ - "bstr", - "gix-command", - "gix-config-value", - "gix-path", - "gix-prompt", - "gix-sec", - "gix-trace", - "gix-url", - "thiserror", -] - -[[package]] -name = "gix-date" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eed6931f21491ee0aeb922751bd7ec97b4b2fe8fbfedcb678e2a2dce5f3b8c0" -dependencies = [ - "bstr", - "itoa", - "thiserror", - "time", -] - -[[package]] -name = "gix-diff" -version = "0.40.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbdcb5e49c4b9729dd1c361040ae5c3cd7c497b2260b18c954f62db3a63e98cf" -dependencies = [ - "bstr", - "gix-hash", - "gix-object", - "thiserror", -] - -[[package]] -name = "gix-discover" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4669218f3ec0cbbf8f16857b32200890f8ca585f36f5817242e4115fe4551af" -dependencies = [ - "bstr", - "dunce", - "gix-fs", - "gix-hash", - "gix-path", - "gix-ref", - "gix-sec", - "thiserror", -] - -[[package]] -name = "gix-features" -version = "0.38.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac7045ac9fe5f9c727f38799d002a7ed3583cd777e3322a7c4b43e3cf437dc69" -dependencies = [ - "bytes", - "crc32fast", - "flate2", - "gix-hash", - "gix-trace", - "gix-utils", - "libc", - "once_cell", - "prodash", - "sha1_smol", - "thiserror", - "walkdir", -] - -[[package]] -name = "gix-filter" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9240862840fb740d209422937195e129e4ed3da49af212383260134bea8f6c1a" -dependencies = [ - "bstr", - "encoding_rs", - "gix-attributes", - "gix-command", - "gix-hash", - "gix-object", - "gix-packetline-blocking", - "gix-path", - "gix-quote", - "gix-trace", - "gix-utils", - "smallvec", - "thiserror", -] - -[[package]] -name = "gix-fs" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2184c40e7910529677831c8b481acf788ffd92427ed21fad65b6aa637e631b8" -dependencies = [ - "gix-features", - "gix-utils", -] - -[[package]] -name = "gix-glob" -version = "0.16.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7df15afa265cc8abe92813cd354d522f1ac06b29ec6dfa163ad320575cb447" -dependencies = [ - "bitflags 2.6.0", - "bstr", - "gix-features", - "gix-path", -] - -[[package]] -name = "gix-hash" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93d7df7366121b5018f947a04d37f034717e113dcf9ccd85c34b58e57a74d5e" -dependencies = [ - "faster-hex", - "thiserror", -] - -[[package]] -name = "gix-hashtable" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ddf80e16f3c19ac06ce415a38b8591993d3f73aede049cb561becb5b3a8e242" -dependencies = [ - "gix-hash", - "hashbrown 0.14.5", - "parking_lot", -] - -[[package]] -name = "gix-ignore" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6afb8f98e314d4e1adc822449389ada863c174b5707cedd327d67b84dba527" -dependencies = [ - "bstr", - "gix-glob", - "gix-path", - "gix-trace", - "unicode-bom", -] - -[[package]] -name = "gix-index" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7152181ba8f0a3addc5075dd612cea31fc3e252b29c8be8c45f4892bf87426" -dependencies = [ - "bitflags 2.6.0", - "bstr", - "btoi", - "filetime", - "gix-bitmap", - "gix-features", - "gix-fs", - "gix-hash", - "gix-lock", - "gix-object", - "gix-traverse", - "itoa", - "libc", - "memmap2", - "rustix 0.38.34", - "smallvec", - "thiserror", -] - -[[package]] -name = "gix-lock" -version = "13.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c359f81f01b8352063319bcb39789b7ea0887b406406381106e38c4a34d049" -dependencies = [ - "gix-tempfile", - "gix-utils", - "thiserror", -] - -[[package]] -name = "gix-macros" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "999ce923619f88194171a67fb3e6d613653b8d4d6078b529b15a765da0edcc17" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - -[[package]] -name = "gix-negotiate" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a163adb84149e522e991cbe27250a6e01de56f98cd05b174614ce3f8a4e8b140" -dependencies = [ - "bitflags 2.6.0", - "gix-commitgraph", - "gix-date", - "gix-hash", - "gix-object", - "gix-revwalk", - "smallvec", - "thiserror", -] - -[[package]] -name = "gix-object" -version = "0.41.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693ce9d30741506cb082ef2d8b797415b48e032cce0ab23eff894c19a7e4777b" -dependencies = [ - "bstr", - "btoi", - "gix-actor", - "gix-date", - "gix-features", - "gix-hash", - "gix-validate", - "itoa", - "smallvec", - "thiserror", - "winnow 0.5.40", -] - -[[package]] -name = "gix-odb" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ba2fa9e81f2461b78b4d81a807867667326c84cdab48e0aed7b73a593aa1be4" -dependencies = [ - "arc-swap", - "gix-date", - "gix-features", - "gix-fs", - "gix-hash", - "gix-object", - "gix-pack", - "gix-path", - "gix-quote", - "parking_lot", - "tempfile", - "thiserror", -] - -[[package]] -name = "gix-pack" -version = "0.47.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da5f3e78c96b76c4e6fe5e8e06b76221e4a0ee9a255aa935ed1fdf68988dfd8" -dependencies = [ - "clru", - "gix-chunk", - "gix-features", - "gix-hash", - "gix-hashtable", - "gix-object", - "gix-path", - "gix-tempfile", - "memmap2", - "parking_lot", - "smallvec", - "thiserror", -] - -[[package]] -name = "gix-packetline" -version = "0.17.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b70486beda0903b6d5b65dfa6e40585098cdf4e6365ca2dff4f74c387354a515" -dependencies = [ - "bstr", - "faster-hex", - "gix-trace", - "thiserror", -] - -[[package]] -name = "gix-packetline-blocking" -version = "0.17.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31d42378a3d284732e4d589979930d0d253360eccf7ec7a80332e5ccb77e14a" -dependencies = [ - "bstr", - "faster-hex", - "gix-trace", - "thiserror", -] - -[[package]] -name = "gix-path" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d23d5bbda31344d8abc8de7c075b3cf26e5873feba7c4a15d916bce67382bd9" -dependencies = [ - "bstr", - "gix-trace", - "home", - "once_cell", - "thiserror", -] - -[[package]] -name = "gix-pathspec" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cbd49750edb26b0a691e5246fc635fa554d344da825cd20fa9ee0da9c1b761f" -dependencies = [ - "bitflags 2.6.0", - "bstr", - "gix-attributes", - "gix-config-value", - "gix-glob", - "gix-path", - "thiserror", -] - -[[package]] -name = "gix-prompt" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e0595d2be4b6d6a71a099e989bdd610882b882da35fb8503d91d6f81aa0936f" -dependencies = [ - "gix-command", - "gix-config-value", - "parking_lot", - "rustix 0.38.34", - "thiserror", -] - -[[package]] -name = "gix-protocol" -version = "0.44.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a905cd00946ed8ed6f4f2281f98a889c5b3d38361cd94b8d5a5771d25ab33b99" -dependencies = [ - "bstr", - "gix-credentials", - "gix-date", - "gix-features", - "gix-hash", - "gix-transport", - "gix-utils", - "maybe-async", - "thiserror", - "winnow 0.6.18", -] - -[[package]] -name = "gix-quote" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbff4f9b9ea3fa7a25a70ee62f545143abef624ac6aa5884344e70c8b0a1d9ff" -dependencies = [ - "bstr", - "gix-utils", - "thiserror", -] - -[[package]] -name = "gix-ref" -version = "0.41.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5818958994ad7879fa566f5441ebcc48f0926aa027b28948e6fbf6578894dc31" -dependencies = [ - "gix-actor", - "gix-date", - "gix-features", - "gix-fs", - "gix-hash", - "gix-lock", - "gix-object", - "gix-path", - "gix-tempfile", - "gix-utils", - "gix-validate", - "memmap2", - "thiserror", - "winnow 0.5.40", -] - -[[package]] -name = "gix-refspec" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613aa4d93034c5791d13bdc635e530f4ddab1412ddfb4a8215f76213177b61c7" -dependencies = [ - "bstr", - "gix-hash", - "gix-revision", - "gix-validate", - "smallvec", - "thiserror", -] - -[[package]] -name = "gix-revision" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "288f6549d7666db74dc3f169a9a333694fc28ecd2f5aa7b2c979c89eb556751a" -dependencies = [ - "bstr", - "gix-date", - "gix-hash", - "gix-hashtable", - "gix-object", - "gix-revwalk", - "gix-trace", - "thiserror", -] - -[[package]] -name = "gix-revwalk" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9b4d91dfc5c14fee61a28c65113ded720403b65a0f46169c0460f731a5d03c" -dependencies = [ - "gix-commitgraph", - "gix-date", - "gix-hash", - "gix-hashtable", - "gix-object", - "smallvec", - "thiserror", -] - -[[package]] -name = "gix-sec" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1547d26fa5693a7f34f05b4a3b59a90890972922172653bcb891ab3f09f436df" -dependencies = [ - "bitflags 2.6.0", - "gix-path", - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "gix-submodule" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73182f6c1f5ed1ed94ba16581ac62593d5e29cd1c028b2af618f836283b8f8d4" -dependencies = [ - "bstr", - "gix-config", - "gix-path", - "gix-pathspec", - "gix-refspec", - "gix-url", - "thiserror", -] - -[[package]] -name = "gix-tempfile" -version = "13.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a761d76594f4443b675e85928e4902dec333273836bd386906f01e7e346a0d11" -dependencies = [ - "gix-fs", - "libc", - "once_cell", - "parking_lot", - "tempfile", -] - -[[package]] -name = "gix-trace" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f924267408915fddcd558e3f37295cc7d6a3e50f8bd8b606cee0808c3915157e" - -[[package]] -name = "gix-transport" -version = "0.41.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf8e5f72ec9cad9ee44714b9a4ec7427b540a2418b62111f5e3a715bebe1ed9d" -dependencies = [ - "base64 0.21.7", - "bstr", - "gix-command", - "gix-credentials", - "gix-features", - "gix-packetline", - "gix-quote", - "gix-sec", - "gix-url", - "reqwest 0.11.27", - "thiserror", -] - -[[package]] -name = "gix-traverse" -version = "0.37.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfc30c5b5e4e838683b59e1b0574ce6bc1c35916df9709aaab32bb7751daf08b" -dependencies = [ - "gix-commitgraph", - "gix-date", - "gix-hash", - "gix-hashtable", - "gix-object", - "gix-revwalk", - "smallvec", - "thiserror", -] - -[[package]] -name = "gix-url" -version = "0.27.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2eb9b35bba92ea8f0b5ab406fad3cf6b87f7929aa677ff10aa042c6da621156" -dependencies = [ - "bstr", - "gix-features", - "gix-path", - "home", - "thiserror", - "url", -] - -[[package]] -name = "gix-utils" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35192df7fd0fa112263bad8021e2df7167df4cc2a6e6d15892e1e55621d3d4dc" -dependencies = [ - "fastrand 2.1.0", - "unicode-normalization", -] - -[[package]] -name = "gix-validate" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c27dd34a49b1addf193c92070bcbf3beaf6e10f16a78544de6372e146a0acf" -dependencies = [ - "bstr", - "thiserror", -] - -[[package]] -name = "gix-worktree" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca36bb3dc54038c66507dc75c4d8edbee2d6d5cc45227b4eb508ad13dd60a006" -dependencies = [ - "bstr", - "gix-attributes", - "gix-features", - "gix-fs", - "gix-glob", - "gix-hash", - "gix-ignore", - "gix-index", - "gix-object", - "gix-path", -] - -[[package]] -name = "gix-worktree-state" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae178614b70bdb0c7f6f21b8c9fb711ab78bd7e8e1866f565fcf28876747f1d" -dependencies = [ - "bstr", - "gix-features", - "gix-filter", - "gix-fs", - "gix-glob", - "gix-hash", - "gix-index", - "gix-object", - "gix-path", - "gix-worktree", - "io-close", - "thiserror", -] - [[package]] name = "glob" version = "0.3.1" @@ -3102,7 +2303,7 @@ dependencies = [ "tokio", "tokio-rustls 0.26.0", "tower-service", - "webpki-roots 0.26.3", + "webpki-roots", ] [[package]] @@ -3275,16 +2476,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "io-close" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cadcf447f06744f8ce713d2d6239bb5bde2c357a452397a9ed90c625da390bc" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "io-lifetimes" version = "1.0.11" @@ -3455,15 +2646,6 @@ dependencies = [ "cpufeatures", ] -[[package]] -name = "kstring" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1" -dependencies = [ - "static_assertions", -] - [[package]] name = "kv-log-macro" version = "1.0.7" @@ -3645,32 +2827,12 @@ dependencies = [ "regex-automata 0.1.10", ] -[[package]] -name = "maybe-async" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", -] - [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "memmap2" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" -dependencies = [ - "libc", -] - [[package]] name = "mime" version = "0.3.17" @@ -3820,15 +2982,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_threads" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" -dependencies = [ - "libc", -] - [[package]] name = "object" version = "0.32.2" @@ -3953,7 +3106,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.3", + "redox_syscall", "smallvec", "windows-targets 0.52.6", ] @@ -4267,12 +3420,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "prodash" -version = "28.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744a264d26b88a6a7e37cbad97953fa233b94d585236310bcbc88474b4092d79" - [[package]] name = "quick-error" version = "1.2.3" @@ -4372,15 +3519,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.5.3" @@ -4460,7 +3598,6 @@ dependencies = [ "http 0.2.12", "http-body 0.4.6", "hyper 0.14.30", - "hyper-rustls 0.24.2", "hyper-tls", "ipnet", "js-sys", @@ -4470,7 +3607,6 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.12", "rustls-pemfile 1.0.4", "serde", "serde_json", @@ -4479,13 +3615,11 @@ dependencies = [ "system-configuration", "tokio", "tokio-native-tls", - "tokio-rustls 0.24.1", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 0.25.4", "winreg", ] @@ -4534,7 +3668,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 0.26.3", + "webpki-roots", "windows-registry", ] @@ -5104,12 +4238,6 @@ dependencies = [ "syn 2.0.77", ] -[[package]] -name = "sha1_smol" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" - [[package]] name = "sha2" version = "0.9.9" @@ -5153,12 +4281,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "shell-words" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" - [[package]] name = "shlex" version = "1.3.0" @@ -5332,7 +4454,6 @@ dependencies = [ "fqdn", "futures", "futures-util", - "gix", "glob", "heck 0.5.0", "hex", @@ -6354,9 +5475,7 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", - "libc", "num-conv", - "num_threads", "powerfmt", "serde", "time-core", @@ -6704,12 +5823,6 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" -[[package]] -name = "unicode-bom" -version = "2.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217" - [[package]] name = "unicode-ident" version = "1.0.12" @@ -7039,12 +6152,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki-roots" -version = "0.25.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" - [[package]] name = "webpki-roots" version = "0.26.3" diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index d0e7cd9dd..5d7816538 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -617,13 +617,6 @@ Initialize a Soroban project with an example contract ###### **Options:** -* `-w`, `--with-example ` — An optional flag to specify Soroban example contracts to include. A hello-world contract will be included by default. - - Possible values: `account`, `alloc`, `atomic_multiswap`, `atomic_swap`, `auth`, `cross_contract`, `custom_types`, `deep_contract_auth`, `deployer`, `errors`, `eth_abi`, `events`, `fuzzing`, `increment`, `liquidity_pool`, `logging`, `mint-lock`, `other_custom_types`, `simple_account`, `single_offer`, `timelock`, `token`, `ttl`, `upgradeable_contract`, `workspace` - -* `--frontend-template ` — An optional flag to pass in a url for a frontend template repository. - - Default value: `` * `--overwrite` — Overwrite all existing files. diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index da142ad57..0cc12e74f 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -106,10 +106,6 @@ directories = { workspace = true } ulid = { workspace = true, features = ["serde"] } strum = "0.17.1" strum_macros = "0.17.1" -gix = { version = "0.58.0", default-features = false, features = [ - "blocking-http-transport-reqwest-rust-tls", - "worktree-mutation", -] } async-compression = { version = "0.4.12", features = ["tokio", "gzip"] } tempfile = "3.8.1" diff --git a/cmd/soroban-cli/src/commands/contract/init.rs b/cmd/soroban-cli/src/commands/contract/init.rs index fd4cf483a..48eb837ad 100644 --- a/cmd/soroban-cli/src/commands/contract/init.rs +++ b/cmd/soroban-cli/src/commands/contract/init.rs @@ -1,85 +1,69 @@ -use clap::{ - builder::{PossibleValue, PossibleValuesParser, ValueParser}, - Parser, -}; -use gix::{clone, create, open, progress, remote}; -use rust_embed::RustEmbed; -use serde_json::{from_str, json, to_string_pretty, Error as JsonError, Value as JsonValue}; use std::{ - env, - ffi::OsStr, - fs::{ - copy, create_dir_all, metadata, read_dir, read_to_string, write, File, Metadata, - OpenOptions, - }, - io::{self, Read, Write}, - num::NonZeroU32, + fs::{create_dir_all, metadata, write, Metadata}, + io, path::{Path, PathBuf}, str, - sync::atomic::AtomicBool, }; -use toml_edit::{Document, TomlError}; -use crate::utils::http; -use crate::{commands::global, print}; +use clap::Parser; +use rust_embed::RustEmbed; -const SOROBAN_EXAMPLES_URL: &str = "https://github.com/stellar/soroban-examples.git"; -const GITHUB_URL: &str = "https://github.com"; -const WITH_EXAMPLE_LONG_HELP_TEXT: &str = - "An optional flag to specify Soroban example contracts to include. A hello-world contract will be included by default."; +use crate::{commands::global, error_on_use_of_removed_arg, print, utils}; + +const EXAMPLE_REMOVAL_NOTICE: &str = "Adding examples via cli is no longer supported. \ +You can still clone examples from the repo https://github.com/stellar/soroban-examples"; +const FRONTEND_EXAMPLE_REMOVAL_NOTICE: &str = "Using frontend template via cli is no longer \ +supported. You can search for frontend templates using github tags, \ +such as `soroban-template` or `soroban-frontend-template`"; #[derive(Parser, Debug, Clone)] #[group(skip)] pub struct Cmd { pub project_path: String, - #[arg(short, long, num_args = 1.., value_parser=possible_example_values(), long_help=WITH_EXAMPLE_LONG_HELP_TEXT)] - pub with_example: Vec, + // TODO: remove in future version (23+) https://github.com/stellar/stellar-cli/issues/1586 + #[arg( + short, + long, + hide = true, + display_order = 100, + value_parser = error_on_use_of_removed_arg!(String, EXAMPLE_REMOVAL_NOTICE) + )] + pub with_example: Option, + // TODO: remove in future version (23+) https://github.com/stellar/stellar-cli/issues/1586 #[arg( long, - default_value = "", - long_help = "An optional flag to pass in a url for a frontend template repository." + hide = true, + display_order = 100, + value_parser = error_on_use_of_removed_arg!(String, FRONTEND_EXAMPLE_REMOVAL_NOTICE), )] - pub frontend_template: String, + pub frontend_template: Option, #[arg(long, long_help = "Overwrite all existing files.")] pub overwrite: bool, } -fn possible_example_values() -> ValueParser { - let example_contracts = include_str!("../../../example_contracts.list") - .lines() - .collect::>(); - let parser = PossibleValuesParser::new(example_contracts.iter().map(PossibleValue::new)); - parser.into() -} - #[derive(thiserror::Error, Debug)] pub enum Error { #[error("{0}: {1}")] Io(String, io::Error), - #[error("io error:")] - StdIo(#[from] io::Error), - - #[error("{0}: {1}")] - Json(String, JsonError), - - #[error("failed to parse toml file: {0}")] - TomlParse(#[from] TomlError), + #[error(transparent)] + Std(#[from] std::io::Error), #[error("failed to convert bytes to string: {0}")] ConvertBytesToString(#[from] str::Utf8Error), - #[error("preparing fetch repository: {0}")] - PrepareFetch(Box), + #[error("contract package already exists: {0}")] + AlreadyExists(String), - #[error("failed to fetch repository: {0}")] - Fetch(Box), + #[error("provided project path exists and is not a directory")] + PathExistsNotDir, - #[error("failed to checkout main worktree: {0}")] - Checkout(#[from] clone::checkout::main_worktree::Error), + #[error("provided project path exists and is not a cargo workspace root directory. Hint: run init on an empty or non-existing directory" + )] + PathExistsNotCargoProject, } impl Cmd { @@ -97,6 +81,7 @@ impl Cmd { #[derive(RustEmbed)] #[folder = "src/utils/contract-init-template"] struct TemplateFiles; + struct Runner { args: Cmd, print: print::Print, @@ -112,40 +97,6 @@ impl Runner { Self::create_dir_all(&project_path)?; self.copy_template_files()?; - if !Self::check_internet_connection() { - self.print.warnln("It doesn't look like you're connected to the internet. We're still able to initialize a new project, but additional examples and the frontend template will not be included."); - return Ok(()); - } - - if !self.args.frontend_template.is_empty() { - // create a temp dir for the template repo - let fe_template_dir = tempfile::tempdir() - .map_err(|e| Error::Io("creating temp dir for frontend template".to_string(), e))?; - - // clone the template repo into the temp dir - Self::clone_repo(&self.args.frontend_template, fe_template_dir.path())?; - - // copy the frontend template files into the project - self.copy_frontend_files(fe_template_dir.path(), &project_path)?; - } - - // if there are --with-example flags, include the example contracts - if self.include_example_contracts() { - // create an examples temp dir - let examples_dir = tempfile::tempdir() - .map_err(|e| Error::Io("creating temp dir for soroban-examples".to_string(), e))?; - - // clone the soroban-examples repo into the temp dir - Self::clone_repo(SOROBAN_EXAMPLES_URL, examples_dir.path())?; - - // copy the example contracts into the project - self.copy_example_contracts( - examples_dir.path(), - &project_path, - &self.args.with_example, - )?; - } - Ok(()) } @@ -169,7 +120,7 @@ impl Runner { }; let file_contents = - std::str::from_utf8(file.data.as_ref()).map_err(Error::ConvertBytesToString)?; + str::from_utf8(file.data.as_ref()).map_err(Error::ConvertBytesToString)?; // We need to include the Cargo.toml file as Cargo.toml.removeextension in the template so that it will be included the package. This is making sure that the Cargo file is written as Cargo.toml in the new project. This is a workaround for this issue: https://github.com/rust-lang/cargo/issues/8597. let item_path = Path::new(item.as_ref()); @@ -186,69 +137,8 @@ impl Runner { } Self::write(&to, file_contents)?; } - Ok(()) - } - - fn copy_contents(&self, from: &Path, to: &Path) -> Result<(), Error> { - let contents_to_exclude_from_copy = [ - ".git", - ".github", - "Makefile", - ".vscode", - "target", - "Cargo.lock", - ]; - for entry in - read_dir(from).map_err(|e| Error::Io(format!("reading directory: {from:?}"), e))? - { - let entry = - entry.map_err(|e| Error::Io(format!("reading entry in directory {from:?}",), e))?; - let path = entry.path(); - let entry_name = entry.file_name().to_string_lossy().to_string(); - let new_path = to.join(&entry_name); - - if contents_to_exclude_from_copy.contains(&entry_name.as_str()) { - continue; - } - if path.is_dir() { - Self::create_dir_all(&new_path)?; - self.copy_contents(&path, &new_path)?; - } else { - let exists = Self::file_exists(&new_path); - let new_path_str = new_path.to_string_lossy(); - if exists { - let append = - new_path_str.contains(".gitignore") || new_path_str.contains("README.md"); - if append { - self.append_contents(&path, &new_path)?; - } - - if self.args.overwrite && !append { - self.print.plusln(format!( - "Writing {new_path_str} (overwriting existing file)" - )); - } else { - self.print.infoln(format!( - "Skipped creating {new_path_str} as it already exists" - )); - continue; - } - } else { - self.print.plus(format!("Writing {new_path_str}")); - } - copy(&path, &new_path).map_err(|e| { - Error::Io( - format!( - "copying from {:?} to {:?}", - path.to_string_lossy(), - new_path - ), - e, - ) - })?; - } - } + Self::create_dir_all(project_path.join("contracts").as_path())?; Ok(()) } @@ -260,172 +150,6 @@ impl Runner { .unwrap_or(false) } - fn check_internet_connection() -> bool { - if let Ok(_req) = http::blocking_client().get(GITHUB_URL).send() { - return true; - } - - false - } - - fn include_example_contracts(&self) -> bool { - !self.args.with_example.is_empty() - } - - fn clone_repo(from_url: &str, to_path: &Path) -> Result<(), Error> { - let mut prepare = clone::PrepareFetch::new( - from_url, - to_path, - create::Kind::WithWorktree, - create::Options { - destination_must_be_empty: false, - fs_capabilities: None, - }, - open::Options::isolated(), - ) - .map_err(|e| Error::PrepareFetch(Box::new(e)))? - .with_shallow(remote::fetch::Shallow::DepthAtRemote( - NonZeroU32::new(1).unwrap(), - )); - - let (mut checkout, _outcome) = prepare - .fetch_then_checkout(progress::Discard, &AtomicBool::new(false)) - .map_err(|e| Error::Fetch(Box::new(e)))?; - let (_repo, _outcome) = checkout - .main_worktree(progress::Discard, &AtomicBool::new(false)) - .map_err(Error::Checkout)?; - - Ok(()) - } - - fn copy_example_contracts( - &self, - from: &Path, - to: &Path, - contracts: &[String], - ) -> Result<(), Error> { - let project_contracts_path = to.join("contracts"); - for contract in contracts { - self.print - .infoln(format!("Initializing example contract: {contract}")); - let contract_as_string = contract.to_string(); - let contract_path = Path::new(&contract_as_string); - let from_contract_path = from.join(contract_path); - let to_contract_path = project_contracts_path.join(contract_path); - Self::create_dir_all(&to_contract_path)?; - - self.copy_contents(&from_contract_path, &to_contract_path)?; - Self::edit_contract_cargo_file(&to_contract_path)?; - } - - Ok(()) - } - - fn edit_contract_cargo_file(contract_path: &Path) -> Result<(), Error> { - let cargo_path = contract_path.join("Cargo.toml"); - - let cargo_toml_str = Self::read_to_string(&cargo_path)?; - let cargo_toml_str = regex::Regex::new(r#"soroban-sdk = "[^\"]+""#) - .unwrap() - .replace_all( - cargo_toml_str.as_str(), - "soroban-sdk = { workspace = true }", - ); - - let cargo_toml_str = regex::Regex::new(r#"soroban-sdk = \{(.*) version = "[^"]+"(.+)}"#) - .unwrap() - .replace_all(&cargo_toml_str, "soroban-sdk = {$1 workspace = true$2}"); - - let mut doc = cargo_toml_str - .parse::() - .map_err(Error::TomlParse)?; - doc.remove("profile"); - - Self::write(&cargo_path, &doc.to_string())?; - - Ok(()) - } - - fn copy_frontend_files(&self, from: &Path, to: &Path) -> Result<(), Error> { - self.print.infoln("Initializing with frontend template"); - self.copy_contents(from, to)?; - Self::edit_package_json_files(to) - } - - fn edit_package_json_files(project_path: &Path) -> Result<(), Error> { - let package_name = if let Some(name) = project_path.file_name() { - name.to_owned() - } else { - let current_dir = env::current_dir() - .map_err(|e| Error::Io("getting current dir from env".to_string(), e))?; - let file_name = current_dir - .file_name() - .unwrap_or(OsStr::new("soroban-astro-template")) - .to_os_string(); - file_name - }; - - Self::edit_package_name(project_path, &package_name, "package.json")?; - Self::edit_package_name(project_path, &package_name, "package-lock.json") - } - - fn edit_package_name( - project_path: &Path, - package_name: &OsStr, - file_name: &str, - ) -> Result<(), Error> { - let file_path = project_path.join(file_name); - let file_contents = Self::read_to_string(&file_path)?; - - let mut doc: JsonValue = from_str(&file_contents).map_err(|e| { - Error::Json(format!("parsing {file_name} file in: {project_path:?}"), e) - })?; - - doc["name"] = json!(package_name.to_string_lossy()); - - let formatted_json = to_string_pretty(&doc) - .map_err(|e| Error::Json("calling to_string_pretty for package.json".to_string(), e))?; - - Self::write(&file_path, &formatted_json)?; - - Ok(()) - } - - // Appends the contents of a file to another file, separated by a delimiter - fn append_contents(&self, from: &Path, to: &Path) -> Result<(), Error> { - let mut from_file = File::open(from)?; - let mut from_content = String::new(); - from_file.read_to_string(&mut from_content)?; - - let mut to_file = OpenOptions::new().read(true).append(true).open(to)?; - let mut to_content = String::new(); - to_file.read_to_string(&mut to_content)?; - - let delimiter = Self::get_merged_file_delimiter(to); - // if the to file already contains the delimiter, we don't need to append the contents again - if to_content.contains(&delimiter) { - return Ok(()); - } - - to_file.write_all(delimiter.as_bytes())?; - to_file.write_all(from_content.as_bytes())?; - - self.print.infoln(format!("Merging {to:?} contents")); - Ok(()) - } - - fn get_merged_file_delimiter(file_path: &Path) -> String { - let comment = if file_path.to_string_lossy().contains("README.md") { - "---\n".to_string() - } else if file_path.to_string_lossy().contains("gitignore") { - "# The following is from the Frontend Template's .gitignore".to_string() - } else { - String::new() - }; - - format!("\n\n{comment}\n\n").to_string() - } - fn create_dir_all(path: &Path) -> Result<(), Error> { create_dir_all(path).map_err(|e| Error::Io(format!("creating directory: {path:?}"), e)) } @@ -433,22 +157,14 @@ impl Runner { fn write(path: &Path, contents: &str) -> Result<(), Error> { write(path, contents).map_err(|e| Error::Io(format!("writing file: {path:?}"), e)) } - - fn read_to_string(path: &Path) -> Result { - read_to_string(path).map_err(|e| Error::Io(format!("reading file: {path:?}"), e)) - } } #[cfg(test)] mod tests { + use std::fs; + use std::fs::read_to_string; + use itertools::Itertools; - use std::{ - collections::HashMap, - fs::{self, read_to_string}, - path::PathBuf, - time::SystemTime, - }; - use walkdir::WalkDir; use super::*; @@ -461,8 +177,8 @@ mod tests { let runner = Runner { args: Cmd { project_path: project_dir.to_string_lossy().to_string(), - with_example: vec![], - frontend_template: String::new(), + with_example: None, + frontend_template: None, overwrite: false, }, print: print::Print::new(false), @@ -480,258 +196,6 @@ mod tests { temp_dir.close().unwrap(); } - #[test] - fn test_init_including_example_contract() { - let temp_dir = tempfile::tempdir().unwrap(); - let project_dir = temp_dir.path().join(TEST_PROJECT_NAME); - let runner = Runner { - args: Cmd { - project_path: project_dir.to_string_lossy().to_string(), - with_example: ["alloc".to_owned()].to_vec(), - frontend_template: String::new(), - overwrite: false, - }, - print: print::Print::new(false), - }; - runner.run().unwrap(); - - assert_base_template_files_exist(&project_dir); - assert_default_hello_world_contract_files_exist(&project_dir); - assert_excluded_paths_do_not_exist(&project_dir); - - // check that alloc contract files exist - assert_contract_files_exist(&project_dir, "alloc"); - - // check that expected files are excluded from the alloc contract dir - assert_example_contract_excluded_files_do_not_exist(&project_dir, "alloc"); - - // check that the alloc contract's Cargo.toml file uses the workspace for dependencies - assert_contract_cargo_file_is_well_formed(&project_dir, "alloc"); - - temp_dir.close().unwrap(); - } - - #[test] - fn test_init_including_multiple_example_contracts() { - let temp_dir = tempfile::tempdir().unwrap(); - let project_dir = temp_dir.path().join("project"); - let runner = Runner { - args: Cmd { - project_path: project_dir.to_string_lossy().to_string(), - with_example: ["account".to_owned(), "atomic_swap".to_owned()].to_vec(), - frontend_template: String::new(), - overwrite: false, - }, - print: print::Print::new(false), - }; - runner.run().unwrap(); - - assert_base_template_files_exist(&project_dir); - assert_default_hello_world_contract_files_exist(&project_dir); - assert_excluded_paths_do_not_exist(&project_dir); - - // check that account contract files exist and that expected files are excluded - assert_contract_files_exist(&project_dir, "account"); - assert_example_contract_excluded_files_do_not_exist(&project_dir, "account"); - assert_contract_cargo_file_is_well_formed(&project_dir, "account"); - - // check that atomic_swap contract files exist and that expected files are excluded - assert_contract_files_exist(&project_dir, "atomic_swap"); - assert_example_contract_excluded_files_do_not_exist(&project_dir, "atomic_swap"); - assert_contract_cargo_file_is_well_formed(&project_dir, "atomic_swap"); - - temp_dir.close().unwrap(); - } - - #[test] - fn test_init_with_invalid_example_contract() { - let temp_dir = tempfile::tempdir().unwrap(); - let project_dir = temp_dir.path().join("project"); - let runner = Runner { - args: Cmd { - project_path: project_dir.to_string_lossy().to_string(), - with_example: ["invalid_example".to_owned(), "atomic_swap".to_owned()].to_vec(), - frontend_template: String::new(), - overwrite: false, - }, - print: print::Print::new(false), - }; - assert!(runner.run().is_err()); - - temp_dir.close().unwrap(); - } - - #[test] - fn test_init_with_frontend_template() { - let temp_dir = tempfile::tempdir().unwrap(); - let project_dir = temp_dir.path().join(TEST_PROJECT_NAME); - let runner = Runner { - args: Cmd { - project_path: project_dir.to_string_lossy().to_string(), - with_example: vec![], - frontend_template: "https://github.com/stellar/soroban-astro-template".to_owned(), - overwrite: false, - }, - print: print::Print::new(false), - }; - runner.run().unwrap(); - - assert_base_template_files_exist(&project_dir); - assert_default_hello_world_contract_files_exist(&project_dir); - assert_excluded_paths_do_not_exist(&project_dir); - - // check that the contract's Cargo.toml file uses the workspace for dependencies - assert_contract_cargo_file_is_well_formed(&project_dir, "hello_world"); - assert_excluded_paths_do_not_exist(&project_dir); - - assert_astro_files_exist(&project_dir); - assert_gitignore_includes_astro_paths(&project_dir); - assert_package_json_files_have_correct_name(&project_dir, TEST_PROJECT_NAME); - assert_readme_includes_frontend_readme_appended(&project_dir); - - temp_dir.close().unwrap(); - } - - #[test] - fn test_init_with_overwrite() { - let temp_dir = tempfile::tempdir().unwrap(); - let project_dir = temp_dir.path().join(TEST_PROJECT_NAME); - - // First initialization - let runner = Runner { - args: Cmd { - project_path: project_dir.to_string_lossy().to_string(), - with_example: vec![], - frontend_template: "https://github.com/stellar/soroban-astro-template".to_owned(), - overwrite: false, - }, - print: print::Print::new(false), - }; - runner.run().unwrap(); - - // Get initial modification times - let initial_mod_times = get_mod_times(&project_dir); - - // Second initialization with overwrite - let runner = Runner { - args: Cmd { - project_path: project_dir.to_string_lossy().to_string(), - with_example: vec![], - frontend_template: "https://github.com/stellar/soroban-astro-template".to_owned(), - overwrite: true, - }, - print: print::Print::new(false), - }; - runner.run().unwrap(); - - // Get new modification times - let new_mod_times = get_mod_times(&project_dir); - - // Compare modification times - for (path, initial_time) in initial_mod_times { - let new_time = new_mod_times.get(&path).expect("File should still exist"); - assert!( - new_time > &initial_time, - "File {} should have a later modification time", - path.display() - ); - } - - temp_dir.close().unwrap(); - } - - fn get_mod_times(dir: &Path) -> HashMap { - let mut mod_times = HashMap::new(); - for entry in WalkDir::new(dir) { - let entry = entry.unwrap(); - if entry.file_type().is_file() { - let path = entry.path().to_owned(); - let metadata = fs::metadata(&path).unwrap(); - mod_times.insert(path, metadata.modified().unwrap()); - } - } - mod_times - } - - #[test] - fn test_init_from_within_an_existing_project() { - let temp_dir = tempfile::tempdir().unwrap(); - let project_dir = temp_dir.path().join("./"); - let runner = Runner { - args: Cmd { - project_path: project_dir.to_string_lossy().to_string(), - with_example: vec![], - frontend_template: "https://github.com/stellar/soroban-astro-template".to_owned(), - overwrite: false, - }, - print: print::Print::new(false), - }; - runner.run().unwrap(); - - assert_base_template_files_exist(&project_dir); - assert_default_hello_world_contract_files_exist(&project_dir); - assert_excluded_paths_do_not_exist(&project_dir); - - // check that the contract's Cargo.toml file uses the workspace for dependencies - assert_contract_cargo_file_is_well_formed(&project_dir, "hello_world"); - assert_excluded_paths_do_not_exist(&project_dir); - - assert_astro_files_exist(&project_dir); - assert_gitignore_includes_astro_paths(&project_dir); - assert_package_json_files_have_correct_name( - &project_dir, - &project_dir.file_name().unwrap().to_string_lossy(), - ); - - temp_dir.close().unwrap(); - } - - #[test] - fn test_init_does_not_duplicate_frontend_readme_contents_when_run_more_than_once() { - let temp_dir = tempfile::tempdir().unwrap(); - let project_dir = temp_dir.path().join(TEST_PROJECT_NAME); - let runner = Runner { - args: Cmd { - project_path: project_dir.to_string_lossy().to_string(), - with_example: vec![], - frontend_template: "https://github.com/stellar/soroban-astro-template".to_owned(), - overwrite: false, - }, - print: print::Print::new(false), - }; - runner.run().unwrap(); - - // call init again to make sure the README.md's contents are not duplicated - let runner = Runner { - args: Cmd { - project_path: project_dir.to_string_lossy().to_string(), - with_example: vec![], - frontend_template: "https://github.com/stellar/soroban-astro-template".to_owned(), - overwrite: false, - }, - print: print::Print::new(false), - }; - runner.run().unwrap(); - - assert_base_template_files_exist(&project_dir); - assert_default_hello_world_contract_files_exist(&project_dir); - assert_excluded_paths_do_not_exist(&project_dir); - - // check that the contract's Cargo.toml file uses the workspace for dependencies - assert_contract_cargo_file_is_well_formed(&project_dir, "hello_world"); - assert_excluded_paths_do_not_exist(&project_dir); - - assert_astro_files_exist(&project_dir); - assert_gitignore_includes_astro_paths(&project_dir); - assert_package_json_files_have_correct_name( - &project_dir, - &project_dir.file_name().unwrap().to_string_lossy(), - ); - assert_readme_includes_frontend_readme_appended(&project_dir); - - temp_dir.close().unwrap(); - } - // test helpers fn assert_base_template_files_exist(project_dir: &Path) { let expected_paths = ["contracts", "Cargo.toml", "README.md"]; @@ -813,14 +277,6 @@ mod tests { ); } - fn assert_example_contract_excluded_files_do_not_exist( - project_dir: &Path, - contract_name: &str, - ) { - let contract_dir = project_dir.join("contracts").join(contract_name); - assert!(!contract_dir.as_path().join("Makefile").exists()); - } - fn assert_excluded_paths_do_not_exist(project_dir: &Path) { let base_excluded_paths = [".git", ".github", "Makefile", ".vscode", "target"]; for path in &base_excluded_paths { @@ -838,50 +294,4 @@ mod tests { assert!(!filepath.exists(), "{filepath:?} should not exist"); }); } - - fn assert_gitignore_includes_astro_paths(project_dir: &Path) { - let gitignore_path = project_dir.join(".gitignore"); - let gitignore_str = read_to_string(gitignore_path).unwrap(); - assert!(gitignore_str.contains(".astro/")); - assert!(gitignore_str.contains("node_modules")); - assert!(gitignore_str.contains("npm-debug.log*")); - } - - fn assert_astro_files_exist(project_dir: &Path) { - assert!(project_dir.join("public").exists()); - assert!(project_dir.join("src").exists()); - assert!(project_dir.join("src").join("components").exists()); - assert!(project_dir.join("src").join("layouts").exists()); - assert!(project_dir.join("src").join("pages").exists()); - assert!(project_dir.join("astro.config.mjs").exists()); - assert!(project_dir.join("tsconfig.json").exists()); - } - - fn assert_package_json_files_have_correct_name( - project_dir: &Path, - expected_package_name: &str, - ) { - let package_json_path = project_dir.join("package.json"); - let package_json_str = read_to_string(package_json_path).unwrap(); - assert!(package_json_str.contains(&format!("\"name\": \"{expected_package_name}\""))); - - let package_lock_json_path = project_dir.join("package-lock.json"); - let package_lock_json_str = read_to_string(package_lock_json_path).unwrap(); - assert!(package_lock_json_str.contains(&format!("\"name\": \"{expected_package_name}\""))); - } - - fn assert_readme_includes_frontend_readme_appended(project_dir: &Path) { - let readme_path = project_dir.join("README.md"); - let readme_str = read_to_string(readme_path).unwrap(); - assert!(readme_str.contains("Soroban Frontend in Astro")); - let expected = "The following is the Frontend Template's README.md"; - assert!(readme_str.contains(expected)); - - let readme_path = project_dir.join("README.md"); - let readme_str = read_to_string(readme_path).unwrap(); - let readme_frontend_merge_delimiter = "The following is the Frontend Template's README.md"; - let count = readme_str.matches(readme_frontend_merge_delimiter).count(); - // making sure it is in there just once so that it isn't duplicated if `contract init` is run again - assert!(count == 1); - } } diff --git a/cmd/soroban-cli/src/utils.rs b/cmd/soroban-cli/src/utils.rs index b52a7b5fe..6e87d15a6 100644 --- a/cmd/soroban-cli/src/utils.rs +++ b/cmd/soroban-cli/src/utils.rs @@ -197,6 +197,25 @@ pub mod http { } pub mod args { + #[derive(thiserror::Error, Debug)] + pub enum DeprecatedError<'a> { + #[error("This argument has been removed and will be not be recognized by the future versions of CLI: {0}" + )] + RemovedArgument(&'a str), + } + + #[macro_export] + /// Mark argument as removed with an error to be printed when it's used. + macro_rules! error_on_use_of_removed_arg { + ($_type:ident, $message: expr) => { + |a: &str| { + Err::<$_type, utils::args::DeprecatedError>( + utils::args::DeprecatedError::RemovedArgument($message), + ) + } + }; + } + /// Mark argument as deprecated with warning to be printed when it's used. #[macro_export] macro_rules! deprecated_arg { From 8f8d2ac1734b56dc385c3fc281a0f9a5aa4a0f74 Mon Sep 17 00:00:00 2001 From: Gleb Date: Thu, 10 Oct 2024 18:20:36 -0700 Subject: [PATCH 32/55] Remove example_contracts.list (#1667) --- .github/workflows/rust.yml | 8 -------- Makefile | 5 ----- cmd/soroban-cli/example_contracts.list | 25 ------------------------- 3 files changed, 38 deletions(-) delete mode 100644 cmd/soroban-cli/example_contracts.list diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index d7c49389b..048015009 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -30,14 +30,6 @@ jobs: - run: rustup update - run: cargo fmt --all --check - check-generated-examples-list: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - run: make generate-examples-list - - name: Check no diffs exist - run: git add -N . && git diff HEAD --exit-code - check-generated-full-help-docs: runs-on: ubuntu-latest-16-cores steps: diff --git a/Makefile b/Makefile index 6d55aacc4..4db0946ce 100644 --- a/Makefile +++ b/Makefile @@ -46,11 +46,6 @@ build-test: build-test-wasms install generate-full-help-doc: cargo run --bin doc-gen --features clap-markdown -generate-examples-list: - curl -sSL https://api.github.com/repos/stellar/soroban-examples/git/trees/main \ - | jq -r '.tree[] | select(.type != "blob" and .path != "hello_world" and (.path | startswith(".") | not)) | .path' \ - > cmd/soroban-cli/example_contracts.list - test: build-test cargo test diff --git a/cmd/soroban-cli/example_contracts.list b/cmd/soroban-cli/example_contracts.list deleted file mode 100644 index 2ad5f2c6e..000000000 --- a/cmd/soroban-cli/example_contracts.list +++ /dev/null @@ -1,25 +0,0 @@ -account -alloc -atomic_multiswap -atomic_swap -auth -cross_contract -custom_types -deep_contract_auth -deployer -errors -eth_abi -events -fuzzing -increment -liquidity_pool -logging -mint-lock -other_custom_types -simple_account -single_offer -timelock -token -ttl -upgradeable_contract -workspace From a041c68b86030f171526d8fa086268a72fbd845f Mon Sep 17 00:00:00 2001 From: Gleb Date: Fri, 11 Oct 2024 00:43:31 -0700 Subject: [PATCH 33/55] Update contract init (#1625) * contract init: add removal memo * Rework contract init * Update impl * Improve messages --- FULL_HELP_DOCS.md | 9 +- cmd/crates/soroban-test/tests/it/init.rs | 63 ++++++++++- cmd/soroban-cli/src/commands/contract/init.rs | 106 +++++++++++++----- cmd/soroban-cli/src/commands/contract/mod.rs | 7 +- .../Cargo.toml.removeextension | 2 +- .../src/utils/contract-template/Makefile | 16 +++ .../src/lib.rs | 0 .../src/test.rs | 0 .../.gitignore | 0 .../Cargo.toml.removeextension | 0 .../README.md | 0 11 files changed, 172 insertions(+), 31 deletions(-) rename cmd/soroban-cli/src/utils/{contract-init-template/contracts/hello_world => contract-template}/Cargo.toml.removeextension (89%) create mode 100644 cmd/soroban-cli/src/utils/contract-template/Makefile rename cmd/soroban-cli/src/utils/{contract-init-template/contracts/hello_world => contract-template}/src/lib.rs (100%) rename cmd/soroban-cli/src/utils/{contract-init-template/contracts/hello_world => contract-template}/src/test.rs (100%) rename cmd/soroban-cli/src/utils/{contract-init-template => contract-workspace-template}/.gitignore (100%) rename cmd/soroban-cli/src/utils/{contract-init-template => contract-workspace-template}/Cargo.toml.removeextension (100%) rename cmd/soroban-cli/src/utils/{contract-init-template => contract-workspace-template}/README.md (100%) diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index 5d7816538..c52636226 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -84,7 +84,7 @@ Tools for smart contract developers * `fetch` — Fetch a contract's Wasm binary * `id` — Generate the contract id for a given contract or asset * `info` — Access info about contracts -* `init` — Initialize a Soroban project with an example contract +* `init` — Initialize a Soroban contract project * `inspect` — (Deprecated in favor of `contract info` subcommands) Inspect a WASM file listing contract functions, meta, etc * `install` — Install a WASM file to the ledger without creating a contract instance * `invoke` — Invoke a contract function @@ -607,7 +607,9 @@ Outputs no data when no data is present in the contract. ## `stellar contract init` -Initialize a Soroban project with an example contract +Initialize a Soroban contract project. + +This command will create a Cargo workspace project and add a sample Stellar contract. The name of the contract can be specified by `--name`. It can be run multiple times with different names in order to generate multiple contracts, and files won't be overwritten unless `--overwrite` is passed. **Usage:** `stellar contract init [OPTIONS] ` @@ -617,6 +619,9 @@ Initialize a Soroban project with an example contract ###### **Options:** +* `--name ` — An optional flag to specify a new contract's name. + + Default value: `hello-world` * `--overwrite` — Overwrite all existing files. diff --git a/cmd/crates/soroban-test/tests/it/init.rs b/cmd/crates/soroban-test/tests/it/init.rs index 6eada4ce6..c3cc9b694 100644 --- a/cmd/crates/soroban-test/tests/it/init.rs +++ b/cmd/crates/soroban-test/tests/it/init.rs @@ -1,6 +1,6 @@ use assert_fs::prelude::*; use predicates::prelude::predicate; -use soroban_test::TestEnv; +use soroban_test::{AssertExt, TestEnv}; #[test] fn init() { @@ -24,3 +24,64 @@ fn init() { == Some(&format!("{major}.0.0")) })); } + +#[test] +fn init_and_deploy() { + let name = "hello_world"; + let sandbox = TestEnv::default(); + + sandbox + .new_assert_cmd("contract") + .arg("init") + .arg("--name") + .arg(name) + .arg("project") + .assert() + .success(); + + let manifest_path = sandbox + .dir() + .join(format!("project/contracts/{name}/Cargo.toml")); + assert!(manifest_path.exists()); + + sandbox + .new_assert_cmd("contract") + .arg("build") + .arg("--manifest-path") + .arg(manifest_path) + .assert() + .success(); + + let target_dir = sandbox + .dir() + .join("project/target/wasm32-unknown-unknown/release"); + assert!(target_dir.exists()); + + let assert = sandbox + .new_assert_cmd("contract") + .arg("deploy") + .arg("--wasm") + .arg(target_dir.join(format!("{name}.wasm"))) + .assert(); + + let contract = assert.stdout_as_str(); + + assert.success(); + + let assert = sandbox + .new_assert_cmd("contract") + .arg("invoke") + .arg("--id") + .arg(contract) + .arg("--") + .arg("hello") + .arg("--to") + .arg("bar") + .assert(); + + let output = assert.stdout_as_str(); + + assert_eq!(output, r#"["Hello","bar"]"#); + + assert.success(); +} diff --git a/cmd/soroban-cli/src/commands/contract/init.rs b/cmd/soroban-cli/src/commands/contract/init.rs index 48eb837ad..96f28c64e 100644 --- a/cmd/soroban-cli/src/commands/contract/init.rs +++ b/cmd/soroban-cli/src/commands/contract/init.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::{ fs::{create_dir_all, metadata, write, Metadata}, io, @@ -21,12 +22,19 @@ such as `soroban-template` or `soroban-frontend-template`"; pub struct Cmd { pub project_path: String, + #[arg( + long, + default_value = "hello-world", + long_help = "An optional flag to specify a new contract's name." + )] + pub name: String, + // TODO: remove in future version (23+) https://github.com/stellar/stellar-cli/issues/1586 #[arg( short, long, hide = true, - display_order = 100, + display_order = 100, value_parser = error_on_use_of_removed_arg!(String, EXAMPLE_REMOVAL_NOTICE) )] pub with_example: Option, @@ -79,8 +87,12 @@ impl Cmd { } #[derive(RustEmbed)] -#[folder = "src/utils/contract-init-template"] -struct TemplateFiles; +#[folder = "src/utils/contract-workspace-template"] +struct WorkspaceTemplateFiles; + +#[derive(RustEmbed)] +#[folder = "src/utils/contract-template"] +struct ContractTemplateFiles; struct Runner { args: Cmd, @@ -91,19 +103,49 @@ impl Runner { fn run(&self) -> Result<(), Error> { let project_path = PathBuf::from(&self.args.project_path); self.print - .infoln(format!("Initializing project at {project_path:?}")); + .infoln(format!("Initializing workspace at {project_path:?}")); // create a project dir, and copy the contents of the base template (contract-init-template) into it Self::create_dir_all(&project_path)?; - self.copy_template_files()?; + self.copy_template_files( + project_path.as_path(), + &mut WorkspaceTemplateFiles::iter(), + WorkspaceTemplateFiles::get, + )?; + + let contract_path = project_path.join("contracts").join(&self.args.name); + self.print + .infoln(format!("Initializing contract at {contract_path:?}")); + + Self::create_dir_all(contract_path.as_path())?; + self.copy_template_files( + contract_path.as_path(), + &mut ContractTemplateFiles::iter(), + ContractTemplateFiles::get, + )?; Ok(()) } - fn copy_template_files(&self) -> Result<(), Error> { - let project_path = Path::new(&self.args.project_path); - for item in TemplateFiles::iter() { - let mut to = project_path.join(item.as_ref()); + fn copy_template_files( + &self, + root_path: &Path, + files: &mut dyn Iterator>, + getter: fn(&str) -> Option, + ) -> Result<(), Error> { + for item in &mut *files { + let mut to = root_path.join(item.as_ref()); + // We need to include the Cargo.toml file as Cargo.toml.removeextension in the template + // so that it will be included the package. This is making sure that the Cargo file is + // written as Cargo.toml in the new project. This is a workaround for this issue: + // https://github.com/rust-lang/cargo/issues/8597. + let item_path = Path::new(item.as_ref()); + let is_toml = item_path.file_name().unwrap() == "Cargo.toml.removeextension"; + if is_toml { + let item_parent_path = item_path.parent().unwrap(); + to = root_path.join(item_parent_path).join("Cargo.toml"); + } + let exists = Self::file_exists(&to); if exists && !self.args.overwrite { self.print @@ -113,20 +155,19 @@ impl Runner { Self::create_dir_all(to.parent().unwrap())?; - let Some(file) = TemplateFiles::get(item.as_ref()) else { + let Some(file) = getter(item.as_ref()) else { self.print .warnln(format!("Failed to read file: {}", item.as_ref())); continue; }; - let file_contents = - str::from_utf8(file.data.as_ref()).map_err(Error::ConvertBytesToString)?; + let mut file_contents = str::from_utf8(file.data.as_ref()) + .map_err(Error::ConvertBytesToString)? + .to_string(); - // We need to include the Cargo.toml file as Cargo.toml.removeextension in the template so that it will be included the package. This is making sure that the Cargo file is written as Cargo.toml in the new project. This is a workaround for this issue: https://github.com/rust-lang/cargo/issues/8597. - let item_path = Path::new(item.as_ref()); - if item_path.file_name().unwrap() == "Cargo.toml.removeextension" { - let item_parent_path = item_path.parent().unwrap(); - to = project_path.join(item_parent_path).join("Cargo.toml"); + if is_toml { + let new_content = file_contents.replace("%contract-template%", &self.args.name); + file_contents = new_content; } if exists { @@ -135,11 +176,9 @@ impl Runner { } else { self.print.plusln(format!("Writing {to:?}")); } - Self::write(&to, file_contents)?; + Self::write(&to, &file_contents)?; } - Self::create_dir_all(project_path.join("contracts").as_path())?; - Ok(()) } @@ -177,6 +216,7 @@ mod tests { let runner = Runner { args: Cmd { project_path: project_dir.to_string_lossy().to_string(), + name: "hello_world".to_string(), with_example: None, frontend_template: None, overwrite: false, @@ -186,11 +226,29 @@ mod tests { runner.run().unwrap(); assert_base_template_files_exist(&project_dir); - assert_default_hello_world_contract_files_exist(&project_dir); + + assert_contract_files_exist(&project_dir, "hello_world"); assert_excluded_paths_do_not_exist(&project_dir); assert_contract_cargo_file_is_well_formed(&project_dir, "hello_world"); + assert_excluded_paths_do_not_exist(&project_dir); + let runner = Runner { + args: Cmd { + project_path: project_dir.to_string_lossy().to_string(), + name: "contract2".to_string(), + with_example: None, + frontend_template: None, + overwrite: false, + }, + print: print::Print::new(false), + }; + runner.run().unwrap(); + + assert_contract_files_exist(&project_dir, "contract2"); + assert_excluded_paths_do_not_exist(&project_dir); + + assert_contract_cargo_file_is_well_formed(&project_dir, "contract2"); assert_excluded_paths_do_not_exist(&project_dir); temp_dir.close().unwrap(); @@ -204,10 +262,6 @@ mod tests { } } - fn assert_default_hello_world_contract_files_exist(project_dir: &Path) { - assert_contract_files_exist(project_dir, "hello_world"); - } - fn assert_contract_files_exist(project_dir: &Path, contract_name: &str) { let contract_dir = project_dir.join("contracts").join(contract_name); @@ -283,7 +337,7 @@ mod tests { let filepath = project_dir.join(path); assert!(!filepath.exists(), "{filepath:?} should not exist"); } - let contract_excluded_paths = ["Makefile", "target", "Cargo.lock"]; + let contract_excluded_paths = ["target", "Cargo.lock"]; let contract_dirs = fs::read_dir(project_dir.join("contracts")) .unwrap() .map(|entry| entry.unwrap().path()); diff --git a/cmd/soroban-cli/src/commands/contract/mod.rs b/cmd/soroban-cli/src/commands/contract/mod.rs index 4f3c6f7f4..d0524e82b 100644 --- a/cmd/soroban-cli/src/commands/contract/mod.rs +++ b/cmd/soroban-cli/src/commands/contract/mod.rs @@ -53,7 +53,12 @@ pub enum Cmd { #[command(subcommand)] Info(info::Cmd), - /// Initialize a Soroban project with an example contract + /// Initialize a Soroban contract project. + /// + /// This command will create a Cargo workspace project and add a sample Stellar contract. + /// The name of the contract can be specified by `--name`. It can be run multiple times + /// with different names in order to generate multiple contracts, and files won't + /// be overwritten unless `--overwrite` is passed. Init(init::Cmd), /// (Deprecated in favor of `contract info` subcommands) Inspect a WASM file listing contract functions, meta, etc diff --git a/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/Cargo.toml.removeextension b/cmd/soroban-cli/src/utils/contract-template/Cargo.toml.removeextension similarity index 89% rename from cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/Cargo.toml.removeextension rename to cmd/soroban-cli/src/utils/contract-template/Cargo.toml.removeextension index 2d8b3ac4e..8da1aed87 100644 --- a/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/Cargo.toml.removeextension +++ b/cmd/soroban-cli/src/utils/contract-template/Cargo.toml.removeextension @@ -1,5 +1,5 @@ [package] -name = "hello-world" +name = "%contract-template%" version = "0.0.0" edition = "2021" publish = false diff --git a/cmd/soroban-cli/src/utils/contract-template/Makefile b/cmd/soroban-cli/src/utils/contract-template/Makefile new file mode 100644 index 000000000..7f774ad12 --- /dev/null +++ b/cmd/soroban-cli/src/utils/contract-template/Makefile @@ -0,0 +1,16 @@ +default: build + +all: test + +test: build + cargo test + +build: + stellar contract build + @ls -l target/wasm32-unknown-unknown/release/*.wasm + +fmt: + cargo fmt --all + +clean: + cargo clean diff --git a/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/src/lib.rs b/cmd/soroban-cli/src/utils/contract-template/src/lib.rs similarity index 100% rename from cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/src/lib.rs rename to cmd/soroban-cli/src/utils/contract-template/src/lib.rs diff --git a/cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/src/test.rs b/cmd/soroban-cli/src/utils/contract-template/src/test.rs similarity index 100% rename from cmd/soroban-cli/src/utils/contract-init-template/contracts/hello_world/src/test.rs rename to cmd/soroban-cli/src/utils/contract-template/src/test.rs diff --git a/cmd/soroban-cli/src/utils/contract-init-template/.gitignore b/cmd/soroban-cli/src/utils/contract-workspace-template/.gitignore similarity index 100% rename from cmd/soroban-cli/src/utils/contract-init-template/.gitignore rename to cmd/soroban-cli/src/utils/contract-workspace-template/.gitignore diff --git a/cmd/soroban-cli/src/utils/contract-init-template/Cargo.toml.removeextension b/cmd/soroban-cli/src/utils/contract-workspace-template/Cargo.toml.removeextension similarity index 100% rename from cmd/soroban-cli/src/utils/contract-init-template/Cargo.toml.removeextension rename to cmd/soroban-cli/src/utils/contract-workspace-template/Cargo.toml.removeextension diff --git a/cmd/soroban-cli/src/utils/contract-init-template/README.md b/cmd/soroban-cli/src/utils/contract-workspace-template/README.md similarity index 100% rename from cmd/soroban-cli/src/utils/contract-init-template/README.md rename to cmd/soroban-cli/src/utils/contract-workspace-template/README.md From 9cc8fcd99b6c2fa2a9c34e43439b3d13ce4ea0e5 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Mon, 14 Oct 2024 02:19:14 -0400 Subject: [PATCH 34/55] Feat/add rpc header arg (#1618) --- Cargo.lock | 2 +- FULL_HELP_DOCS.md | 35 ++++ cmd/crates/soroban-test/src/lib.rs | 1 + .../src/commands/contract/deploy/asset.rs | 4 +- .../src/commands/contract/deploy/wasm.rs | 5 +- .../src/commands/contract/extend.rs | 6 +- .../src/commands/contract/fetch.rs | 12 +- .../src/commands/contract/info/shared.rs | 15 +- .../src/commands/contract/install.rs | 25 ++- .../src/commands/contract/invoke.rs | 35 ++-- cmd/soroban-cli/src/commands/contract/read.rs | 4 +- .../src/commands/contract/restore.rs | 6 +- cmd/soroban-cli/src/commands/events.rs | 8 +- cmd/soroban-cli/src/commands/tx/send.rs | 8 +- cmd/soroban-cli/src/commands/tx/simulate.rs | 4 +- cmd/soroban-cli/src/config/mod.rs | 6 +- cmd/soroban-cli/src/config/network.rs | 171 ++++++++++++++++++ cmd/soroban-cli/src/wasm.rs | 19 +- 18 files changed, 292 insertions(+), 74 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 71ca2b35f..55ebf62a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5022,7 +5022,7 @@ dependencies = [ [[package]] name = "stellar-rpc-client" version = "21.4.0" -source = "git+https://github.com/stellar/rs-stellar-rpc-client?branch=main#9f748334fffb2762db01b566dee455eea3095c79" +source = "git+https://github.com/stellar/rs-stellar-rpc-client?branch=main#7554d4c87c026313a1f5b3c7ae66a92b5ff7e091" dependencies = [ "clap", "hex", diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index c52636226..41575aea0 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -117,6 +117,7 @@ Get Id of builtin Soroban Asset Contract. Deprecated, use `stellar contract id a * `--asset ` — ID of the Stellar classic asset to wrap, e.g. "USDC:G...5" * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--global` — Use global config @@ -134,6 +135,7 @@ Deploy builtin Soroban Asset Contract * `--asset ` — ID of the Stellar classic asset to wrap, e.g. "USDC:G...5" * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail @@ -180,6 +182,7 @@ Remove contract alias * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config @@ -200,6 +203,7 @@ Add contract alias * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--overwrite` — Overwrite the contract alias if it already exists @@ -222,6 +226,7 @@ Show the contract id associated with a given alias * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config @@ -293,6 +298,7 @@ Generate a TypeScript / JavaScript package * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config @@ -359,6 +365,7 @@ If no keys are specified the contract itself is extended. Temporary * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail @@ -387,6 +394,7 @@ Deploy a wasm contract * `--wasm-hash ` — Hash of the already installed/deployed WASM file * `--salt ` — Custom salt 32-byte salt for the token id * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail @@ -420,6 +428,7 @@ Fetch a contract's Wasm binary * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config @@ -448,6 +457,7 @@ Deploy builtin Soroban Asset Contract * `--asset ` — ID of the Stellar classic asset to wrap, e.g. "USDC:G...5" * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--global` — Use global config @@ -465,6 +475,7 @@ Deploy normal Wasm Contract * `--salt ` — ID of the Soroban contract * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail @@ -506,6 +517,7 @@ Outputs no data when no data is present in the contract. * `--wasm-hash ` — Wasm hash to get the data for * `--id ` — Contract id to get the data for * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--global` — Use global config @@ -545,6 +557,7 @@ Outputs no data when no data is present in the contract. * `--wasm-hash ` — Wasm hash to get the data for * `--id ` — Contract id to get the data for * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--global` — Use global config @@ -584,6 +597,7 @@ Outputs no data when no data is present in the contract. * `--wasm-hash ` — Wasm hash to get the data for * `--id ` — Contract id to get the data for * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--global` — Use global config @@ -661,6 +675,7 @@ Install a WASM file to the ledger without creating a contract instance ###### **Options:** * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail @@ -700,6 +715,7 @@ stellar contract invoke ... -- --help * `--id ` — Contract ID to invoke * `--is-view` — View the result simulating and do not sign and submit transaction. Deprecated use `--send=no` * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail @@ -777,6 +793,7 @@ Print the current value of a contract-data ledger entry Temporary * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--global` — Use global config @@ -812,6 +829,7 @@ If no keys are specificed the contract itself is restored. * `--ledgers-to-extend ` — Number of ledgers to extend the entry * `--ttl-ledger-only` — Only print the new Time To Live ledger * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail @@ -872,6 +890,7 @@ Watch the network for contract events * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config @@ -945,6 +964,7 @@ Fund an identity on a test network ###### **Options:** * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--hd-path ` — If identity is a seed phrase use this hd path, default is 0 @@ -973,6 +993,7 @@ Generate a new identity with a seed phrase, currently 12 words * `--hd-path ` — When generating a secret key, which `hd_path` should be used from the original `seed_phrase` * `-d`, `--default-seed` — Generate the default seed phrase. Useful for testing. Equivalent to --seed 0000000000000000 * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--fund` — Fund generated key pair @@ -1060,6 +1081,7 @@ Add a new network ###### **Options:** * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — Optional header (e.g. API Key) to include in requests to the RPC * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." @@ -1272,6 +1294,7 @@ Any invalid contract id passed as `--address` will be ignored. * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--archive-url ` — Archive URL @@ -1303,6 +1326,7 @@ Simulate a transaction envelope from stdin ###### **Options:** * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail @@ -1321,6 +1345,7 @@ Calculate the hash of a transaction envelope from stdin ###### **Options:** * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config @@ -1338,6 +1363,7 @@ Sign a transaction envelope appending the signature to the envelope * `--hd-path ` — If using a seed phrase to sign, sets which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0` * `--sign-with-lab` — Sign with https://lab.stellar.org * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--global` — Use global config @@ -1354,6 +1380,7 @@ Send a transaction envelope to the network ###### **Options:** * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--global` — Use global config @@ -1396,6 +1423,7 @@ Transfers the XLM balance of an account to another account and removes the sourc * `--build-only` — Build the transaction and only write the base64 xdr to stdout * `--sim-only` — (Deprecated) simulate the transaction and only write the base64 xdr to stdout * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail @@ -1422,6 +1450,7 @@ Bumps forward the sequence number of the source account to the given sequence nu * `--build-only` — Build the transaction and only write the base64 xdr to stdout * `--sim-only` — (Deprecated) simulate the transaction and only write the base64 xdr to stdout * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail @@ -1448,6 +1477,7 @@ Creates, updates, or deletes a trustline Learn more about trustlines https://dev * `--build-only` — Build the transaction and only write the base64 xdr to stdout * `--sim-only` — (Deprecated) simulate the transaction and only write the base64 xdr to stdout * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail @@ -1477,6 +1507,7 @@ Creates and funds a new account with the specified starting balance * `--build-only` — Build the transaction and only write the base64 xdr to stdout * `--sim-only` — (Deprecated) simulate the transaction and only write the base64 xdr to stdout * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail @@ -1506,6 +1537,7 @@ Sets, modifies, or deletes a data entry (name/value pair) that is attached to an * `--build-only` — Build the transaction and only write the base64 xdr to stdout * `--sim-only` — (Deprecated) simulate the transaction and only write the base64 xdr to stdout * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail @@ -1533,6 +1565,7 @@ Sends an amount in a specific asset to a destination account * `--build-only` — Build the transaction and only write the base64 xdr to stdout * `--sim-only` — (Deprecated) simulate the transaction and only write the base64 xdr to stdout * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail @@ -1563,6 +1596,7 @@ Set option for an account such as flags, inflation destination, signers, home do * `--build-only` — Build the transaction and only write the base64 xdr to stdout * `--sim-only` — (Deprecated) simulate the transaction and only write the base64 xdr to stdout * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail @@ -1604,6 +1638,7 @@ Allows issuing account to configure authorization and trustline flags to an asse * `--build-only` — Build the transaction and only write the base64 xdr to stdout * `--sim-only` — (Deprecated) simulate the transaction and only write the base64 xdr to stdout * `--rpc-url ` — RPC server endpoint +* `--rpc-header ` — RPC Header(s) to include in requests to the RPC provider * `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server * `--network ` — Name of network to use from config * `--source-account ` — Account that where transaction originates from. Alias `source`. Can be an identity (--source alice), a public key (--source GDKW...), a muxed account (--source MDA…), a secret key (--source SC36…), or a seed phrase (--source "kite urban…"). If `--build-only` or `--sim-only` flags were NOT provided, this key will also be used to sign the final transaction. In that case, trying to sign with public key will fail diff --git a/cmd/crates/soroban-test/src/lib.rs b/cmd/crates/soroban-test/src/lib.rs index 849b78796..2c62578ef 100644 --- a/cmd/crates/soroban-test/src/lib.rs +++ b/cmd/crates/soroban-test/src/lib.rs @@ -235,6 +235,7 @@ impl TestEnv { config::Args { network: network::Args { rpc_url: Some(self.rpc_url.clone()), + rpc_headers: [].to_vec(), network_passphrase: Some(LOCAL_NETWORK_PASSPHRASE.to_string()), network: None, }, diff --git a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs index ad0afce5e..263908521 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs @@ -16,7 +16,7 @@ use crate::{ NetworkRunnable, }, config::{self, data, network}, - rpc::{Client, Error as SorobanRpcError}, + rpc::Error as SorobanRpcError, tx::builder, utils::contract_id_hash_from_asset, }; @@ -88,7 +88,7 @@ impl NetworkRunnable for Cmd { let asset = &self.asset; let network = config.get_network()?; - let client = Client::new(&network.rpc_url)?; + let client = network.rpc_client()?; client .verify_network_passphrase(Some(&network.network_passphrase)) .await?; diff --git a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs index 03ed3f438..21c685b93 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs @@ -16,8 +16,7 @@ use crate::{ assembled::simulate_and_assemble_transaction, commands::{contract::install, HEADING_RPC}, config::{self, data, locator, network}, - rpc::{self, Client}, - utils, wasm, + rpc, utils, wasm, }; use crate::{ commands::{ @@ -208,7 +207,7 @@ impl NetworkRunnable for Cmd { None => rand::thread_rng().gen::<[u8; 32]>(), }; - let client = Client::new(&network.rpc_url)?; + let client = network.rpc_client()?; client .verify_network_passphrase(Some(&network.network_passphrase)) .await?; diff --git a/cmd/soroban-cli/src/commands/contract/extend.rs b/cmd/soroban-cli/src/commands/contract/extend.rs index 92b986a22..24aac54c5 100644 --- a/cmd/soroban-cli/src/commands/contract/extend.rs +++ b/cmd/soroban-cli/src/commands/contract/extend.rs @@ -16,9 +16,7 @@ use crate::{ NetworkRunnable, }, config::{self, data, locator, network}, - key, - rpc::{self, Client}, - wasm, Pwd, + key, rpc, wasm, Pwd, }; const MAX_LEDGERS_TO_EXTEND: u32 = 535_679; @@ -132,7 +130,7 @@ impl NetworkRunnable for Cmd { let network = config.get_network()?; tracing::trace!(?network); let keys = self.key.parse_keys(&config.locator, &network)?; - let client = Client::new(&network.rpc_url)?; + let client = network.rpc_client()?; let source_account = config.source_account()?; let extend_to = self.ledgers_to_extend(); diff --git a/cmd/soroban-cli/src/commands/contract/fetch.rs b/cmd/soroban-cli/src/commands/contract/fetch.rs index fd8070898..2714b1f07 100644 --- a/cmd/soroban-cli/src/commands/contract/fetch.rs +++ b/cmd/soroban-cli/src/commands/contract/fetch.rs @@ -7,12 +7,14 @@ use std::{fmt::Debug, fs, io}; use clap::{arg, command, Parser}; -use crate::commands::{global, NetworkRunnable}; -use crate::config::{ - self, locator, - network::{self, Network}, +use crate::{ + commands::{global, NetworkRunnable}, + config::{ + self, locator, + network::{self, Network}, + }, + wasm, Pwd, }; -use crate::{wasm, Pwd}; #[derive(Parser, Debug, Default, Clone)] #[allow(clippy::struct_excessive_bools)] diff --git a/cmd/soroban-cli/src/commands/contract/info/shared.rs b/cmd/soroban-cli/src/commands/contract/info/shared.rs index 4113f7d5b..0974632ae 100644 --- a/cmd/soroban-cli/src/commands/contract/info/shared.rs +++ b/cmd/soroban-cli/src/commands/contract/info/shared.rs @@ -2,13 +2,13 @@ use std::path::PathBuf; use crate::xdr; use clap::arg; -use soroban_rpc::Client; -use crate::commands::contract::info::shared::Error::InvalidWasmHash; -use crate::config::{locator, network}; -use crate::utils::rpc::get_remote_wasm_from_hash; -use crate::wasm; -use crate::wasm::Error::ContractIsStellarAsset; +use crate::{ + commands::contract::info::shared::Error::InvalidWasmHash, + config::{locator, network}, + utils::rpc::get_remote_wasm_from_hash, + wasm::{self, Error::ContractIsStellarAsset}, +}; #[derive(Debug, clap::Args, Clone, Default)] #[command(group( @@ -71,7 +71,8 @@ pub async fn fetch_wasm(args: &Args) -> Result>, Error> { let hash = xdr::Hash(hash); - let client = Client::new(&network.rpc_url)?; + let client = network.rpc_client()?; + client .verify_network_passphrase(Some(&network.network_passphrase)) .await?; diff --git a/cmd/soroban-cli/src/commands/contract/install.rs b/cmd/soroban-cli/src/commands/contract/install.rs index d51158ec3..cd6e93b24 100644 --- a/cmd/soroban-cli/src/commands/contract/install.rs +++ b/cmd/soroban-cli/src/commands/contract/install.rs @@ -10,15 +10,20 @@ use crate::xdr::{ use clap::{command, Parser}; use super::restore; -use crate::assembled::simulate_and_assemble_transaction; -use crate::commands::txn_result::{TxnEnvelopeResult, TxnResult}; -use crate::commands::{global, NetworkRunnable}; -use crate::config::{self, data, network}; -use crate::key; -use crate::print::Print; -use crate::rpc::{self, Client}; -use crate::tx::builder::{self, TxExt}; -use crate::{utils, wasm}; +use crate::{ + assembled::simulate_and_assemble_transaction, + commands::{ + global, + txn_result::{TxnEnvelopeResult, TxnResult}, + NetworkRunnable, + }, + config::{self, data, network}, + key, + print::Print, + rpc, + tx::builder::{self, TxExt}, + utils, wasm, +}; const CONTRACT_META_SDK_KEY: &str = "rssdkver"; const PUBLIC_NETWORK_PASSPHRASE: &str = "Public Global Stellar Network ; September 2015"; @@ -103,7 +108,7 @@ impl NetworkRunnable for Cmd { let config = config.unwrap_or(&self.config); let contract = self.wasm.read()?; let network = config.get_network()?; - let client = Client::new(&network.rpc_url)?; + let client = network.rpc_client()?; client .verify_network_passphrase(Some(&network.network_passphrase)) .await?; diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index e32d5d5a6..5d43f9bfa 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -7,29 +7,30 @@ use std::{fmt::Debug, fs, io}; use clap::{arg, command, Parser, ValueEnum}; -use crate::xdr::{ - self, AccountEntry, AccountEntryExt, AccountId, ContractEvent, ContractEventType, - DiagnosticEvent, HostFunction, InvokeContractArgs, InvokeHostFunctionOp, LedgerEntryData, - Limits, Memo, MuxedAccount, Operation, OperationBody, Preconditions, PublicKey, ScSpecEntry, - SequenceNumber, String32, StringM, Thresholds, Transaction, TransactionExt, Uint256, VecM, - WriteXdr, -}; - use soroban_rpc::{SimulateHostFunctionResult, SimulateTransactionResponse}; use soroban_spec::read::FromWasmError; use super::super::events; use super::arg_parsing; -use crate::assembled::simulate_and_assemble_transaction; -use crate::commands::contract::arg_parsing::{build_host_function_parameters, output_to_string}; -use crate::commands::txn_result::{TxnEnvelopeResult, TxnResult}; -use crate::commands::NetworkRunnable; -use crate::get_spec::{self, get_remote_contract_spec}; -use crate::print; use crate::{ - commands::global, + assembled::simulate_and_assemble_transaction, + commands::{ + contract::arg_parsing::{build_host_function_parameters, output_to_string}, + global, + txn_result::{TxnEnvelopeResult, TxnResult}, + NetworkRunnable, + }, config::{self, data, locator, network}, - rpc, Pwd, + get_spec::{self, get_remote_contract_spec}, + print, rpc, + xdr::{ + self, AccountEntry, AccountEntryExt, AccountId, ContractEvent, ContractEventType, + DiagnosticEvent, HostFunction, InvokeContractArgs, InvokeHostFunctionOp, LedgerEntryData, + Limits, Memo, MuxedAccount, Operation, OperationBody, Preconditions, PublicKey, + ScSpecEntry, SequenceNumber, String32, StringM, Thresholds, Transaction, TransactionExt, + Uint256, VecM, WriteXdr, + }, + Pwd, }; use soroban_spec_tools::contract; @@ -203,7 +204,7 @@ impl NetworkRunnable for Cmd { // For testing wasm arg parsing let _ = build_host_function_parameters(&contract_id, &self.slop, spec_entries, config)?; } - let client = rpc::Client::new(&network.rpc_url)?; + let client = network.rpc_client()?; let account_details = if self.is_view { default_account_entry() } else { diff --git a/cmd/soroban-cli/src/commands/contract/read.rs b/cmd/soroban-cli/src/commands/contract/read.rs index 4e013a1f4..58efc93b7 100644 --- a/cmd/soroban-cli/src/commands/contract/read.rs +++ b/cmd/soroban-cli/src/commands/contract/read.rs @@ -13,7 +13,7 @@ use crate::{ commands::{global, NetworkRunnable}, config::{self, locator}, key, - rpc::{self, Client, FullLedgerEntries, FullLedgerEntry}, + rpc::{self, FullLedgerEntries, FullLedgerEntry}, }; #[derive(Parser, Debug, Clone)] @@ -181,7 +181,7 @@ impl NetworkRunnable for Cmd { let network = self.config.network.get(&locator)?; tracing::trace!(?network); - let client = Client::new(&network.rpc_url)?; + let client = network.rpc_client()?; let keys = self.key.parse_keys(&locator, &network)?; Ok(client.get_full_ledger_entries(&keys).await?) } diff --git a/cmd/soroban-cli/src/commands/contract/restore.rs b/cmd/soroban-cli/src/commands/contract/restore.rs index 627fd3eee..87a52a9f6 100644 --- a/cmd/soroban-cli/src/commands/contract/restore.rs +++ b/cmd/soroban-cli/src/commands/contract/restore.rs @@ -17,9 +17,7 @@ use crate::{ NetworkRunnable, }, config::{self, data, locator, network}, - key, - rpc::{self, Client}, - wasm, Pwd, + key, rpc, wasm, Pwd, }; #[derive(Parser, Debug, Clone)] @@ -134,7 +132,7 @@ impl NetworkRunnable for Cmd { let network = config.get_network()?; tracing::trace!(?network); let entry_keys = self.key.parse_keys(&config.locator, &network)?; - let client = Client::new(&network.rpc_url)?; + let client = network.rpc_client()?; let source_account = config.source_account()?; // Get the account sequence number diff --git a/cmd/soroban-cli/src/commands/events.rs b/cmd/soroban-cli/src/commands/events.rs index 0c4cfcc0e..a1f5de921 100644 --- a/cmd/soroban-cli/src/commands/events.rs +++ b/cmd/soroban-cli/src/commands/events.rs @@ -4,8 +4,10 @@ use std::io; use crate::xdr::{self, Limits, ReadXdr}; use super::{global, NetworkRunnable}; -use crate::config::{self, locator, network}; -use crate::rpc; +use crate::{ + config::{self, locator, network}, + rpc, +}; #[derive(Parser, Debug, Clone)] #[group(skip)] @@ -207,7 +209,7 @@ impl NetworkRunnable for Cmd { self.network.get(&self.locator) }?; - let client = rpc::Client::new(&network.rpc_url)?; + let client = network.rpc_client()?; client .verify_network_passphrase(Some(&network.network_passphrase)) .await?; diff --git a/cmd/soroban-cli/src/commands/tx/send.rs b/cmd/soroban-cli/src/commands/tx/send.rs index c3856114d..22fbc860a 100644 --- a/cmd/soroban-cli/src/commands/tx/send.rs +++ b/cmd/soroban-cli/src/commands/tx/send.rs @@ -1,8 +1,10 @@ use async_trait::async_trait; use soroban_rpc::GetTransactionResponse; -use crate::commands::{global, NetworkRunnable}; -use crate::config::{self, locator, network}; +use crate::{ + commands::{global, NetworkRunnable}, + config::{self, locator, network}, +}; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -52,7 +54,7 @@ impl NetworkRunnable for Cmd { } else { self.network.get(&self.locator)? }; - let client = crate::rpc::Client::new(&network.rpc_url)?; + let client = network.rpc_client()?; let tx_env = super::xdr::tx_envelope_from_stdin()?; Ok(client.send_transaction_polling(&tx_env).await?) } diff --git a/cmd/soroban-cli/src/commands/tx/simulate.rs b/cmd/soroban-cli/src/commands/tx/simulate.rs index 1f534884d..cb2ed7c69 100644 --- a/cmd/soroban-cli/src/commands/tx/simulate.rs +++ b/cmd/soroban-cli/src/commands/tx/simulate.rs @@ -16,6 +16,8 @@ pub enum Error { Rpc(#[from] crate::rpc::Error), #[error(transparent)] Xdr(#[from] xdr::Error), + #[error(transparent)] + Network(#[from] config::network::Error), } /// Command to simulate a transaction envelope via rpc @@ -50,7 +52,7 @@ impl NetworkRunnable for Cmd { ) -> Result { let config = config.unwrap_or(&self.config); let network = config.get_network()?; - let client = crate::rpc::Client::new(&network.rpc_url)?; + let client = network.rpc_client()?; let tx = super::xdr::unwrap_envelope_v1(super::xdr::tx_envelope_from_stdin()?)?; let tx = simulate_and_assemble_transaction(&client, &tx).await?; Ok(tx) diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index 12f571a50..b961f0f67 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -2,8 +2,6 @@ use address::Address; use clap::{arg, command}; use serde::{Deserialize, Serialize}; -use soroban_rpc::Client; - use crate::{ print::Print, signer::{self, LocalKey, Signer, SignerKind}, @@ -98,7 +96,7 @@ impl Args { ) -> Result, Error> { let network = self.get_network()?; let source_key = self.key_pair()?; - let client = Client::new(&network.rpc_url)?; + let client = network.rpc_client()?; let latest_ledger = client.get_latest_ledger().await?.sequence; let seq_num = latest_ledger + 60; // ~ 5 min Ok(signer::sign_soroban_authorizations( @@ -116,7 +114,7 @@ impl Args { pub async fn next_sequence_number(&self, account_str: &str) -> Result { let network = self.get_network()?; - let client = Client::new(&network.rpc_url)?; + let client = network.rpc_client()?; Ok((client.get_account(account_str).await?.seq_num.0 + 1).into()) } } diff --git a/cmd/soroban-cli/src/config/network.rs b/cmd/soroban-cli/src/config/network.rs index b6f6d8c1d..ac7dc04bc 100644 --- a/cmd/soroban-cli/src/config/network.rs +++ b/cmd/soroban-cli/src/config/network.rs @@ -1,7 +1,11 @@ use clap::arg; +use itertools::Itertools; +use jsonrpsee_http_client::HeaderMap; use phf::phf_map; +use reqwest::header::{HeaderName, HeaderValue, InvalidHeaderName, InvalidHeaderValue}; use serde::{Deserialize, Serialize}; use serde_json::Value; +use std::collections::HashMap; use std::str::FromStr; use stellar_strkey::ed25519::PublicKey; use url::Url; @@ -30,6 +34,12 @@ pub enum Error { InvalidUrl(String), #[error("funding failed: {0}")] FundingFailed(String), + #[error(transparent)] + InvalidHeaderName(#[from] InvalidHeaderName), + #[error(transparent)] + InvalidHeaderValue(#[from] InvalidHeaderValue), + #[error("invalid HTTP header: must be in the form 'key:value'")] + InvalidHeader, } #[derive(Debug, clap::Args, Clone, Default)] @@ -44,6 +54,17 @@ pub struct Args { help_heading = HEADING_RPC, )] pub rpc_url: Option, + /// RPC Header(s) to include in requests to the RPC provider + #[arg( + long = "rpc-header", + env = "STELLAR_RPC_HEADERS", + help_heading = HEADING_RPC, + num_args = 1, + action = clap::ArgAction::Append, + value_delimiter = '\n', + value_parser = parse_http_header, + )] + pub rpc_headers: Vec<(String, String)>, /// Network passphrase to sign the transaction sent to the rpc server #[arg( long = "network-passphrase", @@ -76,6 +97,7 @@ impl Args { { Ok(Network { rpc_url, + rpc_headers: self.rpc_headers.clone(), network_passphrase, }) } else { @@ -94,6 +116,17 @@ pub struct Network { help_heading = HEADING_RPC, )] pub rpc_url: String, + /// Optional header (e.g. API Key) to include in requests to the RPC + #[arg( + long = "rpc-header", + env = "STELLAR_RPC_HEADERS", + help_heading = HEADING_RPC, + num_args = 1, + action = clap::ArgAction::Append, + value_delimiter = '\n', + value_parser = parse_http_header, + )] + pub rpc_headers: Vec<(String, String)>, /// Network passphrase to sign the transaction sent to the rpc server #[arg( long, @@ -103,6 +136,21 @@ pub struct Network { pub network_passphrase: String, } +fn parse_http_header(header: &str) -> Result<(String, String), Error> { + let header_components = header.splitn(2, ':'); + + let (key, value) = header_components + .map(str::trim) + .next_tuple() + .ok_or_else(|| Error::InvalidHeader)?; + + // Check that the headers are properly formatted + HeaderName::from_str(key)?; + HeaderValue::from_str(value)?; + + Ok((key.to_string(), value.to_string())) +} + impl Network { pub async fn helper_url(&self, addr: &str) -> Result { tracing::debug!("address {addr:?}"); @@ -160,6 +208,19 @@ impl Network { pub fn rpc_uri(&self) -> Result { Url::from_str(&self.rpc_url).map_err(|_| Error::InvalidUrl(self.rpc_url.to_string())) } + + pub fn rpc_client(&self) -> Result { + let mut header_hash_map = HashMap::new(); + for (header_name, header_value) in &self.rpc_headers { + header_hash_map.insert(header_name.to_string(), header_value.to_string()); + } + + let header_map: HeaderMap = (&header_hash_map) + .try_into() + .map_err(|_| Error::InvalidHeader)?; + + Ok(rpc::Client::new_with_headers(&self.rpc_url, header_map)?) + } } pub static DEFAULTS: phf::Map<&'static str, (&'static str, &'static str)> = phf_map! { @@ -186,6 +247,7 @@ impl From<&(&str, &str)> for Network { fn from(n: &(&str, &str)) -> Self { Self { rpc_url: n.0.to_string(), + rpc_headers: Vec::new(), network_passphrase: n.1.to_string(), } } @@ -197,11 +259,15 @@ mod tests { use mockito::Server; use serde_json::json; + const INVALID_HEADER_NAME: &str = "api key"; + const INVALID_HEADER_VALUE: &str = "cannot include a carriage return \r in the value"; + #[tokio::test] async fn test_helper_url_local_network() { let network = Network { rpc_url: "http://localhost:8000".to_string(), network_passphrase: passphrase::LOCAL.to_string(), + rpc_headers: Vec::new(), }; let result = network @@ -239,6 +305,7 @@ mod tests { let network = Network { rpc_url: server.url(), network_passphrase: passphrase::TESTNET.to_string(), + rpc_headers: Vec::new(), }; let url = network .helper_url("GBZXN7PIRZGNMHGA7MUUUF4GWPY5AYPV6LY4UV2GL6VJGIQRXFDNMADI") @@ -269,6 +336,7 @@ mod tests { let network = Network { rpc_url: server.url(), network_passphrase: passphrase::TESTNET.to_string(), + rpc_headers: Vec::new(), }; let url = network .helper_url("GBZXN7PIRZGNMHGA7MUUUF4GWPY5AYPV6LY4UV2GL6VJGIQRXFDNMADI") @@ -276,4 +344,107 @@ mod tests { .unwrap(); assert_eq!(url.as_str(), "https://friendbot.stellar.org/secret?api_key=123456&user=demo&addr=GBZXN7PIRZGNMHGA7MUUUF4GWPY5AYPV6LY4UV2GL6VJGIQRXFDNMADI"); } + + // testing parse_header function + #[tokio::test] + async fn test_parse_http_header_ok() { + let result = parse_http_header("Authorization: Bearer 1234"); + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_parse_http_header_error_with_invalid_name() { + let invalid_header = format!("{INVALID_HEADER_NAME}: Bearer 1234"); + let result = parse_http_header(&invalid_header); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().to_string(), + format!("invalid HTTP header name") + ); + } + + #[tokio::test] + async fn test_parse_http_header_error_with_invalid_value() { + let invalid_header = format!("Authorization: {INVALID_HEADER_VALUE}"); + let result = parse_http_header(&invalid_header); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().to_string(), + format!("failed to parse header value") + ); + } + + // testing rpc_client function - we're testing this and the parse_http_header function separately because when a user has their network already configured in a toml file, the parse_http_header function is not called and we want to make sure that if the toml file is correctly formatted, the rpc_client function will work as expected + + #[tokio::test] + async fn test_rpc_client_is_ok_when_there_are_no_headers() { + let network = Network { + rpc_url: "http://localhost:1234".to_string(), + network_passphrase: "Network passphrase".to_string(), + rpc_headers: [].to_vec(), + }; + + let result = network.rpc_client(); + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_rpc_client_is_ok_with_correctly_formatted_headers() { + let network = Network { + rpc_url: "http://localhost:1234".to_string(), + network_passphrase: "Network passphrase".to_string(), + rpc_headers: [("Authorization".to_string(), "Bearer 1234".to_string())].to_vec(), + }; + + let result = network.rpc_client(); + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_rpc_client_is_ok_with_multiple_headers() { + let network = Network { + rpc_url: "http://localhost:1234".to_string(), + network_passphrase: "Network passphrase".to_string(), + rpc_headers: [ + ("Authorization".to_string(), "Bearer 1234".to_string()), + ("api-key".to_string(), "5678".to_string()), + ] + .to_vec(), + }; + + let result = network.rpc_client(); + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_rpc_client_returns_err_with_invalid_header_name() { + let network = Network { + rpc_url: "http://localhost:8000".to_string(), + network_passphrase: passphrase::LOCAL.to_string(), + rpc_headers: [(INVALID_HEADER_NAME.to_string(), "Bearer".to_string())].to_vec(), + }; + + let result = network.rpc_client(); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().to_string(), + format!("invalid HTTP header: must be in the form 'key:value'") + ); + } + + #[tokio::test] + async fn test_rpc_client_returns_err_with_invalid_header_value() { + let network = Network { + rpc_url: "http://localhost:8000".to_string(), + network_passphrase: passphrase::LOCAL.to_string(), + rpc_headers: [("api-key".to_string(), INVALID_HEADER_VALUE.to_string())].to_vec(), + }; + + let result = network.rpc_client(); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().to_string(), + format!("invalid HTTP header: must be in the form 'key:value'") + ); + } } diff --git a/cmd/soroban-cli/src/wasm.rs b/cmd/soroban-cli/src/wasm.rs index bc202a4b3..5f0ed3b5c 100644 --- a/cmd/soroban-cli/src/wasm.rs +++ b/cmd/soroban-cli/src/wasm.rs @@ -1,9 +1,6 @@ -use crate::config::locator; -use crate::config::network::Network; use crate::xdr::{self, Hash, LedgerKey, LedgerKeyContractCode}; use clap::arg; use sha2::{Digest, Sha256}; -use soroban_rpc::Client; use soroban_spec_tools::contract::{self, Spec}; use std::{ fs, io, @@ -11,10 +8,14 @@ use std::{ }; use stellar_xdr::curr::{ContractDataEntry, ContractExecutable, ScVal}; -use crate::utils::rpc::get_remote_wasm_from_hash; -use crate::utils::{self}; - -use crate::wasm::Error::{ContractIsStellarAsset, UnexpectedContractToken}; +use crate::{ + config::{ + locator, + network::{Error as NetworkError, Network}, + }, + utils::{self, rpc::get_remote_wasm_from_hash}, + wasm::Error::{ContractIsStellarAsset, UnexpectedContractToken}, +}; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -47,6 +48,8 @@ pub enum Error { a network built-in asset contract that does not have a downloadable code binary" )] ContractIsStellarAsset, + #[error(transparent)] + Network(#[from] NetworkError), } #[derive(Debug, clap::Args, Clone)] @@ -128,7 +131,7 @@ pub async fn fetch_from_contract( .resolve_contract_id(contract_id, &network.network_passphrase)? .0; - let client = Client::new(&network.rpc_url)?; + let client = network.rpc_client()?; client .verify_network_passphrase(Some(&network.network_passphrase)) .await?; From d538ea9a1b5bbf1847a987ccf316d34e55465313 Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Tue, 15 Oct 2024 09:21:01 +1000 Subject: [PATCH 35/55] Add GitHub Action for installing stellar-cli in repos (#1669) --- action.yml | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 action.yml diff --git a/action.yml b/action.yml new file mode 100644 index 000000000..3ed6af8e7 --- /dev/null +++ b/action.yml @@ -0,0 +1,47 @@ +name: 'Install stellar-cli' +description: 'Install the stellar-cli' +inputs: + version: + description: | + Recommended for use only in testing new versions of the action prior to + release. For regular use, use the version of the action corresponding to + the version of the stellar-cli that should be installed. + required: false +runs: + using: "composite" + steps: + - name: Setup install path + shell: bash + run: | + mkdir -p $HOME/.local/bin + echo "$HOME/.local/bin" >> $GITHUB_PATH + - name: Copy binary to install location + shell: bash + env: + REF: ${{ github.action_ref }} + run: | + version="${{ inputs.version || env.REF }}" + case "${{ runner.os }}-${{ runner.arch }}" in + 'Linux-X64') + os_arch=x86_64-unknown-linux-gnu + ;; + 'Linux-ARM64') + os_arch=aarch64-unknown-linux-gnu + ;; + 'macOS-X64') + os_arch=x86_64-apple-darwin + ;; + 'macOS-ARM64') + os_arch=aarch64-apple-darwin + ;; + 'Windows-X64') + os_arch=x86_64-pc-windows-msvc + ;; + *) + echo "Unsupported OS / Arch pair: ${{ runner.os }} ${{ runner.arch }}" >&2 + exit 1 + esac + file="stellar-cli-$version-$os_arch.tar.gz" + url="https://github.com/stellar/stellar-cli/releases/download/v$version/$file" + echo "$url" + curl -fL "$url" | tar xvz -C $HOME/.local/bin From 18adf2b620e55bd29c3615afffa8a4b0fff0695f Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:24:26 +1000 Subject: [PATCH 36/55] Add attestation of binaries uploaded to release (#1670) --- .github/workflows/binaries.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 42ddb786a..e3fd14926 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -62,6 +62,11 @@ jobs: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc working-directory: ${{ env.BUILD_WORKING_DIR }} run: cargo build --target-dir="$GITHUB_WORKSPACE/target" --package ${{ matrix.crate.name }} --features opt --release --target ${{ matrix.sys.target }} + - name: Build provenance for attestation (release only) + if: github.event_name == 'release' + uses: actions/attest-build-provenance@v1 + with: + subject-path: target/${{ matrix.sys.target }}/release/${{ matrix.crate.binary }}${{ matrix.sys.ext }} - name: Compress run: | cd target/${{ matrix.sys.target }}/release From 5dbc0a7e3559c14b80b21631f93a1bb16b6180b7 Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Wed, 16 Oct 2024 00:16:40 +1000 Subject: [PATCH 37/55] Fix attestation during binary release (#1674) --- .github/workflows/binaries.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index e3fd14926..08641b9ad 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -13,6 +13,11 @@ defaults: run: shell: bash +permissions: + id-token: write + contents: write + attestations: write + jobs: build: From 4b2738e96f955904a9cf815e0a0bb947bf8201eb Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Wed, 16 Oct 2024 09:22:13 +1000 Subject: [PATCH 38/55] Use attestation in install action (#1675) --- action.yml | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/action.yml b/action.yml index 3ed6af8e7..9b1426d94 100644 --- a/action.yml +++ b/action.yml @@ -15,12 +15,15 @@ runs: run: | mkdir -p $HOME/.local/bin echo "$HOME/.local/bin" >> $GITHUB_PATH + - name: Determine version to install + id: version + shell: bash + run: | + echo "version=${{ inputs.version || github.action_ref }}" >> "$GITHUB_OUTPUT" - name: Copy binary to install location shell: bash - env: - REF: ${{ github.action_ref }} run: | - version="${{ inputs.version || env.REF }}" + version="${{ steps.version.outputs.version }}" case "${{ runner.os }}-${{ runner.arch }}" in 'Linux-X64') os_arch=x86_64-unknown-linux-gnu @@ -45,3 +48,17 @@ runs: url="https://github.com/stellar/stellar-cli/releases/download/v$version/$file" echo "$url" curl -fL "$url" | tar xvz -C $HOME/.local/bin + - name: Verify binary against attestation + shell: bash + env: + GH_TOKEN: ${{ github.token }} + run: | + version="${{ steps.version.outputs.version }}" + subject="$(gh attestation verify ~/.local/bin/stellar --repo stellar/stellar-cli --format json -q '.[].verificationResult.signature.certificate.subjectAlternativeName')" + echo "Found subject: $subject" >&2 + expected_subject="https://github.com/stellar/stellar-cli/.github/workflows/binaries.yml@refs/tags/v$version" + echo "Expected subject: $expected_subject" >&2 + if [[ "$subject" != "$expected_subject" ]]; then + echo "Attestation verification found unexpected subject" >&2 + exit 1 + fi From 8b58676d48b71099ccfd7a6efd030d70596ec4ae Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Wed, 16 Oct 2024 17:42:14 +1000 Subject: [PATCH 39/55] Update install instructions to match stellar-docs (#1679) * Update install instructions to match stellar-docs * undo --- README.md | 24 +++++------------------- cmd/stellar-cli/README.md | 7 ++++--- 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index c39d883c2..8fadb6740 100644 --- a/README.md +++ b/README.md @@ -10,16 +10,13 @@ This repo is home to the Stellar CLI, the command-line multi-tool for running an - [Documentation](#documentation) - [Cookbook](#cookbook) -- [Installation](#installation) -- [Installation with Experimental Features](#installation-with-experimental-features) +- [Install](#install) - [Autocomplete](#autocomplete) - [Latest Release](#latest-release) - [Upcoming Features](#upcoming-features) - [To Contribute](#to-contribute) - [Additional Developer Resources](#additional-developer-resources) - - ## Documentation For installation options see below, for usage instructions [see the full help docs](FULL_HELP_DOCS.md). @@ -27,28 +24,17 @@ For installation options see below, for usage instructions [see the full help do ## Cookbook To understand how to get the most of the Stellar CLI, see the [Stellar CLI Cookbook](https://github.com/stellar/stellar-cli/tree/main/cookbook) for recipes and a collection of resources to teach you how to use the CLI. Examples of recipes included in the CLI cookbook include: send payments, manage contract lifecycle, extend contract instance/storage/wasm, and more. -## Installation -Install the latest version from source: -``` -cargo install --locked stellar-cli --features opt -``` +## Install -Install with `cargo-binstall`: -``` -cargo install --locked cargo-binstall -cargo binstall -y stellar-cli -``` - -Install with Homebrew: +Install with Homebrew (macOS, Linux): ``` brew install stellar-cli ``` -## Installation with Experimental Features -To use the potentially unreleased bleeding edge CLI functionalities, install from git: +Install the latest version from source: ``` -cargo install --locked stellar-cli --features opt --git https://github.com/stellar/stellar-cli.git +cargo install --locked stellar-cli --features opt ``` ## Autocomplete diff --git a/cmd/stellar-cli/README.md b/cmd/stellar-cli/README.md index 9b95e474a..48be33da7 100644 --- a/cmd/stellar-cli/README.md +++ b/cmd/stellar-cli/README.md @@ -6,12 +6,13 @@ Docs: https://developers.stellar.org ## Install +Install with Homebrew (macOS, Linux): + ``` -cargo install --locked stellar-cli +brew install stellar-cli ``` -To install with the `opt` feature, which includes a WASM optimization feature and wasm-opt built in: - +Install the latest version from source: ``` cargo install --locked stellar-cli --features opt ``` From 3f06e1dd23fd1b892dc1d1fac2cd406dee4fabf4 Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Fri, 18 Oct 2024 14:18:11 +1000 Subject: [PATCH 40/55] Remove the `soroban` binary from prebuilt binaries in the release (#1680) --- .github/workflows/binaries.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 08641b9ad..f2f53bd78 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -27,8 +27,6 @@ jobs: crate: - name: stellar-cli binary: stellar - - name: soroban-cli - binary: soroban sys: - os: ubuntu-20.04 # Use 20.04 to get an older version of glibc for increased compat target: x86_64-unknown-linux-gnu From fb6be3131eded3d90f308d0cffd1263dcdb74a56 Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Tue, 22 Oct 2024 13:27:08 -0700 Subject: [PATCH 41/55] Add support for plugins using stellar prefix. (#1683) --- Cargo.lock | 4 +++ Cargo.toml | 3 +- Makefile | 1 + .../tests/fixtures/bye/Cargo.lock | 7 ++++ .../tests/fixtures/bye/Cargo.toml | 9 +++++ .../tests/fixtures/bye/src/main.rs | 3 ++ cmd/crates/soroban-test/tests/it/plugin.rs | 34 +++++++++++++------ cmd/soroban-cli/src/commands/plugin.rs | 23 ++++++++++--- 8 files changed, 68 insertions(+), 16 deletions(-) create mode 100644 cmd/crates/soroban-test/tests/fixtures/bye/Cargo.lock create mode 100644 cmd/crates/soroban-test/tests/fixtures/bye/Cargo.toml create mode 100644 cmd/crates/soroban-test/tests/fixtures/bye/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 55ebf62a2..e3916c5f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4974,6 +4974,10 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "stellar-bye" +version = "21.5.0" + [[package]] name = "stellar-cli" version = "21.5.0" diff --git a/Cargo.toml b/Cargo.toml index ef052c569..8b10f373d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,9 +6,10 @@ members = [ "cmd/crates/*", "cmd/crates/soroban-test/tests/fixtures/test-wasms/*", "cmd/crates/soroban-test/tests/fixtures/hello", + "cmd/crates/soroban-test/tests/fixtures/bye", ] default-members = ["cmd/soroban-cli", "cmd/crates/soroban-spec-tools", "cmd/crates/soroban-test"] -exclude = ["cmd/crates/soroban-test/tests/fixtures/hello"] +exclude = ["cmd/crates/soroban-test/tests/fixtures/hello", "cmd/crates/soroban-test/tests/fixtures/bye"] [workspace.package] version = "21.5.0" diff --git a/Makefile b/Makefile index 4db0946ce..8a1333bd9 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,7 @@ install_rust: install install: cargo install --force --locked --path ./cmd/stellar-cli --debug cargo install --force --locked --path ./cmd/crates/soroban-test/tests/fixtures/hello --root ./target --debug --quiet + cargo install --force --locked --path ./cmd/crates/soroban-test/tests/fixtures/bye --root ./target --debug --quiet # regenerate the example lib in `cmd/crates/soroban-spec-typsecript/fixtures/ts` build-snapshot: typescript-bindings-fixtures diff --git a/cmd/crates/soroban-test/tests/fixtures/bye/Cargo.lock b/cmd/crates/soroban-test/tests/fixtures/bye/Cargo.lock new file mode 100644 index 000000000..59924f294 --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/bye/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "stellar-bye" +version = "0.1.0" diff --git a/cmd/crates/soroban-test/tests/fixtures/bye/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/bye/Cargo.toml new file mode 100644 index 000000000..7678eee5e --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/bye/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "stellar-bye" +version = "21.5.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/cmd/crates/soroban-test/tests/fixtures/bye/src/main.rs b/cmd/crates/soroban-test/tests/fixtures/bye/src/main.rs new file mode 100644 index 000000000..5fb678863 --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/bye/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Bye, world!"); +} diff --git a/cmd/crates/soroban-test/tests/it/plugin.rs b/cmd/crates/soroban-test/tests/it/plugin.rs index 450902d65..5e5eabee9 100644 --- a/cmd/crates/soroban-test/tests/it/plugin.rs +++ b/cmd/crates/soroban-test/tests/it/plugin.rs @@ -1,7 +1,7 @@ /* -This function calls the soroban executable via cargo and checks that the output +This function calls the stellar executable via cargo and checks that the output is correct. The PATH environment variable is set to include the target/bin -directory, so that the soroban executable can be found. +directory, so that the stellar executable can be found. */ use std::{ffi::OsString, path::PathBuf}; @@ -11,7 +11,7 @@ fn soroban_hello() { // Add the target/bin directory to the iterator of paths let paths = get_paths(); // Call soroban with the PATH variable set to include the target/bin directory - assert_cmd::Command::cargo_bin("soroban") + assert_cmd::Command::cargo_bin("stellar") .unwrap_or_else(|_| assert_cmd::Command::new("soroban")) .arg("hello") .env("PATH", &paths) @@ -19,23 +19,37 @@ fn soroban_hello() { .stdout("Hello, world!\n"); } +#[test] +fn stellar_bye() { + // Add the target/bin directory to the iterator of paths + let paths = get_paths(); + // Call soroban with the PATH variable set to include the target/bin directory + assert_cmd::Command::cargo_bin("stellar") + .unwrap_or_else(|_| assert_cmd::Command::new("stellar")) + .arg("bye") + .env("PATH", &paths) + .assert() + .stdout("Bye, world!\n"); +} + #[test] fn list() { // Call `soroban --list` with the PATH variable set to include the target/bin directory - assert_cmd::Command::cargo_bin("soroban") - .unwrap_or_else(|_| assert_cmd::Command::new("soroban")) + assert_cmd::Command::cargo_bin("stellar") + .unwrap_or_else(|_| assert_cmd::Command::new("stellar")) .arg("--list") .env("PATH", get_paths()) .assert() - .stdout(predicates::str::contains("hello")); + .stdout(predicates::str::contains("hello")) + .stdout(predicates::str::contains("bye")); } #[test] #[cfg(not(unix))] fn has_no_path() { // Call soroban with the PATH variable set to include just target/bin directory - assert_cmd::Command::cargo_bin("soroban") - .unwrap_or_else(|_| assert_cmd::Command::new("soroban")) + assert_cmd::Command::cargo_bin("stellar") + .unwrap_or_else(|_| assert_cmd::Command::new("stellar")) .arg("hello") .env("PATH", target_bin()) .assert() @@ -45,8 +59,8 @@ fn has_no_path() { #[test] fn has_no_path_failure() { // Call soroban with the PATH variable set to include just target/bin directory - assert_cmd::Command::cargo_bin("soroban") - .unwrap_or_else(|_| assert_cmd::Command::new("soroban")) + assert_cmd::Command::cargo_bin("stellar") + .unwrap_or_else(|_| assert_cmd::Command::new("stellar")) .arg("hello") .assert() .stderr(predicates::str::contains("error: no such command: `hello`")); diff --git a/cmd/soroban-cli/src/commands/plugin.rs b/cmd/soroban-cli/src/commands/plugin.rs index 2fa028d5c..d933564f2 100644 --- a/cmd/soroban-cli/src/commands/plugin.rs +++ b/cmd/soroban-cli/src/commands/plugin.rs @@ -1,4 +1,4 @@ -use std::process::Command; +use std::{path::PathBuf, process::Command}; use clap::CommandFactory; use which::which; @@ -44,7 +44,7 @@ pub fn run() -> Result<(), Error> { return Ok(()); } - let bin = which(format!("soroban-{name}")).map_err(|_| { + let bin = find_bin(&name).map_err(|_| { let suggestion = if let Ok(bins) = list() { let suggested_name = bins .iter() @@ -53,6 +53,7 @@ pub fn run() -> Result<(), Error> { .min_by(|a, b| a.1.total_cmp(&b.1)) .map(|(a, _)| a.to_string()) .unwrap_or_default(); + if suggested_name.is_empty() { suggested_name } else { @@ -64,8 +65,10 @@ pub fn run() -> Result<(), Error> { } else { String::new() }; + Error::ExecutableNotFound(name, suggestion) })?; + std::process::exit( Command::new(bin) .args(args) @@ -78,19 +81,29 @@ pub fn run() -> Result<(), Error> { const MAX_HEX_LENGTH: usize = 10; +fn find_bin(name: &str) -> Result { + if let Ok(path) = which(format!("stellar-{name}")) { + Ok(path) + } else { + which(format!("soroban-{name}")) + } +} + pub fn list() -> Result, Error> { let re_str = if cfg!(target_os = "windows") { - r"^soroban-.*.exe$" + r"^(soroban|stellar)-.*.exe$" } else { - r"^soroban-.*" + r"^(soroban|stellar)-.*" }; + let re = regex::Regex::new(re_str)?; + Ok(which::which_re(re)? .filter_map(|b| { let s = b.file_name()?.to_str()?; Some(s.strip_suffix(".exe").unwrap_or(s).to_string()) }) .filter(|s| !(utils::is_hex_string(s) && s.len() > MAX_HEX_LENGTH)) - .map(|s| s.replace("soroban-", "")) + .map(|s| s.replace("soroban-", "").replace("stellar-", "")) .collect()) } From d16fcf1d6dff3d80a1135bce69f33fbb9c10f47b Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Thu, 24 Oct 2024 14:26:32 -0700 Subject: [PATCH 42/55] Favor stellar config dir. (#1681) --- cmd/crates/soroban-test/tests/it/config.rs | 52 +++++++++++++++++++ .../tests/it/integration/hello_world.rs | 4 +- cmd/soroban-cli/src/cli.rs | 6 ++- cmd/soroban-cli/src/config/locator.rs | 24 +++++++-- cmd/soroban-cli/src/print.rs | 1 + cmd/soroban-cli/src/utils.rs | 2 + .../contract-workspace-template/.gitignore | 3 +- 7 files changed, 84 insertions(+), 8 deletions(-) diff --git a/cmd/crates/soroban-test/tests/it/config.rs b/cmd/crates/soroban-test/tests/it/config.rs index 70dfaa693..31d60e116 100644 --- a/cmd/crates/soroban-test/tests/it/config.rs +++ b/cmd/crates/soroban-test/tests/it/config.rs @@ -1,4 +1,5 @@ use assert_fs::TempDir; +use predicates::prelude::predicate; use soroban_test::{AssertExt, TestEnv}; use std::{fs, path::Path}; @@ -288,3 +289,54 @@ fn use_env() { .success() .stdout("SDIY6AQQ75WMD4W46EYB7O6UYMHOCGQHLAQGQTKHDX4J2DYQCHVCQYFD\n"); } + +#[test] +fn config_dirs_precedence() { + let sandbox = TestEnv::default(); + + sandbox + .new_assert_cmd("keys") + .env( + "SOROBAN_SECRET_KEY", + "SC4ZPYELVR7S7EE7KZDZN3ETFTNQHHLTUL34NUAAWZG5OK2RGJ4V2U3Z", + ) + .arg("add") + .arg("alice") + .assert() + .success(); + + fs::rename( + sandbox.dir().join(".stellar"), + sandbox.dir().join("_soroban"), + ) + .unwrap(); + + sandbox + .new_assert_cmd("keys") + .env( + "SOROBAN_SECRET_KEY", + "SAQMV6P3OWM2SKCK3OEWNXSRYWK5RNNUL5CPHQGIJF2WVT4EI2BZ63GG", + ) + .arg("add") + .arg("alice") + .assert() + .success(); + + fs::rename( + sandbox.dir().join("_soroban"), + sandbox.dir().join(".soroban"), + ) + .unwrap(); + + sandbox + .new_assert_cmd("keys") + .arg("show") + .arg("alice") + .arg("--verbose") + .assert() + .success() + .stderr(predicate::str::contains( + "WARN soroban_cli::utils: the .stellar and .soroban config directories exist at path", + )) + .stdout("SAQMV6P3OWM2SKCK3OEWNXSRYWK5RNNUL5CPHQGIJF2WVT4EI2BZ63GG\n"); +} diff --git a/cmd/crates/soroban-test/tests/it/integration/hello_world.rs b/cmd/crates/soroban-test/tests/it/integration/hello_world.rs index 767c9a07c..272587c53 100644 --- a/cmd/crates/soroban-test/tests/it/integration/hello_world.rs +++ b/cmd/crates/soroban-test/tests/it/integration/hello_world.rs @@ -74,7 +74,7 @@ async fn invoke() { .assert() .stdout_as_str(); let dir = sandbox.dir(); - let seed_phrase = std::fs::read_to_string(dir.join(".soroban/identity/test.toml")).unwrap(); + let seed_phrase = std::fs::read_to_string(dir.join(".stellar/identity/test.toml")).unwrap(); let s = toml::from_str::(&seed_phrase).unwrap(); let secret::Secret::SeedPhrase { seed_phrase } = s else { panic!("Expected seed phrase") @@ -113,7 +113,7 @@ async fn invoke() { }, ) .unwrap(); - let sk_from_file = std::fs::read_to_string(dir.join(".soroban/identity/testone.toml")).unwrap(); + let sk_from_file = std::fs::read_to_string(dir.join(".stellar/identity/testone.toml")).unwrap(); assert_eq!(sk_from_file, format!("secret_key = \"{secret_key_1}\"\n")); let secret_key_1_readin = sandbox diff --git a/cmd/soroban-cli/src/cli.rs b/cmd/soroban-cli/src/cli.rs index 5470562db..9183d0e30 100644 --- a/cmd/soroban-cli/src/cli.rs +++ b/cmd/soroban-cli/src/cli.rs @@ -2,8 +2,9 @@ use clap::CommandFactory; use dotenvy::dotenv; use tracing_subscriber::{fmt, EnvFilter}; +use crate::print::Print; use crate::upgrade_check::upgrade_check; -use crate::{commands, print, Root}; +use crate::{commands, Root}; #[tokio::main] pub async fn main() { @@ -42,6 +43,7 @@ pub async fn main() { std::process::exit(1); } }); + // Now use root to setup the logger if let Some(level) = root.global_args.log_level() { let mut e_filter = EnvFilter::from_default_env() @@ -78,7 +80,7 @@ pub async fn main() { upgrade_check(root.global_args.quiet).await; }); - let printer = print::Print::new(root.global_args.quiet); + let printer = Print::new(root.global_args.quiet); if let Err(e) = root.run().await { printer.errorln(format!("error: {e}")); std::process::exit(1); diff --git a/cmd/soroban-cli/src/config/locator.rs b/cmd/soroban-cli/src/config/locator.rs index 2fad2bb62..70b4f75f8 100644 --- a/cmd/soroban-cli/src/config/locator.rs +++ b/cmd/soroban-cli/src/config/locator.rs @@ -145,7 +145,7 @@ impl Args { pub fn local_config(&self) -> Result { let pwd = self.current_dir()?; - Ok(find_config_dir(pwd.clone()).unwrap_or_else(|_| pwd.join(".soroban"))) + Ok(find_config_dir(pwd.clone()).unwrap_or_else(|_| pwd.join(".stellar"))) } pub fn current_dir(&self) -> Result { @@ -468,14 +468,32 @@ impl KeyType { } pub fn global_config_path() -> Result { - Ok(if let Ok(config_home) = std::env::var("XDG_CONFIG_HOME") { + let config_dir = if let Ok(config_home) = std::env::var("XDG_CONFIG_HOME") { PathBuf::from_str(&config_home).map_err(|_| Error::XdgConfigHome(config_home))? } else { dirs::home_dir() .ok_or(Error::HomeDirNotFound)? .join(".config") + }; + + let soroban_dir = config_dir.join("soroban"); + let stellar_dir = config_dir.join("stellar"); + let soroban_exists = soroban_dir.exists(); + let stellar_exists = stellar_dir.exists(); + + if stellar_exists && soroban_exists { + tracing::warn!("the .stellar and .soroban config directories exist at path {config_dir:?}, using the .stellar"); + } + + if stellar_exists { + return Ok(stellar_dir); } - .join("soroban")) + + if soroban_exists { + return Ok(soroban_dir); + } + + Ok(stellar_dir) } impl Pwd for Args { diff --git a/cmd/soroban-cli/src/print.rs b/cmd/soroban-cli/src/print.rs index 5b8ca2bd2..ca66cdc50 100644 --- a/cmd/soroban-cli/src/print.rs +++ b/cmd/soroban-cli/src/print.rs @@ -8,6 +8,7 @@ use crate::{ const TERMS: &[&str] = &["Apple_Terminal", "vscode"]; +#[derive(Clone)] pub struct Print { pub quiet: bool, } diff --git a/cmd/soroban-cli/src/utils.rs b/cmd/soroban-cli/src/utils.rs index 6e87d15a6..0c4207a4e 100644 --- a/cmd/soroban-cli/src/utils.rs +++ b/cmd/soroban-cli/src/utils.rs @@ -95,10 +95,12 @@ pub fn find_config_dir(mut pwd: std::path::PathBuf) -> std::io::Result Date: Mon, 28 Oct 2024 17:14:51 -0400 Subject: [PATCH 43/55] fix: default value for starting balance in create_account (#1688) The default value parser failed when the string contained underscores. --- FULL_HELP_DOCS.md | 2 +- cmd/soroban-cli/src/commands/tx/new/create_account.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index 41575aea0..b3724b98b 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -1517,7 +1517,7 @@ Creates and funds a new account with the specified starting balance * `--destination ` — Account Id to create, e.g. `GBX...` * `--starting-balance ` — Initial balance in stroops of the account, default 1 XLM - Default value: `10_000_000` + Default value: `10000000` diff --git a/cmd/soroban-cli/src/commands/tx/new/create_account.rs b/cmd/soroban-cli/src/commands/tx/new/create_account.rs index 967c0cf43..9cc3a62ff 100644 --- a/cmd/soroban-cli/src/commands/tx/new/create_account.rs +++ b/cmd/soroban-cli/src/commands/tx/new/create_account.rs @@ -11,7 +11,7 @@ pub struct Cmd { #[arg(long)] pub destination: xdr::AccountId, /// Initial balance in stroops of the account, default 1 XLM - #[arg(long, default_value = "10_000_000")] + #[arg(long, default_value = "10000000")] pub starting_balance: i64, } From 1c3f4d274265180217aa348bca2f54e7cbf78cbd Mon Sep 17 00:00:00 2001 From: Gleb Date: Tue, 29 Oct 2024 14:48:45 -0700 Subject: [PATCH 44/55] Enable unit tests for all platforms (#1689) * Test * Comment out flows * rust workflow * fix * fix * Update dependencies and uncomment arm linux * publish dry run test * cleanup * Comment ARM back * undo diff * PR comments --------- Co-authored-by: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> --- .github/workflows/rust.yml | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 048015009..9ae750a77 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -44,7 +44,7 @@ jobs: fail-fast: false matrix: rust: [msrv, latest] - include: + sys: - os: ubuntu-latest-16-cores target: x86_64-unknown-linux-gnu - os: ubuntu-latest-16-cores @@ -55,7 +55,7 @@ jobs: target: aarch64-apple-darwin - os: windows-latest-8-cores target: x86_64-pc-windows-msvc - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.sys.os }} env: CI_TESTS: true steps: @@ -74,17 +74,18 @@ jobs: run: echo RUSTFLAGS='-Dwarnings -Dclippy::all -Dclippy::pedantic' >> $GITHUB_ENV - run: rustup update - run: cargo version - - run: rustup target add ${{ matrix.target }} + - run: rustup target add ${{ matrix.sys.target }} - run: rustup target add wasm32-unknown-unknown - - if: matrix.target == 'aarch64-unknown-linux-gnu' - run: sudo apt-get update && sudo apt-get -y install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu - - run: cargo clippy --all-targets --target ${{ matrix.target }} + - if: runner.os == 'Linux' + run: sudo apt-get update && sudo apt-get -y install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libudev-dev + - run: cargo clippy --all-targets --target ${{ matrix.sys.target }} - run: make build-test - - if: startsWith(matrix.target, 'x86_64') + # TODO: enable ARM linux + - if: startsWith(matrix.sys.target, 'x86_64') || runner.os == 'macOS' # specify directories explicitly (otherwise it will fail with missing symbols) run: | for I in cmd/soroban-cli cmd/crates/* cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world ; do - cargo test --target ${{ matrix.target }} --manifest-path $I/Cargo.toml + cargo test --target ${{ matrix.sys.target }} --manifest-path $I/Cargo.toml done publish-dry-run: @@ -97,10 +98,11 @@ jobs: target: x86_64-unknown-linux-gnu cargo-hack-feature-options: --feature-powerset additional-deb-packages: libudev-dev - # - os: ubuntu-latest-16-cores - # target: aarch64-unknown-linux-gnu - # cargo-hack-feature-options: --feature-powerset - # additional-deb-packages: libudev-dev libssl-dev + # TODO: add back ARM support + #- os: ubuntu-latest-16-cores + # target: aarch64-unknown-linux-gnu + # cargo-hack-feature-options: --feature-powerset + # additional-deb-packages: libudev-dev libssl-dev - os: macos-latest target: x86_64-apple-darwin cargo-hack-feature-options: --feature-powerset From 1fb8559106640a67cfeb2a782e455e25c22ec44e Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:34:39 -0400 Subject: [PATCH 45/55] Feat/add meta on build (#1684) * Refactor to use wasmgen and not re-parse entire wasm file when adding new meta * Add metadata arg to build command --------- Co-authored-by: Willem Wyndham Co-authored-by: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> --- Cargo.lock | 71 ++++++++++++------- FULL_HELP_DOCS.md | 1 + cmd/crates/soroban-spec-tools/src/contract.rs | 20 ++++-- .../workspace/contracts/add/src/lib.rs | 4 +- cmd/crates/soroban-test/tests/it/build.rs | 34 +++++++++ cmd/soroban-cli/Cargo.toml | 1 + .../src/commands/contract/build.rs | 71 ++++++++++++++++--- 7 files changed, 161 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e3916c5f9..ac115d89a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -727,6 +727,12 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "byteorder" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" + [[package]] name = "byteorder" version = "1.5.0" @@ -1084,7 +1090,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" dependencies = [ - "byteorder", + "byteorder 1.5.0", "digest 0.9.0", "rand_core 0.5.1", "subtle", @@ -2692,6 +2698,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + [[package]] name = "ledger-apdu" version = "0.10.0" @@ -2719,7 +2731,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45ba81a1f5f24396b37211478aff7fbcd605dd4544df8dbed07b9da3c2057aee" dependencies = [ - "byteorder", + "byteorder 1.5.0", "cfg-if", "hex", "hidapi", @@ -4507,6 +4519,7 @@ dependencies = [ "ulid", "url", "walkdir", + "wasm-gen", "wasm-opt", "wasmparser 0.90.0", "which", @@ -4674,9 +4687,9 @@ version = "21.5.0" [[package]] name = "soroban-ledger-snapshot" -version = "21.7.3" +version = "21.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84589856911dfd6731695c9b51c858aed6d4540118c0a1e5c4c858ea13bc744c" +checksum = "9cf596b2083946a95914a55d7d29cee6a8095b515fd06211851f45bf6af5a496" dependencies = [ "serde", "serde_json", @@ -4715,8 +4728,8 @@ dependencies = [ "serde_json", "soroban-env-guest 21.2.1", "soroban-env-host 21.2.1", - "soroban-ledger-snapshot 21.7.3", - "soroban-sdk-macros 21.7.3", + "soroban-ledger-snapshot 21.7.2", + "soroban-sdk-macros 21.7.2", "stellar-strkey 0.0.8", ] @@ -4739,9 +4752,9 @@ dependencies = [ [[package]] name = "soroban-sdk-macros" -version = "21.7.3" +version = "21.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63c2173f1aacd56b4405eed71cb2a9694dff99d51ba72d4f0cbc5e4961fdabf4" +checksum = "da03fa00b8ca6e392f013359c06d790d2d379f9c8d6f8a6dfe563ec64311e5d3" dependencies = [ "crate-git-revision 0.0.6", "darling", @@ -4751,8 +4764,8 @@ dependencies = [ "rustc_version", "sha2 0.10.8", "soroban-env-common 21.2.1", - "soroban-spec 21.7.3", - "soroban-spec-rust 21.7.3", + "soroban-spec 21.7.2", + "soroban-spec-rust 21.7.2", "stellar-xdr 21.2.0", "syn 2.0.77", ] @@ -4779,9 +4792,9 @@ dependencies = [ [[package]] name = "soroban-spec" -version = "21.7.3" +version = "21.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7705bffbcc747c08e81698b87b4a787f8b268c25d88f777160091dc1ee8121cb" +checksum = "64c723195463d8742bcb481520bd8b8325da66c39ea236ad46261e6af992e8a8" dependencies = [ "base64 0.13.1", "stellar-xdr 21.2.0", @@ -4817,15 +4830,15 @@ dependencies = [ [[package]] name = "soroban-spec-rust" -version = "21.7.3" +version = "21.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48207ebc8616c2804a17203d1d86c53c3d3c804b682cbab011a135893db1cf78" +checksum = "c0f1b0ec2af54e38f138910f09e101b100130efe625f69ece51c76dd4f06f8b2" dependencies = [ "prettyplease", "proc-macro2", "quote", "sha2 0.10.8", - "soroban-spec 21.7.3", + "soroban-spec 21.7.2", "stellar-xdr 21.2.0", "syn 2.0.77", "thiserror", @@ -4948,7 +4961,7 @@ dependencies = [ "smallvec", "spin", "wasmi_collections", - "wasmi_core 0.36.3", + "wasmi_core 0.36.5", "wasmparser-nostd", ] @@ -4991,7 +5004,7 @@ version = "21.5.0" dependencies = [ "async-trait", "bollard", - "byteorder", + "byteorder 1.5.0", "ed25519-dalek 2.1.1", "env_logger", "futures", @@ -6014,6 +6027,16 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "wasm-gen" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b854b1461005a7b3365742310f7faa3cac3add809d66928c64a40c7e9e842ebb" +dependencies = [ + "byteorder 0.5.3", + "leb128", +] + [[package]] name = "wasm-opt" version = "0.114.2" @@ -6056,9 +6079,9 @@ dependencies = [ [[package]] name = "wasm-streams" -version = "0.4.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" dependencies = [ "futures-util", "js-sys", @@ -6075,9 +6098,9 @@ checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" [[package]] name = "wasmi_collections" -version = "0.36.3" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ee1cf2328e7fbb8654fda8449395c64c36ef5a30511e79fae0265a96e1a446" +checksum = "8eddc10bfb0069e913399ebd66c5a72c7d9aceabddcaa0296f062a55ab61d404" dependencies = [ "ahash", "hashbrown 0.14.5", @@ -6098,9 +6121,9 @@ dependencies = [ [[package]] name = "wasmi_core" -version = "0.36.3" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e77c1e701d21edfd263e5c6c940861202c6b840c715040cfdca6211bf8857aa" +checksum = "f08b12621457c17cfd5349cce25029eeac3769b63b1b02bd850d595a00f375ff" dependencies = [ "downcast-rs", "libm", @@ -6442,7 +6465,7 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "byteorder", + "byteorder 1.5.0", "zerocopy-derive", ] diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index b3724b98b..8c151cb84 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -334,6 +334,7 @@ To view the commands that will be executed, without executing them, use the --pr If ommitted, wasm files are written only to the cargo target directory. * `--print-commands-only` — Print commands to build without executing them +* `--meta ` — Add key-value to contract meta (adds the meta to the `contractmetav0` custom section) diff --git a/cmd/crates/soroban-spec-tools/src/contract.rs b/cmd/crates/soroban-spec-tools/src/contract.rs index 9984a19d1..501609111 100644 --- a/cmd/crates/soroban-spec-tools/src/contract.rs +++ b/cmd/crates/soroban-spec-tools/src/contract.rs @@ -40,9 +40,9 @@ pub enum Error { impl Spec { pub fn new(bytes: &[u8]) -> Result { - let mut env_meta: Option<&[u8]> = None; - let mut meta: Option<&[u8]> = None; - let mut spec: Option<&[u8]> = None; + let mut env_meta: Option> = None; + let mut meta: Option> = None; + let mut spec: Option> = None; for payload in wasmparser::Parser::new(0).parse_all(bytes) { let payload = payload?; if let wasmparser::Payload::CustomSection(section) = payload { @@ -52,13 +52,19 @@ impl Spec { "contractspecv0" => &mut spec, _ => continue, }; - *out = Some(section.data()); + + if let Some(existing_data) = out { + let combined_data = [existing_data, section.data()].concat(); + *out = Some(combined_data); + } else { + *out = Some(section.data().to_vec()); + } }; } let mut env_meta_base64 = None; let env_meta = if let Some(env_meta) = env_meta { - env_meta_base64 = Some(base64.encode(env_meta)); + env_meta_base64 = Some(base64.encode(&env_meta)); let cursor = Cursor::new(env_meta); let mut read = Limited::new(cursor, Limits::none()); ScEnvMetaEntry::read_xdr_iter(&mut read).collect::, xdr::Error>>()? @@ -68,7 +74,7 @@ impl Spec { let mut meta_base64 = None; let meta = if let Some(meta) = meta { - meta_base64 = Some(base64.encode(meta)); + meta_base64 = Some(base64.encode(&meta)); let cursor = Cursor::new(meta); let mut depth_limit_read = Limited::new(cursor, Limits::none()); ScMetaEntry::read_xdr_iter(&mut depth_limit_read) @@ -78,7 +84,7 @@ impl Spec { }; let (spec_base64, spec) = if let Some(spec) = spec { - let (spec_base64, spec) = Spec::spec_to_base64(spec)?; + let (spec_base64, spec) = Spec::spec_to_base64(&spec)?; (Some(spec_base64), spec) } else { (None, vec![]) diff --git a/cmd/crates/soroban-test/tests/fixtures/workspace/contracts/add/src/lib.rs b/cmd/crates/soroban-test/tests/fixtures/workspace/contracts/add/src/lib.rs index 1552f5855..d04162b5a 100644 --- a/cmd/crates/soroban-test/tests/fixtures/workspace/contracts/add/src/lib.rs +++ b/cmd/crates/soroban-test/tests/fixtures/workspace/contracts/add/src/lib.rs @@ -1,9 +1,11 @@ #![no_std] -use soroban_sdk::{contract, contractimpl}; +use soroban_sdk::{contract, contractimpl, contractmeta}; #[contract] pub struct Contract; +contractmeta!(key = "Description", val = "A test add contract"); + #[contractimpl] impl Contract { pub fn add(x: u64, y: u64) -> u128 { diff --git a/cmd/crates/soroban-test/tests/it/build.rs b/cmd/crates/soroban-test/tests/it/build.rs index aba6aadf5..925d88df1 100644 --- a/cmd/crates/soroban-test/tests/it/build.rs +++ b/cmd/crates/soroban-test/tests/it/build.rs @@ -116,3 +116,37 @@ cargo rustc --manifest-path=contracts/add/Cargo.toml --crate-type=cdylib --targe ", )); } + +#[test] +fn build_with_metadata() { + let sandbox = TestEnv::default(); + let cargo_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let fixture_path = cargo_dir.join("tests/fixtures/workspace/contracts/add"); + let outdir = sandbox.dir().join("out"); + + sandbox + .new_assert_cmd("contract") + .current_dir(&fixture_path) + .arg("build") + .arg("--meta") + .arg("contract meta=added on build") + .arg("--out-dir") + .arg(&outdir) + .assert() + .success(); + + // verify that the metadata added in the contract code via contractmetadata! macro is present + // as well as the meta that is included on build + let wasm_path = fixture_path.join(&outdir).join("add.wasm"); + sandbox + .new_assert_cmd("contract") + .current_dir(&fixture_path) + .arg("info") + .arg("meta") + .arg("--wasm") + .arg(wasm_path) + .assert() + .success() + .stdout(predicate::str::contains("Description: A test add contract")) + .stdout(predicate::str::contains("contract meta: added on build")); +} diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index 0cc12e74f..78c3974f3 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -124,6 +124,7 @@ glob = "0.3.1" fqdn = "0.3.12" open = "5.3.0" url = "2.5.2" +wasm-gen = "0.1.4" [build-dependencies] crate-git-revision = "0.0.4" diff --git a/cmd/soroban-cli/src/commands/contract/build.rs b/cmd/soroban-cli/src/commands/contract/build.rs index c71327a38..01ccce311 100644 --- a/cmd/soroban-cli/src/commands/contract/build.rs +++ b/cmd/soroban-cli/src/commands/contract/build.rs @@ -1,3 +1,4 @@ +use cargo_metadata::{Metadata, MetadataCommand, Package}; use clap::Parser; use itertools::Itertools; use std::{ @@ -9,8 +10,7 @@ use std::{ path::{self, Path, PathBuf}, process::{Command, ExitStatus, Stdio}, }; - -use cargo_metadata::{Metadata, MetadataCommand, Package}; +use stellar_xdr::curr::{Limits, ScMetaEntry, ScMetaV0, StringM, WriteXdr}; /// Build a contract from source /// @@ -62,6 +62,20 @@ pub struct Cmd { /// Print commands to build without executing them #[arg(long, conflicts_with = "out_dir", help_heading = "Other")] pub print_commands_only: bool, + /// Add key-value to contract meta (adds the meta to the `contractmetav0` custom section) + #[arg(long, num_args=1, value_parser=parse_meta_arg, action=clap::ArgAction::Append, help_heading = "Metadata")] + pub meta: Vec<(String, String)>, +} + +fn parse_meta_arg(s: &str) -> Result<(String, String), Error> { + let parts = s.splitn(2, '='); + + let (key, value) = parts + .map(str::trim) + .next_tuple() + .ok_or_else(|| Error::MetaArg("must be in the form 'key=value'".to_string()))?; + + Ok((key.to_string(), value.to_string())) } #[derive(thiserror::Error, Debug)] @@ -82,8 +96,17 @@ pub enum Error { CopyingWasmFile(io::Error), #[error("getting the current directory: {0}")] GettingCurrentDir(io::Error), + #[error("reading wasm file: {0}")] + ReadingWasmFile(io::Error), + #[error("writing wasm file: {0}")] + WritingWasmFile(io::Error), + #[error("invalid meta entry: {0}")] + MetaArg(String), } +const WASM_TARGET: &str = "wasm32-unknown-unknown"; +const META_CUSTOM_SECTION_NAME: &str = "contractmetav0"; + impl Cmd { pub fn run(&self) -> Result<(), Error> { let working_dir = env::current_dir().map_err(Error::GettingCurrentDir)?; @@ -111,7 +134,7 @@ impl Cmd { manifest_path.to_string_lossy() )); cmd.arg("--crate-type=cdylib"); - cmd.arg("--target=wasm32-unknown-unknown"); + cmd.arg(format!("--target={WASM_TARGET}")); if self.profile == "release" { cmd.arg("--release"); } else { @@ -145,14 +168,16 @@ impl Cmd { return Err(Error::Exit(status)); } + let file = format!("{}.wasm", p.name.replace('-', "_")); + let target_file_path = Path::new(target_dir) + .join(WASM_TARGET) + .join(&self.profile) + .join(&file); + + self.handle_contract_metadata_args(&target_file_path)?; + if let Some(out_dir) = &self.out_dir { fs::create_dir_all(out_dir).map_err(Error::CreatingOutDir)?; - - let file = format!("{}.wasm", p.name.replace('-', "_")); - let target_file_path = Path::new(target_dir) - .join("wasm32-unknown-unknown") - .join(&self.profile) - .join(&file); let out_file_path = Path::new(out_dir).join(&file); fs::copy(target_file_path, out_file_path).map_err(Error::CopyingWasmFile)?; } @@ -228,4 +253,32 @@ impl Cmd { // the output. cmd.exec() } + + fn handle_contract_metadata_args(&self, target_file_path: &PathBuf) -> Result<(), Error> { + if self.meta.is_empty() { + return Ok(()); + } + + let mut wasm_bytes = fs::read(target_file_path).map_err(Error::ReadingWasmFile)?; + + for (k, v) in self.meta.clone() { + let key: StringM = k + .clone() + .try_into() + .map_err(|e| Error::MetaArg(format!("{k} is an invalid metadata key: {e}")))?; + + let val: StringM = v + .clone() + .try_into() + .map_err(|e| Error::MetaArg(format!("{v} is an invalid metadata value: {e}")))?; + let meta_entry = ScMetaEntry::ScMetaV0(ScMetaV0 { key, val }); + let xdr: Vec = meta_entry + .to_xdr(Limits::none()) + .map_err(|e| Error::MetaArg(format!("failed to encode metadata entry: {e}")))?; + + wasm_gen::write_custom_section(&mut wasm_bytes, META_CUSTOM_SECTION_NAME, &xdr); + } + + fs::write(target_file_path, wasm_bytes).map_err(Error::WritingWasmFile) + } } From 519c6fa373ddc50e030fb3e49f91f3eae8b00aff Mon Sep 17 00:00:00 2001 From: "Thomas M. DuBuisson" Date: Wed, 30 Oct 2024 15:56:48 -0700 Subject: [PATCH 46/55] Make flake install work (#1686) * Do not use SHELL (default shell) but $0 (current shell) * Cleanup shell hook * Update flake to support profile install * Update stellar rpc hash * Make name match binary for easy execution * Add nix to readme * Remove 'NOREPO' git fallback * Add wasm to test dep, make wasm test files, ignore test not being in git repo * remove old comment * tweak README.md * Revert "Add wasm to test dep, make wasm test files, ignore test not being in git repo" This reverts commit b8268d9cda0def409a04d62cc5077f402d25748f. --------- Co-authored-by: Gleb Co-authored-by: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> --- Makefile | 2 +- README.md | 7 +++++++ flake.lock | 12 ++++++------ flake.nix | 36 +++++++++++++++++++++++++++++------- 4 files changed, 43 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index 8a1333bd9..f534410e0 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ endif # By default make `?=` operator will treat empty assignment as a set value and will not use the default value. # Both cases should fallback to default of getting the version from git tag. ifeq ($(strip $(REPOSITORY_VERSION)),) - override REPOSITORY_VERSION = "$(shell git describe --tags --always --abbrev=0 --match='v[0-9]*.[0-9]*.[0-9]*' 2> /dev/null | sed 's/^.//')" + override REPOSITORY_VERSION = "$(shell ( git describe --tags --always --abbrev=0 --match='v[0-9]*.[0-9]*.[0-9]*' 2> /dev/null | sed 's/^.//' ) )" endif REPOSITORY_BRANCH := "$(shell git rev-parse --abbrev-ref HEAD)" BUILD_TIMESTAMP ?= $(shell date '+%Y-%m-%dT%H:%M:%S') diff --git a/README.md b/README.md index 8fadb6740..381f5a3dd 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,13 @@ Install the latest version from source: cargo install --locked stellar-cli --features opt ``` +Install or run the unreleased main branch with nix: +``` +% nix run 'github:stellar/stellar-cli' -- --help +or install +% nix profile install github:stellar/stellar-cli +``` + ## Autocomplete The Stellar CLI supports some autocompletion. To set up, run the following commands: diff --git a/flake.lock b/flake.lock index fef18714e..fd92bacb2 100644 --- a/flake.lock +++ b/flake.lock @@ -36,11 +36,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1718428119, - "narHash": "sha256-WdWDpNaq6u1IPtxtYHHWpl5BmabtpmLnMAx0RdJ/vo8=", + "lastModified": 1728538411, + "narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e6cea36f83499eb4e9cd184c8a8e823296b50ad5", + "rev": "b69de56fac8c2b6f8fd27f2eca01dcda8e0a4221", "type": "github" }, "original": { @@ -62,11 +62,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1727144949, - "narHash": "sha256-uMZMjoCS2nf40TAE1686SJl3OXWfdfM+BDEfRdr+uLc=", + "lastModified": 1729823394, + "narHash": "sha256-RiinJqorqSLKh1oSpiMHnBe6nQdJzE45lX6fSnAuDnI=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "2e19799819104b46019d339e78d21c14372d3666", + "rev": "7e52e80f5faa374ad4c607d62c6d362589cb523f", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 1923d75bb..0a30ea556 100644 --- a/flake.nix +++ b/flake.nix @@ -1,5 +1,5 @@ { - description = "stellar-cli development shell"; + description = "stellar-cli"; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; @@ -14,14 +14,13 @@ pkgs = import nixpkgs { inherit system overlays; }; - in - with pkgs; - { - devShells.default = mkShell { - nativeBuildInputs = lib.optionals (stdenv.isDarwin) [ + stellardev = { + name = "stellar"; + src = ./.; + nativeBuildInputs = pkgs.lib.optionals (pkgs.stdenv.isDarwin) [ pkgs.darwin.apple_sdk.frameworks.SystemConfiguration ]; - buildInputs = [ + buildInputs = with pkgs; [ openssl pkg-config jq @@ -31,6 +30,29 @@ }) ] ++ lib.optionals (stdenv.isLinux) [libudev-zero]; }; + stellarcli = stellardev // { + cargoLock = { + lockFile = ./Cargo.lock; + }; + + cargoLock.outputHashes = { + # The only git+https dependency in Cargo.lock + "stellar-rpc-client-21.4.0" = "sha256-ue7Ynux9YaDP3f/XkHz2OPd2g0iCX5R0yS5SaVHEYxQ"; + }; + + doCheck = false; + + GIT_REVISION = "${self.rev or self.dirtyRev or "unknown"}"; + }; + rustPlatformMod = pkgs.makeRustPlatform { + cargo = pkgs.rust-bin.stable.latest.default; + rustc = pkgs.rust-bin.stable.latest.default; + }; + in + with pkgs; + { + devShells.default = mkShell stellardev; + packages.default = rustPlatformMod.buildRustPackage stellarcli; } ); } From bd1ef305d226c2151cf186b95fa33b6a891c28dd Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Fri, 1 Nov 2024 12:13:30 -0400 Subject: [PATCH 47/55] feat: add help headings for global command options (#1696) --- cmd/soroban-cli/src/commands/global.rs | 12 ++++++------ cmd/soroban-cli/src/commands/mod.rs | 1 + cmd/soroban-cli/src/config/locator.rs | 6 +++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/cmd/soroban-cli/src/commands/global.rs b/cmd/soroban-cli/src/commands/global.rs index be883b6fd..02c0b3b58 100644 --- a/cmd/soroban-cli/src/commands/global.rs +++ b/cmd/soroban-cli/src/commands/global.rs @@ -4,7 +4,7 @@ use clap::{ }; use std::path::PathBuf; -use super::config; +use super::{config, HEADING_GLOBAL}; const USAGE_STYLES: Styles = Styles::styled() .header(AnsiColor::Green.on_default().effects(Effects::BOLD)) @@ -24,19 +24,19 @@ pub struct Args { pub locator: config::locator::Args, /// Filter logs output. To turn on `stellar_cli::log::footprint=debug` or off `=off`. Can also use env var `RUST_LOG`. - #[arg(long, short = 'f', global = true)] + #[arg(long, short = 'f', global = true, help_heading = HEADING_GLOBAL)] pub filter_logs: Vec, /// Do not write logs to stderr including `INFO` - #[arg(long, short = 'q', global = true)] + #[arg(long, short = 'q', global = true, help_heading = HEADING_GLOBAL)] pub quiet: bool, /// Log DEBUG events - #[arg(long, short = 'v', global = true)] + #[arg(long, short = 'v', global = true, help_heading = HEADING_GLOBAL)] pub verbose: bool, /// Log DEBUG and TRACE events - #[arg(long, visible_alias = "vv", global = true)] + #[arg(long, visible_alias = "vv", global = true, help_heading = HEADING_GLOBAL)] pub very_verbose: bool, /// List installed plugins. E.g. `stellar-hello` @@ -44,7 +44,7 @@ pub struct Args { pub list: bool, /// Do not cache your simulations and transactions - #[arg(long, env = "STELLAR_NO_CACHE", global = true)] + #[arg(long, env = "STELLAR_NO_CACHE", global = true, help_heading = HEADING_GLOBAL)] pub no_cache: bool, } diff --git a/cmd/soroban-cli/src/commands/mod.rs b/cmd/soroban-cli/src/commands/mod.rs index 0a1d4629b..a6a85e816 100644 --- a/cmd/soroban-cli/src/commands/mod.rs +++ b/cmd/soroban-cli/src/commands/mod.rs @@ -20,6 +20,7 @@ pub mod version; pub mod txn_result; pub const HEADING_RPC: &str = "Options (RPC)"; +pub const HEADING_GLOBAL: &str = "Options (Global)"; const ABOUT: &str = "Work seamlessly with Stellar accounts, contracts, and assets from the command line. diff --git a/cmd/soroban-cli/src/config/locator.rs b/cmd/soroban-cli/src/config/locator.rs index 70b4f75f8..0a13c4cc3 100644 --- a/cmd/soroban-cli/src/config/locator.rs +++ b/cmd/soroban-cli/src/config/locator.rs @@ -12,7 +12,7 @@ use std::{ }; use stellar_strkey::{Contract, DecodeError}; -use crate::{utils::find_config_dir, Pwd}; +use crate::{commands::HEADING_GLOBAL, utils::find_config_dir, Pwd}; use super::{ alias, @@ -82,11 +82,11 @@ pub enum Error { #[group(skip)] pub struct Args { /// Use global config - #[arg(long, global = true)] + #[arg(long, global = true, help_heading = HEADING_GLOBAL)] pub global: bool, /// Location of config directory, default is "." - #[arg(long, global = true)] + #[arg(long, global = true, help_heading = HEADING_GLOBAL)] pub config_dir: Option, } From caf6e43bd593de790538e5fd43b73690d5c8ae38 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Fri, 1 Nov 2024 20:10:54 -0400 Subject: [PATCH 48/55] fix: update stellar dependencies (#1695) Still need to update soroban client to a released version. Otherwise just updating to newest release candidates --- Cargo.lock | 171 +++++++++++++++++------------------------------------ Cargo.toml | 12 ++-- 2 files changed, 59 insertions(+), 124 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac115d89a..4e292f191 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -241,12 +241,6 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - [[package]] name = "ascii-canvas" version = "3.0.0" @@ -1948,9 +1942,6 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", -] [[package]] name = "heck" @@ -2896,12 +2887,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "multi-stash" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685a9ac4b61f4e728e1d2c6a7844609c16527aeb5e6c865915c08e619c16410f" - [[package]] name = "native-tls" version = "0.2.12" @@ -3794,9 +3779,9 @@ checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] @@ -4429,9 +4414,9 @@ dependencies = [ [[package]] name = "soroban-builtin-sdk-macros" -version = "22.0.0-rc.1.1" +version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4c8668199d95e3061cd42e1b96a91451c656a238a607fa53f96f0a3fdcf5f3" +checksum = "c45d2492cd44f05cc79eeb857985f153f12a4423ce51b4b746b5925024c473b1" dependencies = [ "itertools 0.10.5", "proc-macro2", @@ -4492,11 +4477,11 @@ dependencies = [ "serde_json", "sha2 0.10.8", "shlex", - "soroban-ledger-snapshot 22.0.0-rc.1.1", - "soroban-sdk 22.0.0-rc.1.1", - "soroban-spec 22.0.0-rc.1.1", + "soroban-ledger-snapshot 22.0.0-rc.3", + "soroban-sdk 22.0.0-rc.3", + "soroban-spec 22.0.0-rc.3", "soroban-spec-json", - "soroban-spec-rust 22.0.0-rc.1.1", + "soroban-spec-rust 22.0.0-rc.3", "soroban-spec-tools", "soroban-spec-typescript", "stellar-rpc-client", @@ -4538,7 +4523,7 @@ dependencies = [ "num-traits", "serde", "soroban-env-macros 21.2.1", - "soroban-wasmi 0.31.1-soroban.20.0.1", + "soroban-wasmi", "static_assertions", "stellar-xdr 21.2.0", "wasmparser 0.116.1", @@ -4546,17 +4531,17 @@ dependencies = [ [[package]] name = "soroban-env-common" -version = "22.0.0-rc.1.1" +version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdf1d66133d6b29e2834acea79decb57c47c71aa01885cae2b9ad621d67525c" +checksum = "39b6d2ec8955243394278e1fae88be3b367fcfed9cf74e5044799a90786a8642" dependencies = [ "crate-git-revision 0.0.6", "ethnum", "num-derive", "num-traits", "serde", - "soroban-env-macros 22.0.0-rc.1.1", - "soroban-wasmi 0.36.1-soroban.22.0.0", + "soroban-env-macros 22.0.0-rc.3", + "soroban-wasmi", "static_assertions", "stellar-xdr 22.0.0-rc.1.1", "wasmparser 0.116.1", @@ -4574,11 +4559,11 @@ dependencies = [ [[package]] name = "soroban-env-guest" -version = "22.0.0-rc.1.1" +version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa9610ac8a4a900e6f35b2ed171bc325c7e9883929f5e9da758e85f1226dd284" +checksum = "4002fc582cd20cc9b9fbb73959bc5d6b5b15feda11485cbfab0c28e78ecbab3e" dependencies = [ - "soroban-env-common 22.0.0-rc.1.1", + "soroban-env-common 22.0.0-rc.3", "static_assertions", ] @@ -4609,7 +4594,7 @@ dependencies = [ "sha3", "soroban-builtin-sdk-macros 21.2.1", "soroban-env-common 21.2.1", - "soroban-wasmi 0.31.1-soroban.20.0.1", + "soroban-wasmi", "static_assertions", "stellar-strkey 0.0.8", "wasmparser 0.116.1", @@ -4617,9 +4602,9 @@ dependencies = [ [[package]] name = "soroban-env-host" -version = "22.0.0-rc.1.1" +version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c695d22888ede1f98c016a4a690be307817d133be0e0f32a25fd6e53bb6c929" +checksum = "8cb9be0260d39a648db0d33e1c6f8f494ec0c4f5be2b8a0a4e15ed4b7c6a92b0" dependencies = [ "ark-bls12-381", "ark-ec", @@ -4643,9 +4628,9 @@ dependencies = [ "sec1", "sha2 0.10.8", "sha3", - "soroban-builtin-sdk-macros 22.0.0-rc.1.1", - "soroban-env-common 22.0.0-rc.1.1", - "soroban-wasmi 0.36.1-soroban.22.0.0", + "soroban-builtin-sdk-macros 22.0.0-rc.3", + "soroban-env-common 22.0.0-rc.3", + "soroban-wasmi", "static_assertions", "stellar-strkey 0.0.9", "wasmparser 0.116.1", @@ -4668,9 +4653,9 @@ dependencies = [ [[package]] name = "soroban-env-macros" -version = "22.0.0-rc.1.1" +version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d926d0daa3ba798cd70ce962ea10012e630d75088352369a6d248b2644dd7a2c" +checksum = "a328297a568ae98999fdb06902e3362dfd8a2bfa9abea40beaeb7dc93a402fe7" dependencies = [ "itertools 0.10.5", "proc-macro2", @@ -4701,15 +4686,15 @@ dependencies = [ [[package]] name = "soroban-ledger-snapshot" -version = "22.0.0-rc.1.1" +version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07ca63290730b803f0637e00994f803d2f24cc6383ac8680724050d07690a89" +checksum = "56375490f176006a636db0e50c2269c55626e0ff7222711bb78d77028376fe0d" dependencies = [ "serde", "serde_json", "serde_with", - "soroban-env-common 22.0.0-rc.1.1", - "soroban-env-host 22.0.0-rc.1.1", + "soroban-env-common 22.0.0-rc.3", + "soroban-env-host 22.0.0-rc.3", "thiserror", ] @@ -4735,18 +4720,19 @@ dependencies = [ [[package]] name = "soroban-sdk" -version = "22.0.0-rc.1.1" +version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b715e15357dbdda2fe0626005b75c2253bb3786deec3001d4077462a9dadd3" +checksum = "6d063d0df000aaec20105aab3d743660322bc0269934ea95d79fa19aa8792385" dependencies = [ "bytes-lit", "rand", + "rustc_version", "serde", "serde_json", - "soroban-env-guest 22.0.0-rc.1.1", - "soroban-env-host 22.0.0-rc.1.1", - "soroban-ledger-snapshot 22.0.0-rc.1.1", - "soroban-sdk-macros 22.0.0-rc.1.1", + "soroban-env-guest 22.0.0-rc.3", + "soroban-env-host 22.0.0-rc.3", + "soroban-ledger-snapshot 22.0.0-rc.3", + "soroban-sdk-macros 22.0.0-rc.3", "stellar-strkey 0.0.9", ] @@ -4772,9 +4758,9 @@ dependencies = [ [[package]] name = "soroban-sdk-macros" -version = "22.0.0-rc.1.1" +version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00d1b8d96ccf53ea9c30d86352756cace630ca5c7e1200fff32ffc793bedd593" +checksum = "508c9d819a05109120664aab86c371e1b72c5bea20b1a13158b4ef7948d9f673" dependencies = [ "crate-git-revision 0.0.6", "darling", @@ -4783,9 +4769,9 @@ dependencies = [ "quote", "rustc_version", "sha2 0.10.8", - "soroban-env-common 22.0.0-rc.1.1", - "soroban-spec 22.0.0-rc.1.1", - "soroban-spec-rust 22.0.0-rc.1.1", + "soroban-env-common 22.0.0-rc.3", + "soroban-spec 22.0.0-rc.3", + "soroban-spec-rust 22.0.0-rc.3", "stellar-xdr 22.0.0-rc.1.1", "syn 2.0.77", ] @@ -4804,9 +4790,9 @@ dependencies = [ [[package]] name = "soroban-spec" -version = "22.0.0-rc.1.1" +version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4603430fd36848da7189e758d7f3fe1d7bbfef024e8aad2230a8be8252a583c1" +checksum = "69001c97783ed3ce197eac2404e7beeabedd16e40e6f0aa210d1bc6a13063c33" dependencies = [ "base64 0.13.1", "stellar-xdr 22.0.0-rc.1.1", @@ -4823,7 +4809,7 @@ dependencies = [ "serde_derive", "serde_json", "sha2 0.9.9", - "soroban-spec 22.0.0-rc.1.1", + "soroban-spec 22.0.0-rc.3", "stellar-xdr 22.0.0-rc.1.1", "thiserror", ] @@ -4846,15 +4832,15 @@ dependencies = [ [[package]] name = "soroban-spec-rust" -version = "22.0.0-rc.1.1" +version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d5f3fe5b27e0b1c1d9ecdff04c4e31f5cd4d12cdc052d54f0e4995cf637b9c" +checksum = "a45dbf346f91ed23ea63b1c256c522da9e6f0e2db1887b990a8f0f1d842a3093" dependencies = [ "prettyplease", "proc-macro2", "quote", "sha2 0.10.8", - "soroban-spec 22.0.0-rc.1.1", + "soroban-spec 22.0.0-rc.3", "stellar-xdr 22.0.0-rc.1.1", "syn 2.0.77", "thiserror", @@ -4869,7 +4855,7 @@ dependencies = [ "hex", "itertools 0.10.5", "serde_json", - "soroban-spec 22.0.0-rc.1.1", + "soroban-spec 22.0.0-rc.3", "stellar-strkey 0.0.11", "stellar-xdr 22.0.0-rc.1.1", "thiserror", @@ -4892,7 +4878,7 @@ dependencies = [ "serde_derive", "serde_json", "sha2 0.9.9", - "soroban-spec 22.0.0-rc.1.1", + "soroban-spec 22.0.0-rc.3", "stellar-xdr 22.0.0-rc.1.1", "temp-dir", "thiserror", @@ -4913,8 +4899,8 @@ dependencies = [ "serde_json", "sha2 0.10.8", "soroban-cli", - "soroban-ledger-snapshot 22.0.0-rc.1.1", - "soroban-spec 22.0.0-rc.1.1", + "soroban-ledger-snapshot 22.0.0-rc.3", + "soroban-spec 22.0.0-rc.3", "soroban-spec-tools", "stellar-rpc-client", "stellar-strkey 0.0.11", @@ -4944,24 +4930,7 @@ dependencies = [ "smallvec", "spin", "wasmi_arena", - "wasmi_core 0.13.0", - "wasmparser-nostd", -] - -[[package]] -name = "soroban-wasmi" -version = "0.36.1-soroban.22.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7044ea0ee6ff67039df1f232f0d3d98121f69a0409e944774912fc5f043c280f" -dependencies = [ - "arrayvec", - "multi-stash", - "num-derive", - "num-traits", - "smallvec", - "spin", - "wasmi_collections", - "wasmi_core 0.36.5", + "wasmi_core", "wasmparser-nostd", ] @@ -5025,7 +4994,7 @@ dependencies = [ "serial_test", "sha2 0.9.9", "slip10", - "soroban-spec 22.0.0-rc.1.1", + "soroban-spec 22.0.0-rc.3", "stellar-rpc-client", "stellar-strkey 0.0.11", "stellar-xdr 22.0.0-rc.1.1", @@ -5039,7 +5008,7 @@ dependencies = [ [[package]] name = "stellar-rpc-client" version = "21.4.0" -source = "git+https://github.com/stellar/rs-stellar-rpc-client?branch=main#7554d4c87c026313a1f5b3c7ae66a92b5ff7e091" +source = "git+https://github.com/stellar/rs-stellar-rpc-client?rev=7554d4c87c026313a1f5b3c7ae66a92b5ff7e091#7554d4c87c026313a1f5b3c7ae66a92b5ff7e091" dependencies = [ "clap", "hex", @@ -5128,17 +5097,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "string-interner" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c6a0d765f5807e98a091107bae0a56ea3799f66a5de47b2c84c94a39c09974e" -dependencies = [ - "cfg-if", - "hashbrown 0.14.5", - "serde", -] - [[package]] name = "string_cache" version = "0.8.7" @@ -6096,17 +6054,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" -[[package]] -name = "wasmi_collections" -version = "0.36.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eddc10bfb0069e913399ebd66c5a72c7d9aceabddcaa0296f062a55ab61d404" -dependencies = [ - "ahash", - "hashbrown 0.14.5", - "string-interner", -] - [[package]] name = "wasmi_core" version = "0.13.0" @@ -6119,18 +6066,6 @@ dependencies = [ "paste", ] -[[package]] -name = "wasmi_core" -version = "0.36.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f08b12621457c17cfd5349cce25029eeac3769b63b1b02bd850d595a00f375ff" -dependencies = [ - "downcast-rs", - "libm", - "num-traits", - "paste", -] - [[package]] name = "wasmparser" version = "0.90.0" diff --git a/Cargo.toml b/Cargo.toml index 8b10f373d..58caad196 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,26 +39,26 @@ default-features = true # Dependencies from the rs-soroban-sdk repo: [workspace.dependencies.soroban-spec] -version = "=22.0.0-rc.1.1" +version = "=22.0.0-rc.3" [workspace.dependencies.soroban-spec-rust] -version = "=22.0.0-rc.1.1" +version = "=22.0.0-rc.3" [workspace.dependencies.soroban-sdk] -version = "=22.0.0-rc.1.1" +version = "=22.0.0-rc.3" [workspace.dependencies.soroban-token-sdk] -version = "=22.0.0-rc.1.1" +version = "=22.0.0-rc.3" [workspace.dependencies.soroban-ledger-snapshot] -version = "=22.0.0-rc.1.1" +version = "=22.0.0-rc.3" # Dependencies from the rs-stellar-rpc-client repo: [workspace.dependencies.soroban-rpc] package = "stellar-rpc-client" version = "21.4.0" git = "https://github.com/stellar/rs-stellar-rpc-client" -branch = "main" +rev = "7554d4c87c026313a1f5b3c7ae66a92b5ff7e091" # Dependencies from elsewhere shared by crates: [workspace.dependencies] From e44b89f946aed2e8cf63eb1acd077a95810c0547 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sun, 3 Nov 2024 16:58:48 -0700 Subject: [PATCH 49/55] Remove absolute registry paths from panic messages in wasm (#1594) --- Cargo.lock | 37 +++-- cmd/soroban-cli/Cargo.toml | 2 +- .../src/commands/contract/build.rs | 131 +++++++++++++++++- cmd/soroban-cli/src/commands/contract/mod.rs | 2 +- 4 files changed, 150 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4e292f191..6fdf9ee5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4278,6 +4278,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shell-escape" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f" + [[package]] name = "shlex" version = "1.3.0" @@ -4476,6 +4482,7 @@ dependencies = [ "serde-aux", "serde_json", "sha2 0.10.8", + "shell-escape", "shlex", "soroban-ledger-snapshot 22.0.0-rc.3", "soroban-sdk 22.0.0-rc.3", @@ -4672,9 +4679,9 @@ version = "21.5.0" [[package]] name = "soroban-ledger-snapshot" -version = "21.7.2" +version = "21.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf596b2083946a95914a55d7d29cee6a8095b515fd06211851f45bf6af5a496" +checksum = "84589856911dfd6731695c9b51c858aed6d4540118c0a1e5c4c858ea13bc744c" dependencies = [ "serde", "serde_json", @@ -4713,8 +4720,8 @@ dependencies = [ "serde_json", "soroban-env-guest 21.2.1", "soroban-env-host 21.2.1", - "soroban-ledger-snapshot 21.7.2", - "soroban-sdk-macros 21.7.2", + "soroban-ledger-snapshot 21.7.3", + "soroban-sdk-macros 21.7.3", "stellar-strkey 0.0.8", ] @@ -4738,9 +4745,9 @@ dependencies = [ [[package]] name = "soroban-sdk-macros" -version = "21.7.2" +version = "21.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da03fa00b8ca6e392f013359c06d790d2d379f9c8d6f8a6dfe563ec64311e5d3" +checksum = "63c2173f1aacd56b4405eed71cb2a9694dff99d51ba72d4f0cbc5e4961fdabf4" dependencies = [ "crate-git-revision 0.0.6", "darling", @@ -4750,8 +4757,8 @@ dependencies = [ "rustc_version", "sha2 0.10.8", "soroban-env-common 21.2.1", - "soroban-spec 21.7.2", - "soroban-spec-rust 21.7.2", + "soroban-spec 21.7.3", + "soroban-spec-rust 21.7.3", "stellar-xdr 21.2.0", "syn 2.0.77", ] @@ -4778,9 +4785,9 @@ dependencies = [ [[package]] name = "soroban-spec" -version = "21.7.2" +version = "21.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c723195463d8742bcb481520bd8b8325da66c39ea236ad46261e6af992e8a8" +checksum = "7705bffbcc747c08e81698b87b4a787f8b268c25d88f777160091dc1ee8121cb" dependencies = [ "base64 0.13.1", "stellar-xdr 21.2.0", @@ -4816,15 +4823,15 @@ dependencies = [ [[package]] name = "soroban-spec-rust" -version = "21.7.2" +version = "21.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f1b0ec2af54e38f138910f09e101b100130efe625f69ece51c76dd4f06f8b2" +checksum = "48207ebc8616c2804a17203d1d86c53c3d3c804b682cbab011a135893db1cf78" dependencies = [ "prettyplease", "proc-macro2", "quote", "sha2 0.10.8", - "soroban-spec 21.7.2", + "soroban-spec 21.7.3", "stellar-xdr 21.2.0", "syn 2.0.77", "thiserror", @@ -6037,9 +6044,9 @@ dependencies = [ [[package]] name = "wasm-streams" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" dependencies = [ "futures-util", "js-sys", diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index 78c3974f3..efef18e0f 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -107,7 +107,7 @@ ulid = { workspace = true, features = ["serde"] } strum = "0.17.1" strum_macros = "0.17.1" async-compression = { version = "0.4.12", features = ["tokio", "gzip"] } - +shell-escape = "0.1.5" tempfile = "3.8.1" toml_edit = "0.21.0" rust-embed = { version = "8.2.0", features = ["debug-embed"] } diff --git a/cmd/soroban-cli/src/commands/contract/build.rs b/cmd/soroban-cli/src/commands/contract/build.rs index 01ccce311..ac1aa1302 100644 --- a/cmd/soroban-cli/src/commands/contract/build.rs +++ b/cmd/soroban-cli/src/commands/contract/build.rs @@ -2,6 +2,7 @@ use cargo_metadata::{Metadata, MetadataCommand, Package}; use clap::Parser; use itertools::Itertools; use std::{ + borrow::Cow, collections::HashSet, env, ffi::OsStr, @@ -12,6 +13,8 @@ use std::{ }; use stellar_xdr::curr::{Limits, ScMetaEntry, ScMetaV0, StringM, WriteXdr}; +use crate::{commands::global, print::Print}; + /// Build a contract from source /// /// Builds all crates that are referenced by the cargo manifest (Cargo.toml) @@ -96,6 +99,8 @@ pub enum Error { CopyingWasmFile(io::Error), #[error("getting the current directory: {0}")] GettingCurrentDir(io::Error), + #[error("retreiving CARGO_HOME: {0}")] + CargoHome(io::Error), #[error("reading wasm file: {0}")] ReadingWasmFile(io::Error), #[error("writing wasm file: {0}")] @@ -108,7 +113,9 @@ const WASM_TARGET: &str = "wasm32-unknown-unknown"; const META_CUSTOM_SECTION_NAME: &str = "contractmetav0"; impl Cmd { - pub fn run(&self) -> Result<(), Error> { + pub fn run(&self, global_args: &global::Args) -> Result<(), Error> { + let print = Print::new(global_args.quiet); + let working_dir = env::current_dir().map_err(Error::GettingCurrentDir)?; let metadata = self.metadata()?; @@ -154,15 +161,31 @@ impl Cmd { cmd.arg(format!("--features={activate}")); } } - let cmd_str = format!( - "cargo {}", - cmd.get_args().map(OsStr::to_string_lossy).join(" ") + + if let Some(rustflags) = make_rustflags_to_remap_absolute_paths(&print)? { + cmd.env("CARGO_BUILD_RUSTFLAGS", rustflags); + } + + let mut cmd_str_parts = Vec::::new(); + cmd_str_parts.extend(cmd.get_envs().map(|(key, val)| { + format!( + "{}={}", + key.to_string_lossy(), + shell_escape::escape(val.unwrap_or_default().to_string_lossy()) + ) + })); + cmd_str_parts.push("cargo".to_string()); + cmd_str_parts.extend( + cmd.get_args() + .map(OsStr::to_string_lossy) + .map(Cow::into_owned), ); + let cmd_str = cmd_str_parts.join(" "); if self.print_commands_only { println!("{cmd_str}"); } else { - eprintln!("{cmd_str}"); + print.infoln(cmd_str); let status = cmd.status().map_err(Error::CargoCmd)?; if !status.success() { return Err(Error::Exit(status)); @@ -282,3 +305,101 @@ impl Cmd { fs::write(target_file_path, wasm_bytes).map_err(Error::WritingWasmFile) } } + +/// Configure cargo/rustc to replace absolute paths in panic messages / debuginfo +/// with relative paths. +/// +/// This is required for reproducible builds. +/// +/// This works for paths to crates in the registry. The compiler already does +/// something similar for standard library paths and local paths. It may not +/// work for crates that come from other sources, including the standard library +/// compiled from source, though it may be possible to accomodate such cases in +/// the future. +/// +/// This in theory breaks the ability of debuggers to find source code, but +/// since we are only targetting wasm, which is not typically run in a debugger, +/// and stellar-cli only compiles contracts in release mode, the impact is on +/// debugging is expected to be minimal. +/// +/// This works by setting the `CARGO_BUILD_RUSTFLAGS` environment variable, +/// with appropriate `--remap-path-prefix` option. It preserves the values of an +/// existing `CARGO_BUILD_RUSTFLAGS` environment variable. +/// +/// This must be done some via some variation of `RUSTFLAGS` and not as +/// arguments to `cargo rustc` because the latter only applies to the crate +/// directly being compiled, while `RUSTFLAGS` applies to all crates, including +/// dependencies. +/// +/// `CARGO_BUILD_RUSTFLAGS` is an alias for the `build.rustflags` configuration +/// variable. Cargo automatically merges the contents of the environment variable +/// and the variables from config files; and `build.rustflags` has the lowest +/// priority of all the variations of rustflags that Cargo accepts. And because +/// we merge our values with an existing `CARGO_BUILD_RUSTFLAGS`, +/// our setting of this environment variable should not interfere with the +/// user's ability to set rustflags in any way they want, but it does mean +/// that if the user sets a higher-priority rustflags that our path remapping +/// will be ignored. +/// +/// The major downside of using `CARGO_BUILD_RUSTFLAGS` is that it is whitespace +/// separated, which means we cannot support paths with spaces. If we encounter +/// such paths we will emit a warning. Spaces could be accomodated by using +/// `CARGO_ENCODED_RUSTFLAGS`, but that has high precedence over other rustflags, +/// so we could be interfering with the user's own use of rustflags. There is +/// no "encoded" variant of `CARGO_BUILD_RUSTFLAGS` at time of writing. +/// +/// This assumes that paths are Unicode and that any existing `CARGO_BUILD_RUSTFLAGS` +/// variables are Unicode. Non-Unicode paths will fail to correctly perform the +/// the absolute path replacement. Non-Unicode `CARGO_BUILD_RUSTFLAGS` will result in the +/// existing rustflags being ignored, which is also the behavior of +/// Cargo itself. +fn make_rustflags_to_remap_absolute_paths(print: &Print) -> Result, Error> { + let cargo_home = home::cargo_home().map_err(Error::CargoHome)?; + let cargo_home = format!("{}", cargo_home.display()); + + if cargo_home.find(|c: char| c.is_whitespace()).is_some() { + print.warnln("Cargo home directory contains whitespace. Dependency paths will not be remapped; builds may not be reproducible."); + return Ok(None); + } + + if env::var("RUSTFLAGS").is_ok() { + print.warnln("`RUSTFLAGS` set. Dependency paths will not be remapped; builds may not be reproducible."); + return Ok(None); + } + + if env::var("CARGO_ENCODED_RUSTFLAGS").is_ok() { + print.warnln("`CARGO_ENCODED_RUSTFLAGS` set. Dependency paths will not be remapped; builds may not be reproducible."); + return Ok(None); + } + + if env::var("TARGET_wasm32-unknown-unknown_RUSTFLAGS").is_ok() { + print.warnln("`TARGET_wasm32-unknown-unknown_RUSTFLAGS` set. Dependency paths will not be remapped; builds may not be reproducible."); + return Ok(None); + } + + let registry_prefix = format!("{cargo_home}/registry/src/"); + let new_rustflag = format!("--remap-path-prefix={registry_prefix}="); + + let mut rustflags = get_rustflags().unwrap_or_default(); + rustflags.push(new_rustflag); + + let rustflags = rustflags.join(" "); + + Ok(Some(rustflags)) +} + +/// Get any existing `CARGO_BUILD_RUSTFLAGS`, split on whitespace. +/// +/// This conveniently ignores non-Unicode values, as does Cargo. +fn get_rustflags() -> Option> { + if let Ok(a) = env::var("CARGO_BUILD_RUSTFLAGS") { + let args = a + .split_whitespace() + .map(str::trim) + .filter(|s| !s.is_empty()) + .map(str::to_string); + return Some(args.collect()); + } + + None +} diff --git a/cmd/soroban-cli/src/commands/contract/mod.rs b/cmd/soroban-cli/src/commands/contract/mod.rs index d0524e82b..d72ce62b6 100644 --- a/cmd/soroban-cli/src/commands/contract/mod.rs +++ b/cmd/soroban-cli/src/commands/contract/mod.rs @@ -146,7 +146,7 @@ impl Cmd { match &self { Cmd::Asset(asset) => asset.run().await?, Cmd::Bindings(bindings) => bindings.run().await?, - Cmd::Build(build) => build.run()?, + Cmd::Build(build) => build.run(global_args)?, Cmd::Extend(extend) => extend.run().await?, Cmd::Alias(alias) => alias.run(global_args)?, Cmd::Deploy(deploy) => deploy.run(global_args).await?, From c873f9f6e281124c147f169d4f9b11b5a0abecf1 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 5 Nov 2024 13:40:02 -0500 Subject: [PATCH 50/55] fix: remove clap from enforcing network args and update error message (#1698) --- cmd/soroban-cli/src/config/network.rs | 46 ++++++++++++++++----------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/cmd/soroban-cli/src/config/network.rs b/cmd/soroban-cli/src/config/network.rs index ac7dc04bc..829716753 100644 --- a/cmd/soroban-cli/src/config/network.rs +++ b/cmd/soroban-cli/src/config/network.rs @@ -22,8 +22,23 @@ pub mod passphrase; pub enum Error { #[error(transparent)] Config(#[from] locator::Error), - #[error("network arg or rpc url and network passphrase are required if using the network")] + #[error( + r#"Access to the network is required +`--network` or `--rpc-url` and `--network-passphrase` are required if using the network. +Alternatively you can use their corresponding environment variables: +STELLAR_NETWORK, STELLAR_RPC_URL and STELLAR_NETWORK_PASSPHRASE"# + )] Network, + #[error( + "rpc-url is used but network passphrase is missing, use `--network-passphrase` or `STELLAR_NETWORK_PASSPHRASE`" + )] + MissingNetworkPassphrase, + #[error( + "network passphrase is used but rpc-url is missing, use `--rpc-url` or `STELLAR_RPC_URL`" + )] + MissingRpcUrl, + #[error("cannot use both `--rpc-url` and `--network`")] + CannotUseBothRpcAndNetwork, #[error(transparent)] Rpc(#[from] rpc::Error), #[error(transparent)] @@ -48,8 +63,6 @@ pub struct Args { /// RPC server endpoint #[arg( long = "rpc-url", - requires = "network_passphrase", - required_unless_present = "network", env = "STELLAR_RPC_URL", help_heading = HEADING_RPC, )] @@ -68,8 +81,6 @@ pub struct Args { /// Network passphrase to sign the transaction sent to the rpc server #[arg( long = "network-passphrase", - requires = "rpc_url", - required_unless_present = "network", env = "STELLAR_NETWORK_PASSPHRASE", help_heading = HEADING_RPC, )] @@ -77,8 +88,6 @@ pub struct Args { /// Name of network to use from config #[arg( long, - required_unless_present = "rpc_url", - required_unless_present = "network_passphrase", env = "STELLAR_NETWORK", help_heading = HEADING_RPC, )] @@ -87,21 +96,20 @@ pub struct Args { impl Args { pub fn get(&self, locator: &locator::Args) -> Result { - if let Some(name) = self.network.as_deref() { - if let Ok(network) = locator.read_network(name) { - return Ok(network); - } - } - if let (Some(rpc_url), Some(network_passphrase)) = - (self.rpc_url.clone(), self.network_passphrase.clone()) - { - Ok(Network { + match ( + self.network.as_deref(), + self.rpc_url.clone(), + self.network_passphrase.clone(), + ) { + (None, None, None) => Err(Error::Network), + (_, Some(_), None) => Err(Error::MissingNetworkPassphrase), + (_, None, Some(_)) => Err(Error::MissingRpcUrl), + (Some(network), None, None) => Ok(locator.read_network(network)?), + (_, Some(rpc_url), Some(network_passphrase)) => Ok(Network { rpc_url, rpc_headers: self.rpc_headers.clone(), network_passphrase, - }) - } else { - Err(Error::Network) + }), } } } From 3209130957a9f8b6c3385d8430b1b0870cf16e10 Mon Sep 17 00:00:00 2001 From: Dhanraj Avhad <95683132+Dhanraj30@users.noreply.github.com> Date: Wed, 6 Nov 2024 04:06:14 +0530 Subject: [PATCH 51/55] =?UTF-8?q?Fix:=20stellar=20contract=20info=20*=20co?= =?UTF-8?q?mmands=20require=20network=20when=20network=20no=E2=80=A6=20(#1?= =?UTF-8?q?676)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix: stellar contract info * commands require network when network not required * Added wasm field to Args struct and updated Cmd::run logic * fix: prevent network resolution when local wasm is provided in fetch_wasm * Update cmd/soroban-cli/src/commands/global.rs Co-authored-by: Willem Wyndham * Update cmd/soroban-cli/src/commands/network/mod.rs Co-authored-by: Willem Wyndham * Update cmd/soroban-cli/src/commands/global.rs Co-authored-by: Willem Wyndham * Update cmd/soroban-cli/src/commands/contract/info/shared.rs Co-authored-by: Willem Wyndham * fix: make network args optional and postpone error until resolution --------- Co-authored-by: Willem Wyndham Co-authored-by: Willem Wyndham Co-authored-by: Jane Wang --- .../src/commands/contract/info/shared.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/info/shared.rs b/cmd/soroban-cli/src/commands/contract/info/shared.rs index 0974632ae..b34ec7da8 100644 --- a/cmd/soroban-cli/src/commands/contract/info/shared.rs +++ b/cmd/soroban-cli/src/commands/contract/info/shared.rs @@ -59,11 +59,16 @@ pub enum Error { } pub async fn fetch_wasm(args: &Args) -> Result>, Error> { - let network = &args.network.get(&args.locator)?; + // Check if a local WASM file path is provided + if let Some(path) = &args.wasm { + // Read the WASM file and return its contents + let wasm_bytes = wasm::Args { wasm: path.clone() }.read()?; + return Ok(Some(wasm_bytes)); + } - let wasm = if let Some(path) = &args.wasm { - wasm::Args { wasm: path.clone() }.read()? - } else if let Some(wasm_hash) = &args.wasm_hash { + // If no local wasm, then check for wasm_hash and fetch from the network + let network = &args.network.get(&args.locator)?; + let wasm = if let Some(wasm_hash) = &args.wasm_hash { let hash = hex::decode(wasm_hash) .map_err(|_| InvalidWasmHash(wasm_hash.clone()))? .try_into() From 6e645f0109ad9c59fe7ccc87b12cc3a164994c87 Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Tue, 5 Nov 2024 21:17:21 -0800 Subject: [PATCH 52/55] Add global config for default network and identity. (#1690) --- FULL_HELP_DOCS.md | 50 +++++++++++++++ cmd/crates/soroban-test/tests/it/config.rs | 53 +++++++++++++++ cmd/soroban-cli/src/commands/env/mod.rs | 64 +++++++++++++++++++ cmd/soroban-cli/src/commands/keys/default.rs | 35 ++++++++++ cmd/soroban-cli/src/commands/keys/mod.rs | 20 ++++++ cmd/soroban-cli/src/commands/mod.rs | 24 ++++++- .../src/commands/network/default.rs | 34 ++++++++++ cmd/soroban-cli/src/commands/network/mod.rs | 15 +++++ cmd/soroban-cli/src/config/locator.rs | 28 ++++++-- cmd/soroban-cli/src/config/mod.rs | 53 +++++++++++++-- 10 files changed, 365 insertions(+), 11 deletions(-) create mode 100644 cmd/soroban-cli/src/commands/env/mod.rs create mode 100644 cmd/soroban-cli/src/commands/keys/default.rs create mode 100644 cmd/soroban-cli/src/commands/network/default.rs diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index 8c151cb84..aa2c8e865 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -45,6 +45,7 @@ Anything after the `--` double dash (the "slop") is parsed as arguments to the c * `contract` — Tools for smart contract developers * `events` — Watch the network for contract events +* `env` — Prints the current environment variables or defaults to the stdout, in a format that can be used as .env file. Environment variables have precedency over defaults * `keys` — Create and manage identities including keys and addresses * `network` — Start and configure networks * `snapshot` — Download a snapshot of a ledger from an archive @@ -897,6 +898,19 @@ Watch the network for contract events +## `stellar env` + +Prints the current environment variables or defaults to the stdout, in a format that can be used as .env file. Environment variables have precedency over defaults + +**Usage:** `stellar env [OPTIONS]` + +###### **Options:** + +* `--global` — Use global config +* `--config-dir ` — Location of config directory, default is "." + + + ## `stellar keys` Create and manage identities including keys and addresses @@ -912,6 +926,7 @@ Create and manage identities including keys and addresses * `ls` — List identities * `rm` — Remove an identity * `show` — Given an identity return its private key +* `use` — Set the default identity that will be used on all commands. This allows you to skip `--source-account` or setting a environment variable, while reusing this value in all commands that require it @@ -1052,6 +1067,23 @@ Given an identity return its private key +## `stellar keys use` + +Set the default identity that will be used on all commands. This allows you to skip `--source-account` or setting a environment variable, while reusing this value in all commands that require it + +**Usage:** `stellar keys use [OPTIONS] ` + +###### **Arguments:** + +* `` — Set the default network name + +###### **Options:** + +* `--global` — Use global config +* `--config-dir ` — Location of config directory, default is "." + + + ## `stellar network` Start and configure networks @@ -1065,6 +1097,7 @@ Start and configure networks * `ls` — List networks * `start` — ⚠️ Deprecated: use `stellar container start` instead * `stop` — ⚠️ Deprecated: use `stellar container stop` instead +* `use` — Set the default network that will be used on all commands. This allows you to skip `--network` or setting a environment variable, while reusing this value in all commands that require it * `container` — Commands to start, stop and get logs for a quickstart container @@ -1174,6 +1207,23 @@ Stop a network started with `network start`. For example, if you ran `stellar ne +## `stellar network use` + +Set the default network that will be used on all commands. This allows you to skip `--network` or setting a environment variable, while reusing this value in all commands that require it + +**Usage:** `stellar network use [OPTIONS] ` + +###### **Arguments:** + +* `` — Set the default network name + +###### **Options:** + +* `--global` — Use global config +* `--config-dir ` — Location of config directory, default is "." + + + ## `stellar network container` Commands to start, stop and get logs for a quickstart container diff --git a/cmd/crates/soroban-test/tests/it/config.rs b/cmd/crates/soroban-test/tests/it/config.rs index 31d60e116..b796910a8 100644 --- a/cmd/crates/soroban-test/tests/it/config.rs +++ b/cmd/crates/soroban-test/tests/it/config.rs @@ -340,3 +340,56 @@ fn config_dirs_precedence() { )) .stdout("SAQMV6P3OWM2SKCK3OEWNXSRYWK5RNNUL5CPHQGIJF2WVT4EI2BZ63GG\n"); } + +#[test] +fn set_default_identity() { + let sandbox = TestEnv::default(); + + sandbox + .new_assert_cmd("keys") + .env( + "SOROBAN_SECRET_KEY", + "SC4ZPYELVR7S7EE7KZDZN3ETFTNQHHLTUL34NUAAWZG5OK2RGJ4V2U3Z", + ) + .arg("add") + .arg("alice") + .assert() + .success(); + + sandbox + .new_assert_cmd("keys") + .arg("use") + .arg("alice") + .assert() + .stderr(predicate::str::contains( + "The default source account is set to `alice`", + )) + .success(); + + sandbox + .new_assert_cmd("env") + .assert() + .stdout(predicate::str::contains("STELLAR_ACCOUNT=alice")) + .success(); +} + +#[test] +fn set_default_network() { + let sandbox = TestEnv::default(); + + sandbox + .new_assert_cmd("network") + .arg("use") + .arg("testnet") + .assert() + .stderr(predicate::str::contains( + "The default network is set to `testnet`", + )) + .success(); + + sandbox + .new_assert_cmd("env") + .assert() + .stdout(predicate::str::contains("STELLAR_NETWORK=testnet")) + .success(); +} diff --git a/cmd/soroban-cli/src/commands/env/mod.rs b/cmd/soroban-cli/src/commands/env/mod.rs new file mode 100644 index 000000000..4d745400c --- /dev/null +++ b/cmd/soroban-cli/src/commands/env/mod.rs @@ -0,0 +1,64 @@ +use crate::{ + commands::global, + config::{ + locator::{self}, + Config, + }, + print::Print, +}; +use clap::Parser; + +#[derive(Debug, Parser)] +pub struct Cmd { + #[command(flatten)] + pub config_locator: locator::Args, +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Locator(#[from] locator::Error), +} + +impl Cmd { + pub fn run(&self, global_args: &global::Args) -> Result<(), Error> { + let print = Print::new(global_args.quiet); + let config = Config::new()?; + let mut lines: Vec<(String, String)> = Vec::new(); + + if let Some(data) = get("STELLAR_NETWORK", config.defaults.network) { + lines.push(data); + } + + if let Some(data) = get("STELLAR_ACCOUNT", config.defaults.identity) { + lines.push(data); + } + + if lines.is_empty() { + print.warnln("No defaults or environment variables set".to_string()); + return Ok(()); + } + + let max_len = lines.iter().map(|l| l.0.len()).max().unwrap_or(0); + + lines.sort(); + + for (value, source) in lines { + println!("{value:max_len$} # {source}"); + } + + Ok(()) + } +} + +fn get(env_var: &str, default_value: Option) -> Option<(String, String)> { + if let Ok(value) = std::env::var(env_var) { + return Some((format!("{env_var}={value}"), "env".to_string())); + } + + if let Some(value) = default_value { + return Some((format!("{env_var}={value}"), "default".to_string())); + } + + None +} diff --git a/cmd/soroban-cli/src/commands/keys/default.rs b/cmd/soroban-cli/src/commands/keys/default.rs new file mode 100644 index 000000000..9aa180b6d --- /dev/null +++ b/cmd/soroban-cli/src/commands/keys/default.rs @@ -0,0 +1,35 @@ +use clap::command; + +use crate::{commands::global, config::locator, print::Print}; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Config(#[from] locator::Error), +} + +#[derive(Debug, clap::Parser, Clone)] +#[group(skip)] +pub struct Cmd { + /// Set the default network name. + pub name: String, + + #[command(flatten)] + pub config_locator: locator::Args, +} + +impl Cmd { + pub fn run(&self, global_args: &global::Args) -> Result<(), Error> { + let printer = Print::new(global_args.quiet); + let _ = self.config_locator.read_identity(&self.name)?; + + self.config_locator.write_default_identity(&self.name)?; + + printer.infoln(format!( + "The default source account is set to `{}`", + self.name, + )); + + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/keys/mod.rs b/cmd/soroban-cli/src/commands/keys/mod.rs index 30df5ccee..8729ee9af 100644 --- a/cmd/soroban-cli/src/commands/keys/mod.rs +++ b/cmd/soroban-cli/src/commands/keys/mod.rs @@ -3,6 +3,7 @@ use clap::Parser; pub mod add; pub mod address; +pub mod default; pub mod fund; pub mod generate; pub mod ls; @@ -13,18 +14,30 @@ pub mod show; pub enum Cmd { /// Add a new identity (keypair, ledger, macOS keychain) Add(add::Cmd), + /// Given an identity return its address (public key) Address(address::Cmd), + /// Fund an identity on a test network Fund(fund::Cmd), + /// Generate a new identity with a seed phrase, currently 12 words Generate(generate::Cmd), + /// List identities Ls(ls::Cmd), + /// Remove an identity Rm(rm::Cmd), + /// Given an identity return its private key Show(show::Cmd), + + /// Set the default identity that will be used on all commands. + /// This allows you to skip `--source-account` or setting a environment + /// variable, while reusing this value in all commands that require it. + #[command(name = "use")] + Default(default::Cmd), } #[derive(thiserror::Error, Debug)] @@ -34,18 +47,24 @@ pub enum Error { #[error(transparent)] Address(#[from] address::Error), + #[error(transparent)] Fund(#[from] fund::Error), #[error(transparent)] Generate(#[from] generate::Error), + #[error(transparent)] Rm(#[from] rm::Error), + #[error(transparent)] Ls(#[from] ls::Error), #[error(transparent)] Show(#[from] show::Error), + + #[error(transparent)] + Default(#[from] default::Error), } impl Cmd { @@ -58,6 +77,7 @@ impl Cmd { Cmd::Ls(cmd) => cmd.run()?, Cmd::Rm(cmd) => cmd.run()?, Cmd::Show(cmd) => cmd.run()?, + Cmd::Default(cmd) => cmd.run(global_args)?, }; Ok(()) } diff --git a/cmd/soroban-cli/src/commands/mod.rs b/cmd/soroban-cli/src/commands/mod.rs index a6a85e816..f1febae27 100644 --- a/cmd/soroban-cli/src/commands/mod.rs +++ b/cmd/soroban-cli/src/commands/mod.rs @@ -8,6 +8,7 @@ use crate::config; pub mod cache; pub mod completion; pub mod contract; +pub mod env; pub mod events; pub mod global; pub mod keys; @@ -116,7 +117,8 @@ impl Root { Cmd::Version(version) => version.run(), Cmd::Keys(id) => id.run(&self.global_args).await?, Cmd::Tx(tx) => tx.run(&self.global_args).await?, - Cmd::Cache(data) => data.run()?, + Cmd::Cache(cache) => cache.run()?, + Cmd::Env(env) => env.run(&self.global_args)?, }; Ok(()) } @@ -135,9 +137,15 @@ pub enum Cmd { /// Tools for smart contract developers #[command(subcommand)] Contract(contract::Cmd), + /// Watch the network for contract events Events(events::Cmd), + /// Prints the current environment variables or defaults to the stdout, in + /// a format that can be used as .env file. Environment variables have + /// precedency over defaults. + Env(env::Cmd), + /// Create and manage identities including keys and addresses #[command(subcommand)] Keys(keys::Cmd), @@ -160,9 +168,11 @@ pub enum Cmd { /// Print shell completion code for the specified shell. #[command(long_about = completion::LONG_ABOUT)] Completion(completion::Cmd), + /// Cache for transactions and contract specs #[command(subcommand)] Cache(cache::Cmd), + /// Print version information Version(version::Cmd), } @@ -172,24 +182,36 @@ pub enum Error { // TODO: stop using Debug for displaying errors #[error(transparent)] Contract(#[from] contract::Error), + #[error(transparent)] Events(#[from] events::Error), + #[error(transparent)] Keys(#[from] keys::Error), + #[error(transparent)] Xdr(#[from] stellar_xdr::cli::Error), + #[error(transparent)] Clap(#[from] clap::error::Error), + #[error(transparent)] Plugin(#[from] plugin::Error), + #[error(transparent)] Network(#[from] network::Error), + #[error(transparent)] Snapshot(#[from] snapshot::Error), + #[error(transparent)] Tx(#[from] tx::Error), + #[error(transparent)] Cache(#[from] cache::Error), + + #[error(transparent)] + Env(#[from] env::Error), } #[async_trait] diff --git a/cmd/soroban-cli/src/commands/network/default.rs b/cmd/soroban-cli/src/commands/network/default.rs new file mode 100644 index 000000000..337e08a69 --- /dev/null +++ b/cmd/soroban-cli/src/commands/network/default.rs @@ -0,0 +1,34 @@ +use clap::command; + +use crate::{commands::global, print::Print}; + +use super::locator; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Config(#[from] locator::Error), +} + +#[derive(Debug, clap::Parser, Clone)] +#[group(skip)] +pub struct Cmd { + /// Set the default network name. + pub name: String, + + #[command(flatten)] + pub config_locator: locator::Args, +} + +impl Cmd { + pub fn run(&self, global_args: &global::Args) -> Result<(), Error> { + let printer = Print::new(global_args.quiet); + let _ = self.config_locator.read_network(&self.name)?; + + self.config_locator.write_default_network(&self.name)?; + + printer.infoln(format!("The default network is set to `{}`", self.name)); + + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/network/mod.rs b/cmd/soroban-cli/src/commands/network/mod.rs index 8dd61b394..a7519bc83 100644 --- a/cmd/soroban-cli/src/commands/network/mod.rs +++ b/cmd/soroban-cli/src/commands/network/mod.rs @@ -6,6 +6,7 @@ use super::{config::locator, global}; pub mod add; pub mod container; +pub mod default; pub mod ls; pub mod rm; @@ -13,10 +14,13 @@ pub mod rm; pub enum Cmd { /// Add a new network Add(add::Cmd), + /// Remove a network Rm(rm::Cmd), + /// List networks Ls(ls::Cmd), + /// ⚠️ Deprecated: use `stellar container start` instead /// /// Start network @@ -29,11 +33,18 @@ pub enum Cmd { /// /// `docker run --rm -p 8000:8000 --name stellar stellar/quickstart:testing --testnet --enable rpc,horizon` Start(container::StartCmd), + /// ⚠️ Deprecated: use `stellar container stop` instead /// /// Stop a network started with `network start`. For example, if you ran `stellar network start local`, you can use `stellar network stop local` to stop it. Stop(container::StopCmd), + /// Set the default network that will be used on all commands. + /// This allows you to skip `--network` or setting a environment variable, + /// while reusing this value in all commands that require it. + #[command(name = "use")] + Default(default::Cmd), + /// Commands to start, stop and get logs for a quickstart container #[command(subcommand)] Container(container::Cmd), @@ -41,6 +52,9 @@ pub enum Cmd { #[derive(thiserror::Error, Debug)] pub enum Error { + #[error(transparent)] + Default(#[from] default::Error), + #[error(transparent)] Add(#[from] add::Error), @@ -81,6 +95,7 @@ pub enum Error { impl Cmd { pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { match self { + Cmd::Default(cmd) => cmd.run(global_args)?, Cmd::Add(cmd) => cmd.run()?, Cmd::Rm(new) => new.run()?, Cmd::Ls(cmd) => cmd.run()?, diff --git a/cmd/soroban-cli/src/config/locator.rs b/cmd/soroban-cli/src/config/locator.rs index 0a13c4cc3..60b7856a6 100644 --- a/cmd/soroban-cli/src/config/locator.rs +++ b/cmd/soroban-cli/src/config/locator.rs @@ -5,8 +5,7 @@ use std::{ ffi::OsStr, fmt::Display, fs::{self, create_dir_all, OpenOptions}, - io, - io::Write, + io::{self, Write}, path::{Path, PathBuf}, str::FromStr, }; @@ -18,10 +17,13 @@ use super::{ alias, network::{self, Network}, secret::Secret, + Config, }; #[derive(thiserror::Error, Debug)] pub enum Error { + #[error(transparent)] + TomlSerialize(#[from] toml::ser::Error), #[error("Failed to find home directory")] HomeDirNotFound, #[error("Failed read current directory")] @@ -34,6 +36,8 @@ pub enum Error { SecretFileRead { path: PathBuf }, #[error("Failed to read network file: {path};\nProbably need to use `stellar network add`")] NetworkFileRead { path: PathBuf }, + #[error("Failed to read file: {path}")] + FileRead { path: PathBuf }, #[error(transparent)] Toml(#[from] toml::de::Error), #[error("Secret file failed to deserialize")] @@ -163,6 +167,14 @@ impl Args { KeyType::Network.write(name, network, &self.config_dir()?) } + pub fn write_default_network(&self, name: &str) -> Result<(), Error> { + Config::new()?.set_network(name).save() + } + + pub fn write_default_identity(&self, name: &str) -> Result<(), Error> { + Config::new()?.set_identity(name).save() + } + pub fn list_identities(&self) -> Result, Error> { Ok(KeyType::Identity .list_paths(&self.local_and_global()?)? @@ -344,6 +356,12 @@ impl Args { } } +impl Pwd for Args { + fn set_pwd(&mut self, pwd: &Path) { + self.config_dir = Some(pwd.to_path_buf()); + } +} + pub fn ensure_directory(dir: PathBuf) -> Result { let parent = dir.parent().ok_or(Error::HomeDirNotFound)?; std::fs::create_dir_all(parent).map_err(|_| dir_creation_failed(parent))?; @@ -496,8 +514,6 @@ pub fn global_config_path() -> Result { Ok(stellar_dir) } -impl Pwd for Args { - fn set_pwd(&mut self, pwd: &Path) { - self.config_dir = Some(pwd.to_path_buf()); - } +pub fn config_file() -> Result { + Ok(global_config_path()?.join("config.toml")) } diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index b961f0f67..21ebb1fe9 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -1,6 +1,10 @@ use address::Address; use clap::{arg, command}; use serde::{Deserialize, Serialize}; +use std::{ + fs::{self, File}, + io::Write, +}; use crate::{ print::Print, @@ -8,7 +12,6 @@ use crate::{ xdr::{self, SequenceNumber, Transaction, TransactionEnvelope}, Pwd, }; - use network::Network; pub mod address; @@ -125,9 +128,6 @@ impl Pwd for Args { } } -#[derive(Default, Serialize, Deserialize)] -pub struct Config {} - #[derive(Debug, clap::Args, Clone, Default)] #[group(skip)] pub struct ArgsLocatorAndNetwork { @@ -143,3 +143,48 @@ impl ArgsLocatorAndNetwork { Ok(self.network.get(&self.locator)?) } } + +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct Config { + pub defaults: Defaults, +} + +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct Defaults { + pub network: Option, + pub identity: Option, +} + +impl Config { + pub fn new() -> Result { + let path = locator::config_file()?; + + if path.exists() { + let data = fs::read(&path).map_err(|_| locator::Error::FileRead { path })?; + + Ok(toml::from_slice(&data)?) + } else { + Ok(Config::default()) + } + } + + #[must_use] + pub fn set_network(mut self, s: &str) -> Self { + self.defaults.network = Some(s.to_string()); + self + } + + #[must_use] + pub fn set_identity(mut self, s: &str) -> Self { + self.defaults.identity = Some(s.to_string()); + self + } + + pub fn save(&self) -> Result<(), locator::Error> { + let toml_string = toml::to_string(&self)?; + let mut file = File::create(locator::config_file()?)?; + file.write_all(toml_string.as_bytes())?; + + Ok(()) + } +} From 8163f30785d66c83101cc4b4e1064374f57d9356 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Wed, 6 Nov 2024 02:42:27 -0500 Subject: [PATCH 53/55] feat: Add parsing to deploy to support new constructor feature (#1574) --- .github/workflows/rpc-tests.yml | 35 ++-- Cargo.lock | 12 ++ Cargo.toml | 11 +- FULL_HELP_DOCS.md | 6 +- .../test-wasms/constructor/Cargo.toml | 18 ++ .../test-wasms/constructor/src/lib.rs | 18 ++ .../soroban-test/tests/it/integration.rs | 1 + .../tests/it/integration/constructor.rs | 73 +++++++++ .../soroban-test/tests/it/integration/util.rs | 1 + .../src/commands/contract/deploy/asset.rs | 6 +- .../src/commands/contract/deploy/wasm.rs | 154 ++++++++++++------ .../src/commands/contract/extend.rs | 9 +- .../src/commands/contract/install.rs | 6 +- .../src/commands/contract/restore.rs | 7 +- cmd/soroban-cli/src/commands/tx/args.rs | 2 +- cmd/soroban-cli/src/config/data.rs | 2 +- cmd/soroban-cli/src/config/mod.rs | 13 +- 17 files changed, 289 insertions(+), 85 deletions(-) create mode 100644 cmd/crates/soroban-test/tests/fixtures/test-wasms/constructor/Cargo.toml create mode 100644 cmd/crates/soroban-test/tests/fixtures/test-wasms/constructor/src/lib.rs create mode 100644 cmd/crates/soroban-test/tests/it/integration/constructor.rs diff --git a/.github/workflows/rpc-tests.yml b/.github/workflows/rpc-tests.yml index 769cd21f7..75b6d7760 100644 --- a/.github/workflows/rpc-tests.yml +++ b/.github/workflows/rpc-tests.yml @@ -1,4 +1,3 @@ - name: RPC Tests on: push: @@ -15,32 +14,32 @@ jobs: runs-on: ubuntu-22.04 services: rpc: - image: stellar/quickstart:v438-testing + image: stellar/quickstart:testing ports: - 8000:8000 env: ENABLE_LOGS: true ENABLE_SOROBAN_DIAGNOSTIC_EVENTS: true NETWORK: local + PROTOCOL_VERSION: 22 options: >- --health-cmd "curl --no-progress-meter --fail-with-body -X POST \"http://localhost:8000/soroban/rpc\" -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":8675309,\"method\":\"getNetwork\"}' && curl --no-progress-meter \"http://localhost:8000/friendbot\" | grep '\"invalid_field\": \"addr\"'" --health-interval 10s --health-timeout 5s --health-retries 50 steps: - - uses: actions/checkout@v4 - - uses: actions/cache@v4 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - run: rustup update - - run: cargo build - - run: rustup target add wasm32-unknown-unknown - - run: make build-test-wasms - - run: SOROBAN_PORT=8000 cargo test --features it --package soroban-test --test it -- integration - + - uses: actions/checkout@v4 + - uses: actions/cache@v4 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - run: rustup update + - run: cargo build + - run: rustup target add wasm32-unknown-unknown + - run: make build-test-wasms + - run: SOROBAN_PORT=8000 cargo test --features it --package soroban-test --test it -- integration diff --git a/Cargo.lock b/Cargo.lock index 6fdf9ee5b..806e767ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4542,6 +4542,7 @@ version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39b6d2ec8955243394278e1fae88be3b367fcfed9cf74e5044799a90786a8642" dependencies = [ + "arbitrary", "crate-git-revision 0.0.6", "ethnum", "num-derive", @@ -4731,7 +4732,10 @@ version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d063d0df000aaec20105aab3d743660322bc0269934ea95d79fa19aa8792385" dependencies = [ + "arbitrary", "bytes-lit", + "ctor", + "ed25519-dalek 2.1.1", "rand", "rustc_version", "serde", @@ -5091,6 +5095,7 @@ version = "22.0.0-rc.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c88dc0e928b9cb65ea43836b52560bb4ead3e32895f5019ca223dc7cd1966cbf" dependencies = [ + "arbitrary", "base64 0.13.1", "clap", "crate-git-revision 0.0.6", @@ -5347,6 +5352,13 @@ dependencies = [ "test-case-core", ] +[[package]] +name = "test_constructor" +version = "21.5.0" +dependencies = [ + "soroban-sdk 22.0.0-rc.3", +] + [[package]] name = "test_custom_account" version = "21.5.0" diff --git a/Cargo.toml b/Cargo.toml index 58caad196..d5f87b38e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,8 +8,15 @@ members = [ "cmd/crates/soroban-test/tests/fixtures/hello", "cmd/crates/soroban-test/tests/fixtures/bye", ] -default-members = ["cmd/soroban-cli", "cmd/crates/soroban-spec-tools", "cmd/crates/soroban-test"] -exclude = ["cmd/crates/soroban-test/tests/fixtures/hello", "cmd/crates/soroban-test/tests/fixtures/bye"] +default-members = [ + "cmd/soroban-cli", + "cmd/crates/soroban-spec-tools", + "cmd/crates/soroban-test", +] +exclude = [ + "cmd/crates/soroban-test/tests/fixtures/hello", + "cmd/crates/soroban-test/tests/fixtures/bye", +] [workspace.package] version = "21.5.0" diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index aa2c8e865..ae25453c6 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -388,7 +388,11 @@ If no keys are specified the contract itself is extended. Deploy a wasm contract -**Usage:** `stellar contract deploy [OPTIONS] --source-account <--wasm |--wasm-hash >` +**Usage:** `stellar contract deploy [OPTIONS] --source-account <--wasm |--wasm-hash > [-- ...]` + +###### **Arguments:** + +* `` — If provided, will be passed to the contract's `__constructor` function with provided arguments for that function as `--arg-name value` ###### **Options:** diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/constructor/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/test-wasms/constructor/Cargo.toml new file mode 100644 index 000000000..ef1dd882c --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/constructor/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "test_constructor" +version = "21.5.0" +authors = ["Stellar Development Foundation "] +license = "Apache-2.0" +edition = "2021" +publish = false +rust-version.workspace = true + +[lib] +crate-type = ["cdylib"] +doctest = false + +[dependencies] +soroban-sdk = { workspace = true } + +[dev-dependencies] +soroban-sdk = { workspace = true, features = ["testutils"]} diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/constructor/src/lib.rs b/cmd/crates/soroban-test/tests/fixtures/test-wasms/constructor/src/lib.rs new file mode 100644 index 000000000..8b867968f --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/constructor/src/lib.rs @@ -0,0 +1,18 @@ +#![no_std] +use soroban_sdk::{contract, contractimpl, symbol_short, Env, Symbol}; + +#[contract] +pub struct Contract; +const COUNTER: Symbol = symbol_short!("COUNTER"); + +#[contractimpl] +impl Contract { + /// Example constructor + pub fn __constructor(env: Env, counter: u32) { + env.storage().persistent().set(&COUNTER, &counter); + } + /// Counter value + pub fn counter(env: Env) -> u32 { + env.storage().persistent().get(&COUNTER).unwrap() + } +} diff --git a/cmd/crates/soroban-test/tests/it/integration.rs b/cmd/crates/soroban-test/tests/it/integration.rs index 3e8521869..d6db5842b 100644 --- a/cmd/crates/soroban-test/tests/it/integration.rs +++ b/cmd/crates/soroban-test/tests/it/integration.rs @@ -1,4 +1,5 @@ mod bindings; +mod constructor; mod cookbook; mod custom_types; mod dotenv; diff --git a/cmd/crates/soroban-test/tests/it/integration/constructor.rs b/cmd/crates/soroban-test/tests/it/integration/constructor.rs new file mode 100644 index 000000000..1919b56b0 --- /dev/null +++ b/cmd/crates/soroban-test/tests/it/integration/constructor.rs @@ -0,0 +1,73 @@ +use assert_cmd::Command; + +use soroban_cli::xdr::{ + self, CreateContractArgsV2, HostFunction, InvokeHostFunctionOp, Limits, OperationBody, ReadXdr, + Transaction, TransactionV1Envelope, +}; +use soroban_test::{AssertExt, TestEnv}; + +use super::util::CONSTRUCTOR; + +fn constructor_cmd(sandbox: &TestEnv, value: u32, arg: &str) -> Command { + let mut cmd = sandbox.new_assert_cmd("contract"); + cmd.arg("deploy") + .arg("--wasm") + .arg(CONSTRUCTOR.path()) + .arg("--alias=init"); + if !arg.is_empty() { + cmd.arg(arg); + } + cmd.arg("--").arg("--counter").arg(value.to_string()); + cmd +} + +#[tokio::test] +async fn deploy_constructor_contract() { + let sandbox = TestEnv::new(); + let value = 100; + let build = constructor_cmd(&sandbox, value, "--build-only") + .assert() + .stdout_as_str(); + let tx = xdr::TransactionEnvelope::from_xdr_base64(&build, Limits::none()).unwrap(); + let ops = if let xdr::TransactionEnvelope::Tx(TransactionV1Envelope { + tx: Transaction { operations, .. }, + .. + }) = tx + { + operations + } else { + panic!() + } + .to_vec(); + let first = ops.first().unwrap(); + let args = match first { + xdr::Operation { + body: + OperationBody::InvokeHostFunction(InvokeHostFunctionOp { + host_function: + HostFunction::CreateContractV2(CreateContractArgsV2 { + constructor_args, .. + }), + .. + }), + .. + } => constructor_args, + _ => panic!("expected invoke host function with create contract v2"), + } + .to_vec(); + + match args.first().unwrap() { + xdr::ScVal::U32(u32) => assert_eq!(*u32, value), + _ => panic!("Expected U32"), + } + + constructor_cmd(&sandbox, value, "").assert().success(); + + let res = sandbox + .new_assert_cmd("contract") + .args(["invoke", "--id=init", "--", "counter"]) + .assert() + .success() + .stdout_as_str(); + assert_eq!(res.trim(), value.to_string()); +} diff --git a/cmd/crates/soroban-test/tests/it/integration/util.rs b/cmd/crates/soroban-test/tests/it/integration/util.rs index 438428e38..486b00a1b 100644 --- a/cmd/crates/soroban-test/tests/it/integration/util.rs +++ b/cmd/crates/soroban-test/tests/it/integration/util.rs @@ -6,6 +6,7 @@ use soroban_test::{TestEnv, Wasm}; use std::fmt::Display; pub const HELLO_WORLD: &Wasm = &Wasm::Custom("test-wasms", "test_hello_world"); +pub const CONSTRUCTOR: &Wasm = &Wasm::Custom("test-wasms", "test_constructor"); 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"); diff --git a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs index 263908521..169c0109a 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs @@ -93,11 +93,11 @@ impl NetworkRunnable for Cmd { .verify_network_passphrase(Some(&network.network_passphrase)) .await?; let source_account = config.source_account()?; - // Get the account sequence number - let public_strkey = source_account.to_string(); // TODO: use symbols for the method names (both here and in serve) - let account_details = client.get_account(&public_strkey).await?; + let account_details = client + .get_account(&source_account.clone().to_string()) + .await?; let sequence: i64 = account_details.seq_num.into(); let network_passphrase = &network.network_passphrase; let contract_id = contract_id_hash_from_asset(asset, network_passphrase); diff --git a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs index 21c685b93..9bf63802c 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs @@ -1,33 +1,38 @@ use std::array::TryFromSliceError; +use std::ffi::OsString; use std::fmt::Debug; use std::num::ParseIntError; use crate::xdr::{ AccountId, ContractExecutable, ContractIdPreimage, ContractIdPreimageFromAddress, - CreateContractArgs, Error as XdrError, Hash, HostFunction, InvokeHostFunctionOp, Limits, Memo, - MuxedAccount, Operation, OperationBody, Preconditions, PublicKey, ScAddress, SequenceNumber, - Transaction, TransactionExt, Uint256, VecM, WriteXdr, + CreateContractArgs, CreateContractArgsV2, Error as XdrError, Hash, HostFunction, + InvokeContractArgs, InvokeHostFunctionOp, Limits, Memo, MuxedAccount, Operation, OperationBody, + Preconditions, PublicKey, ScAddress, SequenceNumber, Transaction, TransactionExt, Uint256, + VecM, WriteXdr, }; use clap::{arg, command, Parser}; use rand::Rng; use regex::Regex; +use soroban_spec_tools::contract as contract_spec; + use crate::{ assembled::simulate_and_assemble_transaction, - commands::{contract::install, HEADING_RPC}, - config::{self, data, locator, network}, - rpc, utils, wasm, -}; -use crate::{ commands::{ - contract::{self, id::wasm::get_contract_id}, + contract::{self, arg_parsing, id::wasm::get_contract_id, install}, global, txn_result::{TxnEnvelopeResult, TxnResult}, - NetworkRunnable, + NetworkRunnable, HEADING_RPC, }, + config::{self, data, locator, network}, print::Print, + rpc, + utils::{self, rpc::get_remote_wasm_from_hash}, + wasm, }; +pub const CONSTRUCTOR_FUNCTION_NAME: &str = "__constructor"; + #[derive(Parser, Debug, Clone)] #[command(group( clap::ArgGroup::new("wasm_src") @@ -60,6 +65,9 @@ pub struct Cmd { /// configuration without asking for confirmation. #[arg(long, value_parser = clap::builder::ValueParser::new(alias_validator))] pub alias: Option, + /// If provided, will be passed to the contract's `__constructor` function with provided arguments for that function as `--arg-name value` + #[arg(last = true, id = "CONTRACT_CONSTRUCTOR_ARGS")] + pub slop: Vec, } #[derive(thiserror::Error, Debug)] @@ -110,6 +118,10 @@ pub enum Error { InvalidAliasFormat { alias: String }, #[error(transparent)] Locator(#[from] locator::Error), + #[error(transparent)] + ContractSpec(#[from] contract_spec::Error), + #[error(transparent)] + ArgParse(#[from] arg_parsing::Error), #[error("Only ed25519 accounts are allowed")] OnlyEd25519AccountsAllowed, } @@ -157,6 +169,7 @@ impl NetworkRunnable for Cmd { type Error = Error; type Result = TxnResult; + #[allow(clippy::too_many_lines)] async fn run_against_rpc_server( &self, global_args: Option<&global::Args>, @@ -211,21 +224,54 @@ impl NetworkRunnable for Cmd { client .verify_network_passphrase(Some(&network.network_passphrase)) .await?; + let MuxedAccount::Ed25519(bytes) = config.source_account()? else { return Err(Error::OnlyEd25519AccountsAllowed); }; + let source_account = AccountId(PublicKey::PublicKeyTypeEd25519(bytes)); + let contract_id_preimage = ContractIdPreimage::Address(ContractIdPreimageFromAddress { + address: ScAddress::Account(source_account.clone()), + salt: Uint256(salt), + }); + let contract_id = + get_contract_id(contract_id_preimage.clone(), &network.network_passphrase)?; + let raw_wasm = if let Some(wasm) = self.wasm.as_ref() { + wasm::Args { wasm: wasm.clone() }.read()? + } else { + get_remote_wasm_from_hash(&client, &wasm_hash).await? + }; + let entries = soroban_spec_tools::contract::Spec::new(&raw_wasm)?.spec; + let res = soroban_spec_tools::Spec::new(entries.clone()); + let constructor_params = if let Ok(func) = res.find_function(CONSTRUCTOR_FUNCTION_NAME) { + if func.inputs.len() == 0 { + None + } else { + let mut slop = vec![OsString::from(CONSTRUCTOR_FUNCTION_NAME)]; + slop.extend_from_slice(&self.slop); + Some( + arg_parsing::build_host_function_parameters( + &stellar_strkey::Contract(contract_id.0), + &slop, + &entries, + config, + )? + .2, + ) + } + } else { + None + }; - let key = stellar_strkey::ed25519::PublicKey(bytes.into()); // Get the account sequence number - let account_details = client.get_account(&key.to_string()).await?; + let account_details = client.get_account(&source_account.to_string()).await?; let sequence: i64 = account_details.seq_num.into(); - let (txn, contract_id) = build_create_contract_tx( + let txn = build_create_contract_tx( wasm_hash, sequence + 1, self.fee.fee, - &network.network_passphrase, - salt, - key, + source_account, + contract_id_preimage, + constructor_params.as_ref(), )?; if self.fee.build_only { @@ -266,33 +312,39 @@ impl NetworkRunnable for Cmd { } fn build_create_contract_tx( - hash: Hash, + wasm_hash: Hash, sequence: i64, fee: u32, - network_passphrase: &str, - salt: [u8; 32], - key: stellar_strkey::ed25519::PublicKey, -) -> Result<(Transaction, stellar_strkey::Contract), Error> { - let source_account = AccountId(PublicKey::PublicKeyTypeEd25519(key.0.into())); - - let contract_id_preimage = ContractIdPreimage::Address(ContractIdPreimageFromAddress { - address: ScAddress::Account(source_account), - salt: Uint256(salt), - }); - let contract_id = get_contract_id(contract_id_preimage.clone(), network_passphrase)?; - - let op = Operation { - source_account: None, - body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { - host_function: HostFunction::CreateContract(CreateContractArgs { - contract_id_preimage, - executable: ContractExecutable::Wasm(hash), + key: AccountId, + contract_id_preimage: ContractIdPreimage, + constructor_params: Option<&InvokeContractArgs>, +) -> Result { + let op = if let Some(InvokeContractArgs { args, .. }) = constructor_params { + Operation { + source_account: None, + body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { + host_function: HostFunction::CreateContractV2(CreateContractArgsV2 { + contract_id_preimage, + executable: ContractExecutable::Wasm(wasm_hash), + constructor_args: args.clone(), + }), + auth: VecM::default(), }), - auth: VecM::default(), - }), + } + } else { + Operation { + source_account: None, + body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { + host_function: HostFunction::CreateContract(CreateContractArgs { + contract_id_preimage, + executable: ContractExecutable::Wasm(wasm_hash), + }), + auth: VecM::default(), + }), + } }; let tx = Transaction { - source_account: MuxedAccount::Ed25519(key.0.into()), + source_account: key.into(), fee, seq_num: SequenceNumber(sequence), cond: Preconditions::None, @@ -301,7 +353,7 @@ fn build_create_contract_tx( ext: TransactionExt::V0, }; - Ok((tx, contract_id)) + Ok(tx) } #[cfg(test)] @@ -314,18 +366,26 @@ mod tests { .unwrap() .try_into() .unwrap(); + let salt = [0u8; 32]; + let key = + &utils::parse_secret_key("SBFGFF27Y64ZUGFAIG5AMJGQODZZKV2YQKAVUUN4HNE24XZXD2OEUVUP") + .unwrap(); + let source_account = AccountId(PublicKey::PublicKeyTypeEd25519(Uint256( + key.verifying_key().to_bytes(), + ))); + + let contract_id_preimage = ContractIdPreimage::Address(ContractIdPreimageFromAddress { + address: ScAddress::Account(source_account.clone()), + salt: Uint256(salt), + }); + let result = build_create_contract_tx( Hash(hash), 300, 1, - "Public Global Stellar Network ; September 2015", - [0u8; 32], - stellar_strkey::ed25519::PublicKey( - utils::parse_secret_key("SBFGFF27Y64ZUGFAIG5AMJGQODZZKV2YQKAVUUN4HNE24XZXD2OEUVUP") - .unwrap() - .verifying_key() - .to_bytes(), - ), + source_account, + contract_id_preimage, + None, ); assert!(result.is_ok()); diff --git a/cmd/soroban-cli/src/commands/contract/extend.rs b/cmd/soroban-cli/src/commands/contract/extend.rs index 24aac54c5..6ec8ebdb1 100644 --- a/cmd/soroban-cli/src/commands/contract/extend.rs +++ b/cmd/soroban-cli/src/commands/contract/extend.rs @@ -135,7 +135,9 @@ impl NetworkRunnable for Cmd { let extend_to = self.ledgers_to_extend(); // Get the account sequence number - let account_details = client.get_account(&source_account.to_string()).await?; + let account_details = client + .get_account(&source_account.clone().to_string()) + .await?; let sequence: i64 = account_details.seq_num.into(); let tx = Transaction { @@ -184,10 +186,7 @@ impl NetworkRunnable for Cmd { if !events.is_empty() { tracing::info!("Events:\n {events:#?}"); } - let meta = res - .result_meta - .as_ref() - .ok_or(Error::MissingOperationResult)?; + let meta = res.result_meta.ok_or(Error::MissingOperationResult)?; // The transaction from core will succeed regardless of whether it actually found & extended // the entry, so we have to inspect the result meta to tell if it worked or not. diff --git a/cmd/soroban-cli/src/commands/contract/install.rs b/cmd/soroban-cli/src/commands/contract/install.rs index cd6e93b24..a215a987d 100644 --- a/cmd/soroban-cli/src/commands/contract/install.rs +++ b/cmd/soroban-cli/src/commands/contract/install.rs @@ -137,7 +137,9 @@ impl NetworkRunnable for Cmd { // Get the account sequence number let source_account = config.source_account()?; - let account_details = client.get_account(&source_account.to_string()).await?; + let account_details = client + .get_account(&source_account.clone().to_string()) + .await?; let sequence: i64 = account_details.seq_num.into(); let (tx_without_preflight, hash) = @@ -204,7 +206,7 @@ impl NetworkRunnable for Cmd { if let Some(TransactionResult { result: TransactionResultResult::TxInternalError, .. - }) = txn_resp.result.as_ref() + }) = txn_resp.result { // Now just need to restore it and don't have to install again restore::Cmd { diff --git a/cmd/soroban-cli/src/commands/contract/restore.rs b/cmd/soroban-cli/src/commands/contract/restore.rs index 87a52a9f6..92fc4b41d 100644 --- a/cmd/soroban-cli/src/commands/contract/restore.rs +++ b/cmd/soroban-cli/src/commands/contract/restore.rs @@ -136,8 +136,9 @@ impl NetworkRunnable for Cmd { let source_account = config.source_account()?; // Get the account sequence number - let public_strkey = source_account.to_string(); - let account_details = client.get_account(&public_strkey).await?; + let account_details = client + .get_account(&source_account.clone().to_string()) + .await?; let sequence: i64 = account_details.seq_num.into(); let tx = Transaction { @@ -206,7 +207,7 @@ impl NetworkRunnable for Cmd { ); } Ok(TxnResult::Res( - parse_operations(operations).ok_or(Error::MissingOperationResult)?, + parse_operations(&operations.to_vec()).ok_or(Error::MissingOperationResult)?, )) } } diff --git a/cmd/soroban-cli/src/commands/tx/args.rs b/cmd/soroban-cli/src/commands/tx/args.rs index 7e032fd53..fc99b9591 100644 --- a/cmd/soroban-cli/src/commands/tx/args.rs +++ b/cmd/soroban-cli/src/commands/tx/args.rs @@ -39,7 +39,7 @@ impl Args { let source_account = self.source_account()?; let seq_num = self .config - .next_sequence_number(&source_account.to_string()) + .next_sequence_number(source_account.clone().account_id()) .await?; // Once we have a way to add operations this will be updated to allow for a different source account let operation = xdr::Operation { diff --git a/cmd/soroban-cli/src/config/data.rs b/cmd/soroban-cli/src/config/data.rs index bbfc6994e..f032a94d3 100644 --- a/cmd/soroban-cli/src/config/data.rs +++ b/cmd/soroban-cli/src/config/data.rs @@ -209,7 +209,7 @@ mod test { assert_eq!(rpc_uri, new_rpc_uri); match (action, original_action) { (Action::Simulate { response: a }, Action::Simulate { response: b }) => { - assert_eq!(a.cost.cpu_insns, b.cost.cpu_insns); + assert_eq!(a.min_resource_fee, b.min_resource_fee); } _ => panic!("Action mismatch"), } diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index 21ebb1fe9..11eb179ee 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -115,10 +115,19 @@ impl Args { Ok(self.network.get(&self.locator)?) } - pub async fn next_sequence_number(&self, account_str: &str) -> Result { + pub async fn next_sequence_number( + &self, + account: impl Into, + ) -> Result { let network = self.get_network()?; let client = network.rpc_client()?; - Ok((client.get_account(account_str).await?.seq_num.0 + 1).into()) + Ok((client + .get_account(&account.into().to_string()) + .await? + .seq_num + .0 + + 1) + .into()) } } From 048d11b88c50abb7d00123818a29991337526fc4 Mon Sep 17 00:00:00 2001 From: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> Date: Thu, 7 Nov 2024 03:58:33 +1000 Subject: [PATCH 54/55] Fix which macOS arch is used for binary builds (#1705) --- .github/workflows/binaries.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index f2f53bd78..25cea7366 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -33,9 +33,9 @@ jobs: - os: ubuntu-20.04 # Use 20.04 to get an older version of glibc for increased compat target: aarch64-unknown-linux-gnu - os: macos-14 - target: x86_64-apple-darwin - - os: macos-12 target: aarch64-apple-darwin + - os: macos-12 + target: x86_64-apple-darwin - os: windows-latest target: x86_64-pc-windows-msvc ext: .exe From 1fc9c343798657952a6a2bae8350589c096389e0 Mon Sep 17 00:00:00 2001 From: Gleb Date: Wed, 6 Nov 2024 13:54:46 -0800 Subject: [PATCH 55/55] Cleanup unit testing/add ARM support (#1691) * WIP: check make test * Update makefile * Cleanup * Deprecation fix * Add build target * Add libssl-dev * Print openssl dir * cfg path * cfg path * Add sysroot * libssl * ls * fix apt * ln * libssl * arm * ... * tmate * apt sources * Remove tmate * libudev arm64 * Change runner to arm * Fix rust job * Update rust-cache * Uncomment other OSes * Cleanup apt * Update rust-cache * Unpin rust-cache version --- .github/workflows/rust.yml | 14 +++++--------- Makefile | 2 +- .../tests/fixtures/test-wasms/swap/src/test.rs | 4 +++- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9ae750a77..7b046c8c3 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -47,7 +47,7 @@ jobs: sys: - os: ubuntu-latest-16-cores target: x86_64-unknown-linux-gnu - - os: ubuntu-latest-16-cores + - os: ubuntu-jammy-16-cores-arm64 target: aarch64-unknown-linux-gnu - os: macos-latest target: x86_64-apple-darwin @@ -79,14 +79,10 @@ jobs: - if: runner.os == 'Linux' run: sudo apt-get update && sudo apt-get -y install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libudev-dev - run: cargo clippy --all-targets --target ${{ matrix.sys.target }} - - run: make build-test - # TODO: enable ARM linux - - if: startsWith(matrix.sys.target, 'x86_64') || runner.os == 'macOS' - # specify directories explicitly (otherwise it will fail with missing symbols) - run: | - for I in cmd/soroban-cli cmd/crates/* cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world ; do - cargo test --target ${{ matrix.sys.target }} --manifest-path $I/Cargo.toml - done + - run: make test + env: + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc + CARGO_BUILD_TARGET: ${{ matrix.sys.target }} publish-dry-run: if: github.event_name == 'push' || startsWith(github.head_ref, 'release/') diff --git a/Makefile b/Makefile index f534410e0..92cdacd73 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ generate-full-help-doc: cargo run --bin doc-gen --features clap-markdown test: build-test - cargo test + cargo test --workspace e2e-test: cargo test --features it --test it -- integration diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/src/test.rs b/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/src/test.rs index e32b45e0a..5c973880e 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/src/test.rs +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/src/test.rs @@ -11,7 +11,9 @@ use token::Client as TokenClient; use token::StellarAssetClient as TokenAdminClient; fn create_token_contract<'a>(e: &Env, admin: &Address) -> (TokenClient<'a>, TokenAdminClient<'a>) { - let contract_address = e.register_stellar_asset_contract(admin.clone()); + let contract_address = e + .register_stellar_asset_contract_v2(admin.clone()) + .address(); ( TokenClient::new(e, &contract_address), TokenAdminClient::new(e, &contract_address),