From 2083a7afcd86ea62a7a40691ebca4e17b9283f91 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 24 Sep 2024 10:04:29 -0400 Subject: [PATCH 01/74] feat: initial ledger signer --- Cargo.lock | 1 + Cargo.toml | 4 ++++ cmd/crates/stellar-ledger/src/lib.rs | 8 ++++++-- cmd/soroban-cli/Cargo.toml | 4 +++- cmd/soroban-cli/src/config/secret.rs | 16 ++++++++++++++++ cmd/soroban-cli/src/signer.rs | 17 +++++++++++++++++ 6 files changed, 47 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cd3761b51..fe9660e0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5158,6 +5158,7 @@ dependencies = [ "soroban-spec-rust", "soroban-spec-tools", "soroban-spec-typescript", + "stellar-ledger", "stellar-rpc-client", "stellar-strkey 0.0.11", "stellar-xdr", diff --git a/Cargo.toml b/Cargo.toml index 4cf12b4d9..41d8f1c12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,10 @@ version = "21.4.0" version = "21.2.0" default-features = true +[workspace.dependencies.stellar-ledger] +version = "=21.5.0" +path = "cmd/crates/stellar-ledger" + [workspace.dependencies] stellar-strkey = "0.0.11" sep5 = "0.0.4" diff --git a/cmd/crates/stellar-ledger/src/lib.rs b/cmd/crates/stellar-ledger/src/lib.rs index c74365af2..fef0e8b80 100644 --- a/cmd/crates/stellar-ledger/src/lib.rs +++ b/cmd/crates/stellar-ledger/src/lib.rs @@ -1,10 +1,14 @@ use hd_path::HdPath; -use ledger_transport::{APDUCommand, Exchange}; +use ledger_transport::APDUCommand; +pub use ledger_transport::Exchange; + use ledger_transport_hid::{ hidapi::{HidApi, HidError}, - LedgerHIDError, TransportNativeHID, + LedgerHIDError, }; +pub use ledger_transport_hid::TransportNativeHID; + use soroban_env_host::xdr::{Hash, Transaction}; use std::vec; use stellar_strkey::DecodeError; diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index 75b562bb6..7623949fb 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -50,6 +50,8 @@ soroban-ledger-snapshot = { workspace = true } stellar-strkey = { workspace = true } soroban-sdk = { workspace = true } soroban-rpc = { workspace = true } +stellar-ledger = { workspace = true } + clap = { workspace = true, features = [ "derive", "env", @@ -113,7 +115,7 @@ 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" diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index a7fd86fda..0c17cdfc6 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -1,8 +1,11 @@ use clap::arg; use serde::{Deserialize, Serialize}; use std::{io::Write, str::FromStr}; + use stellar_strkey::ed25519::{PrivateKey, PublicKey}; +use crate::print::Print; +use crate::signer::native_ledger; use crate::{ print::Print, signer::{self, LocalKey, Signer, SignerKind}, @@ -27,6 +30,8 @@ pub enum Error { InvalidAddress(String), #[error(transparent)] Signer(#[from] signer::Error), + #[error("Ledger does not reveal secret key")] + LedgerDoesNotRevealSecretKey, } #[derive(Debug, clap::Args, Clone)] @@ -78,6 +83,7 @@ impl Args { pub enum Secret { SecretKey { secret_key: String }, SeedPhrase { seed_phrase: String }, + Ledger, } impl FromStr for Secret { @@ -92,6 +98,8 @@ impl FromStr for Secret { Ok(Secret::SeedPhrase { seed_phrase: s.to_string(), }) + } else if s == "ledger" { + Ok(Secret::Ledger) } else { Err(Error::InvalidAddress(s.to_string())) } @@ -116,6 +124,7 @@ impl Secret { .private() .0, )?, + Secret::Ledger => panic!("Ledger does not reveal secret key"), }) } @@ -132,6 +141,13 @@ impl Secret { let key = self.key_pair(index)?; SignerKind::Local(LocalKey { key }) } + Secret::Ledger => { + let hd_path: u32 = index + .unwrap_or_default() + .try_into() + .expect("uszie bigger than u32"); + SignerKind::Ledger(native_ledger(hd_path)?) + } }; Ok(Signer { kind, print }) } diff --git a/cmd/soroban-cli/src/signer.rs b/cmd/soroban-cli/src/signer.rs index bf02fdb17..132b379bb 100644 --- a/cmd/soroban-cli/src/signer.rs +++ b/cmd/soroban-cli/src/signer.rs @@ -8,6 +8,7 @@ use soroban_env_host::xdr::{ SorobanAuthorizedFunction, SorobanCredentials, Transaction, TransactionEnvelope, TransactionV1Envelope, Uint256, VecM, WriteXdr, }; +use stellar_ledger::{Exchange, LedgerSigner, TransportNativeHID}; use crate::{config::network::Network, print::Print, utils::transaction_hash}; @@ -24,6 +25,8 @@ pub enum Error { #[error("User cancelled signing, perhaps need to add -y")] UserCancelledSigning, #[error(transparent)] + Ledger(#[from] stellar_ledger::Error), + #[error(transparent)] Xdr(#[from] xdr::Error), #[error("Only Transaction envelope V1 type is supported")] UnsupportedTransactionEnvelopeType, @@ -206,6 +209,7 @@ pub struct Signer { #[allow(clippy::module_name_repetitions, clippy::large_enum_variant)] pub enum SignerKind { Local(LocalKey), + Ledger(Ledger), Lab, } @@ -235,6 +239,7 @@ impl Signer { 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)?, + SignerKind::Ledger(ledger) => todo!("ledger signing"), }; let mut sigs = signatures.clone().into_vec(); sigs.push(decorated_signature); @@ -252,6 +257,18 @@ pub struct LocalKey { pub key: ed25519_dalek::SigningKey, } +pub struct Ledger { + index: u32, + signer: LedgerSigner, +} +pub fn native_ledger(hd_path: u32) -> Result, Error> { + let signer = stellar_ledger::native()?; + Ok(Ledger { + index: hd_path, + signer, + }) +} + 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()?); From a67f58779e7e8bd9bf8f0cee9f616380f94af552 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Mon, 21 Oct 2024 14:36:55 +0100 Subject: [PATCH 02/74] fix: add arg --- cmd/crates/stellar-ledger/src/lib.rs | 1 - cmd/soroban-cli/src/config/sign_with.rs | 24 ++++++++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/cmd/crates/stellar-ledger/src/lib.rs b/cmd/crates/stellar-ledger/src/lib.rs index 84b1c8455..3421db227 100644 --- a/cmd/crates/stellar-ledger/src/lib.rs +++ b/cmd/crates/stellar-ledger/src/lib.rs @@ -8,7 +8,6 @@ use ledger_transport_hid::{ }; pub use ledger_transport_hid::TransportNativeHID; -use soroban_env_host::xdr::{Hash, Transaction}; use std::vec; use stellar_strkey::DecodeError; diff --git a/cmd/soroban-cli/src/config/sign_with.rs b/cmd/soroban-cli/src/config/sign_with.rs index 475013bc8..514132085 100644 --- a/cmd/soroban-cli/src/config/sign_with.rs +++ b/cmd/soroban-cli/src/config/sign_with.rs @@ -1,6 +1,6 @@ use crate::{ print::Print, - signer::{self, Signer, SignerKind}, + signer::{self, native_ledger, Signer, SignerKind}, xdr::{self, TransactionEnvelope}, }; use clap::arg; @@ -38,7 +38,7 @@ pub struct Args { #[arg(long, env = "STELLAR_SIGN_WITH_KEY")] pub sign_with_key: Option, - #[arg(long, requires = "sign_with_key")] + #[arg(long, conflicts_with = "sign_with_lab")] /// 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, @@ -46,6 +46,15 @@ pub struct Args { /// Sign with https://lab.stellar.org #[arg(long, conflicts_with = "sign_with_key", env = "STELLAR_SIGN_WITH_LAB")] pub sign_with_lab: bool, + + /// Sign with a ledger wallet + #[arg( + long, + conflicts_with = "sign_with_key", + conflicts_with = "sign_with_lab", + env = "STELLAR_SIGN_WITH_LEDGER" + )] + pub sign_with_ledger: bool, } impl Args { @@ -62,6 +71,17 @@ impl Args { kind: SignerKind::Lab, print, } + } else if self.sign_with_ledger { + let ledger = native_ledger( + self.hd_path + .unwrap_or_default() + .try_into() + .unwrap_or_default(), + )?; + Signer { + kind: SignerKind::Ledger(ledger), + print, + } } else { let key_or_name = self.sign_with_key.as_deref().ok_or(Error::NoSignWithKey)?; let secret = locator.key(key_or_name)?; From 0369e050cf57f96fea57af097e009912af1aea38 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Fri, 1 Nov 2024 13:07:37 -0400 Subject: [PATCH 03/74] feat: add ledger key name for lookup and reference as source account --- FULL_HELP_DOCS.md | 4 +- cmd/crates/soroban-test/src/lib.rs | 3 +- cmd/crates/stellar-ledger/src/lib.rs | 3 + .../src/commands/contract/arg_parsing.rs | 18 +-- .../src/commands/contract/deploy/asset.rs | 2 +- .../src/commands/contract/deploy/wasm.rs | 2 +- .../src/commands/contract/extend.rs | 2 +- cmd/soroban-cli/src/commands/contract/id.rs | 4 +- .../src/commands/contract/id/wasm.rs | 4 +- .../src/commands/contract/install.rs | 2 +- .../src/commands/contract/invoke.rs | 2 +- cmd/soroban-cli/src/commands/contract/mod.rs | 2 +- .../src/commands/contract/restore.rs | 2 +- cmd/soroban-cli/src/commands/keys/add.rs | 10 +- cmd/soroban-cli/src/commands/keys/address.rs | 43 +++---- cmd/soroban-cli/src/commands/keys/fund.rs | 2 +- cmd/soroban-cli/src/commands/keys/mod.rs | 2 +- .../src/commands/snapshot/create.rs | 52 ++++++-- cmd/soroban-cli/src/commands/tx/args.rs | 6 +- cmd/soroban-cli/src/commands/tx/sign.rs | 15 ++- cmd/soroban-cli/src/config/address.rs | 111 +++++++++++++++++- cmd/soroban-cli/src/config/mod.rs | 7 +- cmd/soroban-cli/src/config/sign_with.rs | 4 +- cmd/soroban-cli/src/signer.rs | 32 ++++- 24 files changed, 255 insertions(+), 79 deletions(-) diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index 162472891..dc42898f0 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -942,7 +942,7 @@ Given an identity return its address (public key) ###### **Arguments:** -* `` — Name of identity to lookup, default test identity used if not provided +* `` — Name of identity to lookup, ledger, or secret key ###### **Options:** @@ -960,7 +960,7 @@ Fund an identity on a test network ###### **Arguments:** -* `` — Name of identity to lookup, default test identity used if not provided +* `` — Name of identity to lookup, ledger, or secret key ###### **Options:** diff --git a/cmd/crates/soroban-test/src/lib.rs b/cmd/crates/soroban-test/src/lib.rs index 2c62578ef..82e05b78e 100644 --- a/cmd/crates/soroban-test/src/lib.rs +++ b/cmd/crates/soroban-test/src/lib.rs @@ -276,9 +276,10 @@ impl TestEnv { } /// Returns the public key corresponding to the test keys's `hd_path` - pub fn test_address(&self, hd_path: usize) -> String { + pub async fn test_address(&self, hd_path: usize) -> String { self.cmd::(&format!("--hd-path={hd_path}")) .public_key() + .await .unwrap() .to_string() } diff --git a/cmd/crates/stellar-ledger/src/lib.rs b/cmd/crates/stellar-ledger/src/lib.rs index 3421db227..2c4a4006f 100644 --- a/cmd/crates/stellar-ledger/src/lib.rs +++ b/cmd/crates/stellar-ledger/src/lib.rs @@ -96,6 +96,9 @@ where pub fn new(transport: T) -> Self { Self { transport } } + + /// # Errors + /// Returns an error if there is an issue with connecting with the device pub fn native() -> Result, Error> { Ok(LedgerSigner { transport: get_transport()?, diff --git a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs index 2eb3c2696..d11c4b90c 100644 --- a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs +++ b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs @@ -14,7 +14,7 @@ use crate::xdr::{ }; use crate::commands::txn_result::TxnResult; -use crate::config::{self}; +use crate::config::{self, address, secret}; use soroban_spec_tools::Spec; #[derive(thiserror::Error, Debug)] @@ -39,6 +39,10 @@ pub enum Error { Xdr(#[from] xdr::Error), #[error(transparent)] StrVal(#[from] soroban_spec_tools::Error), + #[error(transparent)] + Address(#[from] address::Error), + #[error(transparent)] + Secret(#[from] secret::Error), #[error("Missing argument {0}")] MissingArgument(String), #[error("")] @@ -79,16 +83,12 @@ pub fn build_host_function_parameters( if let Some(mut val) = matches_.get_raw(&name) { let mut s = val.next().unwrap().to_string_lossy().to_string(); if matches!(i.type_, ScSpecTypeDef::Address) { - let cmd = crate::commands::keys::address::Cmd { - name: s.clone(), - hd_path: Some(0), - locator: config.locator.clone(), - }; - if let Ok(address) = cmd.public_key() { + let addr: address::Address = s.parse()?; + if let Ok(address) = addr.resolve_muxed_account_sync(&config.locator, None) { s = address.to_string(); } - if let Ok(key) = cmd.private_key() { - signers.push(key); + if let Ok(key) = addr.resolve_secret(&config.locator) { + signers.push(SigningKey::from_bytes(&key.private_key(None)?.0)); } } spec.from_string(&s, &i.type_) diff --git a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs index 263908521..560f58f9e 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs @@ -92,7 +92,7 @@ impl NetworkRunnable for Cmd { client .verify_network_passphrase(Some(&network.network_passphrase)) .await?; - let source_account = config.source_account()?; + let source_account = config.source_account().await?; // Get the account sequence number let public_strkey = source_account.to_string(); diff --git a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs index 21c685b93..27d494182 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs @@ -211,7 +211,7 @@ impl NetworkRunnable for Cmd { client .verify_network_passphrase(Some(&network.network_passphrase)) .await?; - let MuxedAccount::Ed25519(bytes) = config.source_account()? else { + let MuxedAccount::Ed25519(bytes) = config.source_account().await? else { return Err(Error::OnlyEd25519AccountsAllowed); }; diff --git a/cmd/soroban-cli/src/commands/contract/extend.rs b/cmd/soroban-cli/src/commands/contract/extend.rs index 24aac54c5..3faec3601 100644 --- a/cmd/soroban-cli/src/commands/contract/extend.rs +++ b/cmd/soroban-cli/src/commands/contract/extend.rs @@ -131,7 +131,7 @@ impl NetworkRunnable for Cmd { tracing::trace!(?network); let keys = self.key.parse_keys(&config.locator, &network)?; let client = network.rpc_client()?; - let source_account = config.source_account()?; + let source_account = config.source_account().await?; let extend_to = self.ledgers_to_extend(); // Get the account sequence number diff --git a/cmd/soroban-cli/src/commands/contract/id.rs b/cmd/soroban-cli/src/commands/contract/id.rs index bb8744d51..f07fa8df6 100644 --- a/cmd/soroban-cli/src/commands/contract/id.rs +++ b/cmd/soroban-cli/src/commands/contract/id.rs @@ -18,10 +18,10 @@ pub enum Error { } impl Cmd { - pub fn run(&self) -> Result<(), Error> { + pub async fn run(&self) -> Result<(), Error> { match &self { Cmd::Asset(asset) => asset.run()?, - Cmd::Wasm(wasm) => wasm.run()?, + Cmd::Wasm(wasm) => wasm.run().await?, } Ok(()) } diff --git a/cmd/soroban-cli/src/commands/contract/id/wasm.rs b/cmd/soroban-cli/src/commands/contract/id/wasm.rs index 349dd167b..a469f10c9 100644 --- a/cmd/soroban-cli/src/commands/contract/id/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/id/wasm.rs @@ -29,12 +29,12 @@ pub enum Error { OnlyEd25519AccountsAllowed, } impl Cmd { - pub fn run(&self) -> Result<(), Error> { + pub async fn run(&self) -> Result<(), Error> { let salt: [u8; 32] = soroban_spec_tools::utils::padded_hex_from_str(&self.salt, 32) .map_err(|_| Error::CannotParseSalt(self.salt.clone()))? .try_into() .map_err(|_| Error::CannotParseSalt(self.salt.clone()))?; - let source_account = match self.config.source_account()? { + let source_account = match self.config.source_account().await? { xdr::MuxedAccount::Ed25519(uint256) => stellar_strkey::ed25519::PublicKey(uint256.0), xdr::MuxedAccount::MuxedEd25519(_) => return Err(Error::OnlyEd25519AccountsAllowed), }; diff --git a/cmd/soroban-cli/src/commands/contract/install.rs b/cmd/soroban-cli/src/commands/contract/install.rs index cd6e93b24..2f2c3fc03 100644 --- a/cmd/soroban-cli/src/commands/contract/install.rs +++ b/cmd/soroban-cli/src/commands/contract/install.rs @@ -135,7 +135,7 @@ impl NetworkRunnable for Cmd { } // Get the account sequence number - let source_account = config.source_account()?; + let source_account = config.source_account().await?; let account_details = client.get_account(&source_account.to_string()).await?; let sequence: i64 = account_details.seq_num.into(); diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index 5d43f9bfa..ba07dff94 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -213,7 +213,7 @@ impl NetworkRunnable for Cmd { .await?; client - .get_account(&config.source_account()?.to_string()) + .get_account(&config.source_account().await?.to_string()) .await? }; let sequence: i64 = account_details.seq_num.into(); diff --git a/cmd/soroban-cli/src/commands/contract/mod.rs b/cmd/soroban-cli/src/commands/contract/mod.rs index d0524e82b..34df3c301 100644 --- a/cmd/soroban-cli/src/commands/contract/mod.rs +++ b/cmd/soroban-cli/src/commands/contract/mod.rs @@ -150,7 +150,7 @@ impl Cmd { Cmd::Extend(extend) => extend.run().await?, Cmd::Alias(alias) => alias.run(global_args)?, Cmd::Deploy(deploy) => deploy.run(global_args).await?, - Cmd::Id(id) => id.run()?, + Cmd::Id(id) => id.run().await?, Cmd::Info(info) => info.run().await?, Cmd::Init(init) => init.run(global_args)?, Cmd::Inspect(inspect) => inspect.run(global_args)?, diff --git a/cmd/soroban-cli/src/commands/contract/restore.rs b/cmd/soroban-cli/src/commands/contract/restore.rs index 87a52a9f6..530738592 100644 --- a/cmd/soroban-cli/src/commands/contract/restore.rs +++ b/cmd/soroban-cli/src/commands/contract/restore.rs @@ -133,7 +133,7 @@ impl NetworkRunnable for Cmd { tracing::trace!(?network); let entry_keys = self.key.parse_keys(&config.locator, &network)?; let client = network.rpc_client()?; - let source_account = config.source_account()?; + let source_account = config.source_account().await?; // Get the account sequence number let public_strkey = source_account.to_string(); diff --git a/cmd/soroban-cli/src/commands/keys/add.rs b/cmd/soroban-cli/src/commands/keys/add.rs index d8f528bae..fd796bb86 100644 --- a/cmd/soroban-cli/src/commands/keys/add.rs +++ b/cmd/soroban-cli/src/commands/keys/add.rs @@ -1,21 +1,25 @@ use clap::command; -use crate::config::{locator, secret}; +use crate::config::{ + address::{self, KeyName}, + locator, secret, +}; #[derive(thiserror::Error, Debug)] pub enum Error { #[error(transparent)] Secret(#[from] secret::Error), - #[error(transparent)] Config(#[from] locator::Error), + #[error(transparent)] + Address(#[from] address::Error), } #[derive(Debug, clap::Parser, Clone)] #[group(skip)] pub struct Cmd { /// Name of identity - pub name: String, + pub name: KeyName, #[command(flatten)] pub secrets: secret::Args, diff --git a/cmd/soroban-cli/src/commands/keys/address.rs b/cmd/soroban-cli/src/commands/keys/address.rs index d13381b49..e29c00fe4 100644 --- a/cmd/soroban-cli/src/commands/keys/address.rs +++ b/cmd/soroban-cli/src/commands/keys/address.rs @@ -1,8 +1,10 @@ -use crate::commands::config::secret; - -use super::super::config::locator; use clap::arg; +use crate::{ + commands::config::{address, locator, secret}, + xdr, +}; + #[derive(thiserror::Error, Debug)] pub enum Error { #[error(transparent)] @@ -13,13 +15,16 @@ pub enum Error { #[error(transparent)] StrKey(#[from] stellar_strkey::DecodeError), + + #[error(transparent)] + Address(#[from] address::Error), } #[derive(Debug, clap::Parser, Clone)] #[group(skip)] pub struct Cmd { - /// Name of identity to lookup, default test identity used if not provided - pub name: String, + /// Name of identity to lookup, ledger, or secret key + pub name: address::Address, /// If identity is a seed phrase use this hd path, default is 0 #[arg(long)] @@ -30,25 +35,21 @@ pub struct Cmd { } impl Cmd { - pub fn run(&self) -> Result<(), Error> { - println!("{}", self.public_key()?); + pub async fn run(&self) -> Result<(), Error> { + println!("{}", self.public_key().await?); Ok(()) } - pub fn private_key(&self) -> Result { - Ok(self - .locator - .read_identity(&self.name)? - .key_pair(self.hd_path)?) - } - - pub fn public_key(&self) -> Result { - if let Ok(key) = stellar_strkey::ed25519::PublicKey::from_string(&self.name) { - Ok(key) - } else { - Ok(stellar_strkey::ed25519::PublicKey::from_payload( - self.private_key()?.verifying_key().as_bytes(), - )?) + pub async fn public_key(&self) -> Result { + match self + .name + .resolve_muxed_account(&self.locator, self.hd_path) + .await? + { + xdr::MuxedAccount::Ed25519(pk) => Ok(stellar_strkey::ed25519::PublicKey(pk.0)), + xdr::MuxedAccount::MuxedEd25519(xdr::MuxedAccountMed25519 { ed25519, .. }) => { + Ok(stellar_strkey::ed25519::PublicKey(ed25519.0)) + } } } } diff --git a/cmd/soroban-cli/src/commands/keys/fund.rs b/cmd/soroban-cli/src/commands/keys/fund.rs index d7100c6cb..9660f4241 100644 --- a/cmd/soroban-cli/src/commands/keys/fund.rs +++ b/cmd/soroban-cli/src/commands/keys/fund.rs @@ -24,7 +24,7 @@ pub struct Cmd { impl Cmd { pub async fn run(&self) -> Result<(), Error> { - let addr = self.address.public_key()?; + let addr = self.address.public_key().await?; self.network .get(&self.address.locator)? .fund_address(&addr) diff --git a/cmd/soroban-cli/src/commands/keys/mod.rs b/cmd/soroban-cli/src/commands/keys/mod.rs index 30df5ccee..6f5a817f9 100644 --- a/cmd/soroban-cli/src/commands/keys/mod.rs +++ b/cmd/soroban-cli/src/commands/keys/mod.rs @@ -52,7 +52,7 @@ impl Cmd { pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { match self { Cmd::Add(cmd) => cmd.run()?, - Cmd::Address(cmd) => cmd.run()?, + Cmd::Address(cmd) => cmd.run().await?, Cmd::Fund(cmd) => cmd.run().await?, Cmd::Generate(cmd) => cmd.run(global_args).await?, Cmd::Ls(cmd) => cmd.run()?, diff --git a/cmd/soroban-cli/src/commands/snapshot/create.rs b/cmd/soroban-cli/src/commands/snapshot/create.rs index 6ce48d3f2..546e4e83a 100644 --- a/cmd/soroban-cli/src/commands/snapshot/create.rs +++ b/cmd/soroban-cli/src/commands/snapshot/create.rs @@ -220,7 +220,7 @@ impl Cmd { .address .iter() .cloned() - .filter_map(|a| self.resolve_address(&a, network_passphrase)) + .filter_map(|a| self.resolve_address_sync(&a, network_passphrase)) .partition_map(|a| a); let mut current = SearchInputs { @@ -394,23 +394,61 @@ impl Cmd { .ok_or(Error::ArchiveUrlNotConfigured) } - fn resolve_address( + #[allow(dead_code)] + async 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)) + if let Some(contract) = self.resolve_contract(address, network_passphrase) { + Some(Either::Right(contract)) + } else { + self.resolve_account(address).await.map(Either::Left) + } + } + + fn resolve_address_sync( + &self, + address: &str, + network_passphrase: &str, + ) -> Option> { + if let Some(contract) = self.resolve_contract(address, network_passphrase) { + Some(Either::Right(contract)) + } else { + self.resolve_account_sync(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`). + #[allow(dead_code)] + async 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) + .await + .ok()? + { + xdr::MuxedAccount::Ed25519(uint256) => uint256, + xdr::MuxedAccount::MuxedEd25519(xdr::MuxedAccountMed25519 { ed25519, .. }) => { + ed25519 + } + }, + ))) } // 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 { + fn resolve_account_sync(&self, address: &str) -> Option { let address: Address = address.parse().ok()?; Some(AccountId(xdr::PublicKey::PublicKeyTypeEd25519( - match address.resolve_muxed_account(&self.locator, None).ok()? { + match address + .resolve_muxed_account_sync(&self.locator, None) + .ok()? + { xdr::MuxedAccount::Ed25519(uint256) => uint256, xdr::MuxedAccount::MuxedEd25519(xdr::MuxedAccountMed25519 { ed25519, .. }) => { ed25519 diff --git a/cmd/soroban-cli/src/commands/tx/args.rs b/cmd/soroban-cli/src/commands/tx/args.rs index 7e032fd53..e1847b1b4 100644 --- a/cmd/soroban-cli/src/commands/tx/args.rs +++ b/cmd/soroban-cli/src/commands/tx/args.rs @@ -36,7 +36,7 @@ pub enum Error { impl Args { pub async fn tx(&self, body: impl Into) -> Result { - let source_account = self.source_account()?; + let source_account = self.source_account().await?; let seq_num = self .config .next_sequence_number(&source_account.to_string()) @@ -101,7 +101,7 @@ impl Args { Ok(TxnEnvelopeResult::Res(txn_resp)) } - pub fn source_account(&self) -> Result { - Ok(self.config.source_account()?) + pub async fn source_account(&self) -> Result { + Ok(self.config.source_account().await?) } } diff --git a/cmd/soroban-cli/src/commands/tx/sign.rs b/cmd/soroban-cli/src/commands/tx/sign.rs index c696f8586..750fd7d29 100644 --- a/cmd/soroban-cli/src/commands/tx/sign.rs +++ b/cmd/soroban-cli/src/commands/tx/sign.rs @@ -33,12 +33,15 @@ 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, - )?; + let tx_env_signed = self + .sign_with + .sign_tx_env( + &tx_env, + &self.locator, + &self.network.get(&self.locator)?, + global_args.quiet, + ) + .await?; println!("{}", tx_env_signed.to_xdr_base64(Limits::none())?); Ok(()) } diff --git a/cmd/soroban-cli/src/config/address.rs b/cmd/soroban-cli/src/config/address.rs index 066bc8d91..d7c1ebaba 100644 --- a/cmd/soroban-cli/src/config/address.rs +++ b/cmd/soroban-cli/src/config/address.rs @@ -1,14 +1,18 @@ use std::str::FromStr; -use crate::xdr; +use crate::{ + signer::{self, native_ledger}, + xdr, +}; use super::{locator, secret}; /// Address can be either a public key or eventually an alias of a address. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum Address { MuxedAccount(xdr::MuxedAccount), AliasOrSecret(String), + Ledger(u32), } impl Default for Address { @@ -23,14 +27,27 @@ pub enum Error { Locator(#[from] locator::Error), #[error(transparent)] Secret(#[from] secret::Error), + #[error(transparent)] + Signer(#[from] signer::Error), #[error("Address cannot be used to sign {0}")] CannotSign(xdr::MuxedAccount), + #[error("Ledger not supported")] + LedgerNotSupported, + #[error("Invalid key name: {0}\n only alphanumeric characters, `_`and `-` are allowed")] + InvalidKeyName(String), + #[error("Invalid key name: {0}\n `ledger` is not allowed")] + LedgerIsInvalidKeyName(String), } impl FromStr for Address { type Err = Error; fn from_str(value: &str) -> Result { + if value.starts_with("ledger") { + if let Some(ledger) = parse_ledger(value) { + return Ok(Address::Ledger(ledger)); + } + } Ok(xdr::MuxedAccount::from_str(value).map_or_else( |_| Address::AliasOrSecret(value.to_string()), Address::MuxedAccount, @@ -38,8 +55,37 @@ impl FromStr for Address { } } +fn parse_ledger(value: &str) -> Option { + let vals: Vec<_> = value.split(':').collect(); + if vals.len() > 2 { + return None; + } + if vals.len() == 1 { + return Some(0); + } + vals[1].parse().ok() +} + impl Address { - pub fn resolve_muxed_account( + pub async 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(), + )) + }), + Address::Ledger(hd_path) => Ok(xdr::MuxedAccount::Ed25519( + native_ledger(*hd_path)?.public_key().await?.0.into(), + )), + } + } + + pub fn resolve_muxed_account_sync( &self, locator: &locator::Args, hd_path: Option, @@ -51,13 +97,70 @@ impl Address { locator.read_identity(alias)?.public_key(hd_path)?.0.into(), )) }), + Address::Ledger(_) => Err(Error::LedgerNotSupported), } } 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)?), + a => Err(Error::CannotSign( + a.resolve_muxed_account_sync(locator, None)?, + )), + } + } +} + +#[derive(Clone, Debug)] +pub struct KeyName(pub String); + +impl std::ops::Deref for KeyName { + type Target = str; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::str::FromStr for KeyName { + type Err = Error; + fn from_str(s: &str) -> Result { + if !s.chars().all(allowed_char) { + return Err(Error::InvalidKeyName(s.to_string())); + } + if s == "ledger" { + return Err(Error::InvalidKeyName(s.to_string())); } + Ok(KeyName(s.to_string())) + } +} + +fn allowed_char(c: char) -> bool { + c.is_ascii_alphanumeric() || c == '_' || c == '-' +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn ledger_address() { + let address = Address::from_str("ledger:0").unwrap(); + assert_eq!(address, Address::Ledger(0)); + let address = Address::from_str("ledger:1").unwrap(); + assert_eq!(address, Address::Ledger(1)); + let address = Address::from_str("ledger").unwrap(); + assert_eq!(address, Address::Ledger(0)); + } + + #[test] + fn invalid_ledger_address() { + assert_eq!( + Address::AliasOrSecret("ledger:".to_string()), + Address::from_str("ledger:").unwrap() + ); + assert_eq!( + Address::AliasOrSecret("ledger:1:2".to_string()), + Address::from_str("ledger:1:2").unwrap() + ); } } diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index b961f0f67..88577c077 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -63,10 +63,11 @@ 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 { + pub async fn source_account(&self) -> Result { Ok(self .source_account - .resolve_muxed_account(&self.locator, self.hd_path)?) + .resolve_muxed_account(&self.locator, self.hd_path) + .await?) } pub fn key_pair(&self) -> Result { @@ -86,7 +87,7 @@ impl Args { kind: SignerKind::Local(LocalKey { key }), print: Print::new(false), }; - Ok(signer.sign_tx(tx, network)?) + Ok(signer.sign_tx(tx, network).await?) } pub async fn sign_soroban_authorizations( diff --git a/cmd/soroban-cli/src/config/sign_with.rs b/cmd/soroban-cli/src/config/sign_with.rs index 514132085..a747a5524 100644 --- a/cmd/soroban-cli/src/config/sign_with.rs +++ b/cmd/soroban-cli/src/config/sign_with.rs @@ -58,7 +58,7 @@ pub struct Args { } impl Args { - pub fn sign_tx_env( + pub async fn sign_tx_env( &self, tx: &TransactionEnvelope, locator: &locator::Args, @@ -87,6 +87,6 @@ impl Args { let secret = locator.key(key_or_name)?; secret.signer(self.hd_path, print)? }; - Ok(signer.sign_tx_env(tx, network)?) + Ok(signer.sign_tx_env(tx, network).await?) } } diff --git a/cmd/soroban-cli/src/signer.rs b/cmd/soroban-cli/src/signer.rs index 24570f98b..1593d8c9c 100644 --- a/cmd/soroban-cli/src/signer.rs +++ b/cmd/soroban-cli/src/signer.rs @@ -8,7 +8,7 @@ use crate::xdr::{ SorobanAuthorizedFunction, SorobanCredentials, Transaction, TransactionEnvelope, TransactionV1Envelope, Uint256, VecM, WriteXdr, }; -use stellar_ledger::{Exchange, LedgerSigner, TransportNativeHID}; +use stellar_ledger::{Blob as _, Exchange, LedgerSigner, TransportNativeHID}; use crate::{config::network::Network, print::Print, utils::transaction_hash}; @@ -214,7 +214,7 @@ pub enum SignerKind { } impl Signer { - pub fn sign_tx( + pub async fn sign_tx( &self, tx: Transaction, network: &Network, @@ -223,10 +223,10 @@ impl Signer { tx, signatures: VecM::default(), }); - self.sign_tx_env(&tx_env, network) + self.sign_tx_env(&tx_env, network).await } - pub fn sign_tx_env( + pub async fn sign_tx_env( &self, tx_env: &TransactionEnvelope, network: &Network, @@ -239,7 +239,7 @@ impl Signer { 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)?, - SignerKind::Ledger(_) => todo!("ledger signing"), + SignerKind::Ledger(ledger) => ledger.sign_transaction_hash(&tx_hash).await?, }; let mut sigs = signatures.clone().into_vec(); sigs.push(decorated_signature); @@ -262,6 +262,28 @@ pub struct Ledger { pub(crate) index: u32, pub(crate) signer: LedgerSigner, } + +impl Ledger { + pub async fn sign_transaction_hash( + &self, + tx_hash: &[u8; 32], + ) -> Result { + let key = self.public_key().await?; + let hint = SignatureHint(key.0[28..].try_into()?); + let signature = Signature( + self.signer + .sign_transaction_hash(self.index, tx_hash) + .await? + .try_into()?, + ); + Ok(DecoratedSignature { hint, signature }) + } + + pub async fn public_key(&self) -> Result { + Ok(self.signer.get_public_key(&self.index.into()).await?) + } +} + pub fn native_ledger(hd_path: u32) -> Result, Error> { let signer = stellar_ledger::native()?; Ok(Ledger { From 98b01007cc01d53516c60661739c5dba72d85108 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Mon, 18 Nov 2024 15:22:05 -0500 Subject: [PATCH 04/74] feat(stellar-ledger): make test utilties public --- .gitignore | 1 + Cargo.lock | 4 - cmd/crates/stellar-ledger/Cargo.toml | 14 +- .../src/emulator_test_support.rs | 7 + .../emulator_test_support/http_transport.rs} | 0 .../emulator_test_support}/speculos.rs | 2 +- .../src/emulator_test_support/util.rs | 200 +++++++++++++++++ cmd/crates/stellar-ledger/src/lib.rs | 11 +- .../tests/test/emulator_tests.rs | 206 +----------------- cmd/crates/stellar-ledger/tests/utils/mod.rs | 2 - 10 files changed, 222 insertions(+), 225 deletions(-) create mode 100644 cmd/crates/stellar-ledger/src/emulator_test_support.rs rename cmd/crates/stellar-ledger/{tests/utils/emulator_http_transport.rs => src/emulator_test_support/http_transport.rs} (100%) rename cmd/crates/stellar-ledger/{tests/utils => src/emulator_test_support}/speculos.rs (99%) create mode 100644 cmd/crates/stellar-ledger/src/emulator_test_support/util.rs delete mode 100644 cmd/crates/stellar-ledger/tests/utils/mod.rs diff --git a/.gitignore b/.gitignore index b7150ae5d..578b28adf 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ test_snapshots .idea local.sh .stellar +.zed diff --git a/Cargo.lock b/Cargo.lock index cd98e4f58..6e71d2a9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4684,7 +4684,6 @@ dependencies = [ "byteorder 1.5.0", "ed25519-dalek", "env_logger", - "futures", "hex", "home", "httpmock", @@ -4695,15 +4694,12 @@ dependencies = [ "phf", "pretty_assertions", "reqwest", - "sep5", "serde", "serde_derive", "serde_json", "serial_test", "sha2 0.10.8", "slipped10", - "soroban-spec", - "stellar-rpc-client", "stellar-strkey 0.0.11", "stellar-xdr", "test-case", diff --git a/cmd/crates/stellar-ledger/Cargo.toml b/cmd/crates/stellar-ledger/Cargo.toml index b3d6318a4..17335a453 100644 --- a/cmd/crates/stellar-ledger/Cargo.toml +++ b/cmd/crates/stellar-ledger/Cargo.toml @@ -16,7 +16,6 @@ rust-version.workspace = true publish = false [dependencies] -soroban-spec = { workspace = true } thiserror = "1.0.32" serde = "1.0.82" serde_derive = "1.0.82" @@ -26,7 +25,6 @@ ed25519-dalek = { workspace = true } stellar-strkey = { workspace = true } ledger-transport-hid = "0.10.0" ledger-transport = "0.10.0" -sep5.workspace = true slip10 = { package = "slipped10", version = "0.4.6" } tracing = { workspace = true } hex.workspace = true @@ -35,10 +33,9 @@ bollard = { workspace = true } home = "0.5.9" tokio = { version = "1", features = ["full"] } reqwest = { workspace = true, features = ["json"] } -soroban-rpc.workspace = true -phf = { version = "0.11.2", features = ["macros"] } -futures = "0.3.30" +phf = { version = "0.11.2", features = ["macros"], optional = true } async-trait = { workspace = true } +testcontainers = { version = "0.20.1", optional = true } [dependencies.stellar-xdr] workspace = true @@ -46,15 +43,16 @@ features = ["curr", "std", "serde"] [dev-dependencies] env_logger = "0.11.3" -futures = "0.3.30" log = "0.4.21" once_cell = "1.19.0" pretty_assertions = "1.2.1" serial_test = "3.0.0" httpmock = "0.7.0-rc.1" test-case = "3.3.1" -testcontainers = "0.20.1" + [features] -emulator-tests = [] +default = ["http-transport"] +emulator-tests = ["dep:testcontainers", "http-transport", "dep:phf"] +http-transport = [] diff --git a/cmd/crates/stellar-ledger/src/emulator_test_support.rs b/cmd/crates/stellar-ledger/src/emulator_test_support.rs new file mode 100644 index 000000000..e69ec128f --- /dev/null +++ b/cmd/crates/stellar-ledger/src/emulator_test_support.rs @@ -0,0 +1,7 @@ +pub mod http_transport; +#[cfg(feature = "emulator-tests")] +pub mod speculos; +#[cfg(feature = "emulator-tests")] +pub mod util; +#[cfg(feature = "emulator-tests")] +pub use util::*; diff --git a/cmd/crates/stellar-ledger/tests/utils/emulator_http_transport.rs b/cmd/crates/stellar-ledger/src/emulator_test_support/http_transport.rs similarity index 100% rename from cmd/crates/stellar-ledger/tests/utils/emulator_http_transport.rs rename to cmd/crates/stellar-ledger/src/emulator_test_support/http_transport.rs diff --git a/cmd/crates/stellar-ledger/tests/utils/speculos.rs b/cmd/crates/stellar-ledger/src/emulator_test_support/speculos.rs similarity index 99% rename from cmd/crates/stellar-ledger/tests/utils/speculos.rs rename to cmd/crates/stellar-ledger/src/emulator_test_support/speculos.rs index 57439b0af..9084b8c85 100644 --- a/cmd/crates/stellar-ledger/tests/utils/speculos.rs +++ b/cmd/crates/stellar-ledger/src/emulator_test_support/speculos.rs @@ -92,4 +92,4 @@ impl Image for Speculos { fn cmd(&self) -> impl IntoIterator>> { vec![self.cmd.clone()].into_iter() } -} \ No newline at end of file +} diff --git a/cmd/crates/stellar-ledger/src/emulator_test_support/util.rs b/cmd/crates/stellar-ledger/src/emulator_test_support/util.rs new file mode 100644 index 000000000..cc442eca9 --- /dev/null +++ b/cmd/crates/stellar-ledger/src/emulator_test_support/util.rs @@ -0,0 +1,200 @@ +use ledger_transport::Exchange; +use serde::Deserialize; +use std::ops::Range; +use std::sync::LazyLock; +use std::sync::Mutex; + +use crate::{Error, LedgerSigner}; +use std::net::TcpListener; + +use super::{http_transport::EmulatorHttpTransport, speculos::Speculos}; + +use std::{collections::HashMap, time::Duration}; + +use stellar_xdr::curr::Hash; + +use testcontainers::{core::ContainerPort, runners::AsyncRunner, ContainerAsync, ImageExt}; +use tokio::time::sleep; + +static PORT_RANGE: LazyLock>> = LazyLock::new(|| Mutex::new(40000..50000)); + +pub const TEST_NETWORK_PASSPHRASE: &[u8] = b"Test SDF Network ; September 2015"; +pub fn test_network_hash() -> Hash { + use sha2::Digest; + Hash(sha2::Sha256::digest(TEST_NETWORK_PASSPHRASE).into()) +} + +pub async fn ledger(host_port: u16) -> LedgerSigner { + LedgerSigner::new(get_http_transport("127.0.0.1", host_port).await.unwrap()) +} + +pub async fn click(ui_host_port: u16, url: &str) { + let previous_events = get_emulator_events(ui_host_port).await; + + let client = reqwest::Client::new(); + let mut payload = HashMap::new(); + payload.insert("action", "press-and-release"); + + let mut screen_has_changed = false; + + client + .post(format!("http://localhost:{ui_host_port}/{url}")) + .json(&payload) + .send() + .await + .unwrap(); + + while !screen_has_changed { + let current_events = get_emulator_events(ui_host_port).await; + + if !(previous_events == current_events) { + screen_has_changed = true + } + } + + sleep(Duration::from_secs(1)).await; +} + +pub async fn enable_hash_signing(ui_host_port: u16) { + click(ui_host_port, "button/right").await; + + click(ui_host_port, "button/both").await; + + click(ui_host_port, "button/both").await; + + click(ui_host_port, "button/right").await; + + click(ui_host_port, "button/right").await; + + click(ui_host_port, "button/both").await; +} + +#[derive(Debug, Deserialize, PartialEq)] +pub struct EmulatorEvent { + text: String, + x: u16, + y: u16, + w: u16, + h: u16, +} + +#[derive(Debug, Deserialize)] +struct EventsResponse { + events: Vec, +} + +pub async fn get_container(ledger_device_model: String) -> ContainerAsync { + let (tcp_port_1, tcp_port_2) = get_available_ports(2); + 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() +} + +pub fn get_available_ports(n: usize) -> (u16, u16) { + let mut range = PORT_RANGE.lock().unwrap(); + let mut ports = Vec::with_capacity(n); + while ports.len() < n { + if let Some(port) = range.next() { + if let Ok(listener) = TcpListener::bind(("0.0.0.0", port)) { + ports.push(port); + drop(listener); + } + } else { + panic!("No more available ports"); + } + } + + (ports[0], ports[1]) +} + +pub async fn get_http_transport(host: &str, port: u16) -> Result { + let max_retries = 5; + let mut retries = 0; + let mut wait_time = Duration::from_secs(1); + // ping the emulator port to make sure it's up and running + // retry with exponential backoff + loop { + match reqwest::get(format!("http://{host}:{port}")).await { + Ok(_) => return Ok(EmulatorHttpTransport::new(host, port)), + Err(e) => { + retries += 1; + if retries >= max_retries { + println!("get_http_transport: Exceeded max retries for connecting to emulated device"); + + return Err(Error::APDUExchangeError(format!( + "Failed to connect to emulator: {e}" + ))); + } + sleep(wait_time).await; + wait_time *= 2; + } + } + } +} + +pub async fn wait_for_emulator_start_text(ui_host_port: u16) { + let mut ready = false; + while !ready { + let events = get_emulator_events_with_retries(ui_host_port, 5).await; + + if events.iter().any(|event| event.text == "is ready") { + ready = true; + } + } +} + +pub async fn get_emulator_events(ui_host_port: u16) -> Vec { + // Allowing for less retries here because presumably the emulator should be up and running since we waited + // for the "is ready" text via wait_for_emulator_start_text + get_emulator_events_with_retries(ui_host_port, 1).await +} + +pub async fn get_emulator_events_with_retries( + ui_host_port: u16, + max_retries: u16, +) -> Vec { + let client = reqwest::Client::new(); + let mut retries = 0; + let mut wait_time = Duration::from_secs(1); + loop { + match client + .get(format!("http://localhost:{ui_host_port}/events")) + .send() + .await + { + Ok(req) => { + let resp = req.json::().await.unwrap(); + return resp.events; + } + Err(e) => { + retries += 1; + if retries >= max_retries { + println!("get_emulator_events_with_retries: Exceeded max retries"); + panic!("get_emulator_events_with_retries: Failed to get emulator events: {e}"); + } + sleep(wait_time).await; + wait_time *= 2; + } + } + } +} + +pub async fn approve_tx_hash_signature(ui_host_port: u16, device_model: String) { + let number_of_right_clicks = if device_model == "nanos" { 10 } else { 6 }; + for _ in 0..number_of_right_clicks { + click(ui_host_port, "button/right").await; + } + + click(ui_host_port, "button/both").await; +} + +pub async fn approve_tx_signature(ui_host_port: u16, device_model: String) { + let number_of_right_clicks = if device_model == "nanos" { 17 } else { 11 }; + for _ in 0..number_of_right_clicks { + click(ui_host_port, "button/right").await; + } + click(ui_host_port, "button/both").await; +} diff --git a/cmd/crates/stellar-ledger/src/lib.rs b/cmd/crates/stellar-ledger/src/lib.rs index 2c4a4006f..fa490da0a 100644 --- a/cmd/crates/stellar-ledger/src/lib.rs +++ b/cmd/crates/stellar-ledger/src/lib.rs @@ -20,6 +20,8 @@ pub use crate::signer::Blob; pub mod hd_path; mod signer; +pub mod emulator_test_support; + // this is from https://github.com/LedgerHQ/ledger-live/blob/36cfbf3fa3300fd99bcee2ab72e1fd8f280e6280/libs/ledgerjs/packages/hw-app-str/src/Str.ts#L181 const APDU_MAX_SIZE: u8 = 150; const HD_PATH_ELEMENTS_COUNT: u8 = 3; @@ -305,18 +307,13 @@ pub fn test_network_hash() -> Hash { Hash(sha2::Sha256::digest(TEST_NETWORK_PASSPHRASE).into()) } -#[cfg(test)] +#[cfg(all(test, feature = "http-transport"))] mod test { - mod test_helpers { - pub mod test { - include!("../tests/utils/mod.rs"); - } - } use httpmock::prelude::*; use serde_json::json; + use super::emulator_test_support::http_transport::EmulatorHttpTransport; use crate::Blob; - use test_helpers::test::emulator_http_transport::EmulatorHttpTransport; use std::vec; diff --git a/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs b/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs index 1b0c09bab..c01f55849 100644 --- a/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs +++ b/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs @@ -1,45 +1,16 @@ -use ledger_transport::Exchange; -use once_cell::sync::Lazy; -use serde::Deserialize; -use std::ops::Range; -use std::sync::Mutex; -use std::vec; - -use std::net::TcpListener; use stellar_ledger::hd_path::HdPath; -use stellar_ledger::{Blob, Error, LedgerSigner}; +use stellar_ledger::{Blob, Error}; use std::sync::Arc; -use std::{collections::HashMap, time::Duration}; use stellar_xdr::curr::{ - self as xdr, Hash, Memo, MuxedAccount, Operation, OperationBody, PaymentOp, Preconditions, + self as xdr, Memo, MuxedAccount, Operation, OperationBody, PaymentOp, Preconditions, SequenceNumber, Transaction, TransactionExt, Uint256, }; -use testcontainers::{core::ContainerPort, runners::AsyncRunner, ContainerAsync, ImageExt}; -use tokio::time::sleep; - -static PORT_RANGE: Lazy>> = Lazy::new(|| Mutex::new(40000..50000)); - -pub const TEST_NETWORK_PASSPHRASE: &[u8] = b"Test SDF Network ; September 2015"; -pub fn test_network_hash() -> Hash { - use sha2::Digest; - Hash(sha2::Sha256::digest(TEST_NETWORK_PASSPHRASE).into()) -} - -async fn ledger(host_port: u16) -> LedgerSigner { - LedgerSigner::new(get_http_transport("127.0.0.1", host_port).await.unwrap()) -} - -mod test_helpers { - pub mod test { - include!("../utils/mod.rs"); - } -} +use stellar_ledger::emulator_test_support::*; use test_case::test_case; -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")] @@ -244,174 +215,3 @@ async fn test_sign_tx_hash_when_hash_signing_is_enabled(ledger_device_model: Str } } } - -async fn click(ui_host_port: u16, url: &str) { - let previous_events = get_emulator_events(ui_host_port).await; - - let client = reqwest::Client::new(); - let mut payload = HashMap::new(); - payload.insert("action", "press-and-release"); - - let mut screen_has_changed = false; - - client - .post(format!("http://localhost:{ui_host_port}/{url}")) - .json(&payload) - .send() - .await - .unwrap(); - - while !screen_has_changed { - let current_events = get_emulator_events(ui_host_port).await; - - if !(previous_events == current_events) { - screen_has_changed = true - } - } - - sleep(Duration::from_secs(1)).await; -} - -async fn enable_hash_signing(ui_host_port: u16) { - click(ui_host_port, "button/right").await; - - click(ui_host_port, "button/both").await; - - click(ui_host_port, "button/both").await; - - click(ui_host_port, "button/right").await; - - click(ui_host_port, "button/right").await; - - click(ui_host_port, "button/both").await; -} - -#[derive(Debug, Deserialize, PartialEq)] -struct EmulatorEvent { - text: String, - x: u16, - y: u16, - w: u16, - h: u16, -} - -#[derive(Debug, Deserialize)] -struct EventsResponse { - events: Vec, -} - -async fn get_container(ledger_device_model: String) -> ContainerAsync { - let (tcp_port_1, tcp_port_2) = get_available_ports(2); - 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) { - let mut range = PORT_RANGE.lock().unwrap(); - let mut ports = Vec::with_capacity(n); - while ports.len() < n { - if let Some(port) = range.next() { - if let Ok(listener) = TcpListener::bind(("0.0.0.0", port)) { - ports.push(port); - drop(listener); - } - } else { - panic!("No more available ports"); - } - } - - (ports[0], ports[1]) -} - -async fn get_http_transport(host: &str, port: u16) -> Result { - let max_retries = 5; - let mut retries = 0; - let mut wait_time = Duration::from_secs(1); - // ping the emulator port to make sure it's up and running - // retry with exponential backoff - loop { - match reqwest::get(format!("http://{host}:{port}")).await { - Ok(_) => return Ok(EmulatorHttpTransport::new(host, port)), - Err(e) => { - retries += 1; - if retries >= max_retries { - println!("get_http_transport: Exceeded max retries for connecting to emulated device"); - - return Err(Error::APDUExchangeError(format!( - "Failed to connect to emulator: {e}" - ))); - } - sleep(wait_time).await; - wait_time *= 2; - } - } - } -} - -async fn wait_for_emulator_start_text(ui_host_port: u16) { - let mut ready = false; - while !ready { - let events = get_emulator_events_with_retries(ui_host_port, 5).await; - - if events.iter().any(|event| event.text == "is ready") { - ready = true; - } - } -} - -async fn get_emulator_events(ui_host_port: u16) -> Vec { - // Allowing for less retries here because presumably the emulator should be up and running since we waited - // for the "is ready" text via wait_for_emulator_start_text - get_emulator_events_with_retries(ui_host_port, 1).await -} - -async fn get_emulator_events_with_retries( - ui_host_port: u16, - max_retries: u16, -) -> Vec { - let client = reqwest::Client::new(); - let mut retries = 0; - let mut wait_time = Duration::from_secs(1); - loop { - match client - .get(format!("http://localhost:{ui_host_port}/events")) - .send() - .await - { - Ok(req) => { - let resp = req.json::().await.unwrap(); - return resp.events; - } - Err(e) => { - retries += 1; - if retries >= max_retries { - println!("get_emulator_events_with_retries: Exceeded max retries"); - panic!("get_emulator_events_with_retries: Failed to get emulator events: {e}"); - } - sleep(wait_time).await; - wait_time *= 2; - } - } - } -} - -async fn approve_tx_hash_signature(ui_host_port: u16, device_model: String) { - let number_of_right_clicks = if device_model == "nanos" { 10 } else { 6 }; - for _ in 0..number_of_right_clicks { - click(ui_host_port, "button/right").await; - } - - click(ui_host_port, "button/both").await; -} - -async fn approve_tx_signature(ui_host_port: u16, device_model: String) { - let number_of_right_clicks = if device_model == "nanos" { 17 } else { 11 }; - for _ in 0..number_of_right_clicks { - click(ui_host_port, "button/right").await; - } - click(ui_host_port, "button/both").await; -} diff --git a/cmd/crates/stellar-ledger/tests/utils/mod.rs b/cmd/crates/stellar-ledger/tests/utils/mod.rs deleted file mode 100644 index 5b63732bc..000000000 --- a/cmd/crates/stellar-ledger/tests/utils/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub(crate) mod emulator_http_transport; -pub(crate) mod speculos; From 37f302d67d162e1316e02d5da3e1c3aee3755e53 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 19 Nov 2024 15:37:36 -0500 Subject: [PATCH 05/74] fix: add first proper test using ledger and CLI --- Cargo.lock | 3 + Cargo.toml | 3 + cmd/crates/soroban-test/Cargo.toml | 4 + cmd/crates/soroban-test/src/lib.rs | 12 +++ .../soroban-test/tests/it/integration.rs | 3 + .../tests/it/integration/emulator.rs | 83 +++++++++++++++++++ .../soroban-test/tests/it/integration/tx.rs | 13 ++- cmd/crates/stellar-ledger/Cargo.toml | 2 +- .../emulator_test_support/http_transport.rs | 2 +- .../src/emulator_test_support/speculos.rs | 58 +++++++++++-- .../src/emulator_test_support/util.rs | 9 +- .../tests/test/emulator_tests.rs | 62 +++++++------- cmd/soroban-cli/Cargo.toml | 1 + cmd/soroban-cli/src/config/address.rs | 4 +- cmd/soroban-cli/src/config/secret.rs | 6 +- cmd/soroban-cli/src/config/sign_with.rs | 9 +- cmd/soroban-cli/src/signer.rs | 43 +++++++++- 17 files changed, 261 insertions(+), 56 deletions(-) create mode 100644 cmd/crates/soroban-test/tests/it/integration/emulator.rs diff --git a/Cargo.lock b/Cargo.lock index 6e71d2a9a..df4d0ea3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4610,8 +4610,11 @@ dependencies = [ "soroban-ledger-snapshot", "soroban-spec", "soroban-spec-tools", + "stellar-ledger", "stellar-rpc-client", "stellar-strkey 0.0.11", + "test-case", + "testcontainers", "thiserror", "tokio", "toml", diff --git a/Cargo.toml b/Cargo.toml index 73dbd4cb5..33f8363fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -108,7 +108,10 @@ walkdir = "2.5.0" toml_edit = "0.22.20" toml = "0.8.19" reqwest = "0.12.7" +# testing predicates = "3.1.2" +testcontainers = { version = "0.20.1" } + [profile.test-wasms] inherits = "release" diff --git a/cmd/crates/soroban-test/Cargo.toml b/cmd/crates/soroban-test/Cargo.toml index f694ec533..86656ceb2 100644 --- a/cmd/crates/soroban-test/Cargo.toml +++ b/cmd/crates/soroban-test/Cargo.toml @@ -24,6 +24,7 @@ stellar-strkey = { workspace = true } sep5 = { workspace = true } soroban-cli = { workspace = true } soroban-rpc = { workspace = true } +stellar-ledger = { workspace = true } thiserror = "1.0.31" sha2 = "0.10.6" @@ -32,6 +33,7 @@ assert_fs = "1.0.7" predicates = { workspace = true } fs_extra = "1.3.0" toml = { workspace = true } +testcontainers = { workspace = true } [dev-dependencies] @@ -42,6 +44,8 @@ walkdir = "2.4.0" ulid.workspace = true ed25519-dalek = { workspace = true } hex = { workspace = true } +test-case = "3.3.1" [features] it = [] +emulator-tests = ["stellar-ledger/emulator-tests"] diff --git a/cmd/crates/soroban-test/src/lib.rs b/cmd/crates/soroban-test/src/lib.rs index 82e05b78e..6645acf61 100644 --- a/cmd/crates/soroban-test/src/lib.rs +++ b/cmd/crates/soroban-test/src/lib.rs @@ -36,6 +36,10 @@ use soroban_cli::{ }; mod wasm; +use stellar_ledger::emulator_test_support::{ + enable_hash_signing, get_container, speculos::Speculos, wait_for_emulator_start_text, +}; +use testcontainers::ContainerAsync; pub use wasm::Wasm; pub const TEST_ACCOUNT: &str = "test"; @@ -308,6 +312,14 @@ impl TestEnv { pub fn client(&self) -> soroban_rpc::Client { soroban_rpc::Client::new(&self.rpc_url).unwrap() } + + pub async fn speculos_container(ledger_device_model: &str) -> ContainerAsync { + let container = get_container(ledger_device_model).await; + 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; + container + } } pub fn temp_ledger_file() -> OsString { diff --git a/cmd/crates/soroban-test/tests/it/integration.rs b/cmd/crates/soroban-test/tests/it/integration.rs index 3ec0d61ed..a8a637b20 100644 --- a/cmd/crates/soroban-test/tests/it/integration.rs +++ b/cmd/crates/soroban-test/tests/it/integration.rs @@ -9,3 +9,6 @@ mod snapshot; mod tx; mod util; mod wrap; + +#[cfg(feature = "emulator-tests")] +mod emulator; diff --git a/cmd/crates/soroban-test/tests/it/integration/emulator.rs b/cmd/crates/soroban-test/tests/it/integration/emulator.rs new file mode 100644 index 000000000..9c4ccd1c0 --- /dev/null +++ b/cmd/crates/soroban-test/tests/it/integration/emulator.rs @@ -0,0 +1,83 @@ +use stellar_ledger::{Blob, Error}; + +use soroban_test::{AssertExt, TestEnv, LOCAL_NETWORK_PASSPHRASE}; +use std::sync::Arc; + +use soroban_cli::xdr::{ + self, Memo, MuxedAccount, Operation, OperationBody, PaymentOp, Preconditions, SequenceNumber, + Transaction, TransactionExt, Uint256, +}; + +use stellar_ledger::emulator_test_support::*; + +use test_case::test_case; + +use crate::integration::util::{deploy_contract, DeployKind, HELLO_WORLD}; + +// #[test_case("nanos"; "when the device is NanoS")] +#[test_case("nanox"; "when the device is NanoX")] +// #[test_case("nanosp"; "when the device is NanoS Plus")] +#[tokio::test] +async fn test_get_public_key(ledger_device_model: &str) { + let sandbox = Arc::new(TestEnv::new()); + let container = TestEnv::speculos_container(ledger_device_model).await; + let host_port = container.get_host_port_ipv4(9998).await.unwrap(); + let ui_host_port = container.get_host_port_ipv4(5000).await.unwrap(); + + let ledger = ledger(host_port).await; + + let key = ledger.get_public_key(&0.into()).await.unwrap(); + let account = &key.to_string(); + sandbox.fund_account(account); + sandbox + .new_assert_cmd("contract") + .arg("install") + .args([ + "--wasm", + HELLO_WORLD.path().as_os_str().to_str().unwrap(), + "--source", + account, + ]) + .assert() + .success(); + + let tx_simulated = + deploy_contract(&sandbox, HELLO_WORLD, DeployKind::SimOnly, Some(account)).await; + dbg!("{tx_simulated}"); + let key = ledger.get_public_key(&0.into()).await.unwrap(); + println!("{key}"); + let sign = tokio::task::spawn_blocking({ + let sandbox = Arc::clone(&sandbox); + + move || { + sandbox + .new_assert_cmd("tx") + .arg("sign") + .arg("--sign-with-ledger") + .write_stdin(tx_simulated.as_bytes()) + .env("SPECULOS_PORT", host_port.to_string()) + .env("RUST_LOGS", "trace") + .assert() + .success() + .stdout_as_str() + } + }); + let approve = tokio::task::spawn(approve_tx_hash_signature( + ui_host_port, + ledger_device_model.to_string(), + )); + + let response = sign.await.unwrap(); + approve.await.unwrap(); + + dbg!("{tx_signed}"); + + sandbox + .clone() + .new_assert_cmd("tx") + .arg("send") + .write_stdin(response.as_bytes()) + .assert() + .success() + .stdout(predicates::str::contains("SUCCESS")); +} diff --git a/cmd/crates/soroban-test/tests/it/integration/tx.rs b/cmd/crates/soroban-test/tests/it/integration/tx.rs index c3cd2693b..00d907e77 100644 --- a/cmd/crates/soroban-test/tests/it/integration/tx.rs +++ b/cmd/crates/soroban-test/tests/it/integration/tx.rs @@ -56,10 +56,19 @@ async fn txn_hash() { #[tokio::test] async fn build_simulate_sign_send() { let sandbox = &TestEnv::new(); + build_sim_sign_send(sandbox, "test", "--sign-with-key=test").await; +} + +pub(crate) async fn build_sim_sign_send(sandbox: &TestEnv, account: &str, sign_with: &str) { sandbox .new_assert_cmd("contract") .arg("install") - .args(["--wasm", HELLO_WORLD.path().as_os_str().to_str().unwrap()]) + .args([ + "--wasm", + HELLO_WORLD.path().as_os_str().to_str().unwrap(), + "--source", + account, + ]) .assert() .success(); @@ -69,7 +78,7 @@ async fn build_simulate_sign_send() { let tx_signed = sandbox .new_assert_cmd("tx") .arg("sign") - .arg("--sign-with-key=test") + .arg(sign_with) .write_stdin(tx_simulated.as_bytes()) .assert() .success() diff --git a/cmd/crates/stellar-ledger/Cargo.toml b/cmd/crates/stellar-ledger/Cargo.toml index 17335a453..8b2d22b41 100644 --- a/cmd/crates/stellar-ledger/Cargo.toml +++ b/cmd/crates/stellar-ledger/Cargo.toml @@ -35,7 +35,7 @@ tokio = { version = "1", features = ["full"] } reqwest = { workspace = true, features = ["json"] } phf = { version = "0.11.2", features = ["macros"], optional = true } async-trait = { workspace = true } -testcontainers = { version = "0.20.1", optional = true } +testcontainers = { workspace = true, optional = true } [dependencies.stellar-xdr] workspace = true diff --git a/cmd/crates/stellar-ledger/src/emulator_test_support/http_transport.rs b/cmd/crates/stellar-ledger/src/emulator_test_support/http_transport.rs index c90c28d92..c82c46cac 100644 --- a/cmd/crates/stellar-ledger/src/emulator_test_support/http_transport.rs +++ b/cmd/crates/stellar-ledger/src/emulator_test_support/http_transport.rs @@ -72,7 +72,7 @@ impl Exchange for EmulatorHttpTransport { let resp: Response = HttpClient::new() .post(&self.url) .headers(headers) - .timeout(Duration::from_secs(25)) + .timeout(Duration::from_secs(60)) .json(&request) .send() .await diff --git a/cmd/crates/stellar-ledger/src/emulator_test_support/speculos.rs b/cmd/crates/stellar-ledger/src/emulator_test_support/speculos.rs index 9084b8c85..f8b95c13a 100644 --- a/cmd/crates/stellar-ledger/src/emulator_test_support/speculos.rs +++ b/cmd/crates/stellar-ledger/src/emulator_test_support/speculos.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, collections::HashMap, path::PathBuf}; +use std::{borrow::Cow, collections::HashMap, path::PathBuf, str::FromStr}; use testcontainers::{ core::{Mount, WaitFor}, Image, @@ -55,14 +55,54 @@ impl Speculos { } 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"), - }; - 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}") + let device_model: DeviceModel = ledger_device_model.parse().unwrap(); + let container_elf_path = format!("{DEFAULT_APP_PATH}/{}", device_model.as_file()); + 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}" + ) + } +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum DeviceModel { + NanoS, + NanoSP, + NanoX, +} + +impl FromStr for DeviceModel { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "nanos" => Ok(DeviceModel::NanoS), + "nanosp" => Ok(DeviceModel::NanoSP), + "nanox" => Ok(DeviceModel::NanoX), + _ => Err(format!("Unsupported device model: {}", s)), + } + } +} + +impl std::fmt::Display for DeviceModel { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + DeviceModel::NanoS => write!(f, "nanos"), + DeviceModel::NanoSP => write!(f, "nanosp"), + DeviceModel::NanoX => write!(f, "nanox"), + } + } +} + +impl DeviceModel { + pub fn as_file(&self) -> &str { + match self { + DeviceModel::NanoS => "stellarNanoSApp.elf", + DeviceModel::NanoSP => "stellarNanoSPApp.elf", + DeviceModel::NanoX => "stellarNanoXApp.elf", + } } } diff --git a/cmd/crates/stellar-ledger/src/emulator_test_support/util.rs b/cmd/crates/stellar-ledger/src/emulator_test_support/util.rs index cc442eca9..50479d532 100644 --- a/cmd/crates/stellar-ledger/src/emulator_test_support/util.rs +++ b/cmd/crates/stellar-ledger/src/emulator_test_support/util.rs @@ -1,4 +1,3 @@ -use ledger_transport::Exchange; use serde::Deserialize; use std::ops::Range; use std::sync::LazyLock; @@ -24,7 +23,7 @@ pub fn test_network_hash() -> Hash { Hash(sha2::Sha256::digest(TEST_NETWORK_PASSPHRASE).into()) } -pub async fn ledger(host_port: u16) -> LedgerSigner { +pub async fn ledger(host_port: u16) -> LedgerSigner { LedgerSigner::new(get_http_transport("127.0.0.1", host_port).await.unwrap()) } @@ -83,9 +82,9 @@ struct EventsResponse { events: Vec, } -pub async fn get_container(ledger_device_model: String) -> ContainerAsync { +pub async fn get_container(ledger_device_model: &str) -> ContainerAsync { let (tcp_port_1, tcp_port_2) = get_available_ports(2); - Speculos::new(ledger_device_model) + Speculos::new(ledger_device_model.to_string()) .with_mapped_port(tcp_port_1, ContainerPort::Tcp(9998)) .with_mapped_port(tcp_port_2, ContainerPort::Tcp(5000)) .start() @@ -110,7 +109,7 @@ pub fn get_available_ports(n: usize) -> (u16, u16) { (ports[0], ports[1]) } -pub async fn get_http_transport(host: &str, port: u16) -> Result { +pub async fn get_http_transport(host: &str, port: u16) -> Result { let max_retries = 5; let mut retries = 0; let mut wait_time = Duration::from_secs(1); diff --git a/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs b/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs index c01f55849..880ac95ff 100644 --- a/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs +++ b/cmd/crates/stellar-ledger/tests/test/emulator_tests.rs @@ -12,12 +12,12 @@ use stellar_ledger::emulator_test_support::*; use test_case::test_case; -#[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")] +#[test_case("nanos"; "when the device is NanoS")] +#[test_case("nanox"; "when the device is NanoX")] +#[test_case("nanosp"; "when the device is NanoS Plus")] #[tokio::test] -async fn test_get_public_key(ledger_device_model: String) { - let container = get_container(ledger_device_model.clone()).await; +async fn test_get_public_key(ledger_device_model: &str) { + let container = get_container(ledger_device_model).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; @@ -39,12 +39,12 @@ async fn test_get_public_key(ledger_device_model: String) { } } -#[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")] +#[test_case("nanos"; "when the device is NanoS")] +#[test_case("nanox"; "when the device is NanoX")] +#[test_case("nanosp"; "when the device is NanoS Plus")] #[tokio::test] -async fn test_get_app_configuration(ledger_device_model: String) { - let container = get_container(ledger_device_model.clone()).await; +async fn test_get_app_configuration(ledger_device_model: &str) { + let container = get_container(ledger_device_model).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; @@ -62,12 +62,12 @@ async fn test_get_app_configuration(ledger_device_model: String) { }; } -#[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")] +#[test_case("nanos"; "when the device is NanoS")] +#[test_case("nanox"; "when the device is NanoX")] +#[test_case("nanosp"; "when the device is NanoS Plus")] #[tokio::test] -async fn test_sign_tx(ledger_device_model: String) { - let container = get_container(ledger_device_model.clone()).await; +async fn test_sign_tx(ledger_device_model: &str) { + let container = get_container(ledger_device_model).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; @@ -112,7 +112,7 @@ async fn test_sign_tx(ledger_device_model: String) { fee: 100, seq_num: SequenceNumber(1), cond: Preconditions::None, - memo: Memo::Text("Stellar".as_bytes().try_into().unwrap()), + memo: Memo::Text("Stellar".try_into().unwrap()), ext: TransactionExt::V0, operations: [Operation { source_account: Some(MuxedAccount::Ed25519(Uint256(source_account_bytes))), @@ -130,7 +130,10 @@ async fn test_sign_tx(ledger_device_model: String) { let ledger = Arc::clone(&ledger); async move { ledger.sign_transaction(path, tx, test_network_hash()).await } }); - let approve = tokio::task::spawn(approve_tx_signature(ui_host_port, ledger_device_model)); + let approve = tokio::task::spawn(approve_tx_signature( + ui_host_port, + ledger_device_model.to_string(), + )); let result = sign.await.unwrap(); let _ = approve.await.unwrap(); @@ -146,12 +149,12 @@ async fn test_sign_tx(ledger_device_model: String) { }; } -#[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")] +#[test_case("nanos"; "when the device is NanoS")] +#[test_case("nanox"; "when the device is NanoX")] +#[test_case("nanosp"; "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 container = get_container(ledger_device_model.clone()).await; +async fn test_sign_tx_hash_when_hash_signing_is_not_enabled(ledger_device_model: &str) { + let container = get_container(ledger_device_model).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; @@ -170,12 +173,12 @@ async fn test_sign_tx_hash_when_hash_signing_is_not_enabled(ledger_device_model: } } -#[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")] +#[test_case("nanos"; "when the device is NanoS")] +#[test_case("nanox"; "when the device is NanoX")] +#[test_case("nanosp"; "when the device is NanoS Plus")] #[tokio::test] -async fn test_sign_tx_hash_when_hash_signing_is_enabled(ledger_device_model: String) { - let container = get_container(ledger_device_model.clone()).await; +async fn test_sign_tx_hash_when_hash_signing_is_enabled(ledger_device_model: &str) { + let container = get_container(ledger_device_model).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(); @@ -201,7 +204,10 @@ async fn test_sign_tx_hash_when_hash_signing_is_enabled(ledger_device_model: Str let ledger = Arc::clone(&ledger); async move { ledger.sign_transaction_hash(path, &test_hash).await } }); - let approve = tokio::task::spawn(approve_tx_hash_signature(ui_host_port, ledger_device_model)); + let approve = tokio::task::spawn(approve_tx_hash_signature( + ui_host_port, + ledger_device_model.to_string(), + )); let response = sign.await.unwrap(); let _ = approve.await.unwrap(); diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index 3e65072b5..234a70da9 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -37,6 +37,7 @@ doctest = false [features] default = [] opt = ["dep:wasm-opt"] +emulator-tests = ["stellar-ledger/emulator-tests"] [dependencies] stellar-xdr = { workspace = true, features = ["cli"] } diff --git a/cmd/soroban-cli/src/config/address.rs b/cmd/soroban-cli/src/config/address.rs index d7c1ebaba..43a9521ec 100644 --- a/cmd/soroban-cli/src/config/address.rs +++ b/cmd/soroban-cli/src/config/address.rs @@ -1,7 +1,7 @@ use std::str::FromStr; use crate::{ - signer::{self, native_ledger}, + signer::{self, ledger}, xdr, }; @@ -80,7 +80,7 @@ impl Address { )) }), Address::Ledger(hd_path) => Ok(xdr::MuxedAccount::Ed25519( - native_ledger(*hd_path)?.public_key().await?.0.into(), + ledger(*hd_path).await?.public_key().await?.0.into(), )), } } diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index 6e5703d57..1c8f89c5c 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -6,7 +6,7 @@ use stellar_strkey::ed25519::{PrivateKey, PublicKey}; use crate::{ print::Print, - signer::{self, native_ledger, LocalKey, Signer, SignerKind}, + signer::{self, ledger, LocalKey, Signer, SignerKind}, utils, }; @@ -133,7 +133,7 @@ impl Secret { )?) } - pub fn signer(&self, index: Option, print: Print) -> Result { + pub async fn signer(&self, index: Option, print: Print) -> Result { let kind = match self { Secret::SecretKey { .. } | Secret::SeedPhrase { .. } => { let key = self.key_pair(index)?; @@ -144,7 +144,7 @@ impl Secret { .unwrap_or_default() .try_into() .expect("uszie bigger than u32"); - SignerKind::Ledger(native_ledger(hd_path)?) + SignerKind::Ledger(ledger(hd_path).await?) } }; Ok(Signer { kind, print }) diff --git a/cmd/soroban-cli/src/config/sign_with.rs b/cmd/soroban-cli/src/config/sign_with.rs index a747a5524..8446f086e 100644 --- a/cmd/soroban-cli/src/config/sign_with.rs +++ b/cmd/soroban-cli/src/config/sign_with.rs @@ -1,6 +1,6 @@ use crate::{ print::Print, - signer::{self, native_ledger, Signer, SignerKind}, + signer::{self, ledger, Signer, SignerKind}, xdr::{self, TransactionEnvelope}, }; use clap::arg; @@ -72,12 +72,13 @@ impl Args { print, } } else if self.sign_with_ledger { - let ledger = native_ledger( + let ledger = ledger( self.hd_path .unwrap_or_default() .try_into() .unwrap_or_default(), - )?; + ) + .await?; Signer { kind: SignerKind::Ledger(ledger), print, @@ -85,7 +86,7 @@ impl Args { } 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)? + secret.signer(self.hd_path, print).await? }; Ok(signer.sign_tx_env(tx, network).await?) } diff --git a/cmd/soroban-cli/src/signer.rs b/cmd/soroban-cli/src/signer.rs index 1593d8c9c..3ef79ecaf 100644 --- a/cmd/soroban-cli/src/signer.rs +++ b/cmd/soroban-cli/src/signer.rs @@ -209,7 +209,10 @@ pub struct Signer { #[allow(clippy::module_name_repetitions, clippy::large_enum_variant)] pub enum SignerKind { Local(LocalKey), + #[cfg(not(feature = "emulator-tests"))] Ledger(Ledger), + #[cfg(feature = "emulator-tests")] + Ledger(Ledger), Lab, } @@ -279,12 +282,29 @@ impl Ledger { Ok(DecoratedSignature { hint, signature }) } + pub async fn sign_transaction( + &self, + tx: Transaction, + network_passphrase: &str, + ) -> Result { + let network_id = Hash(Sha256::digest(network_passphrase).into()); + let signature = self + .signer + .sign_transaction(self.index, tx, network_id) + .await?; + let key = self.public_key().await?; + let hint = SignatureHint(key.0[28..].try_into()?); + let signature = Signature(signature.try_into()?); + Ok(DecoratedSignature { hint, signature }) + } + pub async fn public_key(&self) -> Result { Ok(self.signer.get_public_key(&self.index.into()).await?) } } -pub fn native_ledger(hd_path: u32) -> Result, Error> { +#[cfg(not(feature = "emulator-tests"))] +pub async fn ledger(hd_path: u32) -> Result, Error> { let signer = stellar_ledger::native()?; Ok(Ledger { index: hd_path, @@ -292,6 +312,27 @@ pub fn native_ledger(hd_path: u32) -> Result, Error> }) } +#[cfg(feature = "emulator-tests")] +pub async fn ledger( + hd_path: u32, +) -> Result< + Ledger, + Error, +> { + use stellar_ledger::emulator_test_support::ledger as emulator_ledger; + // port from SPECULOS_PORT ENV var + let host_port: u16 = std::env::var("SPECULOS_PORT") + .expect("SPECULOS_PORT env var not set") + .parse() + .expect("port must be a number"); + let signer = emulator_ledger(host_port).await; + + Ok(Ledger { + index: hd_path, + signer, + }) +} + 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()?); From 4680b96d886ac547e58d7d9e982060a3f2ce2a55 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 19 Nov 2024 15:50:38 -0500 Subject: [PATCH 06/74] fix: actually test in CI --- .github/workflows/rpc-tests.yml | 7 +++++-- cmd/crates/soroban-test/src/lib.rs | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/rpc-tests.yml b/.github/workflows/rpc-tests.yml index 75b6d7760..778cc6525 100644 --- a/.github/workflows/rpc-tests.yml +++ b/.github/workflows/rpc-tests.yml @@ -39,7 +39,10 @@ jobs: 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 + - name: install libudev-dev + run: | + sudo apt install -y libudev-dev + - run: cargo build --features emulator-tests + - run: SOROBAN_PORT=8000 cargo test --features it --features emulator-tests --package soroban-test --test it -- integration diff --git a/cmd/crates/soroban-test/src/lib.rs b/cmd/crates/soroban-test/src/lib.rs index 4956521e1..b10b32ec1 100644 --- a/cmd/crates/soroban-test/src/lib.rs +++ b/cmd/crates/soroban-test/src/lib.rs @@ -311,7 +311,10 @@ impl TestEnv { } #[cfg(feature = "emulator-tests")] - pub async fn speculos_container(ledger_device_model: &str) -> testcontainers::ContainerAsync { + pub async fn speculos_container( + ledger_device_model: &str, + ) -> testcontainers::ContainerAsync + { use stellar_ledger::emulator_test_support::{ enable_hash_signing, get_container, wait_for_emulator_start_text, }; From d33a9fc8d1f78310e1b7f16bf8cd8fb9389d09ca Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 5 Nov 2024 11:00:43 -0500 Subject: [PATCH 07/74] feat: Add new `Key` type; allowing public keys to be named keys This will allow users to add public keys with `keys add` so that they can be referenced in commands that need public addresses and contract invoke's that take address arguments. --- Cargo.lock | 9 +- cmd/soroban-cli/Cargo.toml | 1 + cmd/soroban-cli/src/commands/keys/add.rs | 6 +- cmd/soroban-cli/src/commands/keys/address.rs | 18 ++- cmd/soroban-cli/src/commands/keys/show.rs | 7 +- cmd/soroban-cli/src/config/address.rs | 14 +- cmd/soroban-cli/src/config/key.rs | 146 +++++++++++++++++++ cmd/soroban-cli/src/config/locator.rs | 30 +++- cmd/soroban-cli/src/config/mod.rs | 1 + cmd/soroban-cli/src/config/secret.rs | 23 ++- cmd/soroban-cli/src/config/sign_with.rs | 2 +- 11 files changed, 214 insertions(+), 43 deletions(-) create mode 100644 cmd/soroban-cli/src/config/key.rs diff --git a/Cargo.lock b/Cargo.lock index 3f8fcf4ac..609b5be2e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4067,9 +4067,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.9.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857" +checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" dependencies = [ "base64 0.22.1", "chrono", @@ -4085,9 +4085,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.9.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" +checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" dependencies = [ "darling", "proc-macro2", @@ -4336,6 +4336,7 @@ dependencies = [ "serde", "serde-aux", "serde_json", + "serde_with", "sha2 0.10.8", "shell-escape", "shlex", diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index 2f2a26255..e6e205248 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -124,6 +124,7 @@ fqdn = "0.3.12" open = "5.3.0" url = "2.5.2" wasm-gen = "0.1.4" +serde_with = "3.11.0" [build-dependencies] crate-git-revision = "0.0.6" diff --git a/cmd/soroban-cli/src/commands/keys/add.rs b/cmd/soroban-cli/src/commands/keys/add.rs index d8f528bae..5e32944e8 100644 --- a/cmd/soroban-cli/src/commands/keys/add.rs +++ b/cmd/soroban-cli/src/commands/keys/add.rs @@ -1,11 +1,11 @@ use clap::command; -use crate::config::{locator, secret}; +use crate::config::{key, locator, secret}; #[derive(thiserror::Error, Debug)] pub enum Error { #[error(transparent)] - Secret(#[from] secret::Error), + Key(#[from] key::Error), #[error(transparent)] Config(#[from] locator::Error), @@ -28,6 +28,6 @@ impl Cmd { pub fn run(&self) -> Result<(), Error> { Ok(self .config_locator - .write_identity(&self.name, &self.secrets.read_secret()?)?) + .write_key(&self.name, &self.secrets.read_key()?)?) } } diff --git a/cmd/soroban-cli/src/commands/keys/address.rs b/cmd/soroban-cli/src/commands/keys/address.rs index d13381b49..8eda8dd83 100644 --- a/cmd/soroban-cli/src/commands/keys/address.rs +++ b/cmd/soroban-cli/src/commands/keys/address.rs @@ -1,15 +1,14 @@ -use crate::commands::config::secret; - -use super::super::config::locator; use clap::arg; +use crate::commands::config::{key, locator}; + #[derive(thiserror::Error, Debug)] pub enum Error { #[error(transparent)] Config(#[from] locator::Error), #[error(transparent)] - Secret(#[from] secret::Error), + Key(#[from] key::Error), #[error(transparent)] StrKey(#[from] stellar_strkey::DecodeError), @@ -36,10 +35,13 @@ impl Cmd { } pub fn private_key(&self) -> Result { - Ok(self - .locator - .read_identity(&self.name)? - .key_pair(self.hd_path)?) + Ok(ed25519_dalek::SigningKey::from_bytes( + &self + .locator + .read_identity(&self.name)? + .private_key(self.hd_path)? + .0, + )) } pub fn public_key(&self) -> Result { diff --git a/cmd/soroban-cli/src/commands/keys/show.rs b/cmd/soroban-cli/src/commands/keys/show.rs index 58c47740c..ae0faab03 100644 --- a/cmd/soroban-cli/src/commands/keys/show.rs +++ b/cmd/soroban-cli/src/commands/keys/show.rs @@ -1,6 +1,6 @@ use clap::arg; -use crate::config::{locator, secret}; +use crate::config::{key, locator}; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -8,10 +8,7 @@ pub enum Error { Config(#[from] locator::Error), #[error(transparent)] - Secret(#[from] secret::Error), - - #[error(transparent)] - StrKey(#[from] stellar_strkey::DecodeError), + Key(#[from] key::Error), } #[derive(Debug, clap::Parser, Clone)] diff --git a/cmd/soroban-cli/src/config/address.rs b/cmd/soroban-cli/src/config/address.rs index 066bc8d91..af1918103 100644 --- a/cmd/soroban-cli/src/config/address.rs +++ b/cmd/soroban-cli/src/config/address.rs @@ -2,7 +2,7 @@ use std::str::FromStr; use crate::xdr; -use super::{locator, secret}; +use super::{key, locator, secret}; /// Address can be either a public key or eventually an alias of a address. #[derive(Clone, Debug)] @@ -22,7 +22,7 @@ pub enum Error { #[error(transparent)] Locator(#[from] locator::Error), #[error(transparent)] - Secret(#[from] secret::Error), + Key(#[from] key::Error), #[error("Address cannot be used to sign {0}")] CannotSign(xdr::MuxedAccount), } @@ -46,18 +46,16 @@ impl Address { ) -> 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(), - )) - }), + Address::AliasOrSecret(alias) => alias + .parse() + .or_else(|_| Ok(locator.read_identity(alias)?.public_key(hd_path)?)), } } pub fn resolve_secret(&self, locator: &locator::Args) -> Result { match &self { + Address::AliasOrSecret(alias) => Ok(locator.get_private_key(alias)?), 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/key.rs b/cmd/soroban-cli/src/config/key.rs new file mode 100644 index 000000000..e6adface4 --- /dev/null +++ b/cmd/soroban-cli/src/config/key.rs @@ -0,0 +1,146 @@ +use std::{fmt::Display, str::FromStr}; + +use serde::{Deserialize, Serialize}; + +use super::secret::{self, Secret}; +use crate::xdr; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Failed to extract public key from secret")] + SecretPublicKey, + #[error(transparent)] + Secret(#[from] secret::Error), + #[error(transparent)] + StrKey(#[from] stellar_strkey::DecodeError), +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(untagged)] +pub enum Key { + Secret(Secret), + PublicKey { public_key: PublicKey }, + MuxedAccount { muxed_account: MuxedAccount }, +} + +impl Key { + pub fn public_key(&self, hd_path: Option) -> Result { + let bytes = match self { + Key::Secret(secret) => secret.public_key(hd_path)?.0, + Key::PublicKey { + public_key: PublicKey(key), + } => key.0, + Key::MuxedAccount { + muxed_account: MuxedAccount(stellar_strkey::ed25519::MuxedAccount { ed25519, id }), + } => { + return Ok(xdr::MuxedAccount::MuxedEd25519(xdr::MuxedAccountMed25519 { + ed25519: xdr::Uint256(*ed25519), + id: *id, + })) + } + }; + Ok(xdr::MuxedAccount::Ed25519(xdr::Uint256(bytes))) + } + + pub fn private_key( + &self, + hd_path: Option, + ) -> Result { + match self { + Key::Secret(secret) => Ok(secret.private_key(hd_path)?), + _ => Err(Error::SecretPublicKey), + } + } +} + +impl FromStr for Key { + type Err = Error; + + fn from_str(s: &str) -> Result { + if let Ok(secret) = s.parse() { + return Ok(Key::Secret(secret)); + } + if let Ok(public_key) = s.parse() { + return Ok(Key::PublicKey { public_key }); + } + if let Ok(muxed_account) = s.parse() { + return Ok(Key::MuxedAccount { muxed_account }); + } + todo!("Error handling for invalid key format"); + } +} + +impl From for Key { + fn from(value: stellar_strkey::ed25519::PublicKey) -> Self { + Key::PublicKey { + public_key: PublicKey(value), + } + } +} + +impl From<&stellar_strkey::ed25519::PublicKey> for Key { + fn from(stellar_strkey::ed25519::PublicKey(key): &stellar_strkey::ed25519::PublicKey) -> Self { + stellar_strkey::ed25519::PublicKey(*key).into() + } +} + +#[derive(Debug, PartialEq, Eq, serde_with::SerializeDisplay, serde_with::DeserializeFromStr)] +pub struct PublicKey(pub stellar_strkey::ed25519::PublicKey); + +impl FromStr for PublicKey { + type Err = stellar_strkey::DecodeError; + + fn from_str(s: &str) -> Result { + Ok(PublicKey(stellar_strkey::ed25519::PublicKey::from_str(s)?)) + } +} + +impl Display for PublicKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl From<&PublicKey> for stellar_strkey::ed25519::MuxedAccount { + fn from(PublicKey(stellar_strkey::ed25519::PublicKey(key)): &PublicKey) -> Self { + stellar_strkey::ed25519::MuxedAccount { + id: 0, + ed25519: *key, + } + } +} + +#[derive(Debug, PartialEq, Eq, serde_with::SerializeDisplay, serde_with::DeserializeFromStr)] +pub struct MuxedAccount(pub stellar_strkey::ed25519::MuxedAccount); + +impl FromStr for MuxedAccount { + type Err = stellar_strkey::DecodeError; + + fn from_str(s: &str) -> Result { + Ok(MuxedAccount( + stellar_strkey::ed25519::MuxedAccount::from_str(s)?, + )) + } +} + +impl Display for MuxedAccount { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn public_key() { + let key = Key::PublicKey { + public_key: PublicKey(stellar_strkey::ed25519::PublicKey([0; 32])), + }; + let serialized = toml::to_string(&key).unwrap(); + println!("{serialized}"); + let deserialized: Key = toml::from_str(&serialized).unwrap(); + assert_eq!(key, deserialized); + } +} diff --git a/cmd/soroban-cli/src/config/locator.rs b/cmd/soroban-cli/src/config/locator.rs index b6f5c75c1..07c7ee490 100644 --- a/cmd/soroban-cli/src/config/locator.rs +++ b/cmd/soroban-cli/src/config/locator.rs @@ -16,6 +16,7 @@ use crate::{commands::HEADING_GLOBAL, utils::find_config_dir, Pwd}; use super::{ alias, + key::Key, network::{self, Network}, secret::Secret, Config, @@ -163,7 +164,19 @@ impl Args { } pub fn write_identity(&self, name: &str, secret: &Secret) -> Result<(), Error> { - KeyType::Identity.write(name, secret, &self.config_dir()?) + self.write_key(name, &Key::Secret(secret.clone())) + } + + pub fn write_public_key( + &self, + name: &str, + public_key: &stellar_strkey::ed25519::PublicKey, + ) -> Result<(), Error> { + self.write_key(name, &public_key.into()) + } + + pub fn write_key(&self, name: &str, public_key: &Key) -> Result<(), Error> { + KeyType::Identity.write(name, public_key, &self.config_dir()?) } pub fn write_network(&self, name: &str, network: &Network) -> Result<(), Error> { @@ -228,17 +241,20 @@ impl Args { Ok(saved_networks.chain(default_networks).collect()) } - pub fn read_identity(&self, name: &str) -> Result { - Ok(KeyType::Identity - .read_with_global(name, &self.local_config()?) - .or_else(|_| name.parse())?) + pub fn read_identity(&self, name: &str) -> Result { + Ok(KeyType::Identity.read_with_global(name, &self.local_config()?)?) } - pub fn key(&self, key_or_name: &str) -> Result { + pub fn get_private_key(&self, key_or_name: &str) -> Result { if let Ok(signer) = key_or_name.parse::() { Ok(signer) } else { - self.read_identity(key_or_name) + match self.read_identity(key_or_name)? { + Key::Secret(s) => Ok(s), + _ => Err(Error::SecretFileRead { + path: self.alias_path(key_or_name)?, + }), + } } } diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index a429ff434..fb4c787fd 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -17,6 +17,7 @@ use network::Network; pub mod address; pub mod alias; pub mod data; +pub mod key; pub mod locator; pub mod network; pub mod secret; diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index a7fd86fda..995b88723 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -9,6 +9,8 @@ use crate::{ utils, }; +use super::key::{self, Key}; + #[derive(thiserror::Error, Debug)] pub enum Error { #[error("invalid secret key")] @@ -39,19 +41,26 @@ pub struct Args { /// Add using 12 word seed phrase to generate `secret_key` #[arg(long, conflicts_with = "secret_key")] pub seed_phrase: bool, + + /// Add a public key, ed25519, or muxed account, e.g. G1.., M2.. + #[arg(long, conflicts_with = "seed_phrase", conflicts_with = "secret_key")] + pub public_key: Option, } impl Args { - pub fn read_secret(&self) -> Result { + pub fn read_key(&self) -> Result { + if let Some(public_key) = self.public_key.as_ref() { + return Ok(public_key.parse()?); + }; if let Ok(secret_key) = std::env::var("SOROBAN_SECRET_KEY") { - Ok(Secret::SecretKey { secret_key }) + Ok(Key::Secret(Secret::SecretKey { secret_key })) } else if self.secret_key { println!("Type a secret key: "); let secret_key = read_password()?; let secret_key = PrivateKey::from_string(&secret_key) .map_err(|_| Error::InvalidSecretKey)? .to_string(); - Ok(Secret::SecretKey { secret_key }) + Ok(Key::Secret(Secret::SecretKey { secret_key })) } else if self.seed_phrase { println!("Type a 12 word seed phrase: "); let seed_phrase = read_password()?; @@ -60,20 +69,20 @@ impl Args { // let len = seed_phrase.len(); // return Err(Error::InvalidSeedPhrase { len }); // } - Ok(Secret::SeedPhrase { + Ok(Key::Secret(Secret::SeedPhrase { seed_phrase: seed_phrase .into_iter() .map(ToString::to_string) .collect::>() .join(" "), - }) + })) } else { - Err(Error::PasswordRead {}) + Err(Error::PasswordRead {}.into()) } } } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(untagged)] pub enum Secret { SecretKey { secret_key: String }, diff --git a/cmd/soroban-cli/src/config/sign_with.rs b/cmd/soroban-cli/src/config/sign_with.rs index 475013bc8..c9d164914 100644 --- a/cmd/soroban-cli/src/config/sign_with.rs +++ b/cmd/soroban-cli/src/config/sign_with.rs @@ -64,7 +64,7 @@ impl Args { } } else { let key_or_name = self.sign_with_key.as_deref().ok_or(Error::NoSignWithKey)?; - let secret = locator.key(key_or_name)?; + let secret = locator.get_private_key(key_or_name)?; secret.signer(self.hd_path, print)? }; Ok(signer.sign_tx_env(tx, network)?) From 2aec046e6f0b875f1c3c819b6eab6e74377f5c94 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 12 Nov 2024 15:40:29 -0500 Subject: [PATCH 08/74] fix: lookup first before parsing raw key --- cmd/soroban-cli/src/config/address.rs | 2 +- cmd/soroban-cli/src/config/key.rs | 24 +++++++++-------- cmd/soroban-cli/src/config/locator.rs | 38 ++++++++++++++++++--------- 3 files changed, 40 insertions(+), 24 deletions(-) diff --git a/cmd/soroban-cli/src/config/address.rs b/cmd/soroban-cli/src/config/address.rs index af1918103..ac2adc762 100644 --- a/cmd/soroban-cli/src/config/address.rs +++ b/cmd/soroban-cli/src/config/address.rs @@ -48,7 +48,7 @@ impl Address { Address::MuxedAccount(muxed_account) => Ok(muxed_account.clone()), Address::AliasOrSecret(alias) => alias .parse() - .or_else(|_| Ok(locator.read_identity(alias)?.public_key(hd_path)?)), + .or_else(|_| Ok(locator.get_public_key(alias, hd_path)?)), } } diff --git a/cmd/soroban-cli/src/config/key.rs b/cmd/soroban-cli/src/config/key.rs index e6adface4..18f52b876 100644 --- a/cmd/soroban-cli/src/config/key.rs +++ b/cmd/soroban-cli/src/config/key.rs @@ -13,13 +13,15 @@ pub enum Error { Secret(#[from] secret::Error), #[error(transparent)] StrKey(#[from] stellar_strkey::DecodeError), + #[error("failed to parse key {0}")] + Parse(String), } #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] #[serde(untagged)] pub enum Key { Secret(Secret), - PublicKey { public_key: PublicKey }, + PublicKey { public_key: Public }, MuxedAccount { muxed_account: MuxedAccount }, } @@ -28,7 +30,7 @@ impl Key { let bytes = match self { Key::Secret(secret) => secret.public_key(hd_path)?.0, Key::PublicKey { - public_key: PublicKey(key), + public_key: Public(key), } => key.0, Key::MuxedAccount { muxed_account: MuxedAccount(stellar_strkey::ed25519::MuxedAccount { ed25519, id }), @@ -66,14 +68,14 @@ impl FromStr for Key { if let Ok(muxed_account) = s.parse() { return Ok(Key::MuxedAccount { muxed_account }); } - todo!("Error handling for invalid key format"); + Err(Error::Parse(s.to_owned())) } } impl From for Key { fn from(value: stellar_strkey::ed25519::PublicKey) -> Self { Key::PublicKey { - public_key: PublicKey(value), + public_key: Public(value), } } } @@ -85,24 +87,24 @@ impl From<&stellar_strkey::ed25519::PublicKey> for Key { } #[derive(Debug, PartialEq, Eq, serde_with::SerializeDisplay, serde_with::DeserializeFromStr)] -pub struct PublicKey(pub stellar_strkey::ed25519::PublicKey); +pub struct Public(pub stellar_strkey::ed25519::PublicKey); -impl FromStr for PublicKey { +impl FromStr for Public { type Err = stellar_strkey::DecodeError; fn from_str(s: &str) -> Result { - Ok(PublicKey(stellar_strkey::ed25519::PublicKey::from_str(s)?)) + Ok(Public(stellar_strkey::ed25519::PublicKey::from_str(s)?)) } } -impl Display for PublicKey { +impl Display for Public { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } -impl From<&PublicKey> for stellar_strkey::ed25519::MuxedAccount { - fn from(PublicKey(stellar_strkey::ed25519::PublicKey(key)): &PublicKey) -> Self { +impl From<&Public> for stellar_strkey::ed25519::MuxedAccount { + fn from(Public(stellar_strkey::ed25519::PublicKey(key)): &Public) -> Self { stellar_strkey::ed25519::MuxedAccount { id: 0, ed25519: *key, @@ -136,7 +138,7 @@ mod test { #[test] fn public_key() { let key = Key::PublicKey { - public_key: PublicKey(stellar_strkey::ed25519::PublicKey([0; 32])), + public_key: Public(stellar_strkey::ed25519::PublicKey([0; 32])), }; let serialized = toml::to_string(&key).unwrap(); println!("{serialized}"); diff --git a/cmd/soroban-cli/src/config/locator.rs b/cmd/soroban-cli/src/config/locator.rs index 07c7ee490..68a95f628 100644 --- a/cmd/soroban-cli/src/config/locator.rs +++ b/cmd/soroban-cli/src/config/locator.rs @@ -12,11 +12,11 @@ use std::{ }; use stellar_strkey::{Contract, DecodeError}; -use crate::{commands::HEADING_GLOBAL, utils::find_config_dir, Pwd}; +use crate::{commands::HEADING_GLOBAL, utils::find_config_dir, xdr, Pwd}; use super::{ alias, - key::Key, + key::{self, Key}, network::{self, Network}, secret::Secret, Config, @@ -84,6 +84,10 @@ pub enum Error { UpgradeCheckReadFailed { path: PathBuf, error: io::Error }, #[error("Failed to write upgrade check file: {path}: {error}")] UpgradeCheckWriteFailed { path: PathBuf, error: io::Error }, + #[error("Only private keys and seed phrases are supported for getting private keys {0}")] + SecretKeyOnly(String), + #[error(transparent)] + Key(#[from] key::Error), } #[derive(Debug, clap::Args, Default, Clone)] @@ -242,22 +246,32 @@ impl Args { } pub fn read_identity(&self, name: &str) -> Result { - Ok(KeyType::Identity.read_with_global(name, &self.local_config()?)?) + KeyType::Identity.read_with_global(name, &self.local_config()?) } - pub fn get_private_key(&self, key_or_name: &str) -> Result { - if let Ok(signer) = key_or_name.parse::() { - Ok(signer) + pub fn read_key(&self, key_or_name: &str) -> Result { + if let Ok(key) = self.read_identity(key_or_name) { + Ok(key) } else { - match self.read_identity(key_or_name)? { - Key::Secret(s) => Ok(s), - _ => Err(Error::SecretFileRead { - path: self.alias_path(key_or_name)?, - }), - } + Ok(key_or_name.parse()?) + } + } + + pub fn get_private_key(&self, key_or_name: &str) -> Result { + match self.read_key(key_or_name)? { + Key::Secret(s) => Ok(s), + _ => Err(Error::SecretKeyOnly(key_or_name.to_string())), } } + pub fn get_public_key( + &self, + key_or_name: &str, + hd_path: Option, + ) -> Result { + Ok(self.read_key(key_or_name)?.public_key(hd_path)?) + } + 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 { From 52bd5e83d42340064de0c94d6b394f09cca7c357 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Thu, 14 Nov 2024 12:31:22 -0500 Subject: [PATCH 09/74] fix: docs --- FULL_HELP_DOCS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index 55e093acc..3580f42a6 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -958,6 +958,7 @@ Add a new identity (keypair, ledger, macOS keychain) * `--secret-key` — Add using `secret_key` Can provide with `SOROBAN_SECRET_KEY` * `--seed-phrase` — Add using 12 word seed phrase to generate `secret_key` +* `--public-key ` — Add a public key, ed25519, or muxed account, e.g. G1.., M2.. * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." From 8f96343e737670663d83172f3d30c72dd5ab56d3 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Thu, 14 Nov 2024 12:55:00 -0500 Subject: [PATCH 10/74] fix: clippy --- cmd/soroban-cli/src/config/secret.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index 995b88723..82008795b 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -50,7 +50,7 @@ pub struct Args { impl Args { pub fn read_key(&self) -> Result { if let Some(public_key) = self.public_key.as_ref() { - return Ok(public_key.parse()?); + return public_key.parse(); }; if let Ok(secret_key) = std::env::var("SOROBAN_SECRET_KEY") { Ok(Key::Secret(Secret::SecretKey { secret_key })) From 0cc57fead531487490dd213044d437810703289b Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 26 Nov 2024 14:38:43 -0500 Subject: [PATCH 11/74] feat: update tx new commands to use Address type Add tests using --- FULL_HELP_DOCS.md | 4 +- .../tests/it/integration/tx/operations.rs | 92 +++++++++++++++++++ cmd/soroban-cli/src/commands/tx/args.rs | 20 +++- cmd/soroban-cli/src/commands/tx/mod.rs | 2 + .../src/commands/tx/new/account_merge.rs | 15 +-- .../src/commands/tx/new/create_account.rs | 17 ++-- cmd/soroban-cli/src/commands/tx/new/mod.rs | 34 +++++-- .../src/commands/tx/new/payment.rs | 13 +-- .../src/commands/tx/new/set_options.rs | 20 ++-- .../commands/tx/new/set_trustline_flags.rs | 25 ++--- 10 files changed, 192 insertions(+), 50 deletions(-) diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index 3580f42a6..f8f211f37 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -1577,7 +1577,7 @@ Transfers the XLM balance of an account to another account and removes the sourc * `--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...' +* `--account ` — Muxed Account to merge with, e.g. `GBX...`, 'MBX...' or alias @@ -1792,7 +1792,7 @@ Allows issuing account to configure authorization and trustline flags to an asse * `--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 +* `--trustor ` — Account to set trustline flags for, e.g. `GBX...`, or alias, or muxed account, `M123...`` * `--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 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 9988b2cdd..9fc843df1 100644 --- a/cmd/crates/soroban-test/tests/it/integration/tx/operations.rs +++ b/cmd/crates/soroban-test/tests/it/integration/tx/operations.rs @@ -74,6 +74,71 @@ async fn create_account() { invoke_hello_world(sandbox, &id); } +#[tokio::test] +async fn create_account_with_alias() { + let sandbox = &TestEnv::new(); + sandbox + .new_assert_cmd("keys") + .args(["generate", "--no-fund", "new"]) + .assert() + .success(); + 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", + "new", + "--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_with_alias() { + 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", + "--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 payment() { let sandbox = &TestEnv::new(); @@ -157,6 +222,33 @@ async fn account_merge() { assert_eq!(before.balance + before1.balance - fee, after.balance); } +#[tokio::test] +async fn account_merge_with_alias() { + 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", + "--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(); diff --git a/cmd/soroban-cli/src/commands/tx/args.rs b/cmd/soroban-cli/src/commands/tx/args.rs index 1da1f230a..c703ead6b 100644 --- a/cmd/soroban-cli/src/commands/tx/args.rs +++ b/cmd/soroban-cli/src/commands/tx/args.rs @@ -1,6 +1,10 @@ use crate::{ commands::{global, txn_result::TxnEnvelopeResult}, - config::{self, data, network, secret}, + config::{ + self, + address::{self, Address}, + data, network, secret, + }, fee, rpc::{self, Client, GetTransactionResponse}, tx::builder::{self, TxExt}, @@ -32,6 +36,8 @@ pub enum Error { Data(#[from] data::Error), #[error(transparent)] Xdr(#[from] xdr::Error), + #[error(transparent)] + Address(#[from] address::Error), } impl Args { @@ -64,7 +70,7 @@ impl Args { op: impl Into, global_args: &global::Args, ) -> Result, Error> { - let tx = self.tx(op.into()).await?; + let tx = self.tx(op).await?; self.handle_tx(tx, global_args).await } pub async fn handle_and_print( @@ -104,4 +110,14 @@ impl Args { pub fn source_account(&self) -> Result { Ok(self.config.source_account()?) } + + pub fn reslove_muxed_address(&self, address: &Address) -> Result { + Ok(address.resolve_muxed_account(&self.config.locator, self.config.hd_path)?) + } + + pub fn reslove_account_id(&self, address: &Address) -> Result { + Ok(address + .resolve_muxed_account(&self.config.locator, self.config.hd_path)? + .account_id()) + } } diff --git a/cmd/soroban-cli/src/commands/tx/mod.rs b/cmd/soroban-cli/src/commands/tx/mod.rs index c0390f92e..51a10cb0d 100644 --- a/cmd/soroban-cli/src/commands/tx/mod.rs +++ b/cmd/soroban-cli/src/commands/tx/mod.rs @@ -37,6 +37,8 @@ pub enum Error { Sign(#[from] sign::Error), #[error(transparent)] Send(#[from] send::Error), + #[error(transparent)] + Args(#[from] args::Error), } impl Cmd { diff --git a/cmd/soroban-cli/src/commands/tx/new/account_merge.rs b/cmd/soroban-cli/src/commands/tx/new/account_merge.rs index ce01f5e1f..5ff834aaa 100644 --- a/cmd/soroban-cli/src/commands/tx/new/account_merge.rs +++ b/cmd/soroban-cli/src/commands/tx/new/account_merge.rs @@ -1,19 +1,22 @@ use clap::{command, Parser}; -use crate::{commands::tx, xdr}; +use crate::{commands::tx, config::address, 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...' + /// Muxed Account to merge with, e.g. `GBX...`, 'MBX...' or alias #[arg(long)] - pub account: xdr::MuxedAccount, + pub account: address::Address, } -impl From<&Cmd> for xdr::OperationBody { - fn from(cmd: &Cmd) -> Self { - xdr::OperationBody::AccountMerge(cmd.account.clone()) +impl TryFrom<&Cmd> for xdr::OperationBody { + type Error = tx::args::Error; + fn try_from(cmd: &Cmd) -> Result { + Ok(xdr::OperationBody::AccountMerge( + cmd.tx.reslove_muxed_address(&cmd.account)?, + )) } } 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 2826439e9..280fc75bd 100644 --- a/cmd/soroban-cli/src/commands/tx/new/create_account.rs +++ b/cmd/soroban-cli/src/commands/tx/new/create_account.rs @@ -1,6 +1,6 @@ use clap::{command, Parser}; -use crate::{commands::tx, tx::builder, xdr}; +use crate::{commands::tx, config::address, xdr}; #[derive(Parser, Debug, Clone)] #[group(skip)] @@ -9,17 +9,18 @@ pub struct Cmd { pub tx: tx::Args, /// Account Id to create, e.g. `GBX...` #[arg(long)] - pub destination: xdr::AccountId, + pub destination: address::Address, /// Initial balance in stroops of the account, default 1 XLM #[arg(long, default_value = "10_000_000")] pub starting_balance: builder::Amount, } -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.into(), - }) +impl TryFrom<&Cmd> for xdr::OperationBody { + type Error = tx::args::Error; + fn try_from(cmd: &Cmd) -> Result { + Ok(xdr::OperationBody::CreateAccount(xdr::CreateAccountOp { + destination: cmd.tx.reslove_account_id(&cmd.destination)?, + starting_balance: cmd.starting_balance, + })) } } diff --git a/cmd/soroban-cli/src/commands/tx/new/mod.rs b/cmd/soroban-cli/src/commands/tx/new/mod.rs index e5923f4ec..1d5682cc0 100644 --- a/cmd/soroban-cli/src/commands/tx/new/mod.rs +++ b/cmd/soroban-cli/src/commands/tx/new/mod.rs @@ -1,4 +1,5 @@ use clap::Parser; +use soroban_sdk::xdr::OperationBody; use super::global; @@ -52,17 +53,34 @@ pub enum Error { Tx(#[from] super::args::Error), } +impl TryFrom<&Cmd> for OperationBody { + type Error = super::args::Error; + fn try_from(cmd: &Cmd) -> Result { + Ok(match cmd { + Cmd::AccountMerge(cmd) => cmd.try_into()?, + Cmd::BumpSequence(cmd) => cmd.into(), + Cmd::ChangeTrust(cmd) => cmd.into(), + Cmd::CreateAccount(cmd) => cmd.try_into()?, + Cmd::ManageData(cmd) => cmd.into(), + Cmd::Payment(cmd) => cmd.try_into()?, + Cmd::SetOptions(cmd) => cmd.try_into()?, + Cmd::SetTrustlineFlags(cmd) => cmd.try_into()?, + }) + } +} + impl Cmd { pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { + let op = OperationBody::try_from(self)?; 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, + Cmd::AccountMerge(cmd) => cmd.tx.handle_and_print(op, global_args).await, + Cmd::BumpSequence(cmd) => cmd.tx.handle_and_print(op, global_args).await, + Cmd::ChangeTrust(cmd) => cmd.tx.handle_and_print(op, global_args).await, + Cmd::CreateAccount(cmd) => cmd.tx.handle_and_print(op, global_args).await, + Cmd::ManageData(cmd) => cmd.tx.handle_and_print(op, global_args).await, + Cmd::Payment(cmd) => cmd.tx.handle_and_print(op, global_args).await, + Cmd::SetOptions(cmd) => cmd.tx.handle_and_print(op, global_args).await, + Cmd::SetTrustlineFlags(cmd) => cmd.tx.handle_and_print(op, 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 index 3cebfa532..1ab999ab9 100644 --- a/cmd/soroban-cli/src/commands/tx/new/payment.rs +++ b/cmd/soroban-cli/src/commands/tx/new/payment.rs @@ -1,6 +1,6 @@ use clap::{command, Parser}; -use crate::{commands::tx, tx::builder, xdr}; +use crate::{commands::tx, config::address, tx::builder, xdr}; #[derive(Parser, Debug, Clone)] #[group(skip)] @@ -9,7 +9,7 @@ pub struct Cmd { pub tx: tx::Args, /// Account to send to, e.g. `GBX...` #[arg(long)] - pub destination: xdr::MuxedAccount, + pub destination: address::Address, /// Asset to send, default native, e.i. XLM #[arg(long, default_value = "native")] pub asset: builder::Asset, @@ -18,10 +18,11 @@ pub struct Cmd { pub amount: builder::Amount, } -impl From<&Cmd> for xdr::OperationBody { - fn from(cmd: &Cmd) -> Self { - xdr::OperationBody::Payment(xdr::PaymentOp { - destination: cmd.destination.clone(), +impl TryFrom<&Cmd> for xdr::OperationBody { + type Error = tx::args::Error; + fn try_from(cmd: &Cmd) -> Result { + Ok(xdr::OperationBody::Payment(xdr::PaymentOp { + destination: cmd.tx.reslove_muxed_address(&cmd.destination)?, asset: cmd.asset.clone().into(), amount: cmd.amount.into(), }) diff --git a/cmd/soroban-cli/src/commands/tx/new/set_options.rs b/cmd/soroban-cli/src/commands/tx/new/set_options.rs index 69cd10745..e5fce4557 100644 --- a/cmd/soroban-cli/src/commands/tx/new/set_options.rs +++ b/cmd/soroban-cli/src/commands/tx/new/set_options.rs @@ -1,6 +1,6 @@ use clap::{command, Parser}; -use crate::{commands::tx, xdr}; +use crate::{commands::tx, config::address, xdr}; #[derive(Parser, Debug, Clone)] #[allow(clippy::struct_excessive_bools, clippy::doc_markdown)] @@ -10,7 +10,7 @@ pub struct Cmd { pub tx: tx::Args, #[arg(long)] /// Account of the inflation destination. - pub inflation_dest: Option, + 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, @@ -61,8 +61,9 @@ pub struct Cmd { pub clear_clawback_enabled: bool, } -impl From<&Cmd> for xdr::OperationBody { - fn from(cmd: &Cmd) -> Self { +impl TryFrom<&Cmd> for xdr::OperationBody { + type Error = tx::args::Error; + fn try_from(cmd: &Cmd) -> Result { let mut set_flags = None; let mut set_flag = |flag: xdr::AccountFlags| { *set_flags.get_or_insert(0) |= flag as u32; @@ -108,8 +109,13 @@ impl From<&Cmd> for xdr::OperationBody { } else { None }; - xdr::OperationBody::SetOptions(xdr::SetOptionsOp { - inflation_dest: cmd.inflation_dest.clone().map(Into::into), + let inflation_dest: Option = cmd + .inflation_dest + .as_ref() + .map(|dest| cmd.tx.reslove_account_id(dest)) + .transpose()?; + Ok(xdr::OperationBody::SetOptions(xdr::SetOptionsOp { + inflation_dest, clear_flags, set_flags, master_weight: cmd.master_weight.map(Into::into), @@ -118,6 +124,6 @@ impl From<&Cmd> for xdr::OperationBody { 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 index d9b70ecbd..318f3ff1d 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,6 +1,6 @@ use clap::{command, Parser}; -use crate::{commands::tx, tx::builder, xdr}; +use crate::{commands::tx, config::address, tx::builder, xdr}; #[allow(clippy::struct_excessive_bools, clippy::doc_markdown)] #[derive(Parser, Debug, Clone)] @@ -8,9 +8,9 @@ use crate::{commands::tx, tx::builder, xdr}; pub struct Cmd { #[command(flatten)] pub tx: tx::Args, - /// Account to set trustline flags for + /// Account to set trustline flags for, e.g. `GBX...`, or alias, or muxed account, `M123...`` #[arg(long)] - pub trustor: xdr::AccountId, + pub trustor: address::Address, /// Asset to set trustline flags for #[arg(long)] pub asset: builder::Asset, @@ -32,8 +32,9 @@ pub struct Cmd { pub clear_trustline_clawback_enabled: bool, } -impl From<&Cmd> for xdr::OperationBody { - fn from(cmd: &Cmd) -> Self { +impl TryFrom<&Cmd> for xdr::OperationBody { + type Error = tx::args::Error; + fn try_from(cmd: &Cmd) -> Result { let mut set_flags = 0; let mut set_flag = |flag: xdr::TrustLineFlags| set_flags |= flag as u32; @@ -59,11 +60,13 @@ impl From<&Cmd> for xdr::OperationBody { clear_flag(xdr::TrustLineFlags::TrustlineClawbackEnabledFlag); }; - xdr::OperationBody::SetTrustLineFlags(xdr::SetTrustLineFlagsOp { - trustor: cmd.trustor.clone(), - asset: cmd.asset.clone().into(), - clear_flags, - set_flags, - }) + Ok(xdr::OperationBody::SetTrustLineFlags( + xdr::SetTrustLineFlagsOp { + trustor: cmd.tx.reslove_account_id(&cmd.trustor)?, + asset: cmd.asset.clone().into(), + clear_flags, + set_flags, + }, + )) } } From 31cde547fe055d10eec0c445dbe7af8487848268 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Fri, 29 Nov 2024 11:50:54 -0500 Subject: [PATCH 12/74] fix: rebase issue --- cmd/soroban-cli/src/commands/tx/new/create_account.rs | 4 ++-- cmd/soroban-cli/src/commands/tx/new/payment.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) 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 280fc75bd..4ae1c13d2 100644 --- a/cmd/soroban-cli/src/commands/tx/new/create_account.rs +++ b/cmd/soroban-cli/src/commands/tx/new/create_account.rs @@ -1,6 +1,6 @@ use clap::{command, Parser}; -use crate::{commands::tx, config::address, xdr}; +use crate::{commands::tx, config::address, tx::builder, xdr}; #[derive(Parser, Debug, Clone)] #[group(skip)] @@ -20,7 +20,7 @@ impl TryFrom<&Cmd> for xdr::OperationBody { fn try_from(cmd: &Cmd) -> Result { Ok(xdr::OperationBody::CreateAccount(xdr::CreateAccountOp { destination: cmd.tx.reslove_account_id(&cmd.destination)?, - starting_balance: cmd.starting_balance, + starting_balance: cmd.starting_balance.into(), })) } } diff --git a/cmd/soroban-cli/src/commands/tx/new/payment.rs b/cmd/soroban-cli/src/commands/tx/new/payment.rs index 1ab999ab9..397bf68a7 100644 --- a/cmd/soroban-cli/src/commands/tx/new/payment.rs +++ b/cmd/soroban-cli/src/commands/tx/new/payment.rs @@ -25,6 +25,6 @@ impl TryFrom<&Cmd> for xdr::OperationBody { destination: cmd.tx.reslove_muxed_address(&cmd.destination)?, asset: cmd.asset.clone().into(), amount: cmd.amount.into(), - }) + })) } } From a86a98fcc04d91520fcb2ea718ee3c4c25a90aae Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Mon, 2 Dec 2024 08:45:30 -0500 Subject: [PATCH 13/74] Update cmd/soroban-cli/src/config/key.rs Co-authored-by: Leigh McCulloch <351529+leighmcculloch@users.noreply.github.com> --- cmd/soroban-cli/src/config/key.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/soroban-cli/src/config/key.rs b/cmd/soroban-cli/src/config/key.rs index 18f52b876..6c5b99a5d 100644 --- a/cmd/soroban-cli/src/config/key.rs +++ b/cmd/soroban-cli/src/config/key.rs @@ -7,7 +7,7 @@ use crate::xdr; #[derive(thiserror::Error, Debug)] pub enum Error { - #[error("Failed to extract public key from secret")] + #[error("failed to extract public key from secret")] SecretPublicKey, #[error(transparent)] Secret(#[from] secret::Error), From b36db49696d71dde61607be68c732b09d376fc8e Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Mon, 2 Dec 2024 09:57:56 -0500 Subject: [PATCH 14/74] fix: PR review --- cmd/soroban-cli/src/config/key.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/soroban-cli/src/config/key.rs b/cmd/soroban-cli/src/config/key.rs index 6c5b99a5d..dcb96775b 100644 --- a/cmd/soroban-cli/src/config/key.rs +++ b/cmd/soroban-cli/src/config/key.rs @@ -7,14 +7,14 @@ use crate::xdr; #[derive(thiserror::Error, Debug)] pub enum Error { - #[error("failed to extract public key from secret")] + #[error("failed to extract secret from public key ")] SecretPublicKey, #[error(transparent)] Secret(#[from] secret::Error), #[error(transparent)] StrKey(#[from] stellar_strkey::DecodeError), - #[error("failed to parse key {0}")] - Parse(String), + #[error("failed to parse key")] + Parse, } #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] @@ -68,7 +68,7 @@ impl FromStr for Key { if let Ok(muxed_account) = s.parse() { return Ok(Key::MuxedAccount { muxed_account }); } - Err(Error::Parse(s.to_owned())) + Err(Error::Parse) } } From 8c06970bc61df353e1da60e9d81ef8da00dfe776 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 5 Nov 2024 10:39:20 -0500 Subject: [PATCH 15/74] feat: initial work into system keychain --- Cargo.lock | 111 ++++++++++++++++++++++++++ cmd/soroban-cli/Cargo.toml | 11 ++- cmd/soroban-cli/src/bin/secret.rs | 16 ++++ cmd/soroban-cli/src/signer.rs | 2 + cmd/soroban-cli/src/signer/keyring.rs | 100 +++++++++++++++++++++++ 5 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 cmd/soroban-cli/src/bin/secret.rs create mode 100644 cmd/soroban-cli/src/signer/keyring.rs diff --git a/Cargo.lock b/Cargo.lock index 3f8fcf4ac..dbf818db8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1179,6 +1179,30 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +[[package]] +name = "dbus" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +dependencies = [ + "libc", + "libdbus-sys", + "winapi", +] + +[[package]] +name = "dbus-secret-service" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42a16374481d92aed73ae45b1f120207d8e71d24fb89f357fadbd8f946fd84b" +dependencies = [ + "dbus", + "futures-util", + "num", + "once_cell", + "rand", +] + [[package]] name = "der" version = "0.7.9" @@ -2581,6 +2605,18 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "keyring" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fa83d1ca02db069b5fbe94b23b584d588e989218310c9c15015bb5571ef1a94" +dependencies = [ + "byteorder 1.5.0", + "dbus-secret-service", + "security-framework", + "windows-sys 0.59.0", +] + [[package]] name = "kv-log-macro" version = "1.0.7" @@ -2682,6 +2718,15 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "libdbus-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +dependencies = [ + "pkg-config", +] + [[package]] name = "libm" version = "0.2.8" @@ -2870,6 +2915,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "num" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.4" @@ -2881,6 +2940,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-complex" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +dependencies = [ + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -2908,6 +2976,29 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.17" @@ -4320,6 +4411,7 @@ dependencies = [ "itertools 0.10.5", "jsonrpsee-core", "jsonrpsee-http-client", + "keyring", "mockito", "num-bigint", "open", @@ -4370,6 +4462,8 @@ dependencies = [ "wasm-opt", "wasmparser", "which", + "whoami", + "zeroize", ] [[package]] @@ -5587,6 +5681,12 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + [[package]] name = "wasm-bindgen" version = "0.2.92" @@ -5795,6 +5895,17 @@ dependencies = [ "rustix 0.38.34", ] +[[package]] +name = "whoami" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" +dependencies = [ + "redox_syscall", + "wasite", + "web-sys", +] + [[package]] name = "widestring" version = "1.1.0" diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index 2f2a26255..0d6da167d 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -20,6 +20,10 @@ path = "src/bin/stellar.rs" name = "soroban" path = "src/bin/soroban.rs" +[[bin]] +name = "secret" +path = "src/bin/secret.rs" + [package.metadata.binstall] pkg-url = "{ repo }/releases/download/v{ version }/{ name }-{ version }-{ target }{ archive-suffix }" bin-dir = "{ bin }{ binary-ext }" @@ -72,7 +76,8 @@ rand = "0.8.5" wasmparser = { workspace = true } sha2 = { workspace = true } csv = "1.1.6" -ed25519-dalek = { workspace = true } +# zeroize feature ensures that all sensitive data is zeroed out when dropped +ed25519-dalek = { workspace = true, features = ["zeroize"] } reqwest = { version = "0.12.7", default-features = false, features = [ "rustls-tls", "http2", @@ -124,6 +129,10 @@ fqdn = "0.3.12" open = "5.3.0" url = "2.5.2" wasm-gen = "0.1.4" +zeroize = "1.8.1" +keyring = { version = "3", features = ["apple-native", "windows-native", "sync-secret-service"] } +whoami = "1.5.2" + [build-dependencies] crate-git-revision = "0.0.6" diff --git a/cmd/soroban-cli/src/bin/secret.rs b/cmd/soroban-cli/src/bin/secret.rs new file mode 100644 index 000000000..26d1a51c7 --- /dev/null +++ b/cmd/soroban-cli/src/bin/secret.rs @@ -0,0 +1,16 @@ +use soroban_cli::signer::keyring::{add_key, get_public_key, StellarEntry}; + +fn main() { + let entry = StellarEntry::new("test").unwrap(); + if let Ok(key) = entry.get_public_key() { + println!("{key}") + }; + + let secret = soroban_cli::config::secret::Secret::from_seed(None).unwrap(); + let pub_key = secret.public_key(None).unwrap(); + let key_pair = secret.key_pair(None).unwrap(); + entry.add_password(key_pair.as_bytes()).unwrap(); + let pub_key_2 = entry.get_public_key().unwrap(); + assert_eq!(pub_key, pub_key_2); + println!("{pub_key} == {pub_key_2}"); +} diff --git a/cmd/soroban-cli/src/signer.rs b/cmd/soroban-cli/src/signer.rs index 5bf22499c..a141aaf6b 100644 --- a/cmd/soroban-cli/src/signer.rs +++ b/cmd/soroban-cli/src/signer.rs @@ -11,6 +11,8 @@ use crate::xdr::{ use crate::{config::network::Network, print::Print, utils::transaction_hash}; +pub mod keyring; + #[derive(thiserror::Error, Debug)] pub enum Error { #[error("Contract addresses are not supported to sign auth entries {address}")] diff --git a/cmd/soroban-cli/src/signer/keyring.rs b/cmd/soroban-cli/src/signer/keyring.rs new file mode 100644 index 000000000..eca6d8657 --- /dev/null +++ b/cmd/soroban-cli/src/signer/keyring.rs @@ -0,0 +1,100 @@ +use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; +use ed25519_dalek::Signer; +use keyring::Entry; +use zeroize::Zeroize; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Keyring(#[from] keyring::Error), + #[error(transparent)] + Base64(#[from] base64::DecodeError), +} + +pub struct StellarEntry { + name: String, +} + +impl TryFrom<&StellarEntry> for Entry { + type Error = Error; + fn try_from(StellarEntry { name }: &StellarEntry) -> Result { + Ok(Entry::new( + &format!("org.stellar.cli.{name}"), + &whoami::username(), + )?) + } +} + +impl StellarEntry { + pub fn new(name: &str) -> Result { + Ok(StellarEntry { + name: name.to_string(), + }) + } + + pub fn set_password(&self, password: &[u8]) -> Result<(), Error> { + let data = base64.encode(password); + let entry: Entry = self.try_into()?; + entry.set_password(&data)?; + Ok(()) + } + + pub fn get_password(&self) -> Result, Error> { + let entry: Entry = self.try_into()?; + Ok(base64.decode(entry.get_password()?)?) + } + + pub fn get_public_key(&self) -> Result { + let mut key_vec = self.get_password()?; + let mut key_bytes: [u8; 32] = key_vec.as_slice().try_into().unwrap(); + + let pub_key = { + // Use this scope to ensure the keypair is zeroized + let keypair = ed25519_dalek::SigningKey::from_bytes(&key_bytes); + stellar_strkey::ed25519::PublicKey(*keypair.verifying_key().as_bytes()) + }; + key_vec.zeroize(); + key_bytes.zeroize(); + Ok(pub_key) + } +} + +pub fn sign_data(name: &str, data: &[u8]) -> Result, Box> { + // Retrieve the key from the secure storage + let entry = Entry::new("stellar", name)?; + let key_bytes: [u8; 32] = entry.get_secret()?.try_into().unwrap(); + // Create a keypair from the retrieved bytes + let keypair = ed25519_dalek::SigningKey::from_bytes(&key_bytes); + + // Sign the data + let signature = keypair.sign(data); + + // Clear the key from memory + let mut key_bytes = key_bytes; + key_bytes.zeroize(); + + Ok(signature.to_bytes().to_vec()) +} + +pub fn add_key(name: &str, key_bytes: &[u8]) -> Result<(), Box> { + // Create a new keyring entry for "stellar" + StellarEntry::new(name)?.set_password(key_bytes)?; + Ok(()) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_sign_data() -> Result<(), Box> { + let secret = crate::config::secret::Secret::from_seed(None)?; + let pub_key = secret.public_key(None)?; + let key_pair = secret.key_pair(None)?; + + add_key("test", &key_pair.to_bytes()).unwrap(); + let pub_key_2 = get_public_key("test")?; + assert_eq!(pub_key, pub_key_2); + Ok(()) + } +} From 883cb8cf3bb7e53817a35dc70749524ac1588137 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 5 Nov 2024 15:56:04 -0500 Subject: [PATCH 16/74] chore: clean up --- cmd/soroban-cli/src/bin/secret.rs | 7 ++-- cmd/soroban-cli/src/signer/keyring.rs | 51 ++++++++++++--------------- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/cmd/soroban-cli/src/bin/secret.rs b/cmd/soroban-cli/src/bin/secret.rs index 26d1a51c7..4fc43ec6e 100644 --- a/cmd/soroban-cli/src/bin/secret.rs +++ b/cmd/soroban-cli/src/bin/secret.rs @@ -1,15 +1,16 @@ -use soroban_cli::signer::keyring::{add_key, get_public_key, StellarEntry}; +use soroban_cli::signer::keyring::StellarEntry; fn main() { let entry = StellarEntry::new("test").unwrap(); if let Ok(key) = entry.get_public_key() { - println!("{key}") + println!("{key}"); + return; }; let secret = soroban_cli::config::secret::Secret::from_seed(None).unwrap(); let pub_key = secret.public_key(None).unwrap(); let key_pair = secret.key_pair(None).unwrap(); - entry.add_password(key_pair.as_bytes()).unwrap(); + entry.set_password(key_pair.as_bytes()).unwrap(); let pub_key_2 = entry.get_public_key().unwrap(); assert_eq!(pub_key, pub_key_2); println!("{pub_key} == {pub_key_2}"); diff --git a/cmd/soroban-cli/src/signer/keyring.rs b/cmd/soroban-cli/src/signer/keyring.rs index eca6d8657..bde7e97d6 100644 --- a/cmd/soroban-cli/src/signer/keyring.rs +++ b/cmd/soroban-cli/src/signer/keyring.rs @@ -44,42 +44,37 @@ impl StellarEntry { Ok(base64.decode(entry.get_password()?)?) } - pub fn get_public_key(&self) -> Result { + fn use_key( + &self, + f: impl FnOnce(ed25519_dalek::SigningKey) -> Result, + ) -> Result { let mut key_vec = self.get_password()?; let mut key_bytes: [u8; 32] = key_vec.as_slice().try_into().unwrap(); - let pub_key = { + let result = { // Use this scope to ensure the keypair is zeroized let keypair = ed25519_dalek::SigningKey::from_bytes(&key_bytes); - stellar_strkey::ed25519::PublicKey(*keypair.verifying_key().as_bytes()) + f(keypair)? }; key_vec.zeroize(); key_bytes.zeroize(); - Ok(pub_key) + Ok(result) } -} - -pub fn sign_data(name: &str, data: &[u8]) -> Result, Box> { - // Retrieve the key from the secure storage - let entry = Entry::new("stellar", name)?; - let key_bytes: [u8; 32] = entry.get_secret()?.try_into().unwrap(); - // Create a keypair from the retrieved bytes - let keypair = ed25519_dalek::SigningKey::from_bytes(&key_bytes); - // Sign the data - let signature = keypair.sign(data); - - // Clear the key from memory - let mut key_bytes = key_bytes; - key_bytes.zeroize(); - - Ok(signature.to_bytes().to_vec()) -} + pub fn get_public_key(&self) -> Result { + self.use_key(|keypair| { + Ok(stellar_strkey::ed25519::PublicKey( + *keypair.verifying_key().as_bytes(), + )) + }) + } -pub fn add_key(name: &str, key_bytes: &[u8]) -> Result<(), Box> { - // Create a new keyring entry for "stellar" - StellarEntry::new(name)?.set_password(key_bytes)?; - Ok(()) + pub fn sign_data(&self, data: &[u8]) -> Result, Error> { + self.use_key(|keypair| { + let signature = keypair.sign(data); + Ok(signature.to_bytes().to_vec()) + }) + } } #[cfg(test)] @@ -91,9 +86,9 @@ mod test { let secret = crate::config::secret::Secret::from_seed(None)?; let pub_key = secret.public_key(None)?; let key_pair = secret.key_pair(None)?; - - add_key("test", &key_pair.to_bytes()).unwrap(); - let pub_key_2 = get_public_key("test")?; + let entry = StellarEntry::new("test")?; + entry.set_password(&key_pair.to_bytes()); + let pub_key_2 = entry.get_public_key()?; assert_eq!(pub_key, pub_key_2); Ok(()) } From eb7073402548e24101ea2f2fe4d37664b1264d79 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Mon, 2 Dec 2024 17:23:11 -0500 Subject: [PATCH 17/74] Add KeyName struct in address --- cmd/soroban-cli/src/commands/keys/add.rs | 10 +++++-- cmd/soroban-cli/src/commands/keys/generate.rs | 4 +-- cmd/soroban-cli/src/config/address.rs | 29 +++++++++++++++++++ 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/cmd/soroban-cli/src/commands/keys/add.rs b/cmd/soroban-cli/src/commands/keys/add.rs index d8f528bae..fd796bb86 100644 --- a/cmd/soroban-cli/src/commands/keys/add.rs +++ b/cmd/soroban-cli/src/commands/keys/add.rs @@ -1,21 +1,25 @@ use clap::command; -use crate::config::{locator, secret}; +use crate::config::{ + address::{self, KeyName}, + locator, secret, +}; #[derive(thiserror::Error, Debug)] pub enum Error { #[error(transparent)] Secret(#[from] secret::Error), - #[error(transparent)] Config(#[from] locator::Error), + #[error(transparent)] + Address(#[from] address::Error), } #[derive(Debug, clap::Parser, Clone)] #[group(skip)] pub struct Cmd { /// Name of identity - pub name: String, + pub name: KeyName, #[command(flatten)] pub secrets: secret::Args, diff --git a/cmd/soroban-cli/src/commands/keys/generate.rs b/cmd/soroban-cli/src/commands/keys/generate.rs index c6623386c..315fbb7ec 100644 --- a/cmd/soroban-cli/src/commands/keys/generate.rs +++ b/cmd/soroban-cli/src/commands/keys/generate.rs @@ -69,10 +69,10 @@ impl Cmd { if self.config_locator.read_identity(&self.name).is_ok() { if !self.overwrite { - return Err(Error::IdentityAlreadyExists(self.name.clone())); + return Err(Error::IdentityAlreadyExists(self.name.to_string())); } - print.exclaimln(format!("Overwriting identity '{}'", &self.name)); + print.exclaimln(format!("Overwriting identity '{}'", &self.name.to_string())); } if !self.fund { diff --git a/cmd/soroban-cli/src/config/address.rs b/cmd/soroban-cli/src/config/address.rs index 066bc8d91..57c229c76 100644 --- a/cmd/soroban-cli/src/config/address.rs +++ b/cmd/soroban-cli/src/config/address.rs @@ -25,6 +25,8 @@ pub enum Error { Secret(#[from] secret::Error), #[error("Address cannot be used to sign {0}")] CannotSign(xdr::MuxedAccount), + #[error("Invalid key name: {0}\n only alphanumeric characters, `_`and `-` are allowed")] + InvalidKeyName(String), } impl FromStr for Address { @@ -61,3 +63,30 @@ impl Address { } } } + +#[derive(Clone, Debug)] +pub struct KeyName(pub String); + +impl std::ops::Deref for KeyName { + type Target = str; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::str::FromStr for KeyName { + type Err = Error; + fn from_str(s: &str) -> Result { + if !s.chars().all(allowed_char) { + return Err(Error::InvalidKeyName(s.to_string())); + } + if s == "ledger" { + return Err(Error::InvalidKeyName(s.to_string())); + } + Ok(KeyName(s.to_string())) + } +} + +fn allowed_char(c: char) -> bool { + c.is_ascii_alphanumeric() || c == '_' || c == '-' +} From d4e25003db21f05fef829d600e98ff25515342bc Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:25:03 -0500 Subject: [PATCH 18/74] Add Secret::Keychain --- cmd/soroban-cli/src/config/secret.rs | 97 +++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 2 deletions(-) diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index a7fd86fda..d07e644ac 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -34,11 +34,15 @@ pub enum Error { pub struct Args { /// Add using `secret_key` /// Can provide with `SOROBAN_SECRET_KEY` - #[arg(long, conflicts_with = "seed_phrase")] + #[arg(long, conflicts_with_all = ["seed_phrase", "keychain"])] pub secret_key: bool, /// Add using 12 word seed phrase to generate `secret_key` - #[arg(long, conflicts_with = "secret_key")] + #[arg(long, conflicts_with_all = ["secret_key", "keychain"])] pub seed_phrase: bool, + + /// Add using `keychain` + #[arg(long, conflicts_with_all = ["seed_phrase", "secret_key"])] + pub keychain: bool, } impl Args { @@ -67,6 +71,15 @@ impl Args { .collect::>() .join(" "), }) + } else if self.keychain { + // generate a secret, and save it in the keychain + // return a new type of secret? + // for now, put it all in here + println!("generate a secret in the keychain"); + // let keychain = keyring::Keyring::new(" + Ok(Secret::SecretKey { + secret_key: "test".to_owned(), + }) } else { Err(Error::PasswordRead {}) } @@ -78,6 +91,7 @@ impl Args { pub enum Secret { SecretKey { secret_key: String }, SeedPhrase { seed_phrase: String }, + Keychain, } impl FromStr for Secret { @@ -92,6 +106,8 @@ impl FromStr for Secret { Ok(Secret::SeedPhrase { seed_phrase: s.to_string(), }) + } else if s == "keychain" { + Ok(Secret::Keychain) } else { Err(Error::InvalidAddress(s.to_string())) } @@ -116,6 +132,7 @@ impl Secret { .private() .0, )?, + Secret::Keychain => panic!("Keychain does not reveal secret key"), }) } @@ -132,6 +149,7 @@ impl Secret { let key = self.key_pair(index)?; SignerKind::Local(LocalKey { key }) } + Secret::Keychain => todo!(), }; Ok(Signer { kind, print }) } @@ -160,3 +178,78 @@ fn read_password() -> Result { std::io::stdout().flush().map_err(|_| Error::PasswordRead)?; rpassword::read_password().map_err(|_| Error::PasswordRead) } + +#[cfg(test)] +mod tests { + use super::*; + + const TEST_SECRET_KEY: &str = "SBF5HLRREHMS36XZNTUSKZ6FTXDZGNXOHF4EXKUL5UCWZLPBX3NGJ4BH"; + const TEST_SEED_PHRASE: &str = + "depth decade power loud smile spatial sign movie judge february rate broccoli"; + + #[test] + fn test_secret_from_key() { + let secret = Secret::from_str(TEST_SECRET_KEY).unwrap(); + // assert that it is a Secret::SecretKey + match secret { + Secret::SecretKey { secret_key: _ } => assert!(true), + _ => assert!(false), + } + // assert that we can get the private key from it + let private_key = secret.private_key(None).unwrap(); + assert_eq!(private_key.to_string(), TEST_SECRET_KEY); + + let signer = secret.signer(None, Print::new(false)).unwrap(); + println!("signer: {:?}", signer.kind); + } + + #[test] + fn test_secret_from_seed_phrase() { + let secret = Secret::from_str(TEST_SEED_PHRASE).unwrap(); + match secret { + Secret::SeedPhrase { seed_phrase: _ } => assert!(true), + _ => assert!(false), + } + + let private_key = secret.private_key(None).unwrap(); + assert_eq!(private_key.to_string(), TEST_SECRET_KEY); + } + + #[test] + fn test_ledger_secret() { + let secret = Secret::from_str("ledger").unwrap(); + match secret { + Secret::Ledger => assert!(true), + _ => assert!(false), + } + } + + #[test] + #[should_panic] + fn test_ledger_secret_will_not_reveal_private_key() { + let secret = Secret::from_str("ledger").unwrap(); + secret.private_key(None).unwrap(); + } + + #[test] + fn test_keychain_secret() { + let keychain_secret = Secret::from_str("keychain").unwrap(); + match keychain_secret { + Secret::Keychain => assert!(true), + _ => assert!(false), + } + } + + #[test] + #[should_panic] + fn test_keychain_secret_will_not_reveal_private_key() { + let secret = Secret::from_str("keychain").unwrap(); + secret.private_key(None).unwrap(); + } + + #[test] + fn test_secret_from_invalid_string() { + let secret = Secret::from_str("invalid"); + assert!(secret.is_err()); + } +} From 3ee20b8d634e6749cc27cc30ca59ca950cddae80 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Mon, 18 Nov 2024 14:17:36 -0500 Subject: [PATCH 19/74] keys generate: allow for generating keys that are stored in keychain --- cmd/soroban-cli/src/commands/keys/generate.rs | 108 +++++++++++++++--- cmd/soroban-cli/src/config/secret.rs | 55 +++------ 2 files changed, 111 insertions(+), 52 deletions(-) diff --git a/cmd/soroban-cli/src/commands/keys/generate.rs b/cmd/soroban-cli/src/commands/keys/generate.rs index 315fbb7ec..e04c22a1e 100644 --- a/cmd/soroban-cli/src/commands/keys/generate.rs +++ b/cmd/soroban-cli/src/commands/keys/generate.rs @@ -39,6 +39,10 @@ pub struct Cmd { #[arg(long, short = 's')] pub as_secret: bool, + /// Save in `keychain` + #[arg(long)] + pub keychain: bool, + #[command(flatten)] pub config_locator: locator::Args, @@ -63,6 +67,8 @@ pub struct Cmd { pub overwrite: bool, } +const KEYCHAIN_CONSTANT: &str = "keychain"; + impl Cmd { pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { let print = Print::new(global_args.quiet); @@ -83,19 +89,7 @@ impl Cmd { warning. It can be suppressed with -q flag.", ); } - - let seed_phrase = if self.default_seed { - Secret::test_seed_phrase() - } else { - Secret::from_seed(self.seed.as_deref()) - }?; - - let secret = if self.as_secret { - seed_phrase.private_key(self.hd_path)?.into() - } else { - seed_phrase - }; - + let secret = self.secret()?; self.config_locator.write_identity(&self.name, &secret)?; if !self.no_fund { @@ -112,4 +106,92 @@ impl Cmd { Ok(()) } + + fn secret(&self) -> Result { + let seed_phrase = self.seed_phrase()?; + Ok(if self.as_secret { + seed_phrase.private_key(self.hd_path)?.into() + } else if self.keychain { + KEYCHAIN_CONSTANT.parse()? + } else { + seed_phrase + }) + } + + fn seed_phrase(&self) -> Result { + Ok(if self.default_seed { + Secret::test_seed_phrase() + } else { + Secret::from_seed(self.seed.as_deref()) + }?) + } +} + +#[cfg(test)] +mod tests { + use crate::config::secret::Secret; + + fn set_up_test() -> (super::locator::Args, super::Cmd) { + let temp_dir = tempfile::tempdir().unwrap(); + let locator = super::locator::Args { + global: false, + config_dir: Some(temp_dir.path().to_path_buf()), + }; + + let cmd = super::Cmd { + name: "test_name".to_string(), + no_fund: true, + seed: None, + as_secret: false, + keychain: false, + config_locator: locator.clone(), + hd_path: None, + default_seed: false, + network: Default::default(), + fund: false, + }; + + (locator, cmd) + } + + fn global_args() -> super::global::Args { + let mut global_args = super::global::Args::default(); + global_args.quiet = true; + global_args + } + + #[tokio::test] + async fn test_storing_secret_as_a_seed_phrase() { + let (test_locator, cmd) = set_up_test(); + let global_args = global_args(); + + let result = cmd.run(&global_args).await; + assert!(result.is_ok()); + let identity = test_locator.read_identity("test_name").unwrap(); + assert!(matches!(identity, Secret::SeedPhrase { .. })); + } + + #[tokio::test] + async fn test_storing_secret_as_a_secret_key() { + let (test_locator, mut cmd) = set_up_test(); + cmd.as_secret = true; + let global_args = global_args(); + + let result = cmd.run(&global_args).await; + assert!(result.is_ok()); + let identity = test_locator.read_identity("test_name").unwrap(); + assert!(matches!(identity, Secret::SecretKey { .. })); + } + + #[tokio::test] + async fn test_storing_secret_in_keychain() { + let (test_locator, mut cmd) = set_up_test(); + cmd.keychain = true; + let global_args = global_args(); + + let result = cmd.run(&global_args).await; + assert!(result.is_ok()); + let identity = test_locator.read_identity("test_name").unwrap(); + assert!(matches!(identity, Secret::Keychain { .. })); + } } diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index d07e644ac..c88eaa88c 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -9,6 +9,8 @@ use crate::{ utils, }; +const KEYCHAIN_ENTRY_NAME: &str = "org.stellar.cli"; + #[derive(thiserror::Error, Debug)] pub enum Error { #[error("invalid secret key")] @@ -72,13 +74,8 @@ impl Args { .join(" "), }) } else if self.keychain { - // generate a secret, and save it in the keychain - // return a new type of secret? - // for now, put it all in here - println!("generate a secret in the keychain"); - // let keychain = keyring::Keyring::new(" - Ok(Secret::SecretKey { - secret_key: "test".to_owned(), + Ok(Secret::Keychain { + entry_name: KEYCHAIN_ENTRY_NAME.to_owned(), }) } else { Err(Error::PasswordRead {}) @@ -91,7 +88,7 @@ impl Args { pub enum Secret { SecretKey { secret_key: String }, SeedPhrase { seed_phrase: String }, - Keychain, + Keychain { entry_name: String }, } impl FromStr for Secret { @@ -107,7 +104,9 @@ impl FromStr for Secret { seed_phrase: s.to_string(), }) } else if s == "keychain" { - Ok(Secret::Keychain) + Ok(Secret::Keychain { + entry_name: KEYCHAIN_ENTRY_NAME.to_owned(), //TODO: namespace the entry_name to the system user or key name? + }) } else { Err(Error::InvalidAddress(s.to_string())) } @@ -132,7 +131,7 @@ impl Secret { .private() .0, )?, - Secret::Keychain => panic!("Keychain does not reveal secret key"), + Secret::Keychain { entry_name: _ } => panic!("Keychain does not reveal secret key"), }) } @@ -149,7 +148,7 @@ impl Secret { let key = self.key_pair(index)?; SignerKind::Local(LocalKey { key }) } - Secret::Keychain => todo!(), + Secret::Keychain { .. } => todo!(), }; Ok(Signer { kind, print }) } @@ -191,51 +190,29 @@ mod tests { fn test_secret_from_key() { let secret = Secret::from_str(TEST_SECRET_KEY).unwrap(); // assert that it is a Secret::SecretKey - match secret { - Secret::SecretKey { secret_key: _ } => assert!(true), - _ => assert!(false), - } + assert!(matches!(secret, Secret::SecretKey { .. })); // assert that we can get the private key from it let private_key = secret.private_key(None).unwrap(); assert_eq!(private_key.to_string(), TEST_SECRET_KEY); - - let signer = secret.signer(None, Print::new(false)).unwrap(); - println!("signer: {:?}", signer.kind); } #[test] fn test_secret_from_seed_phrase() { let secret = Secret::from_str(TEST_SEED_PHRASE).unwrap(); - match secret { - Secret::SeedPhrase { seed_phrase: _ } => assert!(true), - _ => assert!(false), - } + assert!(matches!(secret, Secret::SeedPhrase { .. })); let private_key = secret.private_key(None).unwrap(); assert_eq!(private_key.to_string(), TEST_SECRET_KEY); } - #[test] - fn test_ledger_secret() { - let secret = Secret::from_str("ledger").unwrap(); - match secret { - Secret::Ledger => assert!(true), - _ => assert!(false), - } - } - - #[test] - #[should_panic] - fn test_ledger_secret_will_not_reveal_private_key() { - let secret = Secret::from_str("ledger").unwrap(); - secret.private_key(None).unwrap(); - } - #[test] fn test_keychain_secret() { let keychain_secret = Secret::from_str("keychain").unwrap(); + match keychain_secret { - Secret::Keychain => assert!(true), + Secret::Keychain { entry_name } => { + assert_eq!(entry_name, KEYCHAIN_ENTRY_NAME); + } _ => assert!(false), } } From 1a24c000314ece3306a449060f4dc99e8fb1e15a Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Mon, 18 Nov 2024 17:16:20 -0500 Subject: [PATCH 20/74] keys generate: Namespace keychain entry to identity name --- cmd/soroban-cli/src/commands/keys/generate.rs | 40 +++++++++++++++++-- cmd/soroban-cli/src/config/secret.rs | 23 ++++++----- cmd/soroban-cli/src/signer/keyring.rs | 8 ++-- 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/cmd/soroban-cli/src/commands/keys/generate.rs b/cmd/soroban-cli/src/commands/keys/generate.rs index e04c22a1e..b7ebdba3d 100644 --- a/cmd/soroban-cli/src/commands/keys/generate.rs +++ b/cmd/soroban-cli/src/commands/keys/generate.rs @@ -4,7 +4,11 @@ use super::super::config::{ locator, network, secret::{self, Secret}, }; -use crate::{commands::global, print::Print}; +use crate::{ + commands::global, + print::Print, + signer::keyring::{self, StellarEntry}, +}; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -19,6 +23,9 @@ pub enum Error { #[error("An identity with the name '{0}' already exists")] IdentityAlreadyExists(String), + + #[error(transparent)] + Keyring(#[from] keyring::Error), } #[derive(Debug, clap::Parser, Clone)] @@ -67,8 +74,6 @@ pub struct Cmd { pub overwrite: bool, } -const KEYCHAIN_CONSTANT: &str = "keychain"; - impl Cmd { pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { let print = Print::new(global_args.quiet); @@ -112,7 +117,21 @@ impl Cmd { Ok(if self.as_secret { seed_phrase.private_key(self.hd_path)?.into() } else if self.keychain { - KEYCHAIN_CONSTANT.parse()? + // keychain:org.stellar.cli: + let entry_name_with_prefix = format!( + "{}{}-{}", + keyring::KEYCHAIN_ENTRY_PREFIX, + keyring::KEYCHAIN_ENTRY_SERVICE, + self.name + ); + + let secret: Secret = entry_name_with_prefix.parse()?; //checking that the entry name is valid before writing to the keychain + + if let Secret::Keychain { entry_name } = &secret { + self.write_to_keychain(entry_name.clone(), seed_phrase)?; + } + + secret } else { seed_phrase }) @@ -125,6 +144,19 @@ impl Cmd { Secret::from_seed(self.seed.as_deref()) }?) } + + fn write_to_keychain(&self, entry_name: String, seed_phrase: Secret) -> Result<(), Error> { + println!("Writing to keychain: {entry_name}"); + let entry = StellarEntry::new(&entry_name)?; + if let Ok(key) = entry.get_public_key() { + println!("A key for {entry_name} already exists in your keychain: {key}"); + } else { + println!("Saving a new key to your keychain: {entry_name}"); + let key_pair = seed_phrase.key_pair(None)?; + entry.set_password(key_pair.as_bytes())?; + } + Ok(()) + } } #[cfg(test)] diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index c88eaa88c..e6d0ce8ec 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -5,12 +5,10 @@ use stellar_strkey::ed25519::{PrivateKey, PublicKey}; use crate::{ print::Print, - signer::{self, LocalKey, Signer, SignerKind}, + signer::{self, keyring, LocalKey, Signer, SignerKind}, utils, }; -const KEYCHAIN_ENTRY_NAME: &str = "org.stellar.cli"; - #[derive(thiserror::Error, Debug)] pub enum Error { #[error("invalid secret key")] @@ -74,9 +72,10 @@ impl Args { .join(" "), }) } else if self.keychain { - Ok(Secret::Keychain { - entry_name: KEYCHAIN_ENTRY_NAME.to_owned(), - }) + todo!(); + // Ok(Secret::Keychain { + // entry_name: KEYCHAIN_ENTRY_NAME.to_owned(), + // }) } else { Err(Error::PasswordRead {}) } @@ -103,9 +102,13 @@ impl FromStr for Secret { Ok(Secret::SeedPhrase { seed_phrase: s.to_string(), }) - } else if s == "keychain" { + } else if s.starts_with(keyring::KEYCHAIN_ENTRY_PREFIX) { + let entry_name = s + .strip_prefix(keyring::KEYCHAIN_ENTRY_PREFIX) + .ok_or(Error::InvalidAddress(s.to_string()))?; + Ok(Secret::Keychain { - entry_name: KEYCHAIN_ENTRY_NAME.to_owned(), //TODO: namespace the entry_name to the system user or key name? + entry_name: entry_name.to_owned(), }) } else { Err(Error::InvalidAddress(s.to_string())) @@ -207,11 +210,11 @@ mod tests { #[test] fn test_keychain_secret() { - let keychain_secret = Secret::from_str("keychain").unwrap(); + let keychain_secret = Secret::from_str("keychain:org.stellar.cli-alice").unwrap(); match keychain_secret { Secret::Keychain { entry_name } => { - assert_eq!(entry_name, KEYCHAIN_ENTRY_NAME); + assert_eq!(entry_name, "org.stellar.cli-alice"); } _ => assert!(false), } diff --git a/cmd/soroban-cli/src/signer/keyring.rs b/cmd/soroban-cli/src/signer/keyring.rs index bde7e97d6..35d72e9b4 100644 --- a/cmd/soroban-cli/src/signer/keyring.rs +++ b/cmd/soroban-cli/src/signer/keyring.rs @@ -3,6 +3,9 @@ use ed25519_dalek::Signer; use keyring::Entry; use zeroize::Zeroize; +pub(crate) const KEYCHAIN_ENTRY_PREFIX: &str = "keychain:"; //TODO: does this belong here, or in secret? +pub(crate) const KEYCHAIN_ENTRY_SERVICE: &str = "org.stellar.cli"; + #[derive(thiserror::Error, Debug)] pub enum Error { #[error(transparent)] @@ -18,10 +21,7 @@ pub struct StellarEntry { impl TryFrom<&StellarEntry> for Entry { type Error = Error; fn try_from(StellarEntry { name }: &StellarEntry) -> Result { - Ok(Entry::new( - &format!("org.stellar.cli.{name}"), - &whoami::username(), - )?) + Ok(Entry::new(name, &whoami::username())?) } } From 4a0c900aa600792c6dc9fad9ec6006fa039d749f Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Wed, 20 Nov 2024 17:02:01 -0500 Subject: [PATCH 21/74] keys generate: don't allow 'keychain:' as a key name --- cmd/soroban-cli/src/commands/keys/generate.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/soroban-cli/src/commands/keys/generate.rs b/cmd/soroban-cli/src/commands/keys/generate.rs index b7ebdba3d..ce8646123 100644 --- a/cmd/soroban-cli/src/commands/keys/generate.rs +++ b/cmd/soroban-cli/src/commands/keys/generate.rs @@ -6,6 +6,7 @@ use super::super::config::{ }; use crate::{ commands::global, + config::address::KeyName, print::Print, signer::keyring::{self, StellarEntry}, }; @@ -33,7 +34,7 @@ pub enum Error { #[allow(clippy::struct_excessive_bools)] pub struct Cmd { /// Name of identity - pub name: String, + pub name: KeyName, /// Do not fund address #[arg(long)] pub no_fund: bool, @@ -122,7 +123,7 @@ impl Cmd { "{}{}-{}", keyring::KEYCHAIN_ENTRY_PREFIX, keyring::KEYCHAIN_ENTRY_SERVICE, - self.name + self.name.to_string() ); let secret: Secret = entry_name_with_prefix.parse()?; //checking that the entry name is valid before writing to the keychain From 755c3186997a588a89874cda1e11ca683611fe0c Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Wed, 20 Nov 2024 16:41:02 -0500 Subject: [PATCH 22/74] keys address: use keychain entry in secret to get the pub key --- cmd/soroban-cli/src/config/secret.rs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index e6d0ce8ec..6364bc2c1 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -27,6 +27,8 @@ pub enum Error { InvalidAddress(String), #[error(transparent)] Signer(#[from] signer::Error), + #[error(transparent)] + Keyring(#[from] keyring::Error), } #[derive(Debug, clap::Args, Clone)] @@ -103,12 +105,8 @@ impl FromStr for Secret { seed_phrase: s.to_string(), }) } else if s.starts_with(keyring::KEYCHAIN_ENTRY_PREFIX) { - let entry_name = s - .strip_prefix(keyring::KEYCHAIN_ENTRY_PREFIX) - .ok_or(Error::InvalidAddress(s.to_string()))?; - Ok(Secret::Keychain { - entry_name: entry_name.to_owned(), + entry_name: s.to_owned(), }) } else { Err(Error::InvalidAddress(s.to_string())) @@ -139,10 +137,18 @@ impl Secret { } pub fn public_key(&self, index: Option) -> Result { - let key = self.key_pair(index)?; - Ok(stellar_strkey::ed25519::PublicKey::from_payload( - key.verifying_key().as_bytes(), - )?) + match self { + Secret::Keychain { entry_name } => { + let entry = keyring::StellarEntry::new(entry_name)?; + Ok(entry.get_public_key()?) + } + _ => { + let key = self.key_pair(index)?; + Ok(stellar_strkey::ed25519::PublicKey::from_payload( + key.verifying_key().as_bytes(), + )?) + } + } } pub fn signer(&self, index: Option, print: Print) -> Result { From dd07bf70d6d8c362a2b2e3922265e9252b04d023 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:12:50 -0500 Subject: [PATCH 23/74] tx sign: allow a keychain identity sign a tx --- cmd/soroban-cli/src/config/secret.rs | 8 +++++--- cmd/soroban-cli/src/signer.rs | 21 ++++++++++++++++++++- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index 6364bc2c1..d1e15bedc 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -5,7 +5,7 @@ use stellar_strkey::ed25519::{PrivateKey, PublicKey}; use crate::{ print::Print, - signer::{self, keyring, LocalKey, Signer, SignerKind}, + signer::{self, keyring, KeychainEntry, LocalKey, Signer, SignerKind}, utils, }; @@ -106,7 +106,7 @@ impl FromStr for Secret { }) } else if s.starts_with(keyring::KEYCHAIN_ENTRY_PREFIX) { Ok(Secret::Keychain { - entry_name: s.to_owned(), + entry_name: s.to_string(), }) } else { Err(Error::InvalidAddress(s.to_string())) @@ -157,7 +157,9 @@ impl Secret { let key = self.key_pair(index)?; SignerKind::Local(LocalKey { key }) } - Secret::Keychain { .. } => todo!(), + Secret::Keychain { entry_name } => SignerKind::Keychain(KeychainEntry { + name: entry_name.to_string(), + }), }; Ok(Signer { kind, print }) } diff --git a/cmd/soroban-cli/src/signer.rs b/cmd/soroban-cli/src/signer.rs index a141aaf6b..e0a1568c2 100644 --- a/cmd/soroban-cli/src/signer.rs +++ b/cmd/soroban-cli/src/signer.rs @@ -1,4 +1,5 @@ -use ed25519_dalek::ed25519::signature::Signer as _; +use ed25519_dalek::ed25519::signature::{self, Signer as _}; +use keyring::StellarEntry; use sha2::{Digest, Sha256}; use crate::xdr::{ @@ -35,6 +36,8 @@ pub enum Error { Open(#[from] std::io::Error), #[error("Returning a signature from Lab is not yet supported; Transaction can be found and submitted in lab")] ReturningSignatureFromLab, + #[error(transparent)] + Keyring(#[from] keyring::Error), } fn requires_auth(txn: &Transaction) -> Option { @@ -209,6 +212,7 @@ pub struct Signer { pub enum SignerKind { Local(LocalKey), Lab, + Keychain(KeychainEntry), } impl Signer { @@ -237,6 +241,7 @@ impl Signer { 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)?, + SignerKind::Keychain(entry) => entry.sign_tx_env(tx_env)?, }; let mut sigs = signatures.clone().into_vec(); sigs.push(decorated_signature); @@ -286,3 +291,17 @@ impl Lab { Err(Error::ReturningSignatureFromLab) } } + +pub struct KeychainEntry { + pub name: String, +} + +impl KeychainEntry { + pub fn sign_tx_env(&self, tx_env: &TransactionEnvelope) -> Result { + let entry = StellarEntry::new(&self.name)?; + let signed_tx_env = entry.sign_data(tx_env.to_xdr_base64(Limits::none())?.as_bytes())?; + let hint = SignatureHint(entry.get_public_key()?.0[28..].try_into()?); + let signature = Signature(signed_tx_env.to_vec().try_into()?); + Ok(DecoratedSignature { hint, signature }) + } +} From f042d19788fd6b725d88f762ea5be0fb4577f417 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Thu, 21 Nov 2024 12:28:50 -0500 Subject: [PATCH 24/74] Cleanup --- cmd/soroban-cli/src/commands/keys/generate.rs | 5 +++-- cmd/soroban-cli/src/config/secret.rs | 9 ++------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/cmd/soroban-cli/src/commands/keys/generate.rs b/cmd/soroban-cli/src/commands/keys/generate.rs index ce8646123..8ecaa0f4e 100644 --- a/cmd/soroban-cli/src/commands/keys/generate.rs +++ b/cmd/soroban-cli/src/commands/keys/generate.rs @@ -162,7 +162,7 @@ impl Cmd { #[cfg(test)] mod tests { - use crate::config::secret::Secret; + use crate::config::{self, address::KeyName, secret::Secret}; fn set_up_test() -> (super::locator::Args, super::Cmd) { let temp_dir = tempfile::tempdir().unwrap(); @@ -172,7 +172,7 @@ mod tests { }; let cmd = super::Cmd { - name: "test_name".to_string(), + name: KeyName("test_name".to_string()), no_fund: true, seed: None, as_secret: false, @@ -182,6 +182,7 @@ mod tests { default_seed: false, network: Default::default(), fund: false, + overwrite: false, }; (locator, cmd) diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index d1e15bedc..ebc2b8d3f 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -73,11 +73,6 @@ impl Args { .collect::>() .join(" "), }) - } else if self.keychain { - todo!(); - // Ok(Secret::Keychain { - // entry_name: KEYCHAIN_ENTRY_NAME.to_owned(), - // }) } else { Err(Error::PasswordRead {}) } @@ -132,7 +127,7 @@ impl Secret { .private() .0, )?, - Secret::Keychain { entry_name: _ } => panic!("Keychain does not reveal secret key"), + Secret::Keychain { .. } => panic!("Keychain does not reveal secret key"), }) } @@ -222,7 +217,7 @@ mod tests { match keychain_secret { Secret::Keychain { entry_name } => { - assert_eq!(entry_name, "org.stellar.cli-alice"); + assert_eq!(entry_name, "keychain:org.stellar.cli-alice"); } _ => assert!(false), } From 9cf591606ac7ddde6676634d8b149717739f85ca Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Fri, 22 Nov 2024 12:22:21 -0500 Subject: [PATCH 25/74] Use keyring mock for generate tests --- cmd/soroban-cli/src/commands/keys/generate.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/soroban-cli/src/commands/keys/generate.rs b/cmd/soroban-cli/src/commands/keys/generate.rs index 8ecaa0f4e..3bf2e48be 100644 --- a/cmd/soroban-cli/src/commands/keys/generate.rs +++ b/cmd/soroban-cli/src/commands/keys/generate.rs @@ -163,6 +163,7 @@ impl Cmd { #[cfg(test)] mod tests { use crate::config::{self, address::KeyName, secret::Secret}; + use keyring::{mock, set_default_credential_builder}; fn set_up_test() -> (super::locator::Args, super::Cmd) { let temp_dir = tempfile::tempdir().unwrap(); @@ -185,6 +186,8 @@ mod tests { overwrite: false, }; + set_default_credential_builder(mock::default_credential_builder()); + (locator, cmd) } From cd515d3d13ed0112ee1f41604b5fad020b5d5ae3 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Mon, 25 Nov 2024 10:24:12 -0500 Subject: [PATCH 26/74] Refactor keyring: add keyring entry as StellarEntry field - previously we were creating a new keyring entry for each interaction with the keyring - this change will allow us use a mock keyring entry for testing --- cmd/soroban-cli/src/signer/keyring.rs | 47 ++++++++++++++------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/cmd/soroban-cli/src/signer/keyring.rs b/cmd/soroban-cli/src/signer/keyring.rs index 35d72e9b4..5165dec43 100644 --- a/cmd/soroban-cli/src/signer/keyring.rs +++ b/cmd/soroban-cli/src/signer/keyring.rs @@ -15,33 +15,24 @@ pub enum Error { } pub struct StellarEntry { - name: String, -} - -impl TryFrom<&StellarEntry> for Entry { - type Error = Error; - fn try_from(StellarEntry { name }: &StellarEntry) -> Result { - Ok(Entry::new(name, &whoami::username())?) - } + keyring: Entry, } impl StellarEntry { pub fn new(name: &str) -> Result { Ok(StellarEntry { - name: name.to_string(), + keyring: Entry::new(name, &whoami::username())?, }) } pub fn set_password(&self, password: &[u8]) -> Result<(), Error> { let data = base64.encode(password); - let entry: Entry = self.try_into()?; - entry.set_password(&data)?; + self.keyring.set_password(&data)?; Ok(()) } pub fn get_password(&self) -> Result, Error> { - let entry: Entry = self.try_into()?; - Ok(base64.decode(entry.get_password()?)?) + Ok(base64.decode(self.keyring.get_password()?)?) } fn use_key( @@ -80,16 +71,28 @@ impl StellarEntry { #[cfg(test)] mod test { use super::*; + use keyring::{ + mock, + set_default_credential_builder, + }; #[test] - fn test_sign_data() -> Result<(), Box> { - let secret = crate::config::secret::Secret::from_seed(None)?; - let pub_key = secret.public_key(None)?; - let key_pair = secret.key_pair(None)?; - let entry = StellarEntry::new("test")?; - entry.set_password(&key_pair.to_bytes()); - let pub_key_2 = entry.get_public_key()?; - assert_eq!(pub_key, pub_key_2); - Ok(()) + fn test_sign_data() { + set_default_credential_builder(mock::default_credential_builder()); + + let secret = crate::config::secret::Secret::from_seed(None).unwrap(); + let pub_key = secret.public_key(None).unwrap(); + let key_pair = secret.key_pair(None).unwrap(); + + let entry = StellarEntry::new("test").unwrap(); + + // set the password + let set_password_result = entry.set_password(&key_pair.to_bytes()); + assert!(set_password_result.is_ok()); + + // confirm that we can get the public key from the entry and that it matches the one we set + let get_public_key_result = entry.get_public_key(); + assert!(get_public_key_result.is_ok()); + assert_eq!(pub_key, get_public_key_result.unwrap()); } } From 276558db228486205b29ded009f193b6d56c513e Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Mon, 25 Nov 2024 12:20:22 -0500 Subject: [PATCH 27/74] Add tests for keyring --- cmd/soroban-cli/src/signer/keyring.rs | 48 +++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/cmd/soroban-cli/src/signer/keyring.rs b/cmd/soroban-cli/src/signer/keyring.rs index 5165dec43..6c32f620d 100644 --- a/cmd/soroban-cli/src/signer/keyring.rs +++ b/cmd/soroban-cli/src/signer/keyring.rs @@ -71,17 +71,33 @@ impl StellarEntry { #[cfg(test)] mod test { use super::*; - use keyring::{ - mock, - set_default_credential_builder, - }; + use keyring::{mock, set_default_credential_builder}; #[test] - fn test_sign_data() { + fn test_get_password() { set_default_credential_builder(mock::default_credential_builder()); let secret = crate::config::secret::Secret::from_seed(None).unwrap(); - let pub_key = secret.public_key(None).unwrap(); + let key_pair = secret.key_pair(None).unwrap(); + + let entry = StellarEntry::new("test").unwrap(); + + // set the password + let set_password_result = entry.set_password(&key_pair.to_bytes()); + assert!(set_password_result.is_ok()); + + // get_password should return the same password we set + let get_password_result = entry.get_password(); + assert!(get_password_result.is_ok()); + assert_eq!(key_pair.to_bytes().to_vec(), get_password_result.unwrap()); + } + + #[test] + fn test_get_public_key() { + set_default_credential_builder(mock::default_credential_builder()); + + let secret = crate::config::secret::Secret::from_seed(None).unwrap(); + let public_key = secret.public_key(None).unwrap(); let key_pair = secret.key_pair(None).unwrap(); let entry = StellarEntry::new("test").unwrap(); @@ -93,6 +109,24 @@ mod test { // confirm that we can get the public key from the entry and that it matches the one we set let get_public_key_result = entry.get_public_key(); assert!(get_public_key_result.is_ok()); - assert_eq!(pub_key, get_public_key_result.unwrap()); + assert_eq!(public_key, get_public_key_result.unwrap()); + } + + #[test] + fn test_sign_data() { + set_default_credential_builder(mock::default_credential_builder()); + + //create a secret + let secret = crate::config::secret::Secret::from_seed(None).unwrap(); + let key_pair = secret.key_pair(None).unwrap(); + + // create a keyring entry and set the password + let entry = StellarEntry::new("test").unwrap(); + entry.set_password(&key_pair.to_bytes()).unwrap(); + + let tx_xdr = r#"AAAAAgAAAADh6eOnZEq1xQgKioffuH7/8D8x8+OdGFEkiYC6QKMWzQAAAGQAAACuAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAYAAAAAQAAAAAAAAAAAAAAAOHp46dkSrXFCAqKh9+4fv/wPzHz450YUSSJgLpAoxbNoFT1s8jZPCv9IJ2DsqGTA8pOtavv58JF53aDycpRPcEAAAAA+N2m5zc3EfWUmLvigYPOHKXhSy8OrWfVibc6y6PrQoYAAAAAAAAAAAAAAAA"#; + + let sign_tx_env_result = entry.sign_data(tx_xdr.as_bytes()); + assert!(sign_tx_env_result.is_ok()); } } From 20c651bc211a253472baaa719ac2ec6c1885ea76 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Mon, 25 Nov 2024 15:17:03 -0500 Subject: [PATCH 28/74] Update config/secret tests --- cmd/soroban-cli/src/config/secret.rs | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index ebc2b8d3f..e7fbea44c 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -188,39 +188,39 @@ fn read_password() -> Result { mod tests { use super::*; + const TEST_PUBLIC_KEY: &str = "GAREAZZQWHOCBJS236KIE3AWYBVFLSBK7E5UW3ICI3TCRWQKT5LNLCEZ"; const TEST_SECRET_KEY: &str = "SBF5HLRREHMS36XZNTUSKZ6FTXDZGNXOHF4EXKUL5UCWZLPBX3NGJ4BH"; const TEST_SEED_PHRASE: &str = "depth decade power loud smile spatial sign movie judge february rate broccoli"; #[test] - fn test_secret_from_key() { + fn test_from_str_for_secret_key() { let secret = Secret::from_str(TEST_SECRET_KEY).unwrap(); - // assert that it is a Secret::SecretKey - assert!(matches!(secret, Secret::SecretKey { .. })); - // assert that we can get the private key from it + let public_key = secret.public_key(None).unwrap(); let private_key = secret.private_key(None).unwrap(); + + assert!(matches!(secret, Secret::SecretKey { .. })); + assert_eq!(public_key.to_string(), TEST_PUBLIC_KEY); assert_eq!(private_key.to_string(), TEST_SECRET_KEY); } #[test] - fn test_secret_from_seed_phrase() { + fn test_from_str_for_seed_phrase() { let secret = Secret::from_str(TEST_SEED_PHRASE).unwrap(); - assert!(matches!(secret, Secret::SeedPhrase { .. })); - + let public_key = secret.public_key(None).unwrap(); let private_key = secret.private_key(None).unwrap(); + + assert!(matches!(secret, Secret::SeedPhrase { .. })); + assert_eq!(public_key.to_string(), TEST_PUBLIC_KEY); assert_eq!(private_key.to_string(), TEST_SECRET_KEY); } #[test] - fn test_keychain_secret() { - let keychain_secret = Secret::from_str("keychain:org.stellar.cli-alice").unwrap(); + fn test_from_str_for_keychain_secret() { + //todo: add assertion for getting public key - will need to mock the keychain and add the keypair to the keychain + let secret = Secret::from_str("keychain:org.stellar.cli-alice").unwrap(); - match keychain_secret { - Secret::Keychain { entry_name } => { - assert_eq!(entry_name, "keychain:org.stellar.cli-alice"); - } - _ => assert!(false), - } + assert!(matches!(secret, Secret::Keychain { .. })); } #[test] From 66d1bfc46b2ab8bef535c66028f7d002fae65be0 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Mon, 25 Nov 2024 16:49:06 -0500 Subject: [PATCH 29/74] Cleanup --- cmd/soroban-cli/src/commands/keys/generate.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/soroban-cli/src/commands/keys/generate.rs b/cmd/soroban-cli/src/commands/keys/generate.rs index 3bf2e48be..93ae67a3b 100644 --- a/cmd/soroban-cli/src/commands/keys/generate.rs +++ b/cmd/soroban-cli/src/commands/keys/generate.rs @@ -35,9 +35,11 @@ pub enum Error { pub struct Cmd { /// Name of identity pub name: KeyName, + /// Do not fund address #[arg(long)] pub no_fund: bool, + /// Optional seed to use when generating seed phrase. /// Random otherwise. #[arg(long, conflicts_with = "default_seed")] @@ -162,7 +164,7 @@ impl Cmd { #[cfg(test)] mod tests { - use crate::config::{self, address::KeyName, secret::Secret}; + use crate::config::{address::KeyName, secret::Secret}; use keyring::{mock, set_default_credential_builder}; fn set_up_test() -> (super::locator::Args, super::Cmd) { @@ -186,8 +188,6 @@ mod tests { overwrite: false, }; - set_default_credential_builder(mock::default_credential_builder()); - (locator, cmd) } @@ -222,6 +222,7 @@ mod tests { #[tokio::test] async fn test_storing_secret_in_keychain() { + set_default_credential_builder(mock::default_credential_builder()); let (test_locator, mut cmd) = set_up_test(); cmd.keychain = true; let global_args = global_args(); From 8b9763ee158eeb746eb4d7b42072478d98e2b1b6 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Mon, 25 Nov 2024 16:58:17 -0500 Subject: [PATCH 30/74] Rename keychain arg to secure_store in generate --- cmd/soroban-cli/src/commands/keys/generate.rs | 31 ++++++++++--------- cmd/soroban-cli/src/config/secret.rs | 2 +- cmd/soroban-cli/src/signer/keyring.rs | 4 +-- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/cmd/soroban-cli/src/commands/keys/generate.rs b/cmd/soroban-cli/src/commands/keys/generate.rs index 93ae67a3b..0314ddf7d 100644 --- a/cmd/soroban-cli/src/commands/keys/generate.rs +++ b/cmd/soroban-cli/src/commands/keys/generate.rs @@ -49,9 +49,9 @@ pub struct Cmd { #[arg(long, short = 's')] pub as_secret: bool, - /// Save in `keychain` + /// Save in OS-specific secure store #[arg(long)] - pub keychain: bool, + pub secure_store: bool, #[command(flatten)] pub config_locator: locator::Args, @@ -119,19 +119,20 @@ impl Cmd { let seed_phrase = self.seed_phrase()?; Ok(if self.as_secret { seed_phrase.private_key(self.hd_path)?.into() - } else if self.keychain { - // keychain:org.stellar.cli: + } else if self.secure_store { + // secure_store:org.stellar.cli: let entry_name_with_prefix = format!( "{}{}-{}", - keyring::KEYCHAIN_ENTRY_PREFIX, - keyring::KEYCHAIN_ENTRY_SERVICE, + keyring::SECURE_STORE_ENTRY_PREFIX, + keyring::SECURE_STORE_ENTRY_SERVICE, self.name.to_string() ); - let secret: Secret = entry_name_with_prefix.parse()?; //checking that the entry name is valid before writing to the keychain + //checking that the entry name is valid before writing to the secure store + let secret: Secret = entry_name_with_prefix.parse()?; if let Secret::Keychain { entry_name } = &secret { - self.write_to_keychain(entry_name.clone(), seed_phrase)?; + self.write_to_secure_store(entry_name.clone(), seed_phrase)?; } secret @@ -148,13 +149,13 @@ impl Cmd { }?) } - fn write_to_keychain(&self, entry_name: String, seed_phrase: Secret) -> Result<(), Error> { - println!("Writing to keychain: {entry_name}"); + fn write_to_secure_store(&self, entry_name: String, seed_phrase: Secret) -> Result<(), Error> { + println!("Writing to secure store: {entry_name}"); let entry = StellarEntry::new(&entry_name)?; if let Ok(key) = entry.get_public_key() { - println!("A key for {entry_name} already exists in your keychain: {key}"); + println!("A key for {entry_name} already exists in your operating system's secure store: {key}"); } else { - println!("Saving a new key to your keychain: {entry_name}"); + println!("Saving a new key to your operating system's secure store: {entry_name}"); let key_pair = seed_phrase.key_pair(None)?; entry.set_password(key_pair.as_bytes())?; } @@ -179,7 +180,7 @@ mod tests { no_fund: true, seed: None, as_secret: false, - keychain: false, + secure_store: false, config_locator: locator.clone(), hd_path: None, default_seed: false, @@ -221,10 +222,10 @@ mod tests { } #[tokio::test] - async fn test_storing_secret_in_keychain() { + async fn test_storing_secret_in_secure_store() { set_default_credential_builder(mock::default_credential_builder()); let (test_locator, mut cmd) = set_up_test(); - cmd.keychain = true; + cmd.secure_store = true; let global_args = global_args(); let result = cmd.run(&global_args).await; diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index e7fbea44c..5cdb30504 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -99,7 +99,7 @@ impl FromStr for Secret { Ok(Secret::SeedPhrase { seed_phrase: s.to_string(), }) - } else if s.starts_with(keyring::KEYCHAIN_ENTRY_PREFIX) { + } else if s.starts_with(keyring::SECURE_STORE_ENTRY_PREFIX) { Ok(Secret::Keychain { entry_name: s.to_string(), }) diff --git a/cmd/soroban-cli/src/signer/keyring.rs b/cmd/soroban-cli/src/signer/keyring.rs index 6c32f620d..f92280f7e 100644 --- a/cmd/soroban-cli/src/signer/keyring.rs +++ b/cmd/soroban-cli/src/signer/keyring.rs @@ -3,8 +3,8 @@ use ed25519_dalek::Signer; use keyring::Entry; use zeroize::Zeroize; -pub(crate) const KEYCHAIN_ENTRY_PREFIX: &str = "keychain:"; //TODO: does this belong here, or in secret? -pub(crate) const KEYCHAIN_ENTRY_SERVICE: &str = "org.stellar.cli"; +pub(crate) const SECURE_STORE_ENTRY_PREFIX: &str = "secure_store:"; +pub(crate) const SECURE_STORE_ENTRY_SERVICE: &str = "org.stellar.cli"; #[derive(thiserror::Error, Debug)] pub enum Error { From fdefa2aee53631c00761089fb4c7b9f162be79d7 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Mon, 25 Nov 2024 17:02:43 -0500 Subject: [PATCH 31/74] Rename Secret::Keychain to Secret::SecureStore --- cmd/soroban-cli/src/commands/keys/generate.rs | 4 ++-- cmd/soroban-cli/src/config/secret.rs | 20 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cmd/soroban-cli/src/commands/keys/generate.rs b/cmd/soroban-cli/src/commands/keys/generate.rs index 0314ddf7d..492d17bd4 100644 --- a/cmd/soroban-cli/src/commands/keys/generate.rs +++ b/cmd/soroban-cli/src/commands/keys/generate.rs @@ -131,7 +131,7 @@ impl Cmd { //checking that the entry name is valid before writing to the secure store let secret: Secret = entry_name_with_prefix.parse()?; - if let Secret::Keychain { entry_name } = &secret { + if let Secret::SecureStore { entry_name } = &secret { self.write_to_secure_store(entry_name.clone(), seed_phrase)?; } @@ -231,6 +231,6 @@ mod tests { let result = cmd.run(&global_args).await; assert!(result.is_ok()); let identity = test_locator.read_identity("test_name").unwrap(); - assert!(matches!(identity, Secret::Keychain { .. })); + assert!(matches!(identity, Secret::SecureStore { .. })); } } diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index 5cdb30504..a1adabeb8 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -84,7 +84,7 @@ impl Args { pub enum Secret { SecretKey { secret_key: String }, SeedPhrase { seed_phrase: String }, - Keychain { entry_name: String }, + SecureStore { entry_name: String }, } impl FromStr for Secret { @@ -100,7 +100,7 @@ impl FromStr for Secret { seed_phrase: s.to_string(), }) } else if s.starts_with(keyring::SECURE_STORE_ENTRY_PREFIX) { - Ok(Secret::Keychain { + Ok(Secret::SecureStore { entry_name: s.to_string(), }) } else { @@ -127,13 +127,13 @@ impl Secret { .private() .0, )?, - Secret::Keychain { .. } => panic!("Keychain does not reveal secret key"), + Secret::SecureStore { .. } => panic!("Secure Store does not reveal secret key"), }) } pub fn public_key(&self, index: Option) -> Result { match self { - Secret::Keychain { entry_name } => { + Secret::SecureStore { entry_name } => { let entry = keyring::StellarEntry::new(entry_name)?; Ok(entry.get_public_key()?) } @@ -152,7 +152,7 @@ impl Secret { let key = self.key_pair(index)?; SignerKind::Local(LocalKey { key }) } - Secret::Keychain { entry_name } => SignerKind::Keychain(KeychainEntry { + Secret::SecureStore { entry_name } => SignerKind::Keychain(KeychainEntry { name: entry_name.to_string(), }), }; @@ -216,17 +216,17 @@ mod tests { } #[test] - fn test_from_str_for_keychain_secret() { + fn test_from_str_for_secure_store_secret() { //todo: add assertion for getting public key - will need to mock the keychain and add the keypair to the keychain - let secret = Secret::from_str("keychain:org.stellar.cli-alice").unwrap(); + let secret = Secret::from_str("secure_store:org.stellar.cli-alice").unwrap(); - assert!(matches!(secret, Secret::Keychain { .. })); + assert!(matches!(secret, Secret::SecureStore { .. })); } #[test] #[should_panic] - fn test_keychain_secret_will_not_reveal_private_key() { - let secret = Secret::from_str("keychain").unwrap(); + fn test_secure_store_will_not_reveal_private_key() { + let secret = Secret::from_str("secure_store").unwrap(); secret.private_key(None).unwrap(); } From 36559eae08f54ac504d8ba90608fe71f83bd5f97 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Mon, 25 Nov 2024 17:22:09 -0500 Subject: [PATCH 32/74] Rename SignerKind::Keychain to SignerKind::SecureStore --- cmd/soroban-cli/src/commands/keys/mod.rs | 2 +- cmd/soroban-cli/src/config/secret.rs | 12 ++++-------- cmd/soroban-cli/src/signer.rs | 10 +++++----- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/cmd/soroban-cli/src/commands/keys/mod.rs b/cmd/soroban-cli/src/commands/keys/mod.rs index 8729ee9af..b3309fa02 100644 --- a/cmd/soroban-cli/src/commands/keys/mod.rs +++ b/cmd/soroban-cli/src/commands/keys/mod.rs @@ -12,7 +12,7 @@ pub mod show; #[derive(Debug, Parser)] pub enum Cmd { - /// Add a new identity (keypair, ledger, macOS keychain) + /// Add a new identity (keypair, ledger, OS specific secure store) Add(add::Cmd), /// Given an identity return its address (public key) diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index a1adabeb8..47ac9f401 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -5,7 +5,7 @@ use stellar_strkey::ed25519::{PrivateKey, PublicKey}; use crate::{ print::Print, - signer::{self, keyring, KeychainEntry, LocalKey, Signer, SignerKind}, + signer::{self, keyring, LocalKey, SecureStoreEntry, Signer, SignerKind}, utils, }; @@ -36,15 +36,11 @@ pub enum Error { pub struct Args { /// Add using `secret_key` /// Can provide with `SOROBAN_SECRET_KEY` - #[arg(long, conflicts_with_all = ["seed_phrase", "keychain"])] + #[arg(long, conflicts_with = "seed_phrase")] pub secret_key: bool, /// Add using 12 word seed phrase to generate `secret_key` - #[arg(long, conflicts_with_all = ["secret_key", "keychain"])] + #[arg(long, conflicts_with = "secret_key")] pub seed_phrase: bool, - - /// Add using `keychain` - #[arg(long, conflicts_with_all = ["seed_phrase", "secret_key"])] - pub keychain: bool, } impl Args { @@ -152,7 +148,7 @@ impl Secret { let key = self.key_pair(index)?; SignerKind::Local(LocalKey { key }) } - Secret::SecureStore { entry_name } => SignerKind::Keychain(KeychainEntry { + Secret::SecureStore { entry_name } => SignerKind::SecureStore(SecureStoreEntry { name: entry_name.to_string(), }), }; diff --git a/cmd/soroban-cli/src/signer.rs b/cmd/soroban-cli/src/signer.rs index e0a1568c2..86db2dcea 100644 --- a/cmd/soroban-cli/src/signer.rs +++ b/cmd/soroban-cli/src/signer.rs @@ -1,4 +1,4 @@ -use ed25519_dalek::ed25519::signature::{self, Signer as _}; +use ed25519_dalek::ed25519::signature::Signer as _; use keyring::StellarEntry; use sha2::{Digest, Sha256}; @@ -212,7 +212,7 @@ pub struct Signer { pub enum SignerKind { Local(LocalKey), Lab, - Keychain(KeychainEntry), + SecureStore(SecureStoreEntry), } impl Signer { @@ -241,7 +241,7 @@ impl Signer { 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)?, - SignerKind::Keychain(entry) => entry.sign_tx_env(tx_env)?, + SignerKind::SecureStore(entry) => entry.sign_tx_env(tx_env)?, }; let mut sigs = signatures.clone().into_vec(); sigs.push(decorated_signature); @@ -292,11 +292,11 @@ impl Lab { } } -pub struct KeychainEntry { +pub struct SecureStoreEntry { pub name: String, } -impl KeychainEntry { +impl SecureStoreEntry { pub fn sign_tx_env(&self, tx_env: &TransactionEnvelope) -> Result { let entry = StellarEntry::new(&self.name)?; let signed_tx_env = entry.sign_data(tx_env.to_xdr_base64(Limits::none())?.as_bytes())?; From f98b709c3a09d22bdfeddba0a1d77fb1b5d64110 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:00:44 -0500 Subject: [PATCH 33/74] Use print for new fns in generate --- cmd/soroban-cli/src/commands/keys/generate.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/cmd/soroban-cli/src/commands/keys/generate.rs b/cmd/soroban-cli/src/commands/keys/generate.rs index 492d17bd4..0395a593c 100644 --- a/cmd/soroban-cli/src/commands/keys/generate.rs +++ b/cmd/soroban-cli/src/commands/keys/generate.rs @@ -97,7 +97,7 @@ impl Cmd { warning. It can be suppressed with -q flag.", ); } - let secret = self.secret()?; + let secret = self.secret(print)?; self.config_locator.write_identity(&self.name, &secret)?; if !self.no_fund { @@ -115,7 +115,7 @@ impl Cmd { Ok(()) } - fn secret(&self) -> Result { + fn secret(&self, print: Print) -> Result { let seed_phrase = self.seed_phrase()?; Ok(if self.as_secret { seed_phrase.private_key(self.hd_path)?.into() @@ -132,7 +132,7 @@ impl Cmd { let secret: Secret = entry_name_with_prefix.parse()?; if let Secret::SecureStore { entry_name } = &secret { - self.write_to_secure_store(entry_name.clone(), seed_phrase)?; + self.write_to_secure_store(entry_name.clone(), seed_phrase, print)?; } secret @@ -149,13 +149,20 @@ impl Cmd { }?) } - fn write_to_secure_store(&self, entry_name: String, seed_phrase: Secret) -> Result<(), Error> { + fn write_to_secure_store( + &self, + entry_name: String, + seed_phrase: Secret, + print: Print, + ) -> Result<(), Error> { println!("Writing to secure store: {entry_name}"); let entry = StellarEntry::new(&entry_name)?; if let Ok(key) = entry.get_public_key() { - println!("A key for {entry_name} already exists in your operating system's secure store: {key}"); + print.warnln(format!("A key for {entry_name} already exists in your operating system's secure store: {key}")); } else { - println!("Saving a new key to your operating system's secure store: {entry_name}"); + print.infoln(format!( + "Saving a new key to your operating system's secure store: {entry_name}" + )); let key_pair = seed_phrase.key_pair(None)?; entry.set_password(key_pair.as_bytes())?; } From 0f3106b2cc3d84d6fc6d28e1b29b66010e03f52e Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:08:09 -0500 Subject: [PATCH 34/74] Return error when trying to get Secure Store secret --- cmd/soroban-cli/src/config/secret.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index 47ac9f401..49887074c 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -29,6 +29,8 @@ pub enum Error { Signer(#[from] signer::Error), #[error(transparent)] Keyring(#[from] keyring::Error), + #[error("Secure Store does not reveal secret key")] + SecureStoreDoesNotRevealSecretKey, } #[derive(Debug, clap::Args, Clone)] @@ -123,7 +125,9 @@ impl Secret { .private() .0, )?, - Secret::SecureStore { .. } => panic!("Secure Store does not reveal secret key"), + Secret::SecureStore { .. } => { + return Err(Error::SecureStoreDoesNotRevealSecretKey); + } }) } From e120595a440760cc8b7c737928d56cbf37929a5d Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Mon, 2 Dec 2024 15:58:49 -0500 Subject: [PATCH 35/74] Cleanup tests --- cmd/soroban-cli/src/config/secret.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index 49887074c..b0070b28c 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -205,7 +205,7 @@ mod tests { } #[test] - fn test_from_str_for_seed_phrase() { + fn test_secret_from_seed_phrase() { let secret = Secret::from_str(TEST_SEED_PHRASE).unwrap(); let public_key = secret.public_key(None).unwrap(); let private_key = secret.private_key(None).unwrap(); @@ -216,18 +216,17 @@ mod tests { } #[test] - fn test_from_str_for_secure_store_secret() { + fn test_secret_from_secure_store() { //todo: add assertion for getting public key - will need to mock the keychain and add the keypair to the keychain let secret = Secret::from_str("secure_store:org.stellar.cli-alice").unwrap(); - assert!(matches!(secret, Secret::SecureStore { .. })); - } - #[test] - #[should_panic] - fn test_secure_store_will_not_reveal_private_key() { - let secret = Secret::from_str("secure_store").unwrap(); - secret.private_key(None).unwrap(); + let private_key_result = secret.private_key(None); + assert!(private_key_result.is_err()); + assert!(matches!( + private_key_result.unwrap_err(), + Error::SecureStoreDoesNotRevealSecretKey + )); } #[test] From 57ba3a489a8294421aae822eb8f2942013c3f98a Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Tue, 3 Dec 2024 09:19:02 -0500 Subject: [PATCH 36/74] Install libdbus for rpc-tests and bindings-ts workflows required for keyring crate --- .github/workflows/bindings-ts.yml | 1 + .github/workflows/rpc-tests.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/bindings-ts.yml b/.github/workflows/bindings-ts.yml index 957661c4c..10cf23007 100644 --- a/.github/workflows/bindings-ts.yml +++ b/.github/workflows/bindings-ts.yml @@ -35,6 +35,7 @@ jobs: target/ key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - run: rustup update + - run: sudo apt install -y libdbus-1-dev - run: cargo build - run: rustup target add wasm32-unknown-unknown - run: make build-test-wasms diff --git a/.github/workflows/rpc-tests.yml b/.github/workflows/rpc-tests.yml index 75b6d7760..5392d9830 100644 --- a/.github/workflows/rpc-tests.yml +++ b/.github/workflows/rpc-tests.yml @@ -39,6 +39,7 @@ jobs: target/ key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - run: rustup update + - run: sudo apt install -y libdbus-1-dev - run: cargo build - run: rustup target add wasm32-unknown-unknown - run: make build-test-wasms From 0814e4b1ea4cca9258de3b615fb1a3b539f971bc Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Tue, 3 Dec 2024 10:14:03 -0500 Subject: [PATCH 37/74] Update generated docs --- FULL_HELP_DOCS.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index a755ef18a..70e0d19ff 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -933,7 +933,7 @@ Create and manage identities including keys and addresses ###### **Subcommands:** -* `add` — Add a new identity (keypair, ledger, macOS keychain) +* `add` — Add a new identity (keypair, ledger, OS specific secure store) * `address` — Given an identity return its address (public key) * `fund` — Fund an identity on a test network * `generate` — Generate a new identity with a seed phrase, currently 12 words @@ -946,7 +946,7 @@ Create and manage identities including keys and addresses ## `stellar keys add` -Add a new identity (keypair, ledger, macOS keychain) +Add a new identity (keypair, ledger, OS specific secure store) **Usage:** `stellar keys add [OPTIONS] ` @@ -1018,6 +1018,7 @@ Generate a new identity with a seed phrase, currently 12 words * `--no-fund` — Do not fund address * `--seed ` — Optional seed to use when generating seed phrase. Random otherwise * `-s`, `--as-secret` — Output the generated identity as a secret key +* `--secure-store` — Save in OS-specific secure store * `--global` — Use global config * `--config-dir ` — Location of config directory, default is "." * `--hd-path ` — When generating a secret key, which `hd_path` should be used from the original `seed_phrase` From 74fa05b75e093fd3c1449ab890c31c2692026a6d Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Tue, 3 Dec 2024 10:17:23 -0500 Subject: [PATCH 38/74] Install libdbus for binaries workflow when target aarch64-unknown-linux-gnu --- .github/workflows/binaries.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index d913cf23d..f22865da8 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -46,7 +46,7 @@ jobs: - run: rustup target add ${{ matrix.sys.target }} - if: matrix.sys.target == 'aarch64-unknown-linux-gnu' - run: sudo apt-get update && sudo apt-get -y install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libudev-dev + run: sudo apt-get update && sudo apt-get -y install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libudev-dev libdbus-1-dev - name: Setup vars run: | From 7ff1f6a4907b7f7c76969952bc4a16f0ea6babcc Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Tue, 3 Dec 2024 10:45:53 -0500 Subject: [PATCH 39/74] Clippy --- cmd/soroban-cli/src/commands/keys/generate.rs | 28 +++++++++---------- cmd/soroban-cli/src/config/secret.rs | 19 ++++++------- cmd/soroban-cli/src/signer.rs | 2 +- cmd/soroban-cli/src/signer/keyring.rs | 2 +- 4 files changed, 24 insertions(+), 27 deletions(-) diff --git a/cmd/soroban-cli/src/commands/keys/generate.rs b/cmd/soroban-cli/src/commands/keys/generate.rs index 0395a593c..72597aff3 100644 --- a/cmd/soroban-cli/src/commands/keys/generate.rs +++ b/cmd/soroban-cli/src/commands/keys/generate.rs @@ -97,7 +97,7 @@ impl Cmd { warning. It can be suppressed with -q flag.", ); } - let secret = self.secret(print)?; + let secret = self.secret(&print)?; self.config_locator.write_identity(&self.name, &secret)?; if !self.no_fund { @@ -115,14 +115,14 @@ impl Cmd { Ok(()) } - fn secret(&self, print: Print) -> Result { + fn secret(&self, print: &Print) -> Result { let seed_phrase = self.seed_phrase()?; Ok(if self.as_secret { seed_phrase.private_key(self.hd_path)?.into() } else if self.secure_store { // secure_store:org.stellar.cli: let entry_name_with_prefix = format!( - "{}{}-{}", + "{}{}-{:?}", keyring::SECURE_STORE_ENTRY_PREFIX, keyring::SECURE_STORE_ENTRY_SERVICE, self.name.to_string() @@ -132,7 +132,7 @@ impl Cmd { let secret: Secret = entry_name_with_prefix.parse()?; if let Secret::SecureStore { entry_name } = &secret { - self.write_to_secure_store(entry_name.clone(), seed_phrase, print)?; + Self::write_to_secure_store(entry_name, &seed_phrase, print)?; } secret @@ -150,13 +150,12 @@ impl Cmd { } fn write_to_secure_store( - &self, - entry_name: String, - seed_phrase: Secret, - print: Print, + entry_name: &String, + seed_phrase: &Secret, + print: &Print, ) -> Result<(), Error> { - println!("Writing to secure store: {entry_name}"); - let entry = StellarEntry::new(&entry_name)?; + print.infoln(format!("Writing to secure store: {entry_name}")); + let entry = StellarEntry::new(entry_name)?; if let Ok(key) = entry.get_public_key() { print.warnln(format!("A key for {entry_name} already exists in your operating system's secure store: {key}")); } else { @@ -191,7 +190,7 @@ mod tests { config_locator: locator.clone(), hd_path: None, default_seed: false, - network: Default::default(), + network: super::network::Args::default(), fund: false, overwrite: false, }; @@ -200,9 +199,10 @@ mod tests { } fn global_args() -> super::global::Args { - let mut global_args = super::global::Args::default(); - global_args.quiet = true; - global_args + super::global::Args { + quiet: true, + ..Default::default() + } } #[tokio::test] diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index b0070b28c..cd3bc908a 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -132,17 +132,14 @@ impl Secret { } pub fn public_key(&self, index: Option) -> Result { - match self { - Secret::SecureStore { entry_name } => { - let entry = keyring::StellarEntry::new(entry_name)?; - Ok(entry.get_public_key()?) - } - _ => { - let key = self.key_pair(index)?; - Ok(stellar_strkey::ed25519::PublicKey::from_payload( - key.verifying_key().as_bytes(), - )?) - } + if let Secret::SecureStore { entry_name } = self { + let entry = keyring::StellarEntry::new(entry_name)?; + Ok(entry.get_public_key()?) + } else { + let key = self.key_pair(index)?; + Ok(stellar_strkey::ed25519::PublicKey::from_payload( + key.verifying_key().as_bytes(), + )?) } } diff --git a/cmd/soroban-cli/src/signer.rs b/cmd/soroban-cli/src/signer.rs index 86db2dcea..ba9ba8667 100644 --- a/cmd/soroban-cli/src/signer.rs +++ b/cmd/soroban-cli/src/signer.rs @@ -301,7 +301,7 @@ impl SecureStoreEntry { let entry = StellarEntry::new(&self.name)?; let signed_tx_env = entry.sign_data(tx_env.to_xdr_base64(Limits::none())?.as_bytes())?; let hint = SignatureHint(entry.get_public_key()?.0[28..].try_into()?); - let signature = Signature(signed_tx_env.to_vec().try_into()?); + let signature = Signature(signed_tx_env.clone().try_into()?); Ok(DecoratedSignature { hint, signature }) } } diff --git a/cmd/soroban-cli/src/signer/keyring.rs b/cmd/soroban-cli/src/signer/keyring.rs index f92280f7e..b5a63a398 100644 --- a/cmd/soroban-cli/src/signer/keyring.rs +++ b/cmd/soroban-cli/src/signer/keyring.rs @@ -124,7 +124,7 @@ mod test { let entry = StellarEntry::new("test").unwrap(); entry.set_password(&key_pair.to_bytes()).unwrap(); - let tx_xdr = r#"AAAAAgAAAADh6eOnZEq1xQgKioffuH7/8D8x8+OdGFEkiYC6QKMWzQAAAGQAAACuAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAYAAAAAQAAAAAAAAAAAAAAAOHp46dkSrXFCAqKh9+4fv/wPzHz450YUSSJgLpAoxbNoFT1s8jZPCv9IJ2DsqGTA8pOtavv58JF53aDycpRPcEAAAAA+N2m5zc3EfWUmLvigYPOHKXhSy8OrWfVibc6y6PrQoYAAAAAAAAAAAAAAAA"#; + let tx_xdr = r"AAAAAgAAAADh6eOnZEq1xQgKioffuH7/8D8x8+OdGFEkiYC6QKMWzQAAAGQAAACuAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAYAAAAAQAAAAAAAAAAAAAAAOHp46dkSrXFCAqKh9+4fv/wPzHz450YUSSJgLpAoxbNoFT1s8jZPCv9IJ2DsqGTA8pOtavv58JF53aDycpRPcEAAAAA+N2m5zc3EfWUmLvigYPOHKXhSy8OrWfVibc6y6PrQoYAAAAAAAAAAAAAAAA"; let sign_tx_env_result = entry.sign_data(tx_xdr.as_bytes()); assert!(sign_tx_env_result.is_ok()); From f263d8dfcd5ed764470be40d92aef48b81abc174 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Tue, 3 Dec 2024 12:17:52 -0500 Subject: [PATCH 40/74] Install libdbus for rust workflow --- .github/workflows/rust.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f3c9b1920..ff4f7d399 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -49,6 +49,7 @@ jobs: - uses: actions/checkout@v4 - uses: stellar/actions/rust-cache@main - run: rustup update + - run: sudo apt install -y libdbus-1-dev - run: make generate-full-help-doc - run: git add -N . && git diff HEAD --exit-code @@ -90,7 +91,7 @@ jobs: - run: rustup target add ${{ matrix.sys.target }} - run: rustup target add wasm32-unknown-unknown - 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: sudo apt-get update && sudo apt-get -y install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libudev-dev libdbus-1-dev - run: cargo clippy --all-targets --target ${{ matrix.sys.target }} - run: make test env: From e2b734204020df530b302eb3666e735a45fa3edb Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Tue, 3 Dec 2024 13:33:12 -0500 Subject: [PATCH 41/74] Install libdbus-1-dev in binaries workflow for build step --- .github/workflows/binaries.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index f22865da8..ebfcf927c 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -69,6 +69,7 @@ jobs: env: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc working-directory: ${{ env.BUILD_WORKING_DIR }} + run: sudo apt-get update && sudo apt-get -y install libdbus-1-dev 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) From dabbb2739f1ef63eebef22bc76ab3532a064fd63 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Tue, 3 Dec 2024 14:30:02 -0500 Subject: [PATCH 42/74] Impl Display for KeyName this change was made so that we can concat the KeyName with secure story prefix and service --- cmd/soroban-cli/src/commands/keys/generate.rs | 4 ++-- cmd/soroban-cli/src/config/address.rs | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/cmd/soroban-cli/src/commands/keys/generate.rs b/cmd/soroban-cli/src/commands/keys/generate.rs index 72597aff3..bd9f8c2f6 100644 --- a/cmd/soroban-cli/src/commands/keys/generate.rs +++ b/cmd/soroban-cli/src/commands/keys/generate.rs @@ -122,10 +122,10 @@ impl Cmd { } else if self.secure_store { // secure_store:org.stellar.cli: let entry_name_with_prefix = format!( - "{}{}-{:?}", + "{}{}-{}", keyring::SECURE_STORE_ENTRY_PREFIX, keyring::SECURE_STORE_ENTRY_SERVICE, - self.name.to_string() + self.name ); //checking that the entry name is valid before writing to the secure store diff --git a/cmd/soroban-cli/src/config/address.rs b/cmd/soroban-cli/src/config/address.rs index 57c229c76..961c26bb6 100644 --- a/cmd/soroban-cli/src/config/address.rs +++ b/cmd/soroban-cli/src/config/address.rs @@ -1,4 +1,7 @@ -use std::str::FromStr; +use std::{ + fmt::{self, Display, Formatter}, + str::FromStr, +}; use crate::xdr; @@ -87,6 +90,12 @@ impl std::str::FromStr for KeyName { } } +impl Display for KeyName { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + fn allowed_char(c: char) -> bool { c.is_ascii_alphanumeric() || c == '_' || c == '-' } From 567e2abb32f8026d7eec218623f797c390935520 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 3 Dec 2024 15:42:23 -0500 Subject: [PATCH 43/74] fix: fix tests --- .../tests/it/integration/emulator.rs | 34 ++++++++----------- .../soroban-test/tests/it/integration/util.rs | 4 --- cmd/stellar-cli/Cargo.toml | 1 + 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/cmd/crates/soroban-test/tests/it/integration/emulator.rs b/cmd/crates/soroban-test/tests/it/integration/emulator.rs index 9c4ccd1c0..da13db2d7 100644 --- a/cmd/crates/soroban-test/tests/it/integration/emulator.rs +++ b/cmd/crates/soroban-test/tests/it/integration/emulator.rs @@ -1,12 +1,8 @@ use stellar_ledger::{Blob, Error}; -use soroban_test::{AssertExt, TestEnv, LOCAL_NETWORK_PASSPHRASE}; -use std::sync::Arc; - -use soroban_cli::xdr::{ - self, Memo, MuxedAccount, Operation, OperationBody, PaymentOp, Preconditions, SequenceNumber, - Transaction, TransactionExt, Uint256, -}; +use soroban_test::{AssertExt, TestEnv}; +use std::{sync::Arc, time::Duration}; +use tokio::time::sleep; use stellar_ledger::emulator_test_support::*; @@ -14,11 +10,11 @@ use test_case::test_case; use crate::integration::util::{deploy_contract, DeployKind, HELLO_WORLD}; -// #[test_case("nanos"; "when the device is NanoS")] -#[test_case("nanox"; "when the device is NanoX")] -// #[test_case("nanosp"; "when the device is NanoS Plus")] +#[test_case("nanos", 0; "when the device is NanoS")] +#[test_case("nanox", 1; "when the device is NanoX")] +#[test_case("nanosp",2; "when the device is NanoS Plus")] #[tokio::test] -async fn test_get_public_key(ledger_device_model: &str) { +async fn test_get_public_key(ledger_device_model: &str, hd_path: u32) { let sandbox = Arc::new(TestEnv::new()); let container = TestEnv::speculos_container(ledger_device_model).await; let host_port = container.get_host_port_ipv4(9998).await.unwrap(); @@ -26,25 +22,21 @@ async fn test_get_public_key(ledger_device_model: &str) { let ledger = ledger(host_port).await; - let key = ledger.get_public_key(&0.into()).await.unwrap(); + let key = ledger.get_public_key(&hd_path.into()).await.unwrap(); let account = &key.to_string(); sandbox.fund_account(account); + sleep(Duration::from_secs(hd_path as u64)).await; sandbox .new_assert_cmd("contract") .arg("install") - .args([ - "--wasm", - HELLO_WORLD.path().as_os_str().to_str().unwrap(), - "--source", - account, - ]) + .args(["--wasm", HELLO_WORLD.path().as_os_str().to_str().unwrap()]) .assert() .success(); let tx_simulated = deploy_contract(&sandbox, HELLO_WORLD, DeployKind::SimOnly, Some(account)).await; dbg!("{tx_simulated}"); - let key = ledger.get_public_key(&0.into()).await.unwrap(); + let key = ledger.get_public_key(&hd_path.into()).await.unwrap(); println!("{key}"); let sign = tokio::task::spawn_blocking({ let sandbox = Arc::clone(&sandbox); @@ -54,6 +46,8 @@ async fn test_get_public_key(ledger_device_model: &str) { .new_assert_cmd("tx") .arg("sign") .arg("--sign-with-ledger") + .arg("--hd-path") + .arg(hd_path.to_string()) .write_stdin(tx_simulated.as_bytes()) .env("SPECULOS_PORT", host_port.to_string()) .env("RUST_LOGS", "trace") @@ -70,7 +64,7 @@ async fn test_get_public_key(ledger_device_model: &str) { let response = sign.await.unwrap(); approve.await.unwrap(); - dbg!("{tx_signed}"); + dbg!("{tx_signed:#?}"); sandbox .clone() diff --git a/cmd/crates/soroban-test/tests/it/integration/util.rs b/cmd/crates/soroban-test/tests/it/integration/util.rs index 486b00a1b..dcee95204 100644 --- a/cmd/crates/soroban-test/tests/it/integration/util.rs +++ b/cmd/crates/soroban-test/tests/it/integration/util.rs @@ -24,8 +24,6 @@ where assert_eq!(res, data); } -pub const TEST_SALT: &str = "f55ff16f66f43360266b95db6f8fec01d76031054306ae4a4b380598f6cfd114"; - pub enum DeployKind { BuildOnly, Normal, @@ -70,8 +68,6 @@ pub async fn deploy_contract( "1000000", "--wasm", &wasm.path().to_string_lossy(), - "--salt", - TEST_SALT, "--ignore-checks", &deploy.to_string(), ], diff --git a/cmd/stellar-cli/Cargo.toml b/cmd/stellar-cli/Cargo.toml index e1f1dce6b..4a2808f6f 100644 --- a/cmd/stellar-cli/Cargo.toml +++ b/cmd/stellar-cli/Cargo.toml @@ -27,6 +27,7 @@ bin-dir = "{ bin }{ binary-ext }" [features] default = [] opt = ["soroban-cli/opt"] +emulator-tests = ["soroban-cli/emulator-tests"] [dependencies] soroban-cli = { workspace = true } From d9131b402fa343bd114d020fa7fb20db818b45cd Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Tue, 3 Dec 2024 17:19:52 -0500 Subject: [PATCH 44/74] Use resolve_muxed_account in resolve_secret --- cmd/soroban-cli/src/config/address.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/soroban-cli/src/config/address.rs b/cmd/soroban-cli/src/config/address.rs index 961c26bb6..b75b89fe2 100644 --- a/cmd/soroban-cli/src/config/address.rs +++ b/cmd/soroban-cli/src/config/address.rs @@ -61,8 +61,8 @@ impl Address { 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)?), + a => Err(Error::CannotSign(a.resolve_muxed_account(locator, None)?)), } } } From 3ad6b9b482d18f3d035560d2bfe841171c71d65d Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Tue, 3 Dec 2024 17:23:16 -0500 Subject: [PATCH 45/74] Use resolve_muxed_account to get public key --- .../src/commands/contract/arg_parsing.rs | 18 +++++----- cmd/soroban-cli/src/commands/keys/address.rs | 34 +++++++++---------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs index 21fa2f383..3681ccd88 100644 --- a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs +++ b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs @@ -14,7 +14,7 @@ use crate::xdr::{ }; use crate::commands::txn_result::TxnResult; -use crate::config::{self}; +use crate::config::{self, address, secret}; use soroban_spec_tools::Spec; #[derive(thiserror::Error, Debug)] @@ -39,6 +39,10 @@ pub enum Error { Xdr(#[from] xdr::Error), #[error(transparent)] StrVal(#[from] soroban_spec_tools::Error), + #[error(transparent)] + Address(#[from] address::Error), + #[error(transparent)] + Secret(#[from] secret::Error), #[error("Missing argument {0}")] MissingArgument(String), #[error("")] @@ -82,16 +86,12 @@ pub fn build_host_function_parameters( if let Some(mut val) = matches_.get_raw(&name) { let mut s = val.next().unwrap().to_string_lossy().to_string(); if matches!(i.type_, ScSpecTypeDef::Address) { - let cmd = crate::commands::keys::address::Cmd { - name: s.clone(), - hd_path: Some(0), - locator: config.locator.clone(), - }; - if let Ok(address) = cmd.public_key() { + let addr: address::Address = s.parse()?; + if let Ok(address) = addr.resolve_muxed_account(&config.locator, None) { s = address.to_string(); } - if let Ok(key) = cmd.private_key() { - signers.push(key); + if let Ok(key) = addr.resolve_secret(&config.locator) { + signers.push(SigningKey::from_bytes(&key.private_key(None)?.0)); } } spec.from_string(&s, &i.type_) diff --git a/cmd/soroban-cli/src/commands/keys/address.rs b/cmd/soroban-cli/src/commands/keys/address.rs index d13381b49..145d8c30a 100644 --- a/cmd/soroban-cli/src/commands/keys/address.rs +++ b/cmd/soroban-cli/src/commands/keys/address.rs @@ -1,8 +1,10 @@ -use crate::commands::config::secret; - -use super::super::config::locator; use clap::arg; +use crate::{ + commands::config::{address, locator, secret}, + xdr, +}; + #[derive(thiserror::Error, Debug)] pub enum Error { #[error(transparent)] @@ -13,13 +15,16 @@ pub enum Error { #[error(transparent)] StrKey(#[from] stellar_strkey::DecodeError), + + #[error(transparent)] + Address(#[from] address::Error), } #[derive(Debug, clap::Parser, Clone)] #[group(skip)] pub struct Cmd { /// Name of identity to lookup, default test identity used if not provided - pub name: String, + pub name: address::Address, /// If identity is a seed phrase use this hd path, default is 0 #[arg(long)] @@ -35,20 +40,15 @@ impl Cmd { Ok(()) } - pub fn private_key(&self) -> Result { - Ok(self - .locator - .read_identity(&self.name)? - .key_pair(self.hd_path)?) - } - pub fn public_key(&self) -> Result { - if let Ok(key) = stellar_strkey::ed25519::PublicKey::from_string(&self.name) { - Ok(key) - } else { - Ok(stellar_strkey::ed25519::PublicKey::from_payload( - self.private_key()?.verifying_key().as_bytes(), - )?) + match self + .name + .resolve_muxed_account(&self.locator, self.hd_path)? + { + xdr::MuxedAccount::Ed25519(pk) => Ok(stellar_strkey::ed25519::PublicKey(pk.0)), + xdr::MuxedAccount::MuxedEd25519(xdr::MuxedAccountMed25519 { ed25519, .. }) => { + Ok(stellar_strkey::ed25519::PublicKey(ed25519.0)) + } } } } From 9d02b01bda2c67c97951563c8e04d6c5cf3105bf Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Tue, 3 Dec 2024 17:46:05 -0500 Subject: [PATCH 46/74] Clippy --- cmd/soroban-cli/src/config/address.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/soroban-cli/src/config/address.rs b/cmd/soroban-cli/src/config/address.rs index b75b89fe2..ff926b60a 100644 --- a/cmd/soroban-cli/src/config/address.rs +++ b/cmd/soroban-cli/src/config/address.rs @@ -62,7 +62,9 @@ impl Address { pub fn resolve_secret(&self, locator: &locator::Args) -> Result { match &self { Address::AliasOrSecret(alias) => Ok(locator.read_identity(alias)?), - a => Err(Error::CannotSign(a.resolve_muxed_account(locator, None)?)), + a @ Address::MuxedAccount(_) => { + Err(Error::CannotSign(a.resolve_muxed_account(locator, None)?)) + } } } } From a72276dc540e3a98638756e57d96a5d0758a671d Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Wed, 4 Dec 2024 12:06:30 -0500 Subject: [PATCH 47/74] fix: Sign tx hash instead of tx env with keychain --- cmd/soroban-cli/src/signer.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/soroban-cli/src/signer.rs b/cmd/soroban-cli/src/signer.rs index ba9ba8667..099ed6186 100644 --- a/cmd/soroban-cli/src/signer.rs +++ b/cmd/soroban-cli/src/signer.rs @@ -241,7 +241,7 @@ impl Signer { 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)?, - SignerKind::SecureStore(entry) => entry.sign_tx_env(tx_env)?, + SignerKind::SecureStore(entry) => entry.sign_tx_hash(tx_hash)?, }; let mut sigs = signatures.clone().into_vec(); sigs.push(decorated_signature); @@ -297,11 +297,11 @@ pub struct SecureStoreEntry { } impl SecureStoreEntry { - pub fn sign_tx_env(&self, tx_env: &TransactionEnvelope) -> Result { + pub fn sign_tx_hash(&self, tx_hash: [u8; 32]) -> Result { let entry = StellarEntry::new(&self.name)?; - let signed_tx_env = entry.sign_data(tx_env.to_xdr_base64(Limits::none())?.as_bytes())?; let hint = SignatureHint(entry.get_public_key()?.0[28..].try_into()?); - let signature = Signature(signed_tx_env.clone().try_into()?); + let signed_tx_hash = entry.sign_data(&tx_hash)?; + let signature = Signature(signed_tx_hash.clone().try_into()?); Ok(DecoratedSignature { hint, signature }) } } From 9226b1b85a009f16b2567f214239ce7962cf9cc5 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Thu, 5 Dec 2024 09:43:00 -0500 Subject: [PATCH 48/74] fix: fmt --- cmd/soroban-cli/src/commands/tx/new/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/soroban-cli/src/commands/tx/new/mod.rs b/cmd/soroban-cli/src/commands/tx/new/mod.rs index 58d2b4a3d..c00d01220 100644 --- a/cmd/soroban-cli/src/commands/tx/new/mod.rs +++ b/cmd/soroban-cli/src/commands/tx/new/mod.rs @@ -67,7 +67,6 @@ impl Cmd { Cmd::Payment(cmd) => cmd.tx.handle_and_print(op, global_args).await, Cmd::SetOptions(cmd) => cmd.tx.handle_and_print(op, global_args).await, Cmd::SetTrustlineFlags(cmd) => cmd.tx.handle_and_print(op, global_args).await, - }?; Ok(()) } From 3a25564ab6dadaf73f4613e9d75d8162efe5412d Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Thu, 5 Dec 2024 11:47:59 -0500 Subject: [PATCH 49/74] fix: update to work with `op add` --- cmd/soroban-cli/src/commands/tx/mod.rs | 2 - .../src/commands/tx/new/account_merge.rs | 2 +- .../src/commands/tx/new/bump_sequence.rs | 6 +-- .../src/commands/tx/new/change_trust.rs | 8 ++-- .../src/commands/tx/new/create_account.rs | 4 +- .../src/commands/tx/new/manage_data.rs | 8 ++-- .../src/commands/tx/new/payment.rs | 6 +-- .../src/commands/tx/new/set_options.rs | 4 +- .../src/commands/tx/op/add/account_merge.rs | 10 +---- .../src/commands/tx/op/add/bump_sequence.rs | 10 +---- .../src/commands/tx/op/add/change_trust.rs | 10 +---- .../src/commands/tx/op/add/create_account.rs | 10 +---- .../src/commands/tx/op/add/manage_data.rs | 10 +---- cmd/soroban-cli/src/commands/tx/op/add/mod.rs | 43 +++++++++++++------ .../src/commands/tx/op/add/payment.rs | 10 +---- .../src/commands/tx/op/add/set_options.rs | 10 +---- .../commands/tx/op/add/set_trustline_flags.rs | 10 +---- 17 files changed, 67 insertions(+), 96 deletions(-) diff --git a/cmd/soroban-cli/src/commands/tx/mod.rs b/cmd/soroban-cli/src/commands/tx/mod.rs index 7ea08dea1..02be68bf0 100644 --- a/cmd/soroban-cli/src/commands/tx/mod.rs +++ b/cmd/soroban-cli/src/commands/tx/mod.rs @@ -43,8 +43,6 @@ pub enum Error { #[error(transparent)] Sign(#[from] sign::Error), #[error(transparent)] - Send(#[from] send::Error), - #[error(transparent)] Args(#[from] args::Error), #[error(transparent)] Simulate(#[from] simulate::Error), diff --git a/cmd/soroban-cli/src/commands/tx/new/account_merge.rs b/cmd/soroban-cli/src/commands/tx/new/account_merge.rs index 9b82f0247..e52e1fc42 100644 --- a/cmd/soroban-cli/src/commands/tx/new/account_merge.rs +++ b/cmd/soroban-cli/src/commands/tx/new/account_merge.rs @@ -22,7 +22,7 @@ impl TryFrom<&Cmd> for xdr::OperationBody { type Error = tx::args::Error; fn try_from(cmd: &Cmd) -> Result { Ok(xdr::OperationBody::AccountMerge( - cmd.tx.reslove_muxed_address(&cmd.account)?, + cmd.tx.reslove_muxed_address(&cmd.op.account)?, )) } } diff --git a/cmd/soroban-cli/src/commands/tx/new/bump_sequence.rs b/cmd/soroban-cli/src/commands/tx/new/bump_sequence.rs index ff04e96a0..96062bba2 100644 --- a/cmd/soroban-cli/src/commands/tx/new/bump_sequence.rs +++ b/cmd/soroban-cli/src/commands/tx/new/bump_sequence.rs @@ -18,10 +18,10 @@ pub struct Args { pub bump_to: i64, } -impl From<&Args> for xdr::OperationBody { - fn from(cmd: &Args) -> Self { +impl From<&Cmd> for xdr::OperationBody { + fn from(cmd: &Cmd) -> Self { xdr::OperationBody::BumpSequence(xdr::BumpSequenceOp { - bump_to: cmd.bump_to.into(), + bump_to: cmd.op.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 index 2013db75b..04f17e87e 100644 --- a/cmd/soroban-cli/src/commands/tx/new/change_trust.rs +++ b/cmd/soroban-cli/src/commands/tx/new/change_trust.rs @@ -20,16 +20,16 @@ pub struct Args { pub limit: i64, } -impl From<&Args> for xdr::OperationBody { - fn from(cmd: &Args) -> Self { - let line = match cmd.line.0.clone() { +impl From<&Cmd> for xdr::OperationBody { + fn from(cmd: &Cmd) -> Self { + let line = match cmd.op.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, + limit: cmd.op.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 index efbafcc73..d062a07d5 100644 --- a/cmd/soroban-cli/src/commands/tx/new/create_account.rs +++ b/cmd/soroban-cli/src/commands/tx/new/create_account.rs @@ -25,8 +25,8 @@ impl TryFrom<&Cmd> for xdr::OperationBody { type Error = tx::args::Error; fn try_from(cmd: &Cmd) -> Result { Ok(xdr::OperationBody::CreateAccount(xdr::CreateAccountOp { - destination: cmd.tx.reslove_account_id(&cmd.destination)?, - starting_balance: cmd.starting_balance.into(), + destination: cmd.tx.reslove_account_id(&cmd.op.destination)?, + starting_balance: cmd.op.starting_balance.into(), })) } } diff --git a/cmd/soroban-cli/src/commands/tx/new/manage_data.rs b/cmd/soroban-cli/src/commands/tx/new/manage_data.rs index 30e9a36fd..672d8f66e 100644 --- a/cmd/soroban-cli/src/commands/tx/new/manage_data.rs +++ b/cmd/soroban-cli/src/commands/tx/new/manage_data.rs @@ -25,10 +25,10 @@ pub struct Args { pub data_value: Option>, } -impl From<&Args> for xdr::OperationBody { - fn from(cmd: &Args) -> Self { - let data_value = cmd.data_value.clone().map(Into::into); - let data_name = cmd.data_name.clone().into(); +impl From<&Cmd> for xdr::OperationBody { + fn from(cmd: &Cmd) -> Self { + let data_value = cmd.op.data_value.clone().map(Into::into); + let data_name = cmd.op.data_name.clone().into(); xdr::OperationBody::ManageData(xdr::ManageDataOp { data_name, data_value, diff --git a/cmd/soroban-cli/src/commands/tx/new/payment.rs b/cmd/soroban-cli/src/commands/tx/new/payment.rs index 03d137cdc..4b472e141 100644 --- a/cmd/soroban-cli/src/commands/tx/new/payment.rs +++ b/cmd/soroban-cli/src/commands/tx/new/payment.rs @@ -28,9 +28,9 @@ impl TryFrom<&Cmd> for xdr::OperationBody { type Error = tx::args::Error; fn try_from(cmd: &Cmd) -> Result { Ok(xdr::OperationBody::Payment(xdr::PaymentOp { - destination: cmd.tx.reslove_muxed_address(&cmd.destination)?, - asset: cmd.asset.clone().into(), - amount: cmd.amount.into(), + destination: cmd.tx.reslove_muxed_address(&cmd.op.destination)?, + asset: cmd.op.asset.clone().into(), + amount: cmd.op.amount.into(), })) } } diff --git a/cmd/soroban-cli/src/commands/tx/new/set_options.rs b/cmd/soroban-cli/src/commands/tx/new/set_options.rs index 272a1689b..42a0767b5 100644 --- a/cmd/soroban-cli/src/commands/tx/new/set_options.rs +++ b/cmd/soroban-cli/src/commands/tx/new/set_options.rs @@ -70,10 +70,12 @@ pub struct Args { impl TryFrom<&Cmd> for xdr::OperationBody { type Error = tx::args::Error; fn try_from(cmd: &Cmd) -> Result { + let tx = &cmd.tx; let mut set_flags = None; let mut set_flag = |flag: xdr::AccountFlags| { *set_flags.get_or_insert(0) |= flag as u32; }; + let cmd = &cmd.op; if cmd.set_required { set_flag(xdr::AccountFlags::RequiredFlag); @@ -118,7 +120,7 @@ impl TryFrom<&Cmd> for xdr::OperationBody { let inflation_dest: Option = cmd .inflation_dest .as_ref() - .map(|dest| cmd.tx.reslove_account_id(dest)) + .map(|dest| tx.reslove_account_id(dest)) .transpose()?; Ok(xdr::OperationBody::SetOptions(xdr::SetOptionsOp { inflation_dest, diff --git a/cmd/soroban-cli/src/commands/tx/op/add/account_merge.rs b/cmd/soroban-cli/src/commands/tx/op/add/account_merge.rs index bd643c199..cf274fd67 100644 --- a/cmd/soroban-cli/src/commands/tx/op/add/account_merge.rs +++ b/cmd/soroban-cli/src/commands/tx/op/add/account_merge.rs @@ -1,14 +1,8 @@ -use clap::{command, Parser}; - -use std::fmt::Debug; - -use super::new; - -#[derive(Parser, Debug, Clone)] +#[derive(clap::Parser, Debug, Clone)] #[group(skip)] pub struct Cmd { #[command(flatten)] pub args: super::args::Args, #[command(flatten)] - pub op: new::account_merge::Args, + pub op: super::new::account_merge::Cmd, } diff --git a/cmd/soroban-cli/src/commands/tx/op/add/bump_sequence.rs b/cmd/soroban-cli/src/commands/tx/op/add/bump_sequence.rs index 907d8d2d6..640b748b3 100644 --- a/cmd/soroban-cli/src/commands/tx/op/add/bump_sequence.rs +++ b/cmd/soroban-cli/src/commands/tx/op/add/bump_sequence.rs @@ -1,14 +1,8 @@ -use clap::{command, Parser}; - -use std::fmt::Debug; - -use super::new; - -#[derive(Parser, Debug, Clone)] +#[derive(clap::Parser, Debug, Clone)] #[group(skip)] pub struct Cmd { #[command(flatten)] pub args: super::args::Args, #[command(flatten)] - pub op: new::bump_sequence::Args, + pub op: super::new::bump_sequence::Cmd, } diff --git a/cmd/soroban-cli/src/commands/tx/op/add/change_trust.rs b/cmd/soroban-cli/src/commands/tx/op/add/change_trust.rs index af9afae1b..5647e4cec 100644 --- a/cmd/soroban-cli/src/commands/tx/op/add/change_trust.rs +++ b/cmd/soroban-cli/src/commands/tx/op/add/change_trust.rs @@ -1,14 +1,8 @@ -use clap::{command, Parser}; - -use std::fmt::Debug; - -use super::new; - -#[derive(Parser, Debug, Clone)] +#[derive(clap::Parser, Debug, Clone)] #[group(skip)] pub struct Cmd { #[command(flatten)] pub args: super::args::Args, #[command(flatten)] - pub op: new::change_trust::Args, + pub op: super::new::change_trust::Cmd, } diff --git a/cmd/soroban-cli/src/commands/tx/op/add/create_account.rs b/cmd/soroban-cli/src/commands/tx/op/add/create_account.rs index e30ff20a1..1ca978e0f 100644 --- a/cmd/soroban-cli/src/commands/tx/op/add/create_account.rs +++ b/cmd/soroban-cli/src/commands/tx/op/add/create_account.rs @@ -1,14 +1,8 @@ -use clap::{command, Parser}; - -use std::fmt::Debug; - -use super::new; - -#[derive(Parser, Debug, Clone)] +#[derive(clap::Parser, Debug, Clone)] #[group(skip)] pub struct Cmd { #[command(flatten)] pub args: super::args::Args, #[command(flatten)] - pub op: new::create_account::Args, + pub op: super::new::create_account::Cmd, } diff --git a/cmd/soroban-cli/src/commands/tx/op/add/manage_data.rs b/cmd/soroban-cli/src/commands/tx/op/add/manage_data.rs index 962233a84..5758478c3 100644 --- a/cmd/soroban-cli/src/commands/tx/op/add/manage_data.rs +++ b/cmd/soroban-cli/src/commands/tx/op/add/manage_data.rs @@ -1,14 +1,8 @@ -use clap::{command, Parser}; - -use std::fmt::Debug; - -use super::new; - -#[derive(Parser, Debug, Clone)] +#[derive(clap::Parser, Debug, Clone)] #[group(skip)] pub struct Cmd { #[command(flatten)] pub args: super::args::Args, #[command(flatten)] - pub op: new::manage_data::Args, + pub op: super::new::manage_data::Cmd, } diff --git a/cmd/soroban-cli/src/commands/tx/op/add/mod.rs b/cmd/soroban-cli/src/commands/tx/op/add/mod.rs index b94fc74ce..d8860c1fc 100644 --- a/cmd/soroban-cli/src/commands/tx/op/add/mod.rs +++ b/cmd/soroban-cli/src/commands/tx/op/add/mod.rs @@ -1,7 +1,5 @@ -use clap::Parser; - use super::super::{global, help, xdr::tx_envelope_from_stdin}; -use crate::xdr::WriteXdr; +use crate::xdr::{OperationBody, WriteXdr}; pub(crate) use super::super::{new, xdr}; @@ -15,7 +13,7 @@ mod payment; mod set_options; mod set_trustline_flags; -#[derive(Debug, Parser)] +#[derive(Debug, clap::Parser)] #[allow(clippy::doc_markdown)] pub enum Cmd { #[command(about = help::ACCOUNT_MERGE)] @@ -44,20 +42,41 @@ pub enum Error { TxXdr(#[from] super::super::xdr::Error), #[error(transparent)] Xdr(#[from] crate::xdr::Error), + #[error(transparent)] + New(#[from] super::super::new::Error), + #[error(transparent)] + Tx(#[from] super::super::args::Error), +} + +impl TryFrom<&Cmd> for OperationBody { + type Error = super::super::new::Error; + fn try_from(cmd: &Cmd) -> Result { + Ok(match &cmd { + Cmd::AccountMerge(account_merge::Cmd { op, .. }) => op.try_into()?, + Cmd::BumpSequence(bump_sequence::Cmd { op, .. }) => op.into(), + Cmd::ChangeTrust(change_trust::Cmd { op, .. }) => op.into(), + Cmd::CreateAccount(create_account::Cmd { op, .. }) => op.try_into()?, + Cmd::ManageData(manage_data::Cmd { op, .. }) => op.into(), + Cmd::Payment(payment::Cmd { op, .. }) => op.try_into()?, + Cmd::SetOptions(set_options::Cmd { op, .. }) => op.try_into()?, + Cmd::SetTrustlineFlags(set_trustline_flags::Cmd { op, .. }) => op.try_into()?, + }) + } } impl Cmd { pub fn run(&self, _: &global::Args) -> Result<(), Error> { let tx_env = tx_envelope_from_stdin()?; + let op = OperationBody::try_from(self)?; let res = match self { - Cmd::AccountMerge(cmd) => cmd.args.add_op(&cmd.op, tx_env), - Cmd::BumpSequence(cmd) => cmd.args.add_op(&cmd.op, tx_env), - Cmd::ChangeTrust(cmd) => cmd.args.add_op(&cmd.op, tx_env), - Cmd::CreateAccount(cmd) => cmd.args.add_op(&cmd.op, tx_env), - Cmd::ManageData(cmd) => cmd.args.add_op(&cmd.op, tx_env), - Cmd::Payment(cmd) => cmd.args.add_op(&cmd.op, tx_env), - Cmd::SetOptions(cmd) => cmd.args.add_op(&cmd.op, tx_env), - Cmd::SetTrustlineFlags(cmd) => cmd.args.add_op(&cmd.op, tx_env), + Cmd::AccountMerge(cmd) => cmd.args.add_op(op, tx_env), + Cmd::BumpSequence(cmd) => cmd.args.add_op(op, tx_env), + Cmd::ChangeTrust(cmd) => cmd.args.add_op(op, tx_env), + Cmd::CreateAccount(cmd) => cmd.args.add_op(op, tx_env), + Cmd::ManageData(cmd) => cmd.args.add_op(op, tx_env), + Cmd::Payment(cmd) => cmd.args.add_op(op, tx_env), + Cmd::SetOptions(cmd) => cmd.args.add_op(op, tx_env), + Cmd::SetTrustlineFlags(cmd) => cmd.args.add_op(op, tx_env), }?; println!("{}", res.to_xdr_base64(crate::xdr::Limits::none())?); Ok(()) diff --git a/cmd/soroban-cli/src/commands/tx/op/add/payment.rs b/cmd/soroban-cli/src/commands/tx/op/add/payment.rs index d8146c91a..f5f9c5fcc 100644 --- a/cmd/soroban-cli/src/commands/tx/op/add/payment.rs +++ b/cmd/soroban-cli/src/commands/tx/op/add/payment.rs @@ -1,14 +1,8 @@ -use clap::{command, Parser}; - -use std::fmt::Debug; - -use super::new; - -#[derive(Parser, Debug, Clone)] +#[derive(clap::Parser, Debug, Clone)] #[group(skip)] pub struct Cmd { #[command(flatten)] pub args: super::args::Args, #[command(flatten)] - pub op: new::payment::Args, + pub op: super::new::payment::Cmd, } diff --git a/cmd/soroban-cli/src/commands/tx/op/add/set_options.rs b/cmd/soroban-cli/src/commands/tx/op/add/set_options.rs index 75b43124a..88323f57c 100644 --- a/cmd/soroban-cli/src/commands/tx/op/add/set_options.rs +++ b/cmd/soroban-cli/src/commands/tx/op/add/set_options.rs @@ -1,14 +1,8 @@ -use clap::{command, Parser}; - -use std::fmt::Debug; - -use super::new; - -#[derive(Parser, Debug, Clone)] +#[derive(clap::Parser, Debug, Clone)] #[group(skip)] pub struct Cmd { #[command(flatten)] pub args: super::args::Args, #[command(flatten)] - pub op: new::set_options::Args, + pub op: super::new::set_options::Cmd, } diff --git a/cmd/soroban-cli/src/commands/tx/op/add/set_trustline_flags.rs b/cmd/soroban-cli/src/commands/tx/op/add/set_trustline_flags.rs index 8ffee7a7b..09ee11607 100644 --- a/cmd/soroban-cli/src/commands/tx/op/add/set_trustline_flags.rs +++ b/cmd/soroban-cli/src/commands/tx/op/add/set_trustline_flags.rs @@ -1,14 +1,8 @@ -use clap::{command, Parser}; - -use std::fmt::Debug; - -use super::new; - -#[derive(Parser, Debug, Clone)] +#[derive(clap::Parser, Debug, Clone)] #[group(skip)] pub struct Cmd { #[command(flatten)] pub args: super::args::Args, #[command(flatten)] - pub op: new::set_trustline_flags::Args, + pub op: super::new::set_trustline_flags::Cmd, } From 5f55e7c642a8ae28294c06ed14ba4aa548e55796 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Thu, 5 Dec 2024 12:25:34 -0500 Subject: [PATCH 50/74] fix: simplify op::add::args::Args Should fix issue with multiple locators --- cmd/soroban-cli/src/commands/tx/args.rs | 20 +++++++++++++ cmd/soroban-cli/src/commands/tx/mod.rs | 2 -- .../src/commands/tx/op/add/args.rs | 30 +++---------------- cmd/soroban-cli/src/commands/tx/op/add/mod.rs | 20 ++++++------- 4 files changed, 33 insertions(+), 39 deletions(-) diff --git a/cmd/soroban-cli/src/commands/tx/args.rs b/cmd/soroban-cli/src/commands/tx/args.rs index c703ead6b..23702f4f2 100644 --- a/cmd/soroban-cli/src/commands/tx/args.rs +++ b/cmd/soroban-cli/src/commands/tx/args.rs @@ -11,6 +11,7 @@ use crate::{ xdr::{self, Limits, WriteXdr}, }; + #[derive(Debug, clap::Args, Clone)] #[group(skip)] pub struct Args { @@ -38,6 +39,8 @@ pub enum Error { Xdr(#[from] xdr::Error), #[error(transparent)] Address(#[from] address::Error), + #[error(transparent)] + TxXdr(#[from] super::xdr::Error), } impl Args { @@ -120,4 +123,21 @@ impl Args { .resolve_muxed_account(&self.config.locator, self.config.hd_path)? .account_id()) } + + + pub fn add_op( + &self, + op_body: impl Into, + tx_env: xdr::TransactionEnvelope, + op_source: Option<&address::Address>, + ) -> Result { + let source_account = op_source + .map(|a| self.reslove_muxed_address(a)) + .transpose()?; + let op = xdr::Operation { + source_account, + body: op_body.into(), + }; + Ok(super::xdr::add_op(tx_env, op)?) + } } diff --git a/cmd/soroban-cli/src/commands/tx/mod.rs b/cmd/soroban-cli/src/commands/tx/mod.rs index 02be68bf0..d9fd79faf 100644 --- a/cmd/soroban-cli/src/commands/tx/mod.rs +++ b/cmd/soroban-cli/src/commands/tx/mod.rs @@ -43,8 +43,6 @@ pub enum Error { #[error(transparent)] Sign(#[from] sign::Error), #[error(transparent)] - Args(#[from] args::Error), - #[error(transparent)] Simulate(#[from] simulate::Error), } diff --git a/cmd/soroban-cli/src/commands/tx/op/add/args.rs b/cmd/soroban-cli/src/commands/tx/op/add/args.rs index f1858e0b0..9d9b4931b 100644 --- a/cmd/soroban-cli/src/commands/tx/op/add/args.rs +++ b/cmd/soroban-cli/src/commands/tx/op/add/args.rs @@ -1,22 +1,8 @@ -use super::xdr::add_op; -use crate::{ - config::{address, locator}, - xdr, -}; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Address(#[from] address::Error), - #[error(transparent)] - TxXdr(#[from] super::xdr::Error), -} +use crate::{commands::tx, config::address, xdr}; #[derive(Debug, clap::Args, Clone)] #[group(skip)] pub struct Args { - #[clap(flatten)] - pub locator: locator::Args, /// Source account used for the operation #[arg( long, @@ -31,16 +17,8 @@ impl Args { &self, op_body: impl Into, tx_env: xdr::TransactionEnvelope, - ) -> Result { - let source_account = self - .operation_source_account - .as_ref() - .map(|a| a.resolve_muxed_account(&self.locator, None)) - .transpose()?; - let op = xdr::Operation { - source_account, - body: op_body.into(), - }; - Ok(add_op(tx_env, op)?) + tx: &tx::args::Args, + ) -> Result { + tx.add_op(op_body, tx_env, self.operation_source_account.as_ref()) } } diff --git a/cmd/soroban-cli/src/commands/tx/op/add/mod.rs b/cmd/soroban-cli/src/commands/tx/op/add/mod.rs index d8860c1fc..5e69cf168 100644 --- a/cmd/soroban-cli/src/commands/tx/op/add/mod.rs +++ b/cmd/soroban-cli/src/commands/tx/op/add/mod.rs @@ -1,7 +1,7 @@ use super::super::{global, help, xdr::tx_envelope_from_stdin}; use crate::xdr::{OperationBody, WriteXdr}; -pub(crate) use super::super::{new, xdr}; +pub(crate) use super::super::new; mod account_merge; mod args; @@ -36,8 +36,6 @@ pub enum Cmd { #[derive(thiserror::Error, Debug)] pub enum Error { - #[error(transparent)] - Args(#[from] args::Error), #[error(transparent)] TxXdr(#[from] super::super::xdr::Error), #[error(transparent)] @@ -69,14 +67,14 @@ impl Cmd { let tx_env = tx_envelope_from_stdin()?; let op = OperationBody::try_from(self)?; let res = match self { - Cmd::AccountMerge(cmd) => cmd.args.add_op(op, tx_env), - Cmd::BumpSequence(cmd) => cmd.args.add_op(op, tx_env), - Cmd::ChangeTrust(cmd) => cmd.args.add_op(op, tx_env), - Cmd::CreateAccount(cmd) => cmd.args.add_op(op, tx_env), - Cmd::ManageData(cmd) => cmd.args.add_op(op, tx_env), - Cmd::Payment(cmd) => cmd.args.add_op(op, tx_env), - Cmd::SetOptions(cmd) => cmd.args.add_op(op, tx_env), - Cmd::SetTrustlineFlags(cmd) => cmd.args.add_op(op, tx_env), + Cmd::AccountMerge(cmd) => cmd.args.add_op(op, tx_env, &cmd.op.tx), + Cmd::BumpSequence(cmd) => cmd.args.add_op(op, tx_env, &cmd.op.tx), + Cmd::ChangeTrust(cmd) => cmd.args.add_op(op, tx_env, &cmd.op.tx), + Cmd::CreateAccount(cmd) => cmd.args.add_op(op, tx_env, &cmd.op.tx), + Cmd::ManageData(cmd) => cmd.args.add_op(op, tx_env, &cmd.op.tx), + Cmd::Payment(cmd) => cmd.args.add_op(op, tx_env, &cmd.op.tx), + Cmd::SetOptions(cmd) => cmd.args.add_op(op, tx_env, &cmd.op.tx), + Cmd::SetTrustlineFlags(cmd) => cmd.args.add_op(op, tx_env, &cmd.op.tx), }?; println!("{}", res.to_xdr_base64(crate::xdr::Limits::none())?); Ok(()) From 0e7f08f12a675a6806d3ee8a56c5f8bbb024d303 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Thu, 5 Dec 2024 12:53:44 -0500 Subject: [PATCH 51/74] fix: remove untagged and use rename to keep same serialized output --- cmd/soroban-cli/src/commands/tx/args.rs | 2 - cmd/soroban-cli/src/commands/tx/mod.rs | 2 + cmd/soroban-cli/src/config/key.rs | 57 ++++++++++++++++--------- 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/cmd/soroban-cli/src/commands/tx/args.rs b/cmd/soroban-cli/src/commands/tx/args.rs index 23702f4f2..c00da6fea 100644 --- a/cmd/soroban-cli/src/commands/tx/args.rs +++ b/cmd/soroban-cli/src/commands/tx/args.rs @@ -11,7 +11,6 @@ use crate::{ xdr::{self, Limits, WriteXdr}, }; - #[derive(Debug, clap::Args, Clone)] #[group(skip)] pub struct Args { @@ -124,7 +123,6 @@ impl Args { .account_id()) } - pub fn add_op( &self, op_body: impl Into, diff --git a/cmd/soroban-cli/src/commands/tx/mod.rs b/cmd/soroban-cli/src/commands/tx/mod.rs index d9fd79faf..02be68bf0 100644 --- a/cmd/soroban-cli/src/commands/tx/mod.rs +++ b/cmd/soroban-cli/src/commands/tx/mod.rs @@ -43,6 +43,8 @@ pub enum Error { #[error(transparent)] Sign(#[from] sign::Error), #[error(transparent)] + Args(#[from] args::Error), + #[error(transparent)] Simulate(#[from] simulate::Error), } diff --git a/cmd/soroban-cli/src/config/key.rs b/cmd/soroban-cli/src/config/key.rs index dcb96775b..dab9d8ac9 100644 --- a/cmd/soroban-cli/src/config/key.rs +++ b/cmd/soroban-cli/src/config/key.rs @@ -18,23 +18,24 @@ pub enum Error { } #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] -#[serde(untagged)] pub enum Key { + #[serde(rename = "public_key")] + PublicKey(Public), + #[serde(rename = "muxed_account")] + MuxedAccount(MuxedAccount), + #[serde(untagged)] Secret(Secret), - PublicKey { public_key: Public }, - MuxedAccount { muxed_account: MuxedAccount }, } impl Key { pub fn public_key(&self, hd_path: Option) -> Result { let bytes = match self { Key::Secret(secret) => secret.public_key(hd_path)?.0, - Key::PublicKey { - public_key: Public(key), - } => key.0, - Key::MuxedAccount { - muxed_account: MuxedAccount(stellar_strkey::ed25519::MuxedAccount { ed25519, id }), - } => { + Key::PublicKey(Public(key)) => key.0, + Key::MuxedAccount(MuxedAccount(stellar_strkey::ed25519::MuxedAccount { + ed25519, + id, + })) => { return Ok(xdr::MuxedAccount::MuxedEd25519(xdr::MuxedAccountMed25519 { ed25519: xdr::Uint256(*ed25519), id: *id, @@ -63,10 +64,10 @@ impl FromStr for Key { return Ok(Key::Secret(secret)); } if let Ok(public_key) = s.parse() { - return Ok(Key::PublicKey { public_key }); + return Ok(Key::PublicKey(public_key)); } if let Ok(muxed_account) = s.parse() { - return Ok(Key::MuxedAccount { muxed_account }); + return Ok(Key::MuxedAccount(muxed_account)); } Err(Error::Parse) } @@ -74,9 +75,7 @@ impl FromStr for Key { impl From for Key { fn from(value: stellar_strkey::ed25519::PublicKey) -> Self { - Key::PublicKey { - public_key: Public(value), - } + Key::PublicKey(Public(value)) } } @@ -135,14 +134,34 @@ impl Display for MuxedAccount { mod test { use super::*; - #[test] - fn public_key() { - let key = Key::PublicKey { - public_key: Public(stellar_strkey::ed25519::PublicKey([0; 32])), - }; + fn round_trip(key: Key) { let serialized = toml::to_string(&key).unwrap(); println!("{serialized}"); let deserialized: Key = toml::from_str(&serialized).unwrap(); assert_eq!(key, deserialized); } + + #[test] + fn public_key() { + let key = Key::PublicKey(Public(stellar_strkey::ed25519::PublicKey([0; 32]))); + round_trip(key); + } + #[test] + fn muxed_key() { + let key: stellar_strkey::ed25519::MuxedAccount = + "MA3D5KRYM6CB7OWQ6TWYRR3Z4T7GNZLKERYNZGGA5SOAOPIFY6YQGAAAAAAAAAPCICBKU" + .parse() + .unwrap(); + let key = Key::MuxedAccount(MuxedAccount(key)); + round_trip(key); + } + #[test] + fn secret_key() { + let secret_key = stellar_strkey::ed25519::PrivateKey([0; 32]); + let secret = Secret::SecretKey { + secret_key: secret_key.to_string(), + }; + let key = Key::Secret(secret); + round_trip(key); + } } From aa8bb16d7824ee5d8ee8b971fcceadd08e7f28ea Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Thu, 5 Dec 2024 13:14:11 -0500 Subject: [PATCH 52/74] fix: docs and clippy in tests --- FULL_HELP_DOCS.md | 140 ++++++++++++++++++++++++++---- cmd/soroban-cli/src/config/key.rs | 23 +++-- 2 files changed, 136 insertions(+), 27 deletions(-) diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index 975845c0b..e453b53d8 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -1536,7 +1536,7 @@ Transfers the XLM balance of an account to another account and removes the sourc * `--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...' or alias +* `--account ` — Muxed Account to merge with, e.g. `GBX...`, 'MBX...' @@ -1825,13 +1825,26 @@ https://developers.stellar.org/docs/learn/glossary#flags Transfers the XLM balance of an account to another account and removes the source account from the ledger -**Usage:** `stellar tx operation add account-merge [OPTIONS] --account ` +**Usage:** `stellar tx operation add account-merge [OPTIONS] --source-account --account ` ###### **Options:** +* `--operation-source-account ` — Source account used for the operation +* `--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` — (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 +* `--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 "." -* `--operation-source-account ` — Source account used for the operation * `--account ` — Muxed Account to merge with, e.g. `GBX...`, 'MBX...' @@ -1840,13 +1853,26 @@ Transfers the XLM balance of an account to another account and removes the sourc 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 operation add bump-sequence [OPTIONS] --bump-to ` +**Usage:** `stellar tx operation add bump-sequence [OPTIONS] --source-account --bump-to ` ###### **Options:** +* `--operation-source-account ` — Source account used for the operation +* `--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` — (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 +* `--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 "." -* `--operation-source-account ` — Source account used for the operation * `--bump-to ` — Sequence number to bump to @@ -1857,13 +1883,26 @@ 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 operation add change-trust [OPTIONS] --line ` +**Usage:** `stellar tx operation add change-trust [OPTIONS] --source-account --line ` ###### **Options:** +* `--operation-source-account ` — Source account used for the operation +* `--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` — (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 +* `--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 "." -* `--operation-source-account ` — Source account used for the operation * `--line ` * `--limit ` — Limit for the trust line, 0 to remove the trust line @@ -1875,13 +1914,26 @@ https://developers.stellar.org/docs/learn/fundamentals/stellar-data-structures/a Creates and funds a new account with the specified starting balance -**Usage:** `stellar tx operation add create-account [OPTIONS] --destination ` +**Usage:** `stellar tx operation add create-account [OPTIONS] --source-account --destination ` ###### **Options:** +* `--operation-source-account ` — Source account used for the operation +* `--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` — (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 +* `--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 "." -* `--operation-source-account ` — Source account used for the operation * `--destination ` — Account Id to create, e.g. `GBX...` * `--starting-balance ` — Initial balance in stroops of the account, default 1 XLM @@ -1895,13 +1947,26 @@ Sets, modifies, or deletes a data entry (name/value pair) that is attached to an Learn more about entries and subentries: https://developers.stellar.org/docs/learn/fundamentals/stellar-data-structures/accounts#subentries -**Usage:** `stellar tx operation add manage-data [OPTIONS] --data-name ` +**Usage:** `stellar tx operation add manage-data [OPTIONS] --source-account --data-name ` ###### **Options:** +* `--operation-source-account ` — Source account used for the operation +* `--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` — (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 +* `--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 "." -* `--operation-source-account ` — Source account used for the operation * `--data-name ` — String up to 64 bytes long. If this is a new Name it will add the given name/value pair to the account. If this Name is already present then the associated value will be modified * `--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` @@ -1911,13 +1976,26 @@ https://developers.stellar.org/docs/learn/fundamentals/stellar-data-structures/a Sends an amount in a specific asset to a destination account -**Usage:** `stellar tx operation add payment [OPTIONS] --destination --amount ` +**Usage:** `stellar tx operation add payment [OPTIONS] --source-account --destination --amount ` ###### **Options:** +* `--operation-source-account ` — Source account used for the operation +* `--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` — (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 +* `--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 "." -* `--operation-source-account ` — Source account used for the operation * `--destination ` — Account to send to, e.g. `GBX...` * `--asset ` — Asset to send, default native, e.i. XLM @@ -1936,13 +2014,26 @@ 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 operation add set-options [OPTIONS]` +**Usage:** `stellar tx operation add set-options [OPTIONS] --source-account ` ###### **Options:** +* `--operation-source-account ` — Source account used for the operation +* `--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` — (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 +* `--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 "." -* `--operation-source-account ` — Source account used for the operation * `--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 @@ -1970,14 +2061,27 @@ If you are modifying a trustline to a pool share, however, this is composed of t Learn more about flags: https://developers.stellar.org/docs/learn/glossary#flags -**Usage:** `stellar tx operation add set-trustline-flags [OPTIONS] --trustor --asset ` +**Usage:** `stellar tx operation add set-trustline-flags [OPTIONS] --source-account --trustor --asset ` ###### **Options:** +* `--operation-source-account ` — Source account used for the operation +* `--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` — (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 +* `--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 "." -* `--operation-source-account ` — Source account used for the operation -* `--trustor ` — Account to set trustline flags for +* `--trustor ` — Account to set trustline flags for, e.g. `GBX...`, or alias, or muxed account, `M123...`` * `--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 diff --git a/cmd/soroban-cli/src/config/key.rs b/cmd/soroban-cli/src/config/key.rs index dab9d8ac9..cecb83522 100644 --- a/cmd/soroban-cli/src/config/key.rs +++ b/cmd/soroban-cli/src/config/key.rs @@ -134,17 +134,17 @@ impl Display for MuxedAccount { mod test { use super::*; - fn round_trip(key: Key) { + fn round_trip(key: &Key) { let serialized = toml::to_string(&key).unwrap(); println!("{serialized}"); let deserialized: Key = toml::from_str(&serialized).unwrap(); - assert_eq!(key, deserialized); + assert_eq!(key, &deserialized); } #[test] fn public_key() { let key = Key::PublicKey(Public(stellar_strkey::ed25519::PublicKey([0; 32]))); - round_trip(key); + round_trip(&key); } #[test] fn muxed_key() { @@ -153,15 +153,20 @@ mod test { .parse() .unwrap(); let key = Key::MuxedAccount(MuxedAccount(key)); - round_trip(key); + round_trip(&key); } #[test] fn secret_key() { - let secret_key = stellar_strkey::ed25519::PrivateKey([0; 32]); - let secret = Secret::SecretKey { - secret_key: secret_key.to_string(), - }; + let secret_key = stellar_strkey::ed25519::PrivateKey([0; 32]).to_string(); + let secret = Secret::SecretKey { secret_key }; + let key = Key::Secret(secret); + round_trip(&key); + } + #[test] + fn secret_seed_phrase() { + let seed_phrase = "singer swing mango apple singer swing mango apple singer swing mango apple singer swing mango apple".to_string(); + let secret = Secret::SeedPhrase { seed_phrase }; let key = Key::Secret(secret); - round_trip(key); + round_trip(&key); } } From 7537edb3e3e8934fc7f9bb39544e55a585162c9d Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Mon, 9 Dec 2024 09:54:51 -0500 Subject: [PATCH 53/74] fix: use different contracts for each test --- .../tests/it/integration/emulator.rs | 38 ++++++++++------- .../soroban-test/tests/it/integration/tx.rs | 41 +++++++++++++++---- .../tests/it/integration/tx/operations.rs | 12 +++++- .../soroban-test/tests/it/integration/util.rs | 34 ++++++++++----- 4 files changed, 91 insertions(+), 34 deletions(-) diff --git a/cmd/crates/soroban-test/tests/it/integration/emulator.rs b/cmd/crates/soroban-test/tests/it/integration/emulator.rs index da13db2d7..f53611139 100644 --- a/cmd/crates/soroban-test/tests/it/integration/emulator.rs +++ b/cmd/crates/soroban-test/tests/it/integration/emulator.rs @@ -1,14 +1,13 @@ -use stellar_ledger::{Blob, Error}; +use stellar_ledger::Blob; use soroban_test::{AssertExt, TestEnv}; -use std::{sync::Arc, time::Duration}; -use tokio::time::sleep; +use std::sync::Arc; use stellar_ledger::emulator_test_support::*; use test_case::test_case; -use crate::integration::util::{deploy_contract, DeployKind, HELLO_WORLD}; +use crate::integration::util::{self, deploy_contract, DeployKind, HELLO_WORLD}; #[test_case("nanos", 0; "when the device is NanoS")] #[test_case("nanox", 1; "when the device is NanoX")] @@ -23,21 +22,32 @@ async fn test_get_public_key(ledger_device_model: &str, hd_path: u32) { let ledger = ledger(host_port).await; let key = ledger.get_public_key(&hd_path.into()).await.unwrap(); - let account = &key.to_string(); - sandbox.fund_account(account); - sleep(Duration::from_secs(hd_path as u64)).await; + let contract = match hd_path { + 0 => HELLO_WORLD, + 1 => util::CUSTOM_ACCOUNT, + 2 => util::CUSTOM_TYPES, + _ => panic!("Invalid hd_path"), + }; + let account = key.to_string(); + sandbox.fund_account(&account); + sandbox .new_assert_cmd("contract") .arg("install") - .args(["--wasm", HELLO_WORLD.path().as_os_str().to_str().unwrap()]) + .args(["--wasm", contract.path().as_os_str().to_str().unwrap()]) .assert() .success(); - let tx_simulated = - deploy_contract(&sandbox, HELLO_WORLD, DeployKind::SimOnly, Some(account)).await; - dbg!("{tx_simulated}"); - let key = ledger.get_public_key(&hd_path.into()).await.unwrap(); - println!("{key}"); + let tx_simulated = deploy_contract( + &sandbox, + contract, + crate::integration::util::DeployOptions { + kind: DeployKind::SimOnly, + deployer: Some(account), + ..Default::default() + }, + ) + .await; let sign = tokio::task::spawn_blocking({ let sandbox = Arc::clone(&sandbox); @@ -64,8 +74,6 @@ async fn test_get_public_key(ledger_device_model: &str, hd_path: u32) { let response = sign.await.unwrap(); approve.await.unwrap(); - dbg!("{tx_signed:#?}"); - sandbox .clone() .new_assert_cmd("tx") diff --git a/cmd/crates/soroban-test/tests/it/integration/tx.rs b/cmd/crates/soroban-test/tests/it/integration/tx.rs index 00d907e77..63fc5e8f3 100644 --- a/cmd/crates/soroban-test/tests/it/integration/tx.rs +++ b/cmd/crates/soroban-test/tests/it/integration/tx.rs @@ -2,20 +2,34 @@ use soroban_cli::assembled::simulate_and_assemble_transaction; use soroban_cli::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, DeployKind, DeployOptions, 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, None).await; - let xdr_base64_sim_only = - deploy_contract(sandbox, HELLO_WORLD, DeployKind::SimOnly, None).await; + let xdr_base64_build_only = deploy_contract( + sandbox, + HELLO_WORLD, + DeployOptions { + kind: DeployKind::BuildOnly, + ..Default::default() + }, + ) + .await; + let xdr_base64_sim_only = deploy_contract( + sandbox, + HELLO_WORLD, + DeployOptions { + kind: DeployKind::SimOnly, + ..Default::default() + }, + ) + .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(); + let tx = soroban_cli::commands::tx::xdr::unwrap_envelope_v1(tx_env.clone()).unwrap(); let assembled_str = sandbox .new_assert_cmd("tx") .arg("simulate") @@ -23,6 +37,11 @@ async fn simulate() { .assert() .success() .stdout_as_str(); + let tx_env_from_cli_tx = + TransactionEnvelope::from_xdr_base64(&assembled_str, Limits::none()).unwrap(); + let tx_env_sim_only = + TransactionEnvelope::from_xdr_base64(&xdr_base64_sim_only, Limits::none()).unwrap(); + assert_eq!(tx_env_from_cli_tx, tx_env_sim_only); assert_eq!(xdr_base64_sim_only, assembled_str); let assembled = simulate_and_assemble_transaction(&sandbox.client(), &tx) .await @@ -72,7 +91,15 @@ pub(crate) async fn build_sim_sign_send(sandbox: &TestEnv, account: &str, sign_w .assert() .success(); - let tx_simulated = deploy_contract(sandbox, HELLO_WORLD, DeployKind::SimOnly, None).await; + let tx_simulated = deploy_contract( + sandbox, + HELLO_WORLD, + DeployOptions { + kind: DeployKind::SimOnly, + ..Default::default() + }, + ) + .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 index 1ce1f06c9..8492e5078 100644 --- a/cmd/crates/soroban-test/tests/it/integration/tx/operations.rs +++ b/cmd/crates/soroban-test/tests/it/integration/tx/operations.rs @@ -8,7 +8,7 @@ use soroban_test::{AssertExt, TestEnv}; use crate::integration::{ hello_world::invoke_hello_world, - util::{deploy_contract, DeployKind, HELLO_WORLD}, + util::{deploy_contract, DeployOptions, HELLO_WORLD}, }; fn test_address(sandbox: &TestEnv) -> String { @@ -84,7 +84,15 @@ async fn create_account() { .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; + let id = deploy_contract( + sandbox, + HELLO_WORLD, + DeployOptions { + deployer: Some("new".to_string()), + ..Default::default() + }, + ) + .await; println!("{id}"); invoke_hello_world(sandbox, &id); } diff --git a/cmd/crates/soroban-test/tests/it/integration/util.rs b/cmd/crates/soroban-test/tests/it/integration/util.rs index dcee95204..2299cc11a 100644 --- a/cmd/crates/soroban-test/tests/it/integration/util.rs +++ b/cmd/crates/soroban-test/tests/it/integration/util.rs @@ -24,8 +24,10 @@ where assert_eq!(res, data); } +#[derive(Default)] pub enum DeployKind { BuildOnly, + #[default] Normal, SimOnly, } @@ -41,43 +43,55 @@ impl Display for DeployKind { } pub async fn deploy_hello(sandbox: &TestEnv) -> String { - deploy_contract(sandbox, HELLO_WORLD, DeployKind::Normal, None).await + deploy_contract(sandbox, HELLO_WORLD, DeployOptions::default()).await } pub async fn deploy_custom(sandbox: &TestEnv) -> String { - deploy_contract(sandbox, CUSTOM_TYPES, DeployKind::Normal, None).await + deploy_contract(sandbox, CUSTOM_TYPES, DeployOptions::default()).await } pub async fn deploy_swap(sandbox: &TestEnv) -> String { - deploy_contract(sandbox, SWAP, DeployKind::Normal, None).await + deploy_contract(sandbox, SWAP, DeployOptions::default()).await } pub async fn deploy_custom_account(sandbox: &TestEnv) -> String { - deploy_contract(sandbox, CUSTOM_ACCOUNT, DeployKind::Normal, None).await + deploy_contract(sandbox, CUSTOM_ACCOUNT, DeployOptions::default()).await +} + +#[derive(Default)] +pub struct DeployOptions { + pub kind: DeployKind, + pub deployer: Option, + pub salt: Option, } pub async fn deploy_contract( sandbox: &TestEnv, wasm: &Wasm<'static>, - deploy: DeployKind, - deployer: Option<&str>, + DeployOptions { + kind, + deployer, + salt, + }: DeployOptions, ) -> String { - let cmd = sandbox.cmd_with_config::<_, commands::contract::deploy::wasm::Cmd>( + let mut cmd = sandbox.cmd_with_config::<_, commands::contract::deploy::wasm::Cmd>( &[ "--fee", "1000000", "--wasm", &wasm.path().to_string_lossy(), "--ignore-checks", - &deploy.to_string(), + &kind.to_string(), ], None, ); + cmd.salt = salt; + let res = sandbox - .run_cmd_with(cmd, deployer.unwrap_or("test")) + .run_cmd_with(cmd, deployer.as_deref().unwrap_or("test")) .await .unwrap(); - match deploy { + match kind { DeployKind::BuildOnly | DeployKind::SimOnly => match res.to_envelope() { commands::txn_result::TxnEnvelopeResult::TxnEnvelope(e) => { return e.to_xdr_base64(Limits::none()).unwrap() From 2d22fb467ed9958306d30d856fd86b0952cf105d Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Mon, 9 Dec 2024 10:21:18 -0500 Subject: [PATCH 54/74] fix: use salt for sim test and separate CI tests --- .github/workflows/rpc-tests.yml | 85 ++++++++++--------- .../soroban-test/tests/it/integration/tx.rs | 3 + 2 files changed, 46 insertions(+), 42 deletions(-) diff --git a/.github/workflows/rpc-tests.yml b/.github/workflows/rpc-tests.yml index 778cc6525..14fecb2f1 100644 --- a/.github/workflows/rpc-tests.yml +++ b/.github/workflows/rpc-tests.yml @@ -1,48 +1,49 @@ name: RPC Tests on: - push: - branches: [main, release/**] - pull_request: + push: + branches: [main, release/**] + pull_request: concurrency: - group: ${{ github.workflow }}-${{ github.ref_protected == 'true' && github.sha || github.ref }} - cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.ref_protected == 'true' && github.sha || github.ref }} + cancel-in-progress: true jobs: - test: - name: test RPC - runs-on: ubuntu-22.04 - services: - rpc: - 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: rustup target add wasm32-unknown-unknown - - run: make build-test-wasms - - name: install libudev-dev - run: | - sudo apt install -y libudev-dev - - run: cargo build --features emulator-tests - - run: SOROBAN_PORT=8000 cargo test --features it --features emulator-tests --package soroban-test --test it -- integration + test: + name: test RPC + runs-on: ubuntu-22.04 + services: + rpc: + 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: rustup target add wasm32-unknown-unknown + - run: make build-test-wasms + - name: install libudev-dev + run: | + sudo apt install -y libudev-dev + - run: cargo build --features emulator-tests + - run: SOROBAN_PORT=8000 cargo test --features it --package soroban-test --test it -- integration + - run: SOROBAN_PORT=8000 cargo test --features it --features emulator-tests --package soroban-test --test it -- integration::emulator diff --git a/cmd/crates/soroban-test/tests/it/integration/tx.rs b/cmd/crates/soroban-test/tests/it/integration/tx.rs index 63fc5e8f3..44d5264b0 100644 --- a/cmd/crates/soroban-test/tests/it/integration/tx.rs +++ b/cmd/crates/soroban-test/tests/it/integration/tx.rs @@ -9,11 +9,13 @@ mod operations; #[tokio::test] async fn simulate() { let sandbox = &TestEnv::new(); + let salt = Some(String::from("A")); let xdr_base64_build_only = deploy_contract( sandbox, HELLO_WORLD, DeployOptions { kind: DeployKind::BuildOnly, + salt: salt.clone(), ..Default::default() }, ) @@ -23,6 +25,7 @@ async fn simulate() { HELLO_WORLD, DeployOptions { kind: DeployKind::SimOnly, + salt: salt.clone(), ..Default::default() }, ) From a1c654b0f7db6b0d447a6025011b9c2377579c9a Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Mon, 9 Dec 2024 11:33:59 -0500 Subject: [PATCH 55/74] fix: clippy --- .github/workflows/bindings-ts.yml | 77 ++++++++++--------- .../emulator_test_support/http_transport.rs | 7 +- .../src/emulator_test_support/util.rs | 8 +- cmd/crates/stellar-ledger/src/hd_path.rs | 4 + cmd/crates/stellar-ledger/src/lib.rs | 8 +- cmd/soroban-cli/src/signer.rs | 7 +- 6 files changed, 59 insertions(+), 52 deletions(-) diff --git a/.github/workflows/bindings-ts.yml b/.github/workflows/bindings-ts.yml index 957661c4c..b480be4b3 100644 --- a/.github/workflows/bindings-ts.yml +++ b/.github/workflows/bindings-ts.yml @@ -1,42 +1,45 @@ name: bindings typescript on: - push: - branches: [main, release/**] - pull_request: + push: + branches: [main, release/**] + pull_request: jobs: - test: - name: test generated libraries - runs-on: ubuntu-22.04 - services: - rpc: - image: stellar/quickstart:testing - ports: - - 8000:8000 - env: - ENABLE_LOGS: true - NETWORK: local - ENABLE_SOROBAN_RPC: true - 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: npm ci && npm run test - working-directory: cmd/crates/soroban-spec-typescript/ts-tests + test: + name: test generated libraries + runs-on: ubuntu-22.04 + services: + rpc: + image: stellar/quickstart:testing + ports: + - 8000:8000 + env: + ENABLE_LOGS: true + NETWORK: local + ENABLE_SOROBAN_RPC: true + 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 + - name: install libudev-dev + run: | + sudo apt install -y libudev-dev + - run: cargo build + - run: rustup target add wasm32-unknown-unknown + - run: make build-test-wasms + - run: npm ci && npm run test + working-directory: cmd/crates/soroban-spec-typescript/ts-tests diff --git a/cmd/crates/stellar-ledger/src/emulator_test_support/http_transport.rs b/cmd/crates/stellar-ledger/src/emulator_test_support/http_transport.rs index c82c46cac..48ab4423d 100644 --- a/cmd/crates/stellar-ledger/src/emulator_test_support/http_transport.rs +++ b/cmd/crates/stellar-ledger/src/emulator_test_support/http_transport.rs @@ -23,7 +23,7 @@ pub enum LedgerZemuError { InnerError, } -pub struct EmulatorHttpTransport { +pub struct Emulator { url: String, } @@ -39,8 +39,9 @@ struct ZemuResponse { error: Option, } -impl EmulatorHttpTransport { +impl Emulator { #[allow(dead_code)] //this is being used in tests only + #[must_use] pub fn new(host: &str, port: u16) -> Self { Self { url: format!("http://{host}:{port}"), @@ -49,7 +50,7 @@ impl EmulatorHttpTransport { } #[async_trait] -impl Exchange for EmulatorHttpTransport { +impl Exchange for Emulator { type Error = LedgerZemuError; type AnswerType = Vec; diff --git a/cmd/crates/stellar-ledger/src/emulator_test_support/util.rs b/cmd/crates/stellar-ledger/src/emulator_test_support/util.rs index 50479d532..143cfca2b 100644 --- a/cmd/crates/stellar-ledger/src/emulator_test_support/util.rs +++ b/cmd/crates/stellar-ledger/src/emulator_test_support/util.rs @@ -6,7 +6,7 @@ use std::sync::Mutex; use crate::{Error, LedgerSigner}; use std::net::TcpListener; -use super::{http_transport::EmulatorHttpTransport, speculos::Speculos}; +use super::{http_transport::Emulator, speculos::Speculos}; use std::{collections::HashMap, time::Duration}; @@ -23,7 +23,7 @@ pub fn test_network_hash() -> Hash { Hash(sha2::Sha256::digest(TEST_NETWORK_PASSPHRASE).into()) } -pub async fn ledger(host_port: u16) -> LedgerSigner { +pub async fn ledger(host_port: u16) -> LedgerSigner { LedgerSigner::new(get_http_transport("127.0.0.1", host_port).await.unwrap()) } @@ -109,7 +109,7 @@ pub fn get_available_ports(n: usize) -> (u16, u16) { (ports[0], ports[1]) } -pub async fn get_http_transport(host: &str, port: u16) -> Result { +pub async fn get_http_transport(host: &str, port: u16) -> Result { let max_retries = 5; let mut retries = 0; let mut wait_time = Duration::from_secs(1); @@ -117,7 +117,7 @@ pub async fn get_http_transport(host: &str, port: u16) -> Result return Ok(EmulatorHttpTransport::new(host, port)), + Ok(_) => return Ok(Emulator::new(host, port)), Err(e) => { retries += 1; if retries >= max_retries { diff --git a/cmd/crates/stellar-ledger/src/hd_path.rs b/cmd/crates/stellar-ledger/src/hd_path.rs index 07ed133f1..79fca40a2 100644 --- a/cmd/crates/stellar-ledger/src/hd_path.rs +++ b/cmd/crates/stellar-ledger/src/hd_path.rs @@ -4,6 +4,7 @@ use crate::Error; pub struct HdPath(pub u32); impl HdPath { + #[must_use] pub fn depth(&self) -> u8 { let path: slip10::BIP32Path = self.into(); path.depth() @@ -23,6 +24,9 @@ impl From<&u32> for HdPath { } impl HdPath { + /// # Errors + /// + /// Could fail to convert the path to bytes pub fn to_vec(&self) -> Result, Error> { hd_path_to_bytes(&self.into()) } diff --git a/cmd/crates/stellar-ledger/src/lib.rs b/cmd/crates/stellar-ledger/src/lib.rs index fa490da0a..1cfc5d133 100644 --- a/cmd/crates/stellar-ledger/src/lib.rs +++ b/cmd/crates/stellar-ledger/src/lib.rs @@ -85,6 +85,8 @@ pub struct LedgerSigner { unsafe impl Send for LedgerSigner where T: Exchange {} unsafe impl Sync for LedgerSigner where T: Exchange {} +/// # Errors +/// Could fail to make the connection to the Ledger device pub fn native() -> Result, Error> { Ok(LedgerSigner { transport: get_transport()?, @@ -312,7 +314,7 @@ mod test { use httpmock::prelude::*; use serde_json::json; - use super::emulator_test_support::http_transport::EmulatorHttpTransport; + use super::emulator_test_support::http_transport::Emulator; use crate::Blob; use std::vec; @@ -325,8 +327,8 @@ mod test { Memo, MuxedAccount, PaymentOp, Preconditions, SequenceNumber, TransactionExt, }; - fn ledger(server: &MockServer) -> LedgerSigner { - let transport = EmulatorHttpTransport::new(&server.host(), server.port()); + fn ledger(server: &MockServer) -> LedgerSigner { + let transport = Emulator::new(&server.host(), server.port()); LedgerSigner::new(transport) } diff --git a/cmd/soroban-cli/src/signer.rs b/cmd/soroban-cli/src/signer.rs index 3ef79ecaf..ca29df0ff 100644 --- a/cmd/soroban-cli/src/signer.rs +++ b/cmd/soroban-cli/src/signer.rs @@ -212,7 +212,7 @@ pub enum SignerKind { #[cfg(not(feature = "emulator-tests"))] Ledger(Ledger), #[cfg(feature = "emulator-tests")] - Ledger(Ledger), + Ledger(Ledger), Lab, } @@ -315,10 +315,7 @@ pub async fn ledger(hd_path: u32) -> Result, Error> { #[cfg(feature = "emulator-tests")] pub async fn ledger( hd_path: u32, -) -> Result< - Ledger, - Error, -> { +) -> Result, Error> { use stellar_ledger::emulator_test_support::ledger as emulator_ledger; // port from SPECULOS_PORT ENV var let host_port: u16 = std::env::var("SPECULOS_PORT") From a04ab20f614b21577a17df8305cf4bf92b4426e4 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Mon, 9 Dec 2024 12:00:57 -0500 Subject: [PATCH 56/74] fix: reuse container for ledger tests --- .../soroban-test/tests/it/integration/emulator.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/cmd/crates/soroban-test/tests/it/integration/emulator.rs b/cmd/crates/soroban-test/tests/it/integration/emulator.rs index f53611139..25ba5367c 100644 --- a/cmd/crates/soroban-test/tests/it/integration/emulator.rs +++ b/cmd/crates/soroban-test/tests/it/integration/emulator.rs @@ -5,16 +5,17 @@ use std::sync::Arc; use stellar_ledger::emulator_test_support::*; -use test_case::test_case; - use crate::integration::util::{self, deploy_contract, DeployKind, HELLO_WORLD}; -#[test_case("nanos", 0; "when the device is NanoS")] -#[test_case("nanox", 1; "when the device is NanoX")] -#[test_case("nanosp",2; "when the device is NanoS Plus")] #[tokio::test] -async fn test_get_public_key(ledger_device_model: &str, hd_path: u32) { +async fn test_ledger_signer() { let sandbox = Arc::new(TestEnv::new()); + test_signer(&sandbox, "nanos", 0).await; + test_signer(&sandbox, "nanox", 1).await; + test_signer(&sandbox, "nanosp", 2).await; +} + +async fn test_signer(sandbox: &Arc, ledger_device_model: &str, hd_path: u32) { let container = TestEnv::speculos_container(ledger_device_model).await; let host_port = container.get_host_port_ipv4(9998).await.unwrap(); let ui_host_port = container.get_host_port_ipv4(5000).await.unwrap(); From 428863026466e0f581dcbb4bbbd932d0e417c121 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Mon, 9 Dec 2024 16:19:01 -0500 Subject: [PATCH 57/74] Remove unused bin/secret --- cmd/soroban-cli/Cargo.toml | 4 ---- cmd/soroban-cli/src/bin/secret.rs | 17 ----------------- 2 files changed, 21 deletions(-) delete mode 100644 cmd/soroban-cli/src/bin/secret.rs diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index 0d6da167d..3d366464b 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -20,10 +20,6 @@ path = "src/bin/stellar.rs" name = "soroban" path = "src/bin/soroban.rs" -[[bin]] -name = "secret" -path = "src/bin/secret.rs" - [package.metadata.binstall] pkg-url = "{ repo }/releases/download/v{ version }/{ name }-{ version }-{ target }{ archive-suffix }" bin-dir = "{ bin }{ binary-ext }" diff --git a/cmd/soroban-cli/src/bin/secret.rs b/cmd/soroban-cli/src/bin/secret.rs deleted file mode 100644 index 4fc43ec6e..000000000 --- a/cmd/soroban-cli/src/bin/secret.rs +++ /dev/null @@ -1,17 +0,0 @@ -use soroban_cli::signer::keyring::StellarEntry; - -fn main() { - let entry = StellarEntry::new("test").unwrap(); - if let Ok(key) = entry.get_public_key() { - println!("{key}"); - return; - }; - - let secret = soroban_cli::config::secret::Secret::from_seed(None).unwrap(); - let pub_key = secret.public_key(None).unwrap(); - let key_pair = secret.key_pair(None).unwrap(); - entry.set_password(key_pair.as_bytes()).unwrap(); - let pub_key_2 = entry.get_public_key().unwrap(); - assert_eq!(pub_key, pub_key_2); - println!("{pub_key} == {pub_key_2}"); -} From 6143b84bbf03b37e967552e45caaa8adf77f66a7 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 10 Dec 2024 15:28:32 -0500 Subject: [PATCH 58/74] fix: clippy and run each test independently --- .github/workflows/rpc-tests.yml | 6 +++++- .../tests/it/integration/emulator.rs | 18 +++++++++++++++--- cmd/soroban-cli/src/signer.rs | 6 +++--- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/.github/workflows/rpc-tests.yml b/.github/workflows/rpc-tests.yml index 14fecb2f1..1c0228556 100644 --- a/.github/workflows/rpc-tests.yml +++ b/.github/workflows/rpc-tests.yml @@ -12,6 +12,8 @@ jobs: test: name: test RPC runs-on: ubuntu-22.04 + env: + CI_TESTS: true services: rpc: image: stellar/quickstart:testing @@ -46,4 +48,6 @@ jobs: sudo apt install -y libudev-dev - run: cargo build --features emulator-tests - run: SOROBAN_PORT=8000 cargo test --features it --package soroban-test --test it -- integration - - run: SOROBAN_PORT=8000 cargo test --features it --features emulator-tests --package soroban-test --test it -- integration::emulator + - run: SOROBAN_PORT=8000 cargo test --features it --features emulator-tests --package soroban-test --test it -- integration::emulator::nanos + - run: SOROBAN_PORT=8000 cargo test --features it --features emulator-tests --package soroban-test --test it -- integration::emulator::nanox + - run: SOROBAN_PORT=8000 cargo test --features it --features emulator-tests --package soroban-test --test it -- integration::emulator::nanosp diff --git a/cmd/crates/soroban-test/tests/it/integration/emulator.rs b/cmd/crates/soroban-test/tests/it/integration/emulator.rs index 25ba5367c..9125d60c2 100644 --- a/cmd/crates/soroban-test/tests/it/integration/emulator.rs +++ b/cmd/crates/soroban-test/tests/it/integration/emulator.rs @@ -8,13 +8,25 @@ use stellar_ledger::emulator_test_support::*; use crate::integration::util::{self, deploy_contract, DeployKind, HELLO_WORLD}; #[tokio::test] -async fn test_ledger_signer() { +async fn nanos() { let sandbox = Arc::new(TestEnv::new()); test_signer(&sandbox, "nanos", 0).await; test_signer(&sandbox, "nanox", 1).await; test_signer(&sandbox, "nanosp", 2).await; } +#[tokio::test] +async fn nanox() { + let sandbox = Arc::new(TestEnv::new()); + test_signer(&sandbox, "nanox", 1).await; +} + +#[tokio::test] +async fn nanosp() { + let sandbox = Arc::new(TestEnv::new()); + test_signer(&sandbox, "nanosp", 2).await; +} + async fn test_signer(sandbox: &Arc, ledger_device_model: &str, hd_path: u32) { let container = TestEnv::speculos_container(ledger_device_model).await; let host_port = container.get_host_port_ipv4(9998).await.unwrap(); @@ -40,7 +52,7 @@ async fn test_signer(sandbox: &Arc, ledger_device_model: &str, hd_path: .success(); let tx_simulated = deploy_contract( - &sandbox, + sandbox, contract, crate::integration::util::DeployOptions { kind: DeployKind::SimOnly, @@ -50,7 +62,7 @@ async fn test_signer(sandbox: &Arc, ledger_device_model: &str, hd_path: ) .await; let sign = tokio::task::spawn_blocking({ - let sandbox = Arc::clone(&sandbox); + let sandbox = Arc::clone(sandbox); move || { sandbox diff --git a/cmd/soroban-cli/src/signer.rs b/cmd/soroban-cli/src/signer.rs index ca29df0ff..bf3b385f4 100644 --- a/cmd/soroban-cli/src/signer.rs +++ b/cmd/soroban-cli/src/signer.rs @@ -8,7 +8,7 @@ use crate::xdr::{ SorobanAuthorizedFunction, SorobanCredentials, Transaction, TransactionEnvelope, TransactionV1Envelope, Uint256, VecM, WriteXdr, }; -use stellar_ledger::{Blob as _, Exchange, LedgerSigner, TransportNativeHID}; +use stellar_ledger::{Blob as _, Exchange, LedgerSigner}; use crate::{config::network::Network, print::Print, utils::transaction_hash}; @@ -210,7 +210,7 @@ pub struct Signer { pub enum SignerKind { Local(LocalKey), #[cfg(not(feature = "emulator-tests"))] - Ledger(Ledger), + Ledger(Ledger), #[cfg(feature = "emulator-tests")] Ledger(Ledger), Lab, @@ -304,7 +304,7 @@ impl Ledger { } #[cfg(not(feature = "emulator-tests"))] -pub async fn ledger(hd_path: u32) -> Result, Error> { +pub async fn ledger(hd_path: u32) -> Result, Error> { let signer = stellar_ledger::native()?; Ok(Ledger { index: hd_path, From 9a136739d35dfa6c3bb9bf1cab0462f3c9841aa3 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 10 Dec 2024 15:50:18 -0500 Subject: [PATCH 59/74] fix: run each ledger test by themselves --- .github/workflows/rpc-tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/rpc-tests.yml b/.github/workflows/rpc-tests.yml index 1c0228556..e7c240c3c 100644 --- a/.github/workflows/rpc-tests.yml +++ b/.github/workflows/rpc-tests.yml @@ -48,6 +48,6 @@ jobs: sudo apt install -y libudev-dev - run: cargo build --features emulator-tests - run: SOROBAN_PORT=8000 cargo test --features it --package soroban-test --test it -- integration - - run: SOROBAN_PORT=8000 cargo test --features it --features emulator-tests --package soroban-test --test it -- integration::emulator::nanos - - run: SOROBAN_PORT=8000 cargo test --features it --features emulator-tests --package soroban-test --test it -- integration::emulator::nanox - - run: SOROBAN_PORT=8000 cargo test --features it --features emulator-tests --package soroban-test --test it -- integration::emulator::nanosp + - run: SOROBAN_PORT=8000 cargo test --features it --features emulator-tests --package soroban-test --test it -- integration::emulator::nanos --exact + - run: SOROBAN_PORT=8000 cargo test --features it --features emulator-tests --package soroban-test --test it -- integration::emulator::nanox --exact + - run: SOROBAN_PORT=8000 cargo test --features it --features emulator-tests --package soroban-test --test it -- integration::emulator::nanosp --exact From a210205f7b41f2e45eacff39c36351121838c018 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Wed, 11 Dec 2024 13:36:41 -0500 Subject: [PATCH 60/74] fix: separate into separate job --- .github/workflows/rpc-tests.yml | 40 ++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rpc-tests.yml b/.github/workflows/rpc-tests.yml index e7c240c3c..325c571d5 100644 --- a/.github/workflows/rpc-tests.yml +++ b/.github/workflows/rpc-tests.yml @@ -46,8 +46,46 @@ jobs: - name: install libudev-dev run: | sudo apt install -y libudev-dev - - run: cargo build --features emulator-tests + - run: cargo build - run: SOROBAN_PORT=8000 cargo test --features it --package soroban-test --test it -- integration + test-ledger: + name: test RPC + runs-on: ubuntu-22.04 + env: + CI_TESTS: true + services: + rpc: + 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: rustup target add wasm32-unknown-unknown + - run: make build-test-wasms + - name: install libudev-dev + run: | + sudo apt install -y libudev-dev + - run: cargo build --features emulator-tests - run: SOROBAN_PORT=8000 cargo test --features it --features emulator-tests --package soroban-test --test it -- integration::emulator::nanos --exact - run: SOROBAN_PORT=8000 cargo test --features it --features emulator-tests --package soroban-test --test it -- integration::emulator::nanox --exact - run: SOROBAN_PORT=8000 cargo test --features it --features emulator-tests --package soroban-test --test it -- integration::emulator::nanosp --exact From 64cd37b6771367041ed37a459e014c799405f355 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Fri, 13 Dec 2024 14:54:28 -0500 Subject: [PATCH 61/74] Apply suggestions from code review Co-authored-by: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> --- cmd/soroban-cli/src/commands/tx/args.rs | 4 ++-- cmd/soroban-cli/src/config/locator.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/soroban-cli/src/commands/tx/args.rs b/cmd/soroban-cli/src/commands/tx/args.rs index c00da6fea..8c0ebb387 100644 --- a/cmd/soroban-cli/src/commands/tx/args.rs +++ b/cmd/soroban-cli/src/commands/tx/args.rs @@ -113,11 +113,11 @@ impl Args { Ok(self.config.source_account()?) } - pub fn reslove_muxed_address(&self, address: &Address) -> Result { + pub fn resolve_muxed_address(&self, address: &Address) -> Result { Ok(address.resolve_muxed_account(&self.config.locator, self.config.hd_path)?) } - pub fn reslove_account_id(&self, address: &Address) -> Result { + pub fn resolve_account_id(&self, address: &Address) -> Result { Ok(address .resolve_muxed_account(&self.config.locator, self.config.hd_path)? .account_id()) diff --git a/cmd/soroban-cli/src/config/locator.rs b/cmd/soroban-cli/src/config/locator.rs index 68a95f628..930da0ece 100644 --- a/cmd/soroban-cli/src/config/locator.rs +++ b/cmd/soroban-cli/src/config/locator.rs @@ -257,7 +257,7 @@ impl Args { } } - pub fn get_private_key(&self, key_or_name: &str) -> Result { + pub fn get_secret_key(&self, key_or_name: &str) -> Result { match self.read_key(key_or_name)? { Key::Secret(s) => Ok(s), _ => Err(Error::SecretKeyOnly(key_or_name.to_string())), From d4ba93d1dbd902826b76be9b8b50cfe828926337 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Fri, 13 Dec 2024 14:58:48 -0500 Subject: [PATCH 62/74] Update cmd/soroban-cli/src/config/locator.rs Co-authored-by: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> --- cmd/soroban-cli/src/config/locator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/soroban-cli/src/config/locator.rs b/cmd/soroban-cli/src/config/locator.rs index 930da0ece..e769f1314 100644 --- a/cmd/soroban-cli/src/config/locator.rs +++ b/cmd/soroban-cli/src/config/locator.rs @@ -179,7 +179,7 @@ impl Args { self.write_key(name, &public_key.into()) } - pub fn write_key(&self, name: &str, public_key: &Key) -> Result<(), Error> { + pub fn write_key(&self, name: &str, key: &Key) -> Result<(), Error> { KeyType::Identity.write(name, public_key, &self.config_dir()?) } From 318f579b29c11fdee15d5ed305759b3c69b276ee Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Fri, 13 Dec 2024 15:03:50 -0500 Subject: [PATCH 63/74] fix: refactor with renames from PR --- cmd/soroban-cli/src/commands/tx/args.rs | 2 +- cmd/soroban-cli/src/commands/tx/new/account_merge.rs | 2 +- cmd/soroban-cli/src/commands/tx/new/create_account.rs | 2 +- cmd/soroban-cli/src/commands/tx/new/payment.rs | 2 +- cmd/soroban-cli/src/commands/tx/new/set_options.rs | 2 +- cmd/soroban-cli/src/commands/tx/new/set_trustline_flags.rs | 2 +- cmd/soroban-cli/src/config/address.rs | 2 +- cmd/soroban-cli/src/config/locator.rs | 2 +- cmd/soroban-cli/src/config/sign_with.rs | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cmd/soroban-cli/src/commands/tx/args.rs b/cmd/soroban-cli/src/commands/tx/args.rs index 8c0ebb387..5d2099d26 100644 --- a/cmd/soroban-cli/src/commands/tx/args.rs +++ b/cmd/soroban-cli/src/commands/tx/args.rs @@ -130,7 +130,7 @@ impl Args { op_source: Option<&address::Address>, ) -> Result { let source_account = op_source - .map(|a| self.reslove_muxed_address(a)) + .map(|a| self.resolve_muxed_address(a)) .transpose()?; let op = xdr::Operation { source_account, diff --git a/cmd/soroban-cli/src/commands/tx/new/account_merge.rs b/cmd/soroban-cli/src/commands/tx/new/account_merge.rs index e52e1fc42..c087c6b13 100644 --- a/cmd/soroban-cli/src/commands/tx/new/account_merge.rs +++ b/cmd/soroban-cli/src/commands/tx/new/account_merge.rs @@ -22,7 +22,7 @@ impl TryFrom<&Cmd> for xdr::OperationBody { type Error = tx::args::Error; fn try_from(cmd: &Cmd) -> Result { Ok(xdr::OperationBody::AccountMerge( - cmd.tx.reslove_muxed_address(&cmd.op.account)?, + cmd.tx.resolve_muxed_address(&cmd.op.account)?, )) } } 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 d062a07d5..13bcac4d4 100644 --- a/cmd/soroban-cli/src/commands/tx/new/create_account.rs +++ b/cmd/soroban-cli/src/commands/tx/new/create_account.rs @@ -25,7 +25,7 @@ impl TryFrom<&Cmd> for xdr::OperationBody { type Error = tx::args::Error; fn try_from(cmd: &Cmd) -> Result { Ok(xdr::OperationBody::CreateAccount(xdr::CreateAccountOp { - destination: cmd.tx.reslove_account_id(&cmd.op.destination)?, + destination: cmd.tx.resolve_account_id(&cmd.op.destination)?, starting_balance: cmd.op.starting_balance.into(), })) } diff --git a/cmd/soroban-cli/src/commands/tx/new/payment.rs b/cmd/soroban-cli/src/commands/tx/new/payment.rs index 4b472e141..3599d7010 100644 --- a/cmd/soroban-cli/src/commands/tx/new/payment.rs +++ b/cmd/soroban-cli/src/commands/tx/new/payment.rs @@ -28,7 +28,7 @@ impl TryFrom<&Cmd> for xdr::OperationBody { type Error = tx::args::Error; fn try_from(cmd: &Cmd) -> Result { Ok(xdr::OperationBody::Payment(xdr::PaymentOp { - destination: cmd.tx.reslove_muxed_address(&cmd.op.destination)?, + destination: cmd.tx.resolve_muxed_address(&cmd.op.destination)?, asset: cmd.op.asset.clone().into(), amount: cmd.op.amount.into(), })) diff --git a/cmd/soroban-cli/src/commands/tx/new/set_options.rs b/cmd/soroban-cli/src/commands/tx/new/set_options.rs index 42a0767b5..cf38bf574 100644 --- a/cmd/soroban-cli/src/commands/tx/new/set_options.rs +++ b/cmd/soroban-cli/src/commands/tx/new/set_options.rs @@ -120,7 +120,7 @@ impl TryFrom<&Cmd> for xdr::OperationBody { let inflation_dest: Option = cmd .inflation_dest .as_ref() - .map(|dest| tx.reslove_account_id(dest)) + .map(|dest| tx.resolve_account_id(dest)) .transpose()?; Ok(xdr::OperationBody::SetOptions(xdr::SetOptionsOp { inflation_dest, 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 3e6465115..7a175915e 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 @@ -68,7 +68,7 @@ impl TryFrom<&Cmd> for xdr::OperationBody { Ok(xdr::OperationBody::SetTrustLineFlags( xdr::SetTrustLineFlagsOp { - trustor: cmd.tx.reslove_account_id(&cmd.op.trustor)?, + trustor: cmd.tx.resolve_account_id(&cmd.op.trustor)?, asset: cmd.op.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 index ac2adc762..6975d9e53 100644 --- a/cmd/soroban-cli/src/config/address.rs +++ b/cmd/soroban-cli/src/config/address.rs @@ -54,7 +54,7 @@ impl Address { pub fn resolve_secret(&self, locator: &locator::Args) -> Result { match &self { - Address::AliasOrSecret(alias) => Ok(locator.get_private_key(alias)?), + Address::AliasOrSecret(alias) => Ok(locator.get_secret_key(alias)?), Address::MuxedAccount(muxed_account) => Err(Error::CannotSign(muxed_account.clone())), } } diff --git a/cmd/soroban-cli/src/config/locator.rs b/cmd/soroban-cli/src/config/locator.rs index e769f1314..4ab698111 100644 --- a/cmd/soroban-cli/src/config/locator.rs +++ b/cmd/soroban-cli/src/config/locator.rs @@ -180,7 +180,7 @@ impl Args { } pub fn write_key(&self, name: &str, key: &Key) -> Result<(), Error> { - KeyType::Identity.write(name, public_key, &self.config_dir()?) + KeyType::Identity.write(name, key, &self.config_dir()?) } pub fn write_network(&self, name: &str, network: &Network) -> Result<(), Error> { diff --git a/cmd/soroban-cli/src/config/sign_with.rs b/cmd/soroban-cli/src/config/sign_with.rs index c9d164914..baac7a528 100644 --- a/cmd/soroban-cli/src/config/sign_with.rs +++ b/cmd/soroban-cli/src/config/sign_with.rs @@ -64,7 +64,7 @@ impl Args { } } else { let key_or_name = self.sign_with_key.as_deref().ok_or(Error::NoSignWithKey)?; - let secret = locator.get_private_key(key_or_name)?; + let secret = locator.get_secret_key(key_or_name)?; secret.signer(self.hd_path, print)? }; Ok(signer.sign_tx_env(tx, network)?) From cb3f4997cf474814ecdfe8e3086d031e9ee1f54a Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Fri, 13 Dec 2024 16:55:53 -0500 Subject: [PATCH 64/74] fix: build local transaction to test valid signature instead of relying on RPC --- .github/workflows/ledger-emulator.yml | 41 ++++---- .github/workflows/rpc-tests.yml | 41 -------- cmd/crates/soroban-test/tests/it/emulator.rs | 88 +++++++++++++++++ .../soroban-test/tests/it/integration.rs | 3 - .../tests/it/integration/emulator.rs | 98 ------------------- cmd/crates/soroban-test/tests/it/main.rs | 4 +- 6 files changed, 113 insertions(+), 162 deletions(-) create mode 100644 cmd/crates/soroban-test/tests/it/emulator.rs delete mode 100644 cmd/crates/soroban-test/tests/it/integration/emulator.rs diff --git a/.github/workflows/ledger-emulator.yml b/.github/workflows/ledger-emulator.yml index 7a3068d2e..907a55e7f 100644 --- a/.github/workflows/ledger-emulator.yml +++ b/.github/workflows/ledger-emulator.yml @@ -1,28 +1,31 @@ name: Ledger Emulator Tests on: - push: - branches: [main, release/**] - pull_request: + push: + branches: [main, release/**] + pull_request: concurrency: - group: ${{ github.workflow }}-${{ github.ref_protected == 'true' && github.sha || github.ref }} - cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.ref_protected == 'true' && github.sha || github.ref }} + cancel-in-progress: true defaults: - run: - shell: bash + run: + shell: bash jobs: - emulator-tests: - runs-on: ubuntu-latest - env: - CI_TESTS: true - steps: - - uses: actions/checkout@v4 - - uses: stellar/actions/rust-cache@main - - name: install libudev-dev - run: | - sudo apt install -y libudev-dev - - run: | - cargo test --manifest-path cmd/crates/stellar-ledger/Cargo.toml --features "emulator-tests" -- --nocapture \ No newline at end of file + emulator-tests: + runs-on: ubuntu-latest + env: + CI_TESTS: true + steps: + - uses: actions/checkout@v4 + - uses: stellar/actions/rust-cache@main + - name: install libudev-dev + run: | + sudo apt install -y libudev-dev + - run: | + cargo test --manifest-path cmd/crates/stellar-ledger/Cargo.toml --features "emulator-tests" -- --nocapture + - run: cargo build --features emulator-tests + - run: | + cargo test --features emulator-tests --package soroban-test --test it -- emulator diff --git a/.github/workflows/rpc-tests.yml b/.github/workflows/rpc-tests.yml index 325c571d5..20e7b4d7b 100644 --- a/.github/workflows/rpc-tests.yml +++ b/.github/workflows/rpc-tests.yml @@ -48,44 +48,3 @@ jobs: sudo apt install -y libudev-dev - run: cargo build - run: SOROBAN_PORT=8000 cargo test --features it --package soroban-test --test it -- integration - test-ledger: - name: test RPC - runs-on: ubuntu-22.04 - env: - CI_TESTS: true - services: - rpc: - 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: rustup target add wasm32-unknown-unknown - - run: make build-test-wasms - - name: install libudev-dev - run: | - sudo apt install -y libudev-dev - - run: cargo build --features emulator-tests - - run: SOROBAN_PORT=8000 cargo test --features it --features emulator-tests --package soroban-test --test it -- integration::emulator::nanos --exact - - run: SOROBAN_PORT=8000 cargo test --features it --features emulator-tests --package soroban-test --test it -- integration::emulator::nanox --exact - - run: SOROBAN_PORT=8000 cargo test --features it --features emulator-tests --package soroban-test --test it -- integration::emulator::nanosp --exact diff --git a/cmd/crates/soroban-test/tests/it/emulator.rs b/cmd/crates/soroban-test/tests/it/emulator.rs new file mode 100644 index 000000000..711d6dc8c --- /dev/null +++ b/cmd/crates/soroban-test/tests/it/emulator.rs @@ -0,0 +1,88 @@ +use stellar_ledger::Blob; + +use soroban_test::{AssertExt, TestEnv}; +use std::sync::Arc; + +use stellar_ledger::emulator_test_support::*; + +use soroban_cli::{ + tx::builder::TxExt, + xdr::{self, Limits, OperationBody, ReadXdr, TransactionEnvelope, WriteXdr}, +}; + +use test_case::test_case; + +#[test_case("nanos"; "when the device is NanoS")] +#[test_case("nanox"; "when the device is NanoX")] +#[test_case("nanosp"; "when the device is NanoS Plus")] +#[tokio::test] +async fn test_signer(ledger_device_model: &str) { + let sandbox = Arc::new(TestEnv::new()); + let container = TestEnv::speculos_container(ledger_device_model).await; + let host_port = container.get_host_port_ipv4(9998).await.unwrap(); + let ui_host_port = container.get_host_port_ipv4(5000).await.unwrap(); + + let ledger = ledger(host_port).await; + + let key = ledger.get_public_key(&0.into()).await.unwrap(); + + let verifying_key = ed25519_dalek::VerifyingKey::from_bytes(&key.0).unwrap(); + let body: OperationBody = + (&soroban_cli::commands::tx::new::bump_sequence::Args { bump_to: 100 }).into(); + let operation = xdr::Operation { + body, + source_account: None, + }; + let source_account = xdr::MuxedAccount::Ed25519(key.0.into()); + let tx_env: TransactionEnvelope = + xdr::Transaction::new_tx(source_account, 100, 100, operation).into(); + let tx_env = tx_env.to_xdr_base64(Limits::none()).unwrap(); + + let hash: xdr::Hash = sandbox + .new_assert_cmd("tx") + .arg("hash") + .write_stdin(tx_env.as_bytes()) + .assert() + .success() + .stdout_as_str() + .parse() + .unwrap(); + + let sign = tokio::task::spawn_blocking({ + let sandbox = Arc::clone(&sandbox); + + move || { + sandbox + .new_assert_cmd("tx") + .arg("sign") + .arg("--sign-with-ledger") + .write_stdin(tx_env.as_bytes()) + .env("SPECULOS_PORT", host_port.to_string()) + .env("RUST_LOGS", "trace") + .assert() + .success() + .stdout_as_str() + } + }); + + let approve = tokio::task::spawn(approve_tx_hash_signature( + ui_host_port, + ledger_device_model.to_string(), + )); + + let response = sign.await.unwrap(); + approve.await.unwrap(); + let txn_env = + xdr::TransactionEnvelope::from_xdr_base64(&response, xdr::Limits::none()).unwrap(); + let xdr::TransactionEnvelope::Tx(tx_env) = txn_env else { + panic!("expected Tx") + }; + let signatures = tx_env.signatures.to_vec(); + let signature = signatures[0].signature.to_vec(); + verifying_key + .verify_strict( + &hash.0, + &ed25519_dalek::Signature::from_slice(&signature).unwrap(), + ) + .unwrap(); +} diff --git a/cmd/crates/soroban-test/tests/it/integration.rs b/cmd/crates/soroban-test/tests/it/integration.rs index a8a637b20..3ec0d61ed 100644 --- a/cmd/crates/soroban-test/tests/it/integration.rs +++ b/cmd/crates/soroban-test/tests/it/integration.rs @@ -9,6 +9,3 @@ mod snapshot; mod tx; mod util; mod wrap; - -#[cfg(feature = "emulator-tests")] -mod emulator; diff --git a/cmd/crates/soroban-test/tests/it/integration/emulator.rs b/cmd/crates/soroban-test/tests/it/integration/emulator.rs deleted file mode 100644 index 9125d60c2..000000000 --- a/cmd/crates/soroban-test/tests/it/integration/emulator.rs +++ /dev/null @@ -1,98 +0,0 @@ -use stellar_ledger::Blob; - -use soroban_test::{AssertExt, TestEnv}; -use std::sync::Arc; - -use stellar_ledger::emulator_test_support::*; - -use crate::integration::util::{self, deploy_contract, DeployKind, HELLO_WORLD}; - -#[tokio::test] -async fn nanos() { - let sandbox = Arc::new(TestEnv::new()); - test_signer(&sandbox, "nanos", 0).await; - test_signer(&sandbox, "nanox", 1).await; - test_signer(&sandbox, "nanosp", 2).await; -} - -#[tokio::test] -async fn nanox() { - let sandbox = Arc::new(TestEnv::new()); - test_signer(&sandbox, "nanox", 1).await; -} - -#[tokio::test] -async fn nanosp() { - let sandbox = Arc::new(TestEnv::new()); - test_signer(&sandbox, "nanosp", 2).await; -} - -async fn test_signer(sandbox: &Arc, ledger_device_model: &str, hd_path: u32) { - let container = TestEnv::speculos_container(ledger_device_model).await; - let host_port = container.get_host_port_ipv4(9998).await.unwrap(); - let ui_host_port = container.get_host_port_ipv4(5000).await.unwrap(); - - let ledger = ledger(host_port).await; - - let key = ledger.get_public_key(&hd_path.into()).await.unwrap(); - let contract = match hd_path { - 0 => HELLO_WORLD, - 1 => util::CUSTOM_ACCOUNT, - 2 => util::CUSTOM_TYPES, - _ => panic!("Invalid hd_path"), - }; - let account = key.to_string(); - sandbox.fund_account(&account); - - sandbox - .new_assert_cmd("contract") - .arg("install") - .args(["--wasm", contract.path().as_os_str().to_str().unwrap()]) - .assert() - .success(); - - let tx_simulated = deploy_contract( - sandbox, - contract, - crate::integration::util::DeployOptions { - kind: DeployKind::SimOnly, - deployer: Some(account), - ..Default::default() - }, - ) - .await; - let sign = tokio::task::spawn_blocking({ - let sandbox = Arc::clone(sandbox); - - move || { - sandbox - .new_assert_cmd("tx") - .arg("sign") - .arg("--sign-with-ledger") - .arg("--hd-path") - .arg(hd_path.to_string()) - .write_stdin(tx_simulated.as_bytes()) - .env("SPECULOS_PORT", host_port.to_string()) - .env("RUST_LOGS", "trace") - .assert() - .success() - .stdout_as_str() - } - }); - let approve = tokio::task::spawn(approve_tx_hash_signature( - ui_host_port, - ledger_device_model.to_string(), - )); - - let response = sign.await.unwrap(); - approve.await.unwrap(); - - sandbox - .clone() - .new_assert_cmd("tx") - .arg("send") - .write_stdin(response.as_bytes()) - .assert() - .success() - .stdout(predicates::str::contains("SUCCESS")); -} diff --git a/cmd/crates/soroban-test/tests/it/main.rs b/cmd/crates/soroban-test/tests/it/main.rs index e06c1a47d..045e10205 100644 --- a/cmd/crates/soroban-test/tests/it/main.rs +++ b/cmd/crates/soroban-test/tests/it/main.rs @@ -1,9 +1,11 @@ mod arg_parsing; mod build; mod config; +#[cfg(feature = "emulator-tests")] +mod emulator; mod help; mod init; -// #[cfg(feature = "it")] +#[cfg(feature = "it")] mod integration; mod plugin; mod util; From f2d8fe70a1253e07a0b3dc7be58f162b69a7d4b2 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Mon, 16 Dec 2024 09:43:14 -0500 Subject: [PATCH 65/74] fix: split tests to different hd_pahs --- cmd/crates/soroban-test/tests/it/emulator.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/cmd/crates/soroban-test/tests/it/emulator.rs b/cmd/crates/soroban-test/tests/it/emulator.rs index 711d6dc8c..b959c541c 100644 --- a/cmd/crates/soroban-test/tests/it/emulator.rs +++ b/cmd/crates/soroban-test/tests/it/emulator.rs @@ -12,11 +12,11 @@ use soroban_cli::{ use test_case::test_case; -#[test_case("nanos"; "when the device is NanoS")] -#[test_case("nanox"; "when the device is NanoX")] -#[test_case("nanosp"; "when the device is NanoS Plus")] +#[test_case("nanos", 0; "when the device is NanoS")] +#[test_case("nanox", 1; "when the device is NanoX")] +#[test_case("nanosp", 2; "when the device is NanoS Plus")] #[tokio::test] -async fn test_signer(ledger_device_model: &str) { +async fn test_signer(ledger_device_model: &str, hd_path: u32) { let sandbox = Arc::new(TestEnv::new()); let container = TestEnv::speculos_container(ledger_device_model).await; let host_port = container.get_host_port_ipv4(9998).await.unwrap(); @@ -24,7 +24,7 @@ async fn test_signer(ledger_device_model: &str) { let ledger = ledger(host_port).await; - let key = ledger.get_public_key(&0.into()).await.unwrap(); + let key = ledger.get_public_key(&hd_path.into()).await.unwrap(); let verifying_key = ed25519_dalek::VerifyingKey::from_bytes(&key.0).unwrap(); let body: OperationBody = @@ -56,6 +56,8 @@ async fn test_signer(ledger_device_model: &str) { .new_assert_cmd("tx") .arg("sign") .arg("--sign-with-ledger") + .arg("--hd-path") + .arg(hd_path.to_string()) .write_stdin(tx_env.as_bytes()) .env("SPECULOS_PORT", host_port.to_string()) .env("RUST_LOGS", "trace") From 6212d318d81362efa8c5573e4075b89756ed3648 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Mon, 16 Dec 2024 12:39:47 -0500 Subject: [PATCH 66/74] fix: CI install needed deps --- .github/workflows/binaries.yml | 5 ++++- .github/workflows/rust.yml | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index d913cf23d..847d4f123 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -46,7 +46,10 @@ jobs: - run: rustup target add ${{ matrix.sys.target }} - if: matrix.sys.target == 'aarch64-unknown-linux-gnu' - run: sudo apt-get update && sudo apt-get -y install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libudev-dev + run: sudo apt-get update && sudo apt-get -y install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libudev-dev libssl-dev + + - if: matrix.sys.target == 'x86_64-unknown-linux-gnu' + run: sudo apt-get update && sudo apt-get -y install libudev-dev libssl-dev - name: Setup vars run: | diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f3c9b1920..a9712dc5a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -49,6 +49,7 @@ jobs: - uses: actions/checkout@v4 - uses: stellar/actions/rust-cache@main - run: rustup update + - run: sudo apt install -y libudev-dev - run: make generate-full-help-doc - run: git add -N . && git diff HEAD --exit-code From 24269ce1db89b88ace0bff395e0f0d105606f106 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Fri, 20 Dec 2024 16:34:55 -0500 Subject: [PATCH 67/74] Fix after merging with main --- cmd/soroban-cli/src/commands/keys/address.rs | 28 +++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/cmd/soroban-cli/src/commands/keys/address.rs b/cmd/soroban-cli/src/commands/keys/address.rs index 145d8c30a..aa5a2940d 100644 --- a/cmd/soroban-cli/src/commands/keys/address.rs +++ b/cmd/soroban-cli/src/commands/keys/address.rs @@ -1,9 +1,6 @@ use clap::arg; -use crate::{ - commands::config::{address, locator, secret}, - xdr, -}; +use crate::commands::config::{address, locator, secret}; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -24,7 +21,7 @@ pub enum Error { #[group(skip)] pub struct Cmd { /// Name of identity to lookup, default test identity used if not provided - pub name: address::Address, + pub name: String, /// If identity is a seed phrase use this hd path, default is 0 #[arg(long)] @@ -40,15 +37,20 @@ impl Cmd { Ok(()) } + pub fn private_key(&self) -> Result { + Ok(self + .locator + .read_identity(&self.name)? + .key_pair(self.hd_path)?) + } + pub fn public_key(&self) -> Result { - match self - .name - .resolve_muxed_account(&self.locator, self.hd_path)? - { - xdr::MuxedAccount::Ed25519(pk) => Ok(stellar_strkey::ed25519::PublicKey(pk.0)), - xdr::MuxedAccount::MuxedEd25519(xdr::MuxedAccountMed25519 { ed25519, .. }) => { - Ok(stellar_strkey::ed25519::PublicKey(ed25519.0)) - } + if let Ok(key) = stellar_strkey::ed25519::PublicKey::from_string(&self.name) { + Ok(key) + } else { + Ok(stellar_strkey::ed25519::PublicKey::from_payload( + self.private_key()?.verifying_key().as_bytes(), + )?) } } } From e95fa3a38b53cfb09045f70de0adc6c91c09773d Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Fri, 20 Dec 2024 16:59:25 -0500 Subject: [PATCH 68/74] Apply suggestion from code review Co-authored-by: Willem Wyndham --- cmd/soroban-cli/src/signer/keyring.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/soroban-cli/src/signer/keyring.rs b/cmd/soroban-cli/src/signer/keyring.rs index b5a63a398..4e6b19c99 100644 --- a/cmd/soroban-cli/src/signer/keyring.rs +++ b/cmd/soroban-cli/src/signer/keyring.rs @@ -31,7 +31,7 @@ impl StellarEntry { Ok(()) } - pub fn get_password(&self) -> Result, Error> { + fn get_password(&self) -> Result, Error> { Ok(base64.decode(self.keyring.get_password()?)?) } From 95d8b329a233b0fb7be7e74cb1daebe47b2c78d6 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Mon, 23 Dec 2024 09:47:44 -0500 Subject: [PATCH 69/74] Limit key name length --- cmd/soroban-cli/src/config/address.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/cmd/soroban-cli/src/config/address.rs b/cmd/soroban-cli/src/config/address.rs index 36cc90f42..dadd8853f 100644 --- a/cmd/soroban-cli/src/config/address.rs +++ b/cmd/soroban-cli/src/config/address.rs @@ -28,7 +28,11 @@ pub enum Error { Secret(#[from] secret::Error), #[error("Address cannot be used to sign {0}")] CannotSign(xdr::MuxedAccount), - #[error("Invalid key name: {0}\n only alphanumeric characters, `_`and `-` are allowed")] + #[error("Invalid key name: {0}\n only alphanumeric characters, underscores (_), and hyphens (-) are allowed.")] + InvalidKeyNameCharacters(String), + #[error("Invalid key name: {0}\n keys cannot exceed 250 characters")] + InvalidKeyNameLength(String), + #[error("Invalid key name: {0}\n keys cannot be the word \"ledger\"")] InvalidKeyName(String), } @@ -93,11 +97,14 @@ impl std::str::FromStr for KeyName { type Err = Error; fn from_str(s: &str) -> Result { if !s.chars().all(allowed_char) { - return Err(Error::InvalidKeyName(s.to_string())); + return Err(Error::InvalidKeyNameCharacters(s.to_string())); } if s == "ledger" { return Err(Error::InvalidKeyName(s.to_string())); } + if s.len() > 250 { + return Err(Error::InvalidKeyNameLength(s.to_string())); + } Ok(KeyName(s.to_string())) } } From 01c87538c6e9c79ad32737bead38a82e7591bf68 Mon Sep 17 00:00:00 2001 From: Elizabeth Engelman <4752801+elizabethengelman@users.noreply.github.com> Date: Mon, 23 Dec 2024 12:06:36 -0500 Subject: [PATCH 70/74] Update public_key to work with secure storage keys --- cmd/soroban-cli/src/commands/keys/address.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cmd/soroban-cli/src/commands/keys/address.rs b/cmd/soroban-cli/src/commands/keys/address.rs index aa5a2940d..1a7ab21cd 100644 --- a/cmd/soroban-cli/src/commands/keys/address.rs +++ b/cmd/soroban-cli/src/commands/keys/address.rs @@ -1,6 +1,9 @@ use clap::arg; -use crate::commands::config::{address, locator, secret}; +use crate::{ + commands::config::{address, locator, secret}, + config::UnresolvedMuxedAccount, +}; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -47,6 +50,11 @@ impl Cmd { pub fn public_key(&self) -> Result { if let Ok(key) = stellar_strkey::ed25519::PublicKey::from_string(&self.name) { Ok(key) + } else if let Ok(unresolved) = self.name.parse::() { + let muxed = unresolved.resolve_muxed_account(&self.locator, self.hd_path)?; + Ok(stellar_strkey::ed25519::PublicKey::from_string( + &muxed.to_string(), + )?) } else { Ok(stellar_strkey::ed25519::PublicKey::from_payload( self.private_key()?.verifying_key().as_bytes(), From 085317fdbfdaf9ca91702d2466ab46128768cfea Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Mon, 6 Jan 2025 16:31:37 -0500 Subject: [PATCH 71/74] fix(address): remove private key function & use unresolved Address This simplifies the lookup of the address. --- .../src/commands/contract/arg_parsing.rs | 7 +---- cmd/soroban-cli/src/commands/keys/address.rs | 29 ++++++------------- cmd/soroban-cli/src/config/address.rs | 2 +- 3 files changed, 11 insertions(+), 27 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs index b522fc91e..98a6d3004 100644 --- a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs +++ b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs @@ -274,10 +274,5 @@ fn resolve_address(addr_or_alias: &str, config: &config::Args) -> Result Option { - let cmd = crate::commands::keys::address::Cmd { - name: addr_or_alias.to_string(), - hd_path: Some(0), - locator: config.locator.clone(), - }; - cmd.private_key().ok() + config.locator.key(addr_or_alias).ok()?.key_pair(None).ok() } diff --git a/cmd/soroban-cli/src/commands/keys/address.rs b/cmd/soroban-cli/src/commands/keys/address.rs index 1a7ab21cd..9727988e0 100644 --- a/cmd/soroban-cli/src/commands/keys/address.rs +++ b/cmd/soroban-cli/src/commands/keys/address.rs @@ -24,7 +24,7 @@ pub enum Error { #[group(skip)] pub struct Cmd { /// Name of identity to lookup, default test identity used if not provided - pub name: String, + pub name: UnresolvedMuxedAccount, /// If identity is a seed phrase use this hd path, default is 0 #[arg(long)] @@ -40,25 +40,14 @@ impl Cmd { Ok(()) } - pub fn private_key(&self) -> Result { - Ok(self - .locator - .read_identity(&self.name)? - .key_pair(self.hd_path)?) - } - pub fn public_key(&self) -> Result { - if let Ok(key) = stellar_strkey::ed25519::PublicKey::from_string(&self.name) { - Ok(key) - } else if let Ok(unresolved) = self.name.parse::() { - let muxed = unresolved.resolve_muxed_account(&self.locator, self.hd_path)?; - Ok(stellar_strkey::ed25519::PublicKey::from_string( - &muxed.to_string(), - )?) - } else { - Ok(stellar_strkey::ed25519::PublicKey::from_payload( - self.private_key()?.verifying_key().as_bytes(), - )?) - } + let muxed = self + .name + .resolve_muxed_account(&self.locator, self.hd_path)?; + let bytes = match muxed { + soroban_sdk::xdr::MuxedAccount::Ed25519(uint256) => uint256.0, + soroban_sdk::xdr::MuxedAccount::MuxedEd25519(muxed_account) => muxed_account.ed25519.0, + }; + Ok(stellar_strkey::ed25519::PublicKey(bytes)) } } diff --git a/cmd/soroban-cli/src/config/address.rs b/cmd/soroban-cli/src/config/address.rs index dadd8853f..24a40a62a 100644 --- a/cmd/soroban-cli/src/config/address.rs +++ b/cmd/soroban-cli/src/config/address.rs @@ -78,7 +78,7 @@ impl UnresolvedMuxedAccount { UnresolvedMuxedAccount::Resolved(muxed_account) => { Err(Error::CannotSign(muxed_account.clone())) } - UnresolvedMuxedAccount::AliasOrSecret(alias) => Ok(locator.read_identity(alias)?), + UnresolvedMuxedAccount::AliasOrSecret(alias) => Ok(locator.key(alias)?), } } } From aecc5ac4c40f6f52c15f51fb4947952aa8b2d597 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 7 Jan 2025 14:18:45 -0500 Subject: [PATCH 72/74] feat: store seedphrase instead of private key This will allow for exporting the phrase later --- cmd/soroban-cli/src/commands/keys/generate.rs | 28 ++--- cmd/soroban-cli/src/config/secret.rs | 38 +++++-- cmd/soroban-cli/src/signer.rs | 5 +- cmd/soroban-cli/src/signer/keyring.rs | 107 ++++++++++-------- 4 files changed, 106 insertions(+), 72 deletions(-) diff --git a/cmd/soroban-cli/src/commands/keys/generate.rs b/cmd/soroban-cli/src/commands/keys/generate.rs index 1e3727478..8ec0158bb 100644 --- a/cmd/soroban-cli/src/commands/keys/generate.rs +++ b/cmd/soroban-cli/src/commands/keys/generate.rs @@ -1,4 +1,5 @@ use clap::{arg, command}; +use sep5::SeedPhrase; use super::super::config::{ locator, network, @@ -122,9 +123,7 @@ impl Cmd { fn secret(&self, print: &Print) -> Result { let seed_phrase = self.seed_phrase()?; - Ok(if self.as_secret { - seed_phrase.private_key(self.hd_path)?.into() - } else if self.secure_store { + if self.secure_store { // secure_store:org.stellar.cli: let entry_name_with_prefix = format!( "{}{}-{}", @@ -137,38 +136,41 @@ impl Cmd { let secret: Secret = entry_name_with_prefix.parse()?; if let Secret::SecureStore { entry_name } = &secret { - Self::write_to_secure_store(entry_name, &seed_phrase, print)?; + Self::write_to_secure_store(entry_name, seed_phrase, print)?; } - secret + return Ok(secret); + } + let secret: Secret = seed_phrase.into(); + Ok(if self.as_secret { + secret.private_key(self.hd_path)?.into() } else { - seed_phrase + secret }) } - fn seed_phrase(&self) -> Result { + fn seed_phrase(&self) -> Result { Ok(if self.default_seed { - Secret::test_seed_phrase() + secret::test_seed_phrase() } else { - Secret::from_seed(self.seed.as_deref()) + secret::seed_phrase_from_seed(self.seed.as_deref()) }?) } fn write_to_secure_store( entry_name: &String, - seed_phrase: &Secret, + seed_phrase: SeedPhrase, print: &Print, ) -> Result<(), Error> { print.infoln(format!("Writing to secure store: {entry_name}")); let entry = StellarEntry::new(entry_name)?; - if let Ok(key) = entry.get_public_key() { + if let Ok(key) = entry.get_public_key(None) { print.warnln(format!("A key for {entry_name} already exists in your operating system's secure store: {key}")); } else { print.infoln(format!( "Saving a new key to your operating system's secure store: {entry_name}" )); - let key_pair = seed_phrase.key_pair(None)?; - entry.set_password(key_pair.as_bytes())?; + entry.set_seed_phrase(seed_phrase)?; } Ok(()) } diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index 8f4b20c60..f32b291e8 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -1,6 +1,8 @@ use clap::arg; use serde::{Deserialize, Serialize}; use std::{io::Write, str::FromStr}; + +use sep5::SeedPhrase; use stellar_strkey::ed25519::{PrivateKey, PublicKey}; use crate::{ @@ -94,6 +96,14 @@ impl From for Secret { } } +impl From for Secret { + fn from(value: SeedPhrase) -> Self { + Secret::SeedPhrase { + seed_phrase: value.seed_phrase.into_phrase(), + } + } +} + impl Secret { pub fn private_key(&self, index: Option) -> Result { Ok(match self { @@ -113,7 +123,7 @@ impl Secret { pub fn public_key(&self, index: Option) -> Result { if let Secret::SecureStore { entry_name } = self { let entry = keyring::StellarEntry::new(entry_name)?; - Ok(entry.get_public_key()?) + Ok(entry.get_public_key(index)?) } else { let key = self.key_pair(index)?; Ok(stellar_strkey::ed25519::PublicKey::from_payload( @@ -122,14 +132,15 @@ impl Secret { } } - pub fn signer(&self, index: Option, print: Print) -> Result { + pub fn signer(&self, hd_path: Option, print: Print) -> Result { let kind = match self { Secret::SecretKey { .. } | Secret::SeedPhrase { .. } => { - let key = self.key_pair(index)?; + let key = self.key_pair(hd_path)?; SignerKind::Local(LocalKey { key }) } Secret::SecureStore { entry_name } => SignerKind::SecureStore(SecureStoreEntry { name: entry_name.to_string(), + hd_path, }), }; Ok(Signer { kind, print }) @@ -140,14 +151,7 @@ impl Secret { } pub fn from_seed(seed: Option<&str>) -> Result { - let seed_phrase = if let Some(seed) = seed.map(str::as_bytes) { - sep5::SeedPhrase::from_entropy(seed) - } else { - sep5::SeedPhrase::random(sep5::MnemonicType::Words24) - }? - .seed_phrase - .into_phrase(); - Ok(Secret::SeedPhrase { seed_phrase }) + Ok(seed_phrase_from_seed(seed)?.into()) } pub fn test_seed_phrase() -> Result { @@ -155,6 +159,18 @@ impl Secret { } } +pub fn seed_phrase_from_seed(seed: Option<&str>) -> Result { + Ok(if let Some(seed) = seed.map(str::as_bytes) { + sep5::SeedPhrase::from_entropy(seed)? + } else { + sep5::SeedPhrase::random(sep5::MnemonicType::Words24)? + }) +} + +pub fn test_seed_phrase() -> Result { + Ok("0000000000000000".parse()?) +} + fn read_password() -> Result { std::io::stdout().flush().map_err(|_| Error::PasswordRead)?; rpassword::read_password().map_err(|_| Error::PasswordRead) diff --git a/cmd/soroban-cli/src/signer.rs b/cmd/soroban-cli/src/signer.rs index 099ed6186..ebd650d40 100644 --- a/cmd/soroban-cli/src/signer.rs +++ b/cmd/soroban-cli/src/signer.rs @@ -294,13 +294,14 @@ impl Lab { pub struct SecureStoreEntry { pub name: String, + pub hd_path: Option, } impl SecureStoreEntry { pub fn sign_tx_hash(&self, tx_hash: [u8; 32]) -> Result { let entry = StellarEntry::new(&self.name)?; - let hint = SignatureHint(entry.get_public_key()?.0[28..].try_into()?); - let signed_tx_hash = entry.sign_data(&tx_hash)?; + let hint = SignatureHint(entry.get_public_key(self.hd_path)?.0[28..].try_into()?); + let signed_tx_hash = entry.sign_data(&tx_hash, self.hd_path)?; let signature = Signature(signed_tx_hash.clone().try_into()?); Ok(DecoratedSignature { hint, signature }) } diff --git a/cmd/soroban-cli/src/signer/keyring.rs b/cmd/soroban-cli/src/signer/keyring.rs index 4e6b19c99..0e6c49137 100644 --- a/cmd/soroban-cli/src/signer/keyring.rs +++ b/cmd/soroban-cli/src/signer/keyring.rs @@ -1,6 +1,6 @@ -use base64::{engine::general_purpose::STANDARD as base64, Engine as _}; use ed25519_dalek::Signer; use keyring::Entry; +use sep5::seed_phrase::SeedPhrase; use zeroize::Zeroize; pub(crate) const SECURE_STORE_ENTRY_PREFIX: &str = "secure_store:"; @@ -11,7 +11,7 @@ pub enum Error { #[error(transparent)] Keyring(#[from] keyring::Error), #[error(transparent)] - Base64(#[from] base64::DecodeError), + Sep5(#[from] sep5::error::Error), } pub struct StellarEntry { @@ -25,46 +25,60 @@ impl StellarEntry { }) } - pub fn set_password(&self, password: &[u8]) -> Result<(), Error> { - let data = base64.encode(password); + pub fn set_seed_phrase(&self, seed_phrase: SeedPhrase) -> Result<(), Error> { + let mut data = seed_phrase.seed_phrase.into_phrase(); self.keyring.set_password(&data)?; + data.zeroize(); Ok(()) } - fn get_password(&self) -> Result, Error> { - Ok(base64.decode(self.keyring.get_password()?)?) + fn get_seed_phrase(&self) -> Result { + Ok(self.keyring.get_password()?.parse()?) } fn use_key( &self, f: impl FnOnce(ed25519_dalek::SigningKey) -> Result, + hd_path: Option, ) -> Result { - let mut key_vec = self.get_password()?; - let mut key_bytes: [u8; 32] = key_vec.as_slice().try_into().unwrap(); - + // The underlying Mnemonic type is zeroized when dropped + let mut key_bytes: [u8; 32] = { + self.get_seed_phrase()? + .from_path_index(hd_path.unwrap_or_default(), None)? + .private() + .0 + }; let result = { - // Use this scope to ensure the keypair is zeroized + // Use this scope to ensure the keypair is zeroized when dropped let keypair = ed25519_dalek::SigningKey::from_bytes(&key_bytes); f(keypair)? }; - key_vec.zeroize(); key_bytes.zeroize(); Ok(result) } - pub fn get_public_key(&self) -> Result { - self.use_key(|keypair| { - Ok(stellar_strkey::ed25519::PublicKey( - *keypair.verifying_key().as_bytes(), - )) - }) + pub fn get_public_key( + &self, + hd_path: Option, + ) -> Result { + self.use_key( + |keypair| { + Ok(stellar_strkey::ed25519::PublicKey( + *keypair.verifying_key().as_bytes(), + )) + }, + hd_path, + ) } - pub fn sign_data(&self, data: &[u8]) -> Result, Error> { - self.use_key(|keypair| { - let signature = keypair.sign(data); - Ok(signature.to_bytes().to_vec()) - }) + pub fn sign_data(&self, data: &[u8], hd_path: Option) -> Result, Error> { + self.use_key( + |keypair| { + let signature = keypair.sign(data); + Ok(signature.to_bytes().to_vec()) + }, + hd_path, + ) } } @@ -77,56 +91,57 @@ mod test { fn test_get_password() { set_default_credential_builder(mock::default_credential_builder()); - let secret = crate::config::secret::Secret::from_seed(None).unwrap(); - let key_pair = secret.key_pair(None).unwrap(); + let seed_phrase = crate::config::secret::seed_phrase_from_seed(None).unwrap(); + let seed_phrase_clone = seed_phrase.clone(); let entry = StellarEntry::new("test").unwrap(); - // set the password - let set_password_result = entry.set_password(&key_pair.to_bytes()); - assert!(set_password_result.is_ok()); - - // get_password should return the same password we set - let get_password_result = entry.get_password(); - assert!(get_password_result.is_ok()); - assert_eq!(key_pair.to_bytes().to_vec(), get_password_result.unwrap()); + // set the seed phrase + let set_seed_phrase_result = entry.set_seed_phrase(seed_phrase); + assert!(set_seed_phrase_result.is_ok()); + + // get_seed_phrase should return the same seed phrase we set + let get_seed_phrase_result = entry.get_seed_phrase(); + assert!(get_seed_phrase_result.is_ok()); + assert_eq!( + seed_phrase_clone.phrase(), + get_seed_phrase_result.unwrap().phrase() + ); } #[test] fn test_get_public_key() { set_default_credential_builder(mock::default_credential_builder()); - let secret = crate::config::secret::Secret::from_seed(None).unwrap(); - let public_key = secret.public_key(None).unwrap(); - let key_pair = secret.key_pair(None).unwrap(); + let seed_phrase = crate::config::secret::seed_phrase_from_seed(None).unwrap(); + let public_key = seed_phrase.from_path_index(0, None).unwrap().public().0; let entry = StellarEntry::new("test").unwrap(); - // set the password - let set_password_result = entry.set_password(&key_pair.to_bytes()); - assert!(set_password_result.is_ok()); + // set the seed_phrase + let set_seed_phrase_result = entry.set_seed_phrase(seed_phrase); + assert!(set_seed_phrase_result.is_ok()); // confirm that we can get the public key from the entry and that it matches the one we set - let get_public_key_result = entry.get_public_key(); + let get_public_key_result = entry.get_public_key(None); assert!(get_public_key_result.is_ok()); - assert_eq!(public_key, get_public_key_result.unwrap()); + assert_eq!(public_key, get_public_key_result.unwrap().0); } #[test] fn test_sign_data() { set_default_credential_builder(mock::default_credential_builder()); - //create a secret - let secret = crate::config::secret::Secret::from_seed(None).unwrap(); - let key_pair = secret.key_pair(None).unwrap(); + //create a seed phrase + let seed_phrase = crate::config::secret::seed_phrase_from_seed(None).unwrap(); - // create a keyring entry and set the password + // create a keyring entry and set the seed_phrase let entry = StellarEntry::new("test").unwrap(); - entry.set_password(&key_pair.to_bytes()).unwrap(); + entry.set_seed_phrase(seed_phrase).unwrap(); let tx_xdr = r"AAAAAgAAAADh6eOnZEq1xQgKioffuH7/8D8x8+OdGFEkiYC6QKMWzQAAAGQAAACuAAAAAQAAAAAAAAAAAAAAAQAAAAAAAAAYAAAAAQAAAAAAAAAAAAAAAOHp46dkSrXFCAqKh9+4fv/wPzHz450YUSSJgLpAoxbNoFT1s8jZPCv9IJ2DsqGTA8pOtavv58JF53aDycpRPcEAAAAA+N2m5zc3EfWUmLvigYPOHKXhSy8OrWfVibc6y6PrQoYAAAAAAAAAAAAAAAA"; - let sign_tx_env_result = entry.sign_data(tx_xdr.as_bytes()); + let sign_tx_env_result = entry.sign_data(tx_xdr.as_bytes(), None); assert!(sign_tx_env_result.is_ok()); } } From 63f247e5e3f11e858a0637b547a16e2ebae814c9 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 7 Jan 2025 15:15:59 -0500 Subject: [PATCH 73/74] fix: clean up --- cmd/soroban-cli/src/commands/contract/arg_parsing.rs | 7 +------ cmd/soroban-cli/src/commands/keys/add.rs | 8 ++------ cmd/soroban-cli/src/commands/keys/address.rs | 11 +---------- cmd/soroban-cli/src/config/address.rs | 8 +++++--- 4 files changed, 9 insertions(+), 25 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs index 98a6d3004..bb2d2aa76 100644 --- a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs +++ b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs @@ -14,9 +14,8 @@ use crate::xdr::{ use crate::commands::txn_result::TxnResult; use crate::config::{ - self, address, + self, sc_address::{self, UnresolvedScAddress}, - secret, }; use soroban_spec_tools::Spec; @@ -42,10 +41,6 @@ pub enum Error { Xdr(#[from] xdr::Error), #[error(transparent)] StrVal(#[from] soroban_spec_tools::Error), - #[error(transparent)] - Address(#[from] address::Error), - #[error(transparent)] - Secret(#[from] secret::Error), #[error("Missing argument {0}")] MissingArgument(String), #[error("")] diff --git a/cmd/soroban-cli/src/commands/keys/add.rs b/cmd/soroban-cli/src/commands/keys/add.rs index 57a218ede..4c5ddbd9b 100644 --- a/cmd/soroban-cli/src/commands/keys/add.rs +++ b/cmd/soroban-cli/src/commands/keys/add.rs @@ -2,10 +2,7 @@ use clap::command; use crate::{ commands::global, - config::{ - address::{self, KeyName}, - locator, secret, - }, + config::{address::KeyName, locator, secret}, print::Print, }; @@ -13,10 +10,9 @@ use crate::{ pub enum Error { #[error(transparent)] Secret(#[from] secret::Error), + #[error(transparent)] Config(#[from] locator::Error), - #[error(transparent)] - Address(#[from] address::Error), } #[derive(Debug, clap::Parser, Clone)] diff --git a/cmd/soroban-cli/src/commands/keys/address.rs b/cmd/soroban-cli/src/commands/keys/address.rs index 9727988e0..51ce90ed2 100644 --- a/cmd/soroban-cli/src/commands/keys/address.rs +++ b/cmd/soroban-cli/src/commands/keys/address.rs @@ -1,21 +1,12 @@ use clap::arg; use crate::{ - commands::config::{address, locator, secret}, + commands::config::{address, locator}, config::UnresolvedMuxedAccount, }; #[derive(thiserror::Error, Debug)] pub enum Error { - #[error(transparent)] - Config(#[from] locator::Error), - - #[error(transparent)] - Secret(#[from] secret::Error), - - #[error(transparent)] - StrKey(#[from] stellar_strkey::DecodeError), - #[error(transparent)] Address(#[from] address::Error), } diff --git a/cmd/soroban-cli/src/config/address.rs b/cmd/soroban-cli/src/config/address.rs index 24a40a62a..f86f88ecb 100644 --- a/cmd/soroban-cli/src/config/address.rs +++ b/cmd/soroban-cli/src/config/address.rs @@ -55,8 +55,8 @@ impl UnresolvedMuxedAccount { ) -> Result { match self { UnresolvedMuxedAccount::Resolved(muxed_account) => Ok(muxed_account.clone()), - UnresolvedMuxedAccount::AliasOrSecret(alias) => { - Self::resolve_muxed_account_with_alias(alias, locator, hd_path) + UnresolvedMuxedAccount::AliasOrSecret(alias_or_secret) => { + Self::resolve_muxed_account_with_alias(alias_or_secret, locator, hd_path) } } } @@ -78,7 +78,9 @@ impl UnresolvedMuxedAccount { UnresolvedMuxedAccount::Resolved(muxed_account) => { Err(Error::CannotSign(muxed_account.clone())) } - UnresolvedMuxedAccount::AliasOrSecret(alias) => Ok(locator.key(alias)?), + UnresolvedMuxedAccount::AliasOrSecret(alias_or_secret) => { + Ok(locator.key(alias_or_secret)?) + } } } } From ed797e026e3d0c5ab86514061ab224f09a16ddae Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Thu, 9 Jan 2025 18:35:24 -0500 Subject: [PATCH 74/74] fix: wait to lookup muxed account --- Cargo.lock | 1720 +++++++++-------- cmd/soroban-cli/src/commands/keys/generate.rs | 1 + cmd/soroban-cli/src/config/address.rs | 2 +- cmd/soroban-cli/src/config/key.rs | 2 +- cmd/soroban-cli/src/config/locator.rs | 10 +- cmd/soroban-cli/src/config/sc_address.rs | 12 +- 6 files changed, 973 insertions(+), 774 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 72386cbc9..20d184b0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "ahash" @@ -55,9 +55,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -70,43 +70,43 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "arbitrary" @@ -237,9 +237,9 @@ dependencies = [ [[package]] name = "arrayref" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "ascii-canvas" @@ -326,9 +326,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.12" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa" +checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" dependencies = [ "flate2", "futures-core", @@ -339,14 +339,14 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" dependencies = [ "async-task", "concurrent-queue", - "fastrand 2.1.0", - "futures-lite 2.3.0", + "fastrand", + "futures-lite", "slab", ] @@ -358,59 +358,30 @@ checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ "async-channel 2.3.1", "async-executor", - "async-io 2.3.3", - "async-lock 3.4.0", + "async-io", + "async-lock", "blocking", - "futures-lite 2.3.0", + "futures-lite", "once_cell", ] [[package]] name = "async-io" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite 1.13.0", - "log", - "parking", - "polling 2.8.0", - "rustix 0.37.27", - "slab", - "socket2 0.4.10", - "waker-fn", -] - -[[package]] -name = "async-io" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" dependencies = [ - "async-lock 3.4.0", + "async-lock", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.3.0", + "futures-lite", "parking", - "polling 3.7.2", - "rustix 0.38.34", + "polling", + "rustix", "slab", "tracing", - "windows-sys 0.52.0", -] - -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "event-listener 2.5.3", + "windows-sys 0.59.0", ] [[package]] @@ -419,72 +390,74 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 5.3.1", + "event-listener 5.4.0", "event-listener-strategy", "pin-project-lite", ] [[package]] name = "async-object-pool" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeb901c30ebc2fc4ab46395bbfbdba9542c16559d853645d75190c3056caf3bc" +checksum = "333c456b97c3f2d50604e8b2624253b7f787208cb72eb75e64b0ad11b221652c" dependencies = [ "async-std", ] [[package]] name = "async-process" -version = "1.8.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" +checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" dependencies = [ - "async-io 1.13.0", - "async-lock 2.8.0", + "async-channel 2.3.1", + "async-io", + "async-lock", "async-signal", + "async-task", "blocking", "cfg-if", - "event-listener 3.1.0", - "futures-lite 1.13.0", - "rustix 0.38.34", - "windows-sys 0.48.0", + "event-listener 5.4.0", + "futures-lite", + "rustix", + "tracing", ] [[package]] name = "async-signal" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb3634b73397aa844481f814fad23bbf07fdb0eabec10f2eb95e58944b1ec32" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" dependencies = [ - "async-io 2.3.3", - "async-lock 3.4.0", + "async-io", + "async-lock", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 0.38.34", + "rustix", "signal-hook-registry", "slab", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "async-std" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +checksum = "c634475f29802fde2b8f0b505b1bd00dfe4df7d4a000f0b36f7671197d5c3615" dependencies = [ "async-attributes", "async-channel 1.9.0", "async-global-executor", - "async-io 1.13.0", - "async-lock 2.8.0", + "async-io", + "async-lock", "async-process", "crossbeam-utils", "futures-channel", "futures-core", "futures-io", - "futures-lite 1.13.0", + "futures-lite", "gloo-timers", "kv-log-macro", "log", @@ -504,13 +477,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.76" +version = "0.1.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531b97fb4cd3dfdce92c35dedbfdc1f0b9d8091c8ca943d6dae340ef5012d514" +checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] @@ -521,23 +494,23 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] @@ -611,12 +584,6 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.6.0" @@ -650,7 +617,7 @@ dependencies = [ "async-channel 2.3.1", "async-task", "futures-io", - "futures-lite 2.3.0", + "futures-lite", "piper", ] @@ -667,9 +634,9 @@ dependencies = [ "futures-util", "hex", "home", - "http 1.1.0", + "http 1.2.0", "http-body-util", - "hyper 1.4.1", + "hyper 1.5.2", "hyper-named-pipe", "hyper-rustls 0.26.0", "hyper-util", @@ -678,14 +645,14 @@ dependencies = [ "pin-project-lite", "rustls 0.22.4", "rustls-native-certs 0.7.3", - "rustls-pemfile 2.1.3", + "rustls-pemfile 2.2.0", "rustls-pki-types", "serde", "serde_derive", "serde_json", "serde_repr", "serde_urlencoded", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-util", "tower-service", @@ -706,12 +673,12 @@ dependencies = [ [[package]] name = "bstr" -version = "1.10.0" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" dependencies = [ "memchr", - "regex-automata 0.4.7", + "regex-automata 0.4.9", "serde", ] @@ -735,9 +702,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "bytes-lit" @@ -748,7 +715,7 @@ dependencies = [ "num-bigint", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] @@ -759,18 +726,18 @@ checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" [[package]] name = "camino" -version = "1.1.7" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" dependencies = [ "serde", ] [[package]] name = "cargo-platform" -version = "0.1.6" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceed8ef69d8518a5dda55c07425450b58a4e1946f4951eab6d7191ee86c2443d" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" dependencies = [ "serde", ] @@ -786,17 +753,18 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "cc" -version = "1.1.8" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504bdec147f2cc13c8b57ed9401fd8a147cc66b67ad5cb241394244f2c947549" +checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" dependencies = [ "jobserver", "libc", + "shlex", ] [[package]] @@ -805,11 +773,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", @@ -822,9 +796,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.14" +version = "4.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c937d4061031a6d0c8da4b9a4f98a172fc2976dfb1c19213a9cf7d0d3c837e36" +checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783" dependencies = [ "clap_builder", "clap_derive", @@ -841,9 +815,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.14" +version = "4.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85379ba512b21a328adf887e85f7742d12e96eb31f3ef077df4ffc26b506ffed" +checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121" dependencies = [ "anstream", "anstyle", @@ -853,30 +827,30 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.13" +version = "4.5.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa3c596da3cf0983427b0df0dba359df9182c13bd5b519b585a482b0c351f4e8" +checksum = "33a7e468e750fa4b6be660e8b5651ad47372e8fb114030b594c2d75d48c5ffd0" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "codespan-reporting" @@ -890,18 +864,18 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "colored" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -929,17 +903,27 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -966,18 +950,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.13" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -994,9 +978,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" @@ -1038,9 +1022,9 @@ dependencies = [ [[package]] name = "csv" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" dependencies = [ "csv-core", "itoa", @@ -1064,7 +1048,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] @@ -1091,51 +1075,66 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] name = "cxx" -version = "1.0.112" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ab30434ea0ff6aa640a08dda5284026a366d47565496fd40b6cbfbdd7e31a2" +checksum = "ad7c7515609502d316ab9a24f67dc045132d93bfd3f00713389e90d9898bf30d" dependencies = [ "cc", + "cxxbridge-cmd", "cxxbridge-flags", "cxxbridge-macro", + "foldhash", "link-cplusplus", ] [[package]] name = "cxx-build" -version = "1.0.112" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b649d7dfae8268450d53d109388b337b9352c7cba1fc10db4a1bc23c3dc189fb" +checksum = "8bfd16fca6fd420aebbd80d643c201ee4692114a0de208b790b9cd02ceae65fb" dependencies = [ "cc", "codespan-reporting", - "once_cell", "proc-macro2", "quote", "scratch", - "syn 2.0.77", + "syn 2.0.95", +] + +[[package]] +name = "cxxbridge-cmd" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c33fd49f5d956a1b7ee5f7a9768d58580c6752838d92e39d0d56439efdedc35" +dependencies = [ + "clap", + "codespan-reporting", + "proc-macro2", + "quote", + "syn 2.0.95", ] [[package]] name = "cxxbridge-flags" -version = "1.0.112" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42281b20eba5218c539295c667c18e2f50211bb11902419194c6ed1ae808e547" +checksum = "be0f1077278fac36299cce8446effd19fe93a95eedb10d39265f3bf67b3036c9" [[package]] name = "cxxbridge-macro" -version = "1.0.112" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45506e3c66512b0a65d291a6b452128b7b1dd9841e20d1e151addbd2c00ea50" +checksum = "3da7e4d6e74af6b79031d264b2f13c3ea70af1978083741c41ffce9308f1f24f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "rustversion", + "syn 2.0.95", ] [[package]] @@ -1159,7 +1158,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] @@ -1170,7 +1169,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] @@ -1242,7 +1241,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] @@ -1329,6 +1328,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.95", +] + [[package]] name = "doc-comment" version = "0.3.3" @@ -1437,9 +1447,9 @@ dependencies = [ [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] @@ -1453,14 +1463,14 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] name = "env_filter" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" dependencies = [ "log", "regex", @@ -1468,9 +1478,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" dependencies = [ "anstream", "anstyle", @@ -1487,12 +1497,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1515,20 +1525,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener" -version = "5.3.1" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" dependencies = [ "concurrent-queue", "parking", @@ -1537,28 +1536,19 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" dependencies = [ - "event-listener 5.3.1", + "event-listener 5.4.0", "pin-project-lite", ] [[package]] name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - -[[package]] -name = "fastrand" -version = "2.1.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "ff" @@ -1584,9 +1574,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.31" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", "miniz_oxide", @@ -1594,9 +1584,9 @@ dependencies = [ [[package]] name = "float-cmp" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" dependencies = [ "num-traits", ] @@ -1607,6 +1597,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "foreign-types" version = "0.3.2" @@ -1645,9 +1641,9 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -1676,9 +1672,9 @@ checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -1693,26 +1689,11 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - -[[package]] -name = "futures-lite" -version = "2.3.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1" dependencies = [ - "fastrand 2.1.0", + "fastrand", "futures-core", "futures-io", "parking", @@ -1727,7 +1708,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] @@ -1773,9 +1754,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", @@ -1786,27 +1767,27 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "globset" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", ] [[package]] @@ -1815,16 +1796,16 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" dependencies = [ - "bitflags 2.6.0", + "bitflags", "ignore", "walkdir", ] [[package]] name = "gloo-timers" -version = "0.2.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" dependencies = [ "futures-channel", "futures-core", @@ -1855,7 +1836,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.3.0", + "indexmap 2.7.0", "slab", "tokio", "tokio-util", @@ -1864,17 +1845,17 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.1.0", - "indexmap 2.3.0", + "http 1.2.0", + "indexmap 2.7.0", "slab", "tokio", "tokio-util", @@ -1898,9 +1879,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" @@ -1923,12 +1904,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "hermit-abi" version = "0.4.0" @@ -1952,9 +1927,9 @@ checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "hickory-proto" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07698b8420e2f0d6447a436ba999ec85d8fbf2a398bbd737b82cac4a2e96e512" +checksum = "447afdcdb8afb9d0a852af6dc65d9b285ce720ed7a59e42a8bf2e931c67bc1b5" dependencies = [ "async-trait", "cfg-if", @@ -1963,11 +1938,11 @@ dependencies = [ "futures-channel", "futures-io", "futures-util", - "idna 0.4.0", + "idna", "ipnet", "once_cell", "rand", - "thiserror", + "thiserror 1.0.69", "tinyvec", "tokio", "tracing", @@ -1976,9 +1951,9 @@ dependencies = [ [[package]] name = "hickory-resolver" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28757f23aa75c98f254cf0405e6d8c25b831b32921b050a66692427679b1f243" +checksum = "0a2e2aba9c389ce5267d31cf1e4dace82390ae276b0b364ea55630b1fa1b44b4" dependencies = [ "cfg-if", "futures-util", @@ -1990,7 +1965,7 @@ dependencies = [ "rand", "resolv-conf", "smallvec", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", ] @@ -2028,11 +2003,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2059,9 +2034,9 @@ dependencies = [ [[package]] name = "http" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", "fnv", @@ -2086,7 +2061,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.1.0", + "http 1.2.0", ] [[package]] @@ -2097,16 +2072,16 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -2129,7 +2104,7 @@ dependencies = [ "crossbeam-utils", "form_urlencoded", "futures-util", - "hyper 0.14.30", + "hyper 0.14.32", "lazy_static", "levenshtein", "log", @@ -2150,9 +2125,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.30" +version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ "bytes", "futures-channel", @@ -2165,7 +2140,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.7", + "socket2", "tokio", "tower-service", "tracing", @@ -2174,15 +2149,15 @@ dependencies = [ [[package]] name = "hyper" -version = "1.4.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.6", - "http 1.1.0", + "h2 0.4.7", + "http 1.2.0", "http-body 1.0.1", "httparse", "httpdate", @@ -2200,7 +2175,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73b7d8abf35697b81a825e386fc151e0d503e8cb5fcb93cc8669c376dfd6f278" dependencies = [ "hex", - "hyper 1.4.1", + "hyper 1.5.2", "hyper-util", "pin-project-lite", "tokio", @@ -2216,7 +2191,7 @@ checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http 0.2.12", - "hyper 0.14.30", + "hyper 0.14.32", "log", "rustls 0.21.12", "rustls-native-certs 0.6.3", @@ -2231,8 +2206,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" dependencies = [ "futures-util", - "http 1.1.0", - "hyper 1.4.1", + "http 1.2.0", + "hyper 1.5.2", "hyper-util", "log", "rustls 0.22.4", @@ -2245,19 +2220,19 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.3" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", - "http 1.1.0", - "hyper 1.4.1", + "http 1.2.0", + "hyper 1.5.2", "hyper-util", - "rustls 0.23.12", - "rustls-native-certs 0.8.0", + "rustls 0.23.20", + "rustls-native-certs 0.8.1", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls 0.26.1", "tower-service", "webpki-roots", ] @@ -2270,7 +2245,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.4.1", + "hyper 1.5.2", "hyper-util", "native-tls", "tokio", @@ -2280,20 +2255,19 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.7" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", - "hyper 1.4.1", + "hyper 1.5.2", "pin-project-lite", - "socket2 0.5.7", + "socket2", "tokio", - "tower", "tower-service", "tracing", ] @@ -2306,7 +2280,7 @@ checksum = "acf569d43fa9848e510358c07b80f4adf34084ddc28c6a4a651ee8474c070dcc" dependencies = [ "hex", "http-body-util", - "hyper 1.4.1", + "hyper 1.5.2", "hyper-util", "pin-project-lite", "tokio", @@ -2315,9 +2289,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.60" +version = "0.1.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2336,6 +2310,124 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.95", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -2344,35 +2436,36 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.4.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", ] [[package]] -name = "idna" -version = "0.5.0" +name = "idna_adapter" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", ] [[package]] name = "ignore" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" dependencies = [ "crossbeam-deque", "globset", "log", "memchr", - "regex-automata 0.4.7", + "regex-automata 0.4.9", "same-file", "walkdir", "winapi-util", @@ -2411,12 +2504,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.3.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.2", "serde", ] @@ -2426,33 +2519,13 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.9", - "libc", - "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", + "socket2", "widestring", "windows-sys 0.48.0", "winreg", @@ -2460,9 +2533,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.9.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" [[package]] name = "is-docker" @@ -2509,9 +2582,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "jobserver" @@ -2524,63 +2597,64 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ + "once_cell", "wasm-bindgen", ] [[package]] name = "jsonrpsee-core" -version = "0.20.3" +version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2327ba8df2fdbd5e897e2b5ed25ce7f299d345b9736b6828814c3dbd1fd47b" +checksum = "f24ea59b037b6b9b0e2ebe2c30a3e782b56bd7c76dcc5d6d70ba55d442af56e3" dependencies = [ "anyhow", "async-trait", "beef", "futures-util", - "hyper 0.14.30", + "hyper 0.14.32", "jsonrpsee-types", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", ] [[package]] name = "jsonrpsee-http-client" -version = "0.20.3" +version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f80c17f62c7653ce767e3d7288b793dfec920f97067ceb189ebdd3570f2bc20" +checksum = "57c7b9f95208927653e7965a98525e7fc641781cab89f0e27c43fa2974405683" dependencies = [ "async-trait", - "hyper 0.14.30", + "hyper 0.14.32", "hyper-rustls 0.24.2", "jsonrpsee-core", "jsonrpsee-types", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "tokio", - "tower", + "tower 0.4.13", "tracing", "url", ] [[package]] name = "jsonrpsee-types" -version = "0.20.3" +version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be0be325642e850ed0bdff426674d2e66b2b7117c9be23a7caef68a2902b7d9" +checksum = "3264e339143fe37ed081953842ee67bfafa99e3b91559bdded6e4abd8fc8535e" dependencies = [ "anyhow", "beef", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "tracing", ] @@ -2607,13 +2681,15 @@ dependencies = [ [[package]] name = "keyring" -version = "3.3.0" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fa83d1ca02db069b5fbe94b23b584d588e989218310c9c15015bb5571ef1a94" +checksum = "2f8fe839464d4e4b37d756d7e910063696af79a7e877282cb1825e4ec5f10833" dependencies = [ "byteorder 1.5.0", "dbus-secret-service", - "security-framework", + "log", + "security-framework 2.11.1", + "security-framework 3.2.0", "windows-sys 0.59.0", ] @@ -2640,7 +2716,7 @@ dependencies = [ "petgraph", "pico-args", "regex", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", "string_cache", "term", "tiny-keccak", @@ -2654,7 +2730,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" dependencies = [ - "regex-automata 0.4.7", + "regex-automata 0.4.9", ] [[package]] @@ -2703,7 +2779,7 @@ dependencies = [ "ledger-transport", "libc", "log", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -2714,9 +2790,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libdbus-sys" @@ -2729,9 +2805,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libredox" @@ -2739,7 +2815,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags", "libc", ] @@ -2760,15 +2836,15 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.3.8" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] -name = "linux-raw-sys" -version = "0.4.14" +name = "litemap" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "lock_api" @@ -2827,20 +2903,19 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.7.4" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" dependencies = [ - "adler", + "adler2", ] [[package]] name = "mio" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi 0.3.9", "libc", "wasi", "windows-sys 0.52.0", @@ -2848,18 +2923,18 @@ dependencies = [ [[package]] name = "mockito" -version = "1.5.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b34bd91b9e5c5b06338d392463e1318d683cf82ec3d3af4014609be6e2108d" +checksum = "652cd6d169a36eaf9d1e6bce1a221130439a966d7f27858af66a33a66e9c4ee2" dependencies = [ "assert-json-diff", "bytes", "colored", "futures-util", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.4.1", + "hyper 1.5.2", "hyper-util", "log", "rand", @@ -2882,7 +2957,7 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "security-framework", + "security-framework 2.11.1", "security-framework-sys", "tempfile", ] @@ -2917,9 +2992,9 @@ dependencies = [ [[package]] name = "num" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" dependencies = [ "num-bigint", "num-complex", @@ -2931,20 +3006,19 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ - "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-complex" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", ] @@ -2957,30 +3031,29 @@ checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-derive" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -2989,11 +3062,10 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "autocfg", "num-bigint", "num-integer", "num-traits", @@ -3001,27 +3073,27 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "object" -version = "0.32.2" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "opaque-debug" @@ -3031,9 +3103,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "open" -version = "5.3.0" +version = "5.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a877bf6abd716642a53ef1b89fb498923a4afca5c754f9050b4d081c05c4b3" +checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" dependencies = [ "is-wsl", "libc", @@ -3046,7 +3118,7 @@ version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ - "bitflags 2.6.0", + "bitflags", "cfg-if", "foreign-types", "libc", @@ -3063,7 +3135,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] @@ -3110,9 +3182,9 @@ dependencies = [ [[package]] name = "parking" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" @@ -3145,7 +3217,7 @@ checksum = "914a1c2265c98e2446911282c6ac86d8524f495792c38c5bd884f80499c7538a" dependencies = [ "parse-display-derive", "regex", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", ] [[package]] @@ -3157,9 +3229,9 @@ dependencies = [ "proc-macro2", "quote", "regex", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", "structmeta", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] @@ -3170,9 +3242,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pathdiff" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "pbkdf2" @@ -3196,40 +3268,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.3.0", + "indexmap 2.7.0", ] [[package]] name = "phf" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ "phf_macros", - "phf_shared 0.11.2", + "phf_shared 0.11.3", ] [[package]] name = "phf_generator" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "phf_shared 0.11.2", + "phf_shared 0.11.3", "rand", ] [[package]] name = "phf_macros" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ "phf_generator", - "phf_shared 0.11.2", + "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] @@ -3238,16 +3310,16 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" dependencies = [ - "siphasher", + "siphasher 0.3.11", ] [[package]] name = "phf_shared" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "siphasher", + "siphasher 1.0.1", ] [[package]] @@ -3258,29 +3330,29 @@ checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -3290,12 +3362,12 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "piper" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", - "fastrand 2.1.0", + "fastrand", "futures-io", ] @@ -3311,39 +3383,23 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" - -[[package]] -name = "polling" -version = "2.8.0" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys 0.48.0", -] +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "polling" -version = "3.7.2" +version = "3.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.4.0", + "hermit-abi", "pin-project-lite", - "rustix 0.38.34", + "rustix", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3369,9 +3425,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "predicates" -version = "3.1.2" +version = "3.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" dependencies = [ "anstyle", "difflib", @@ -3383,15 +3439,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" [[package]] name = "predicates-tree" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" dependencies = [ "predicates-core", "termtree", @@ -3399,9 +3455,9 @@ dependencies = [ [[package]] name = "pretty_assertions" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" dependencies = [ "diff", "yansi", @@ -3409,12 +3465,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.15" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +checksum = "483f8c21f64f3ea09fe0f30f5d48c3e8eefe5dac9129f0075f76593b4c1da705" dependencies = [ "proc-macro2", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] @@ -3428,9 +3484,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -3443,57 +3499,61 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quinn" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" dependencies = [ "bytes", "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.0.0", - "rustls 0.23.12", - "socket2 0.5.7", - "thiserror", + "rustc-hash 2.1.0", + "rustls 0.23.20", + "socket2", + "thiserror 2.0.10", "tokio", "tracing", ] [[package]] name = "quinn-proto" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" dependencies = [ "bytes", + "getrandom", "rand", "ring", - "rustc-hash 2.0.0", - "rustls 0.23.12", + "rustc-hash 2.1.0", + "rustls 0.23.20", + "rustls-pki-types", "slab", - "thiserror", + "thiserror 2.0.10", "tinyvec", "tracing", + "web-time", ] [[package]] name = "quinn-udp" -version = "0.5.4" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bffec3605b73c6f1754535084a85229fa8a30f86014e6c81aeec4abb68b0285" +checksum = "1c40286217b4ba3a71d644d752e6a0b71f13f1b6a2c5311acfcbe0c2418ed904" dependencies = [ + "cfg_aliases", "libc", "once_cell", - "socket2 0.5.7", + "socket2", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -3530,34 +3590,34 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 2.6.0", + "bitflags", ] [[package]] name = "redox_users" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "regex" -version = "1.10.6" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", ] [[package]] @@ -3571,13 +3631,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", ] [[package]] @@ -3588,15 +3648,15 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.7" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ "base64 0.22.1", "bytes", @@ -3604,13 +3664,13 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.4.6", + "h2 0.4.7", "hickory-resolver", - "http 1.1.0", + "http 1.2.0", "http-body 1.0.1", "http-body-util", - "hyper 1.4.1", - "hyper-rustls 0.27.3", + "hyper 1.5.2", + "hyper-rustls 0.27.5", "hyper-tls", "hyper-util", "ipnet", @@ -3622,9 +3682,9 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.12", - "rustls-native-certs 0.7.3", - "rustls-pemfile 2.1.3", + "rustls 0.23.20", + "rustls-native-certs 0.8.1", + "rustls-pemfile 2.2.0", "rustls-pki-types", "serde", "serde_json", @@ -3633,8 +3693,9 @@ dependencies = [ "system-configuration", "tokio", "tokio-native-tls", - "tokio-rustls 0.26.0", + "tokio-rustls 0.26.1", "tokio-util", + "tower 0.5.2", "tower-service", "url", "wasm-bindgen", @@ -3721,7 +3782,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.77", + "syn 2.0.95", "walkdir", ] @@ -3749,9 +3810,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" [[package]] name = "rustc_version" @@ -3764,29 +3825,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustix" -version = "0.38.34" +version = "0.38.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" dependencies = [ - "bitflags 2.6.0", + "bitflags", "errno", "libc", - "linux-raw-sys 0.4.14", - "windows-sys 0.52.0", + "linux-raw-sys", + "windows-sys 0.59.0", ] [[package]] @@ -3810,21 +3857,21 @@ dependencies = [ "log", "ring", "rustls-pki-types", - "rustls-webpki 0.102.6", + "rustls-webpki 0.102.8", "subtle", "zeroize", ] [[package]] name = "rustls" -version = "0.23.12" +version = "0.23.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.102.6", + "rustls-webpki 0.102.8", "subtle", "zeroize", ] @@ -3838,7 +3885,7 @@ dependencies = [ "openssl-probe", "rustls-pemfile 1.0.4", "schannel", - "security-framework", + "security-framework 2.11.1", ] [[package]] @@ -3848,23 +3895,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.3", + "rustls-pemfile 2.2.0", "rustls-pki-types", "schannel", - "security-framework", + "security-framework 2.11.1", ] [[package]] name = "rustls-native-certs" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.3", "rustls-pki-types", "schannel", - "security-framework", + "security-framework 3.2.0", ] [[package]] @@ -3878,19 +3924,21 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64 0.22.1", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.8.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" +dependencies = [ + "web-time", +] [[package]] name = "rustls-webpki" @@ -3904,9 +3952,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.6" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring", "rustls-pki-types", @@ -3915,9 +3963,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "ryu" @@ -3936,20 +3984,20 @@ dependencies = [ [[package]] name = "scc" -version = "2.1.9" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ff467073ddaff34c3a39e5b454f25dd982484a26fff50254ca793c56a1b714" +checksum = "28e1c91382686d21b5ac7959341fcb9780fa7c03773646995a87c950fa7be640" dependencies = [ "sdd", ] [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3973,7 +4021,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] @@ -4000,15 +4048,15 @@ dependencies = [ [[package]] name = "sdd" -version = "2.1.0" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177258b64c0faaa9ffd3c65cd3262c2bc7e2588dbbd9c1641d0346145c1bbda8" +checksum = "478f121bb72bbf63c52c93011ea1791dca40140dfe13f8336c4c5ac952c33aa9" [[package]] name = "sec1" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", "der", @@ -4023,8 +4071,21 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", - "core-foundation", + "bitflags", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +dependencies = [ + "bitflags", + "core-foundation 0.10.0", "core-foundation-sys", "libc", "security-framework-sys", @@ -4032,9 +4093,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -4042,9 +4103,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.20" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" dependencies = [ "serde", ] @@ -4057,15 +4118,15 @@ checksum = "cec6914e06f503f83e431e1762c82003c5233b1dffb6185e51e4c40dd1c26eaa" dependencies = [ "slipped10", "stellar-strkey 0.0.8", - "thiserror", + "thiserror 1.0.69", "tiny-bip39", ] [[package]] name = "serde" -version = "1.0.192" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] @@ -4083,33 +4144,34 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.192" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] name = "serde_derive_internals" -version = "0.29.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -4126,20 +4188,20 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.17" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] name = "serde_spanned" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -4158,15 +4220,15 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.11.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.3.0", + "indexmap 2.7.0", "serde", "serde_derive", "serde_json", @@ -4176,21 +4238,21 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.11.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" +checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] name = "serial_test" -version = "3.1.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b4b487fe2acf240a021cf57c6b2b4903b1e78ca0ecd862a71b71d2a51fed77d" +checksum = "1b258109f244e1d6891bf1053a55d63a5cd4f8f4c30cf9a1280989f80e7a1fa9" dependencies = [ "futures", "log", @@ -4202,13 +4264,13 @@ dependencies = [ [[package]] name = "serial_test_derive" -version = "3.1.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" +checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] @@ -4277,9 +4339,9 @@ dependencies = [ [[package]] name = "signature" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest 0.10.7", "rand_core", @@ -4297,6 +4359,12 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "slab" version = "0.4.9" @@ -4347,19 +4415,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -4374,7 +4432,7 @@ dependencies = [ "itertools 0.10.5", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] @@ -4448,7 +4506,7 @@ dependencies = [ "tempfile", "termcolor", "termcolor_output", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-util", "toml", @@ -4544,7 +4602,7 @@ dependencies = [ "serde", "serde_json", "stellar-xdr", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] @@ -4562,7 +4620,7 @@ dependencies = [ "serde_with", "soroban-env-common", "soroban-env-host", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -4604,7 +4662,7 @@ dependencies = [ "soroban-spec", "soroban-spec-rust", "stellar-xdr", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] @@ -4615,7 +4673,7 @@ checksum = "413559f8b6c77af0cc97cd52cca0ecb59ad81004d3fd064711d91ea50274965a" dependencies = [ "base64 0.13.1", "stellar-xdr", - "thiserror", + "thiserror 1.0.69", "wasmparser", ] @@ -4630,7 +4688,7 @@ dependencies = [ "sha2 0.9.9", "soroban-spec", "stellar-xdr", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -4645,8 +4703,8 @@ dependencies = [ "sha2 0.10.8", "soroban-spec", "stellar-xdr", - "syn 2.0.77", - "thiserror", + "syn 2.0.95", + "thiserror 1.0.69", ] [[package]] @@ -4661,7 +4719,7 @@ dependencies = [ "soroban-spec", "stellar-strkey 0.0.11", "stellar-xdr", - "thiserror", + "thiserror 1.0.69", "tokio", "wasmparser", "which", @@ -4684,7 +4742,7 @@ dependencies = [ "soroban-spec", "stellar-xdr", "temp-dir", - "thiserror", + "thiserror 1.0.69", "walkdir", ] @@ -4708,7 +4766,7 @@ dependencies = [ "soroban-spec-tools", "stellar-rpc-client", "stellar-strkey 0.0.11", - "thiserror", + "thiserror 1.0.69", "tokio", "toml", "ulid", @@ -4754,6 +4812,12 @@ dependencies = [ "der", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -4804,7 +4868,7 @@ dependencies = [ "stellar-xdr", "test-case", "testcontainers", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", ] @@ -4817,7 +4881,7 @@ checksum = "d7c4daeb7f113802cc4bcce8736be32bde243e8e2bf29dda76614b949ed445cf" dependencies = [ "clap", "hex", - "http 1.1.0", + "http 1.2.0", "itertools 0.10.5", "jsonrpsee-core", "jsonrpsee-http-client", @@ -4830,7 +4894,7 @@ dependencies = [ "stellar-xdr", "termcolor", "termcolor_output", - "thiserror", + "thiserror 1.0.69", "tokio", "tracing", ] @@ -4843,7 +4907,7 @@ checksum = "12d2bf45e114117ea91d820a846fd1afbe3ba7d717988fee094ce8227a3bf8bd" dependencies = [ "base32", "crate-git-revision", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -4854,7 +4918,7 @@ checksum = "5e3aa3ed00e70082cb43febc1c2afa5056b9bb3e348bbb43d0cd0aa88a611144" dependencies = [ "crate-git-revision", "data-encoding", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -4884,7 +4948,7 @@ dependencies = [ "serde_json", "serde_with", "stellar-strkey 0.0.9", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -4915,7 +4979,7 @@ dependencies = [ "proc-macro2", "quote", "structmeta-derive", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] @@ -4926,7 +4990,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] @@ -4985,9 +5049,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a" dependencies = [ "proc-macro2", "quote", @@ -4996,21 +5060,32 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ "futures-core", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.95", +] + [[package]] name = "system-configuration" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.6.0", - "core-foundation", + "bitflags", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -5026,20 +5101,21 @@ dependencies = [ [[package]] name = "temp-dir" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f227968ec00f0e5322f9b8173c7a0cbcff6181a0a5b28e9892491c286277231" +checksum = "bc1ee6eef34f12f765cb94725905c6312b6610ab2b0940889cfe58dae7bc3c72" [[package]] name = "tempfile" -version = "3.12.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", - "fastrand 2.1.0", + "fastrand", + "getrandom", "once_cell", - "rustix 0.38.34", + "rustix", "windows-sys 0.59.0", ] @@ -5081,9 +5157,9 @@ checksum = "f34dde0bb841eb3762b42bdff8db11bbdbc0a3bd7b32012955f5ce1d081f86c1" [[package]] name = "termtree" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" [[package]] name = "test-case" @@ -5103,7 +5179,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] @@ -5114,7 +5190,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", "test-case-core", ] @@ -5190,7 +5266,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", "tokio-util", @@ -5199,22 +5275,42 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.64" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3ac7f54ca534db81081ef1c1e7f6ea8a3ef428d2fc069097c079443d24124d3" +dependencies = [ + "thiserror-impl 2.0.10", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "thiserror-impl", + "proc-macro2", + "quote", + "syn 2.0.95", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "2.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "9e9465d30713b56a37ede7185763c3492a91be2f5fa68d958c44e41ab9248beb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] @@ -5229,9 +5325,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", "itoa", @@ -5250,9 +5346,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" dependencies = [ "num-conv", "time-core", @@ -5271,7 +5367,7 @@ dependencies = [ "rand", "rustc-hash 1.1.0", "sha2 0.10.8", - "thiserror", + "thiserror 1.0.69", "unicode-normalization", "wasm-bindgen", "zeroize", @@ -5286,11 +5382,21 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" dependencies = [ "tinyvec_macros", ] @@ -5303,9 +5409,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.39.2" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", @@ -5314,20 +5420,20 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.7", + "socket2", "tokio-macros", "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] @@ -5363,20 +5469,19 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ - "rustls 0.23.12", - "rustls-pki-types", + "rustls 0.23.20", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -5385,9 +5490,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.11" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", @@ -5420,11 +5525,11 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.20" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.3.0", + "indexmap 2.7.0", "serde", "serde_spanned", "toml_datetime", @@ -5441,29 +5546,43 @@ dependencies = [ "futures-util", "pin-project", "pin-project-lite", - "tokio", "tower-layer", "tower-service", "tracing", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -5478,27 +5597,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" dependencies = [ "crossbeam-channel", - "thiserror", + "thiserror 1.0.69", "time", "tracing-subscriber", ] [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -5517,9 +5636,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", @@ -5547,54 +5666,47 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ulid" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f903f293d11f31c0c29e4148f6dc0d033a7f80cebc0282bea147611667d289" +checksum = "f294bff79170ed1c5633812aff1e565c35d993a36e757f9bc0accf5eec4e6045" dependencies = [ - "getrandom", "rand", "serde", "web-time", ] -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "untrusted" @@ -5604,16 +5716,28 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna", "percent-encoding", "serde", ] +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -5628,9 +5752,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a84c137d37ab0142f0f2ddfe332651fdbf252e7b7dbb4e67b6c1f1b2e925101" +checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2" [[package]] name = "vcpkg" @@ -5653,12 +5777,6 @@ dependencies = [ "libc", ] -[[package]] -name = "waker-fn" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" - [[package]] name = "walkdir" version = "2.5.0" @@ -5692,46 +5810,47 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5739,22 +5858,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "wasm-gen" @@ -5777,7 +5896,7 @@ dependencies = [ "strum 0.24.1", "strum_macros 0.24.3", "tempfile", - "thiserror", + "thiserror 1.0.69", "wasm-opt-cxx-sys", "wasm-opt-sys", ] @@ -5808,9 +5927,9 @@ dependencies = [ [[package]] name = "wasm-streams" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" dependencies = [ "futures-util", "js-sys", @@ -5843,7 +5962,7 @@ version = "0.116.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" dependencies = [ - "indexmap 2.3.0", + "indexmap 2.7.0", "semver", ] @@ -5858,9 +5977,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", @@ -5878,9 +5997,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.3" +version = "0.26.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" dependencies = [ "rustls-pki-types", ] @@ -5895,7 +6014,7 @@ dependencies = [ "home", "once_cell", "regex", - "rustix 0.38.34", + "rustix", ] [[package]] @@ -6135,9 +6254,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.18" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980" dependencies = [ "memchr", ] @@ -6152,11 +6271,47 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "yansi" -version = "0.5.1" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.95", + "synstructure", +] [[package]] name = "zerocopy" @@ -6176,7 +6331,28 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.95", + "synstructure", ] [[package]] @@ -6196,5 +6372,27 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.95", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.95", ] diff --git a/cmd/soroban-cli/src/commands/keys/generate.rs b/cmd/soroban-cli/src/commands/keys/generate.rs index b731c8c02..7b9376dcd 100644 --- a/cmd/soroban-cli/src/commands/keys/generate.rs +++ b/cmd/soroban-cli/src/commands/keys/generate.rs @@ -5,6 +5,7 @@ use super::super::config::{ locator, network, secret::{self, Secret}, }; + use crate::{ commands::global, config::address::KeyName, diff --git a/cmd/soroban-cli/src/config/address.rs b/cmd/soroban-cli/src/config/address.rs index 202569471..6987b523c 100644 --- a/cmd/soroban-cli/src/config/address.rs +++ b/cmd/soroban-cli/src/config/address.rs @@ -56,7 +56,7 @@ impl UnresolvedMuxedAccount { match self { UnresolvedMuxedAccount::Resolved(muxed_account) => Ok(muxed_account.clone()), UnresolvedMuxedAccount::AliasOrSecret(alias_or_secret) => { - Ok(locator.read_key(alias_or_secret)?.public_key(hd_path)?) + Ok(locator.read_key(alias_or_secret)?.muxed_account(hd_path)?) } } } diff --git a/cmd/soroban-cli/src/config/key.rs b/cmd/soroban-cli/src/config/key.rs index add554f96..9d21ad061 100644 --- a/cmd/soroban-cli/src/config/key.rs +++ b/cmd/soroban-cli/src/config/key.rs @@ -28,7 +28,7 @@ pub enum Key { } impl Key { - pub fn public_key(&self, hd_path: Option) -> Result { + pub fn muxed_account(&self, hd_path: Option) -> Result { let bytes = match self { Key::Secret(secret) => secret.public_key(hd_path)?.0, Key::PublicKey(Public(key)) => key.0, diff --git a/cmd/soroban-cli/src/config/locator.rs b/cmd/soroban-cli/src/config/locator.rs index e43c19972..ecc31247e 100644 --- a/cmd/soroban-cli/src/config/locator.rs +++ b/cmd/soroban-cli/src/config/locator.rs @@ -257,11 +257,9 @@ impl Args { } pub fn read_key(&self, key_or_name: &str) -> Result { - if let Ok(key) = self.read_identity(key_or_name) { - Ok(key) - } else { - Ok(key_or_name.parse()?) - } + key_or_name + .parse() + .or_else(|_| self.read_identity(key_or_name)) } pub fn get_secret_key(&self, key_or_name: &str) -> Result { @@ -276,7 +274,7 @@ impl Args { key_or_name: &str, hd_path: Option, ) -> Result { - Ok(self.read_key(key_or_name)?.public_key(hd_path)?) + Ok(self.read_key(key_or_name)?.muxed_account(hd_path)?) } pub fn read_network(&self, name: &str) -> Result { diff --git a/cmd/soroban-cli/src/config/sc_address.rs b/cmd/soroban-cli/src/config/sc_address.rs index c66b0c13c..b580504d5 100644 --- a/cmd/soroban-cli/src/config/sc_address.rs +++ b/cmd/soroban-cli/src/config/sc_address.rs @@ -2,7 +2,7 @@ use std::str::FromStr; use crate::xdr; -use super::{address, locator, UnresolvedContract}; +use super::{key, locator, UnresolvedContract}; /// `ScAddress` can be either a resolved `xdr::ScAddress` or an alias of a `Contract` or `MuxedAccount`. #[allow(clippy::module_name_repetitions)] @@ -23,7 +23,7 @@ pub enum Error { #[error(transparent)] Locator(#[from] locator::Error), #[error(transparent)] - Address(#[from] address::Error), + Key(#[from] key::Error), #[error("Account alias not Found{0}")] AccountAliasNotFound(String), } @@ -50,8 +50,8 @@ impl UnresolvedScAddress { UnresolvedScAddress::Alias(alias) => alias, }; let contract = UnresolvedContract::resolve_alias(&alias, locator, network_passphrase); - let muxed_account = locator.read_key(&alias)?.public_key(None); - match (contract, muxed_account) { + let key = locator.read_key(&alias); + match (contract, key) { (Ok(contract), Ok(_)) => { eprintln!( "Warning: ScAddress alias {alias} is ambiguous, assuming it is a contract" @@ -59,7 +59,9 @@ impl UnresolvedScAddress { Ok(xdr::ScAddress::Contract(xdr::Hash(contract.0))) } (Ok(contract), _) => Ok(xdr::ScAddress::Contract(xdr::Hash(contract.0))), - (_, Ok(muxed_account)) => Ok(xdr::ScAddress::Account(muxed_account.account_id())), + (_, Ok(key)) => Ok(xdr::ScAddress::Account( + key.muxed_account(None)?.account_id(), + )), _ => Err(Error::AccountAliasNotFound(alias)), } }