From 02b294b89f434232635a90b7d8751870020fab2e Mon Sep 17 00:00:00 2001 From: elizabethengelman <4752801+elizabethengelman@users.noreply.github.com> Date: Wed, 24 Sep 2025 17:58:54 -0400 Subject: [PATCH 01/23] Start to rework invoke to allow for a Signer or a SigningKey --- .../src/commands/contract/arg_parsing.rs | 127 ++++++++++-------- .../src/commands/contract/deploy/wasm.rs | 2 +- .../src/commands/contract/invoke.rs | 4 +- cmd/soroban-cli/src/config/mod.rs | 6 +- cmd/soroban-cli/src/signer/mod.rs | 88 ++++++++---- 5 files changed, 139 insertions(+), 88 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs index 8dc50fc15f..e9b6964e1a 100644 --- a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs +++ b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs @@ -1,6 +1,8 @@ use crate::commands::contract::arg_parsing::Error::HelpMessage; use crate::commands::txn_result::TxnResult; use crate::config::{self, sc_address, UnresolvedScAddress}; +use crate::print::Print; +use crate::signer::Signer; use crate::xdr::{ self, Hash, InvokeContractArgs, ScSpecEntry, ScSpecFunctionV0, ScSpecTypeDef, ScVal, ScVec, }; @@ -53,7 +55,7 @@ pub enum Error { UnsupportedScAddress(String), } -pub type HostFunctionParameters = (String, Spec, InvokeContractArgs, Vec); +pub type HostFunctionParameters = (String, Spec, InvokeContractArgs, Vec); fn running_cmd() -> String { let mut args: Vec = env::args().collect(); @@ -65,7 +67,7 @@ fn running_cmd() -> String { format!("{} --", args.join(" ")) } -pub fn build_host_function_parameters( +pub async fn build_host_function_parameters( contract_id: &stellar_strkey::Contract, slop: &[OsString], spec_entries: &[ScSpecEntry], @@ -102,60 +104,60 @@ pub fn build_host_function_parameters( let func = spec.find_function(function)?; // create parsed_args in same order as the inputs to func - let mut signers: Vec = vec![]; - let parsed_args = func - .inputs - .iter() - .map(|i| { - let name = i.name.to_utf8_string()?; - if let Some(mut val) = matches_.get_raw(&name) { - let mut s = val - .next() - .unwrap() - .to_string_lossy() - .trim_matches('"') - .to_string(); - if matches!( - i.type_, - ScSpecTypeDef::Address | ScSpecTypeDef::MuxedAddress - ) { - let addr = resolve_address(&s, config)?; - let signer = resolve_signer(&s, config); - s = addr; - if let Some(signer) = signer { - signers.push(signer); - } - } - spec.from_string(&s, &i.type_) - .map_err(|error| Error::CannotParseArg { arg: name, error }) - } else if matches!(i.type_, ScSpecTypeDef::Option(_)) { - Ok(ScVal::Void) - } else if let Some(arg_path) = matches_.get_one::(&fmt_arg_file_name(&name)) { - if matches!(i.type_, ScSpecTypeDef::Bytes | ScSpecTypeDef::BytesN(_)) { - Ok(ScVal::try_from( - &std::fs::read(arg_path) - .map_err(|_| Error::MissingFileArg(arg_path.clone()))?, - ) - .map_err(|()| Error::CannotParseArg { - arg: name.clone(), - error: soroban_spec_tools::Error::Unknown, - })?) - } else { - let file_contents = std::fs::read_to_string(arg_path) - .map_err(|_| Error::MissingFileArg(arg_path.clone()))?; - tracing::debug!( - "file {arg_path:?}, has contents:\n{file_contents}\nAnd type {:#?}\n{}", - i.type_, - file_contents.len() - ); - spec.from_string(&file_contents, &i.type_) - .map_err(|error| Error::CannotParseArg { arg: name, error }) + let mut parsed_args = Vec::with_capacity(func.inputs.len()); + let mut signers = Vec::::new(); + for i in func.inputs.iter() { + let name = i.name.to_utf8_string()?; + if let Some(mut val) = matches_.get_raw(&name) { + let mut s = val + .next() + .unwrap() + .to_string_lossy() + .trim_matches('"') + .to_string(); + if matches!( + i.type_, + ScSpecTypeDef::Address | ScSpecTypeDef::MuxedAddress + ) { + let addr = resolve_address(&s, config)?; + let signer = resolve_signer(&s, config).await; + s = addr; + if let Some(signer) = signer { + signers.push(signer); } + } + let scval = spec + .from_string(&s, &i.type_) + .map_err(|error| Error::CannotParseArg { arg: name, error })?; + + parsed_args.push(scval); + } else if matches!(i.type_, ScSpecTypeDef::Option(_)) { + parsed_args.push(ScVal::Void); + } else if let Some(arg_path) = matches_.get_one::(&fmt_arg_file_name(&name)) { + if matches!(i.type_, ScSpecTypeDef::Bytes | ScSpecTypeDef::BytesN(_)) { + let bytes = + std::fs::read(arg_path).map_err(|_| Error::MissingFileArg(arg_path.clone()))?; + parsed_args.push(ScVal::try_from(&bytes).map_err(|()| Error::CannotParseArg { + arg: name.clone(), + error: soroban_spec_tools::Error::Unknown, + })?); } else { - Err(Error::MissingArgument(name)) + let file_contents = std::fs::read_to_string(arg_path) + .map_err(|_| Error::MissingFileArg(arg_path.clone()))?; + tracing::debug!( + "file {arg_path:?}, has contents:\n{file_contents}\nAnd type {:#?}\n{}", + i.type_, + file_contents.len() + ); + parsed_args.push( + spec.from_string(&file_contents, &i.type_) + .map_err(|error| Error::CannotParseArg { arg: name, error })?, + ); } - }) - .collect::, Error>>()?; + } else { + return Err(Error::MissingArgument(name)); + } + } let contract_address_arg = xdr::ScAddress::Contract(ContractId(Hash(contract_id.0))); let function_symbol_arg = function @@ -299,12 +301,25 @@ fn resolve_address(addr_or_alias: &str, config: &config::Args) -> Result Option { - config +//todo: rename the variants +pub enum SignerKey { + Local(SigningKey), + Other(Signer), +} + +async fn resolve_signer(addr_or_alias: &str, config: &config::Args) -> Option { + if let Some(pk) = config .locator .read_key(addr_or_alias) .ok()? .private_key(None) .ok() - .map(|pk| SigningKey::from_bytes(&pk.0)) + { + Some(SignerKey::Local(SigningKey::from_bytes(&pk.0))) + } else { + let secret = config.locator.get_secret_key(addr_or_alias).ok().unwrap(); // handl;e error + let print = Print::new(false); + let signer = secret.signer(None, print).await.ok()?; // can the hd_path be none here?? + Some(SignerKey::Other(signer)) + } } diff --git a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs index 5d96d0c352..3b93104a89 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs @@ -253,7 +253,7 @@ impl NetworkRunnable for Cmd { &slop, &entries, config, - )? + ).await? .2, ) } diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index 8217395f8c..baf04e30b9 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -224,7 +224,7 @@ impl NetworkRunnable for Cmd { if let Some(spec_entries) = &spec_entries { // For testing wasm arg parsing - build_host_function_parameters(&contract_id, &self.slop, spec_entries, config)?; + build_host_function_parameters(&contract_id, &self.slop, spec_entries, config).await?; } let client = network.rpc_client()?; @@ -240,7 +240,7 @@ impl NetworkRunnable for Cmd { .map_err(Error::from)?; let params = - build_host_function_parameters(&contract_id, &self.slop, &spec_entries, config)?; + build_host_function_parameters(&contract_id, &self.slop, &spec_entries, config).await?; let (function, spec, host_function_params, signers) = params; diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index 0a058b4356..97dc584d8b 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -6,9 +6,7 @@ use std::{ }; use crate::{ - signer, - xdr::{self, SequenceNumber, Transaction, TransactionEnvelope, TransactionV1Envelope, VecM}, - Pwd, + commands::contract::arg_parsing::SignerKey, signer, xdr::{self, SequenceNumber, Transaction, TransactionEnvelope, TransactionV1Envelope, VecM}, Pwd }; use network::Network; @@ -104,7 +102,7 @@ impl Args { pub async fn sign_soroban_authorizations( &self, tx: &Transaction, - signers: &[ed25519_dalek::SigningKey], + signers: &[SignerKey], ) -> Result, Error> { let network = self.get_network()?; let source_key = self.key_pair()?; diff --git a/cmd/soroban-cli/src/signer/mod.rs b/cmd/soroban-cli/src/signer/mod.rs index 0940d5191f..1787def53c 100644 --- a/cmd/soroban-cli/src/signer/mod.rs +++ b/cmd/soroban-cli/src/signer/mod.rs @@ -1,10 +1,10 @@ -use crate::xdr::{ +use crate::{commands::contract::arg_parsing::SignerKey, xdr::{ self, AccountId, DecoratedSignature, Hash, HashIdPreimage, HashIdPreimageSorobanAuthorization, InvokeHostFunctionOp, Limits, Operation, OperationBody, PublicKey, ScAddress, ScMap, ScSymbol, ScVal, Signature, SignatureHint, SorobanAddressCredentials, SorobanAuthorizationEntry, SorobanAuthorizedFunction, SorobanCredentials, Transaction, TransactionEnvelope, TransactionV1Envelope, Uint256, VecM, WriteXdr, -}; +}}; use ed25519_dalek::ed25519::signature::Signer as _; use sha2::{Digest, Sha256}; @@ -64,7 +64,7 @@ fn requires_auth(txn: &Transaction) -> Option { pub fn sign_soroban_authorizations( raw: &Transaction, source_key: &ed25519_dalek::SigningKey, - signers: &[ed25519_dalek::SigningKey], + signers: &[SignerKey], signature_expiration_ledger: u32, network_passphrase: &str, ) -> Result, Error> { @@ -118,27 +118,55 @@ pub fn sign_soroban_authorizations( }); } }; - let signer = if let Some(s) = signers - .iter() - .find(|s| needle == s.verifying_key().as_bytes()) - { - s - } else if needle == source_address { - // This is the source address, so we can sign it - source_key - } else { - // We don't have a signer for this address + + let mut signer: Option<&SignerKey> = None; + for s in signers { + match s { + SignerKey::Local(signing_key) => { + if needle == signing_key.verifying_key().as_bytes() { + signer = Some(s); + } + }, + SignerKey::Other(_signer) => todo!(), + } + } + + let sk= SignerKey::Local(source_key.clone()); + if needle == source_address { + signer = Some(&sk); + } + + if signer.is_none() { return Err(Error::MissingSignerForAddress { address: stellar_strkey::Strkey::PublicKeyEd25519( stellar_strkey::ed25519::PublicKey(*needle), ) .to_string(), - }); - }; + }); + } + + + // let signer = if let Some(s) = signers + // .iter() + // .find(|s| needle == s.verifying_key().as_bytes()) + // { + // s + // } else if needle == source_address { + // // This is the source address, so we can sign it + // source_key + // } else { + // // We don't have a signer for this address + // return Err(Error::MissingSignerForAddress { + // address: stellar_strkey::Strkey::PublicKeyEd25519( + // stellar_strkey::ed25519::PublicKey(*needle), + // ) + // .to_string(), + // }); + // }; sign_soroban_authorization_entry( raw_auth, - signer, + &signer.unwrap(), // handle this signature_expiration_ledger, &network_id, ) @@ -152,7 +180,7 @@ pub fn sign_soroban_authorizations( fn sign_soroban_authorization_entry( raw: &SorobanAuthorizationEntry, - signer: &ed25519_dalek::SigningKey, + signer: &SignerKey, signature_expiration_ledger: u32, network_id: &Hash, ) -> Result { @@ -176,23 +204,33 @@ fn sign_soroban_authorization_entry( .to_xdr(Limits::none())?; let payload = Sha256::digest(preimage); - let signature = signer.sign(&payload); + let signature = match signer { + SignerKey::Local(signing_key) => { + signing_key.sign(&payload) + }, + SignerKey::Other(_signer) => todo!(), + }; + + let public_key_vec = match signer { + SignerKey::Local(signing_key) => { + signing_key + .verifying_key() + .to_bytes() + .to_vec() + }, + SignerKey::Other(_signer) => todo!(), + }; let map = ScMap::sorted_from(vec![ ( ScVal::Symbol(ScSymbol("public_key".try_into()?)), ScVal::Bytes( - signer - .verifying_key() - .to_bytes() - .to_vec() - .try_into() - .map_err(Error::Xdr)?, + public_key_vec.try_into().map_err(Error::Xdr)?, ), ), ( ScVal::Symbol(ScSymbol("signature".try_into()?)), - ScVal::Bytes( + ScVal::Bytes ( signature .to_bytes() .to_vec() From 136efdf99c3352d6115a6e3ab4890c87a8611e87 Mon Sep 17 00:00:00 2001 From: elizabethengelman <4752801+elizabethengelman@users.noreply.github.com> Date: Fri, 26 Sep 2025 09:53:45 -0400 Subject: [PATCH 02/23] Start to check needle match depending on the signerkey type --- .../src/commands/contract/arg_parsing.rs | 99 ++++++++++++++++++- cmd/soroban-cli/src/signer/mod.rs | 24 +++-- 2 files changed, 113 insertions(+), 10 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs index e9b6964e1a..e97aa6573e 100644 --- a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs +++ b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs @@ -8,7 +8,7 @@ use crate::xdr::{ }; use clap::error::ErrorKind::DisplayHelp; use clap::value_parser; -use ed25519_dalek::SigningKey; +use ed25519_dalek::{SigningKey, VerifyingKey}; use heck::ToKebabCase; use soroban_spec_tools::Spec; use std::collections::HashMap; @@ -307,7 +307,104 @@ pub enum SignerKey { Other(Signer), } +impl SignerKey { + pub fn verifying_key(&self) -> VerifyingKey { + match self { + SignerKey::Local(s) => s.verifying_key(), + SignerKey::Other(_s) => todo!() + } + } + + pub fn matches_verifying_key(&self, needle: &[u8; 32]) -> bool { + match self { + SignerKey::Local(s) => { + // let needle_muxed = xdr::MuxedAccount::Ed25519(xdr::Uint256(*needle)); + needle == s.verifying_key().as_bytes() + // if s.matches_verifying_key(needle) { + // signer = Some(s); + // } + // if needle == s.verifying_key().as_bytes() { + // signer = Some(s); + // } + // s.verifying_key(), + } + SignerKey::Other(_s) => { + // can i get the public key? + _s.get_public_key(); + let _needle_muxed_acct = xdr::MuxedAccount::Ed25519(xdr::Uint256(*needle)); + // needle_muxed_acct == + todo!() + } + } + + } +} + +// impl SignerKey { +// pub fn verifying_key(&self) -> VerifyingKey { +// match self { +// SignerKey::Local(s) => s.verifying_key(), +// SignerKey::Other(s) => todo!(), +// } +// } +// pub fn sign(&self, msg: &[u8]) -> Result { +// match self { +// SignerKey::Local(s) => s.sign(msg), +// SignerKey::Other(s) => s.sign(msg), +// } +// } +// } + + +// pub trait SignerKeyTrait { +// fn verifying_key(&self) -> VerifyingKey; +// fn sign(&self, msg: &[u8]) -> Result; +// } + +// #[derive(thiserror::Error, Debug)] +// pub enum SignerKeyError { +// #[error("secure store error: {0}")] +// SecureStore(String), +// #[error("in-memory sign error: {0}")] +// InMemory(String), +// } + +// pub struct LocalSigner { +// sk: ed25519_dalek::SigningKey, +// } + +// impl LocalSigner { +// pub fn new(sk: ed25519_dalek::SigningKey) -> Self { Self { sk } } +// } + +// impl SignerKeyTrait for LocalSigner { +// fn verifying_key(&self) -> VerifyingKey { self.sk.verifying_key() } +// fn sign(&self, msg: &[u8]) -> Result { +// Ok(signature::Signer::::try_sign(&self.sk, msg) +// .map_err(|e| SignerKeyError::InMemory(e.to_string()))?) +// } +// } + async fn resolve_signer(addr_or_alias: &str, config: &config::Args) -> Option { + // let key = config.locator.read_key(addr_or_alias).unwrap(); + + // match key { + // config::key::Key::PublicKey(_public) => todo!(), + // config::key::Key::MuxedAccount(_muxed_account) => todo!(), + // config::key::Key::Secret(secret) => { + // match secret { + // config::secret::Secret::SecretKey { secret_key } => key.private_key(None), + // config::secret::Secret::SeedPhrase { seed_phrase } => key.private_key(None), + // config::secret::Secret::Ledger => todo!(), + // config::secret::Secret::SecureStore { entry_name } => todo!(), + // } + // }, + // } + + let key = config.locator.read_key(addr_or_alias); + let m = key.unwrap().muxed_account(None).unwrap(); + println!("muxed {:?}", m); + if let Some(pk) = config .locator .read_key(addr_or_alias) diff --git a/cmd/soroban-cli/src/signer/mod.rs b/cmd/soroban-cli/src/signer/mod.rs index 1787def53c..d483c4f44e 100644 --- a/cmd/soroban-cli/src/signer/mod.rs +++ b/cmd/soroban-cli/src/signer/mod.rs @@ -104,7 +104,7 @@ pub fn sign_soroban_authorizations( // See if we have a signer for this authorizationEntry // If not, then we Error - let needle = match address { + let needle: &[u8; 32] = match address { ScAddress::MuxedAccount(_) => todo!("muxed accounts are not supported"), ScAddress::ClaimableBalance(_) => todo!("claimable balance not supported"), ScAddress::LiquidityPool(_) => todo!("liquidity pool not supported"), @@ -120,18 +120,15 @@ pub fn sign_soroban_authorizations( }; let mut signer: Option<&SignerKey> = None; + // let needle_muxed = xdr::MuxedAccount::Ed25519(xdr::Uint256(*needle)); + // println!("NEedle Muxed {:?}", needle_muxed); for s in signers { - match s { - SignerKey::Local(signing_key) => { - if needle == signing_key.verifying_key().as_bytes() { - signer = Some(s); - } - }, - SignerKey::Other(_signer) => todo!(), + if s.matches_verifying_key(needle) { + signer = Some(s); } } - let sk= SignerKey::Local(source_key.clone()); + let sk= SignerKey::Local(source_key.clone()); //this may not necessarily be a local signer if needle == source_address { signer = Some(&sk); } @@ -302,6 +299,15 @@ impl Signer { _ => Err(Error::UnsupportedTransactionEnvelopeType), } } + + pub fn get_public_key(&self) { + match &self.kind { + SignerKind::Local(_local_key) => todo!("local key"), + SignerKind::Ledger(_ledger) => todo!("ledger key"), + SignerKind::Lab => todo!("lab"), + SignerKind::SecureStore(_secure_store_entry) => todo!("secure store"), + } + } } pub struct LocalKey { From f1cd73d443cc1438fc9b1d30aeb063276fc563f8 Mon Sep 17 00:00:00 2001 From: elizabethengelman <4752801+elizabethengelman@users.noreply.github.com> Date: Fri, 26 Sep 2025 10:52:24 -0400 Subject: [PATCH 03/23] Change map in sign_soroban_authorizations to for loop so we can call an async function more easily --- cmd/soroban-cli/src/signer/mod.rs | 189 ++++++++++++++---------------- 1 file changed, 91 insertions(+), 98 deletions(-) diff --git a/cmd/soroban-cli/src/signer/mod.rs b/cmd/soroban-cli/src/signer/mod.rs index d483c4f44e..f84e291ff2 100644 --- a/cmd/soroban-cli/src/signer/mod.rs +++ b/cmd/soroban-cli/src/signer/mod.rs @@ -1,10 +1,14 @@ -use crate::{commands::contract::arg_parsing::SignerKey, xdr::{ - self, AccountId, DecoratedSignature, Hash, HashIdPreimage, HashIdPreimageSorobanAuthorization, - InvokeHostFunctionOp, Limits, Operation, OperationBody, PublicKey, ScAddress, ScMap, ScSymbol, - ScVal, Signature, SignatureHint, SorobanAddressCredentials, SorobanAuthorizationEntry, - SorobanAuthorizedFunction, SorobanCredentials, Transaction, TransactionEnvelope, - TransactionV1Envelope, Uint256, VecM, WriteXdr, -}}; +use crate::{ + commands::contract::arg_parsing::SignerKey, + xdr::{ + self, AccountId, DecoratedSignature, Hash, HashIdPreimage, + HashIdPreimageSorobanAuthorization, InvokeHostFunctionOp, Limits, Operation, OperationBody, + PublicKey, ScAddress, ScMap, ScSymbol, ScVal, Signature, SignatureHint, + SorobanAddressCredentials, SorobanAuthorizationEntry, SorobanAuthorizedFunction, + SorobanCredentials, Transaction, TransactionEnvelope, TransactionV1Envelope, Uint256, VecM, + WriteXdr, + }, +}; use ed25519_dalek::ed25519::signature::Signer as _; use sha2::{Digest, Sha256}; @@ -86,89 +90,87 @@ pub fn sign_soroban_authorizations( let verification_key = source_key.verifying_key(); let source_address = verification_key.as_bytes(); - let signed_auths = body - .auth - .as_slice() - .iter() - .map(|raw_auth| { - let mut auth = raw_auth.clone(); - let SorobanAuthorizationEntry { - credentials: SorobanCredentials::Address(ref mut credentials), - .. - } = auth - else { - // Doesn't need special signing - return Ok(auth); - }; - let SorobanAddressCredentials { ref address, .. } = credentials; - - // See if we have a signer for this authorizationEntry - // If not, then we Error - let needle: &[u8; 32] = match address { - ScAddress::MuxedAccount(_) => todo!("muxed accounts are not supported"), - ScAddress::ClaimableBalance(_) => todo!("claimable balance not supported"), - ScAddress::LiquidityPool(_) => todo!("liquidity pool not supported"), - ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519(Uint256(ref a)))) => a, - ScAddress::Contract(stellar_xdr::curr::ContractId(Hash(c))) => { - // This address is for a contract. This means we're using a custom - // smart-contract account. Currently the CLI doesn't support that yet. - return Err(Error::MissingSignerForAddress { - address: stellar_strkey::Strkey::Contract(stellar_strkey::Contract(*c)) - .to_string(), - }); - } - }; - - let mut signer: Option<&SignerKey> = None; - // let needle_muxed = xdr::MuxedAccount::Ed25519(xdr::Uint256(*needle)); - // println!("NEedle Muxed {:?}", needle_muxed); - for s in signers { - if s.matches_verifying_key(needle) { - signer = Some(s); - } + let mut signed_auths = Vec::with_capacity(body.auth.len()); + for raw_auth in body.auth.as_slice().iter() { + let mut auth = raw_auth.clone(); + let SorobanAuthorizationEntry { + credentials: SorobanCredentials::Address(ref mut credentials), + .. + } = auth + else { + // Doesn't need special signing + // return Ok(auth); + signed_auths.push(auth); + continue; + }; + let SorobanAddressCredentials { ref address, .. } = credentials; + + // See if we have a signer for this authorizationEntry + // If not, then we Error + let needle: &[u8; 32] = match address { + ScAddress::MuxedAccount(_) => todo!("muxed accounts are not supported"), + ScAddress::ClaimableBalance(_) => todo!("claimable balance not supported"), + ScAddress::LiquidityPool(_) => todo!("liquidity pool not supported"), + ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519(Uint256(ref a)))) => a, + ScAddress::Contract(stellar_xdr::curr::ContractId(Hash(c))) => { + // This address is for a contract. This means we're using a custom + // smart-contract account. Currently the CLI doesn't support that yet. + return Err(Error::MissingSignerForAddress { + address: stellar_strkey::Strkey::Contract(stellar_strkey::Contract(*c)) + .to_string(), + }); } - - let sk= SignerKey::Local(source_key.clone()); //this may not necessarily be a local signer - if needle == source_address { - signer = Some(&sk); + }; + + let mut signer: Option<&SignerKey> = None; + // let needle_muxed = xdr::MuxedAccount::Ed25519(xdr::Uint256(*needle)); + // println!("NEedle Muxed {:?}", needle_muxed); + for s in signers { + if s.matches_verifying_key(needle) { + signer = Some(s); } + } - if signer.is_none() { - return Err(Error::MissingSignerForAddress { - address: stellar_strkey::Strkey::PublicKeyEd25519( - stellar_strkey::ed25519::PublicKey(*needle), - ) - .to_string(), - }); - } + let sk = SignerKey::Local(source_key.clone()); //this may not necessarily be a local signer + if needle == source_address { + signer = Some(&sk); + } + if signer.is_none() { + return Err(Error::MissingSignerForAddress { + address: stellar_strkey::Strkey::PublicKeyEd25519( + stellar_strkey::ed25519::PublicKey(*needle), + ) + .to_string(), + }); + } - // let signer = if let Some(s) = signers - // .iter() - // .find(|s| needle == s.verifying_key().as_bytes()) - // { - // s - // } else if needle == source_address { - // // This is the source address, so we can sign it - // source_key - // } else { - // // We don't have a signer for this address - // return Err(Error::MissingSignerForAddress { - // address: stellar_strkey::Strkey::PublicKeyEd25519( - // stellar_strkey::ed25519::PublicKey(*needle), - // ) - // .to_string(), - // }); - // }; - - sign_soroban_authorization_entry( - raw_auth, - &signer.unwrap(), // handle this - signature_expiration_ledger, - &network_id, - ) - }) - .collect::, Error>>()?; + // let signer = if let Some(s) = signers + // .iter() + // .find(|s| needle == s.verifying_key().as_bytes()) + // { + // s + // } else if needle == source_address { + // // This is the source address, so we can sign it + // source_key + // } else { + // // We don't have a signer for this address + // return Err(Error::MissingSignerForAddress { + // address: stellar_strkey::Strkey::PublicKeyEd25519( + // stellar_strkey::ed25519::PublicKey(*needle), + // ) + // .to_string(), + // }); + // }; + + let signed = sign_soroban_authorization_entry( + raw_auth, + &signer.unwrap(), // handle this + signature_expiration_ledger, + &network_id, + )?; + signed_auths.push(signed); + } body.auth = signed_auths.try_into()?; tx.operations = vec![op].try_into()?; @@ -202,32 +204,23 @@ fn sign_soroban_authorization_entry( let payload = Sha256::digest(preimage); let signature = match signer { - SignerKey::Local(signing_key) => { - signing_key.sign(&payload) - }, + SignerKey::Local(signing_key) => signing_key.sign(&payload), SignerKey::Other(_signer) => todo!(), }; let public_key_vec = match signer { - SignerKey::Local(signing_key) => { - signing_key - .verifying_key() - .to_bytes() - .to_vec() - }, + SignerKey::Local(signing_key) => signing_key.verifying_key().to_bytes().to_vec(), SignerKey::Other(_signer) => todo!(), }; let map = ScMap::sorted_from(vec![ ( ScVal::Symbol(ScSymbol("public_key".try_into()?)), - ScVal::Bytes( - public_key_vec.try_into().map_err(Error::Xdr)?, - ), + ScVal::Bytes(public_key_vec.try_into().map_err(Error::Xdr)?), ), ( ScVal::Symbol(ScSymbol("signature".try_into()?)), - ScVal::Bytes ( + ScVal::Bytes( signature .to_bytes() .to_vec() From bb055500449d70998d430c22ca83b0dfe5e47c5b Mon Sep 17 00:00:00 2001 From: elizabethengelman <4752801+elizabethengelman@users.noreply.github.com> Date: Fri, 26 Sep 2025 10:54:04 -0400 Subject: [PATCH 04/23] Await matches_verifying_key result --- cmd/soroban-cli/src/commands/contract/arg_parsing.rs | 2 +- cmd/soroban-cli/src/config/mod.rs | 2 +- cmd/soroban-cli/src/signer/mod.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs index e97aa6573e..72c37439de 100644 --- a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs +++ b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs @@ -315,7 +315,7 @@ impl SignerKey { } } - pub fn matches_verifying_key(&self, needle: &[u8; 32]) -> bool { + pub async fn matches_verifying_key(&self, needle: &[u8; 32]) -> bool { match self { SignerKey::Local(s) => { // let needle_muxed = xdr::MuxedAccount::Ed25519(xdr::Uint256(*needle)); diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index 97dc584d8b..f03f1cb6c0 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -115,7 +115,7 @@ impl Args { signers, seq_num, &network.network_passphrase, - )?) + ).await?) } pub fn get_network(&self) -> Result { diff --git a/cmd/soroban-cli/src/signer/mod.rs b/cmd/soroban-cli/src/signer/mod.rs index f84e291ff2..d25f4edc4d 100644 --- a/cmd/soroban-cli/src/signer/mod.rs +++ b/cmd/soroban-cli/src/signer/mod.rs @@ -65,7 +65,7 @@ fn requires_auth(txn: &Transaction) -> Option { // Use the given source_key and signers, to sign all SorobanAuthorizationEntry's in the given // transaction. If unable to sign, return an error. -pub fn sign_soroban_authorizations( +pub async fn sign_soroban_authorizations( raw: &Transaction, source_key: &ed25519_dalek::SigningKey, signers: &[SignerKey], @@ -126,7 +126,7 @@ pub fn sign_soroban_authorizations( // let needle_muxed = xdr::MuxedAccount::Ed25519(xdr::Uint256(*needle)); // println!("NEedle Muxed {:?}", needle_muxed); for s in signers { - if s.matches_verifying_key(needle) { + if s.matches_verifying_key(needle).await { signer = Some(s); } } From e688f9b6b07bbfa583bf6981293e10c1f57bfe9e Mon Sep 17 00:00:00 2001 From: elizabethengelman <4752801+elizabethengelman@users.noreply.github.com> Date: Fri, 26 Sep 2025 11:37:28 -0400 Subject: [PATCH 05/23] Sign payload with secure store --- .../src/commands/contract/arg_parsing.rs | 16 +---- cmd/soroban-cli/src/signer/mod.rs | 59 ++++++++++++++++--- 2 files changed, 55 insertions(+), 20 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs index 72c37439de..65c448c572 100644 --- a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs +++ b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs @@ -320,20 +320,10 @@ impl SignerKey { SignerKey::Local(s) => { // let needle_muxed = xdr::MuxedAccount::Ed25519(xdr::Uint256(*needle)); needle == s.verifying_key().as_bytes() - // if s.matches_verifying_key(needle) { - // signer = Some(s); - // } - // if needle == s.verifying_key().as_bytes() { - // signer = Some(s); - // } - // s.verifying_key(), } - SignerKey::Other(_s) => { - // can i get the public key? - _s.get_public_key(); - let _needle_muxed_acct = xdr::MuxedAccount::Ed25519(xdr::Uint256(*needle)); - // needle_muxed_acct == - todo!() + SignerKey::Other(s) => { + let signer_pk = s.get_public_key().await.unwrap(); + signer_pk.0 == *needle } } diff --git a/cmd/soroban-cli/src/signer/mod.rs b/cmd/soroban-cli/src/signer/mod.rs index d25f4edc4d..baf8b31273 100644 --- a/cmd/soroban-cli/src/signer/mod.rs +++ b/cmd/soroban-cli/src/signer/mod.rs @@ -9,7 +9,7 @@ use crate::{ WriteXdr, }, }; -use ed25519_dalek::ed25519::signature::Signer as _; +use ed25519_dalek::{ed25519::signature::Signer as _, Signature as Ed25519Signature}; use sha2::{Digest, Sha256}; use crate::{config::network::Network, print::Print, utils::transaction_hash}; @@ -168,7 +168,7 @@ pub async fn sign_soroban_authorizations( &signer.unwrap(), // handle this signature_expiration_ledger, &network_id, - )?; + ).await?; signed_auths.push(signed); } @@ -177,7 +177,7 @@ pub async fn sign_soroban_authorizations( Ok(Some(tx)) } -fn sign_soroban_authorization_entry( +async fn sign_soroban_authorization_entry( raw: &SorobanAuthorizationEntry, signer: &SignerKey, signature_expiration_ledger: u32, @@ -205,12 +205,12 @@ fn sign_soroban_authorization_entry( let payload = Sha256::digest(preimage); let signature = match signer { SignerKey::Local(signing_key) => signing_key.sign(&payload), - SignerKey::Other(_signer) => todo!(), + SignerKey::Other(signer) => signer.sign_payload(&payload).await?, }; let public_key_vec = match signer { SignerKey::Local(signing_key) => signing_key.verifying_key().to_bytes().to_vec(), - SignerKey::Other(_signer) => todo!(), + SignerKey::Other(signer) => signer.get_public_key().await?.0.to_vec(), }; let map = ScMap::sorted_from(vec![ @@ -293,14 +293,49 @@ impl Signer { } } - pub fn get_public_key(&self) { + pub async fn get_public_key(&self) -> Result{ match &self.kind { SignerKind::Local(_local_key) => todo!("local key"), SignerKind::Ledger(_ledger) => todo!("ledger key"), SignerKind::Lab => todo!("lab"), - SignerKind::SecureStore(_secure_store_entry) => todo!("secure store"), + SignerKind::SecureStore(secure_store_entry) => secure_store_entry.get_public_key(), } } + + pub async fn sign_payload( + &self, + payload: &[u8], + ) -> Result { + match &self.kind { + SignerKind::Local(_local_key) => todo!("local key"), + SignerKind::Ledger(_ledger) => todo!("ledger"), + SignerKind::Lab => todo!("lab"), + SignerKind::SecureStore(secure_store_entry) => { + let p = <[u8; 32]>::try_from(payload)?; + secure_store_entry.sign_payload(p) + } + } + // match &tx_env { + // TransactionEnvelope::Tx(TransactionV1Envelope { tx, signatures }) => { + // let tx_hash = transaction_hash(tx, &network.network_passphrase)?; + // self.print + // .infoln(format!("Signing transaction: {}", hex::encode(tx_hash),)); + // let decorated_signature = match &self.kind { + // SignerKind::Local(key) => key.sign_tx_hash(tx_hash)?, + // SignerKind::Lab => Lab::sign_tx_env(tx_env, network, &self.print)?, + // SignerKind::Ledger(ledger) => ledger.sign_transaction_hash(&tx_hash).await?, + // SignerKind::SecureStore(entry) => entry.sign_tx_hash(tx_hash)?, + // }; + // let mut sigs = signatures.clone().into_vec(); + // sigs.push(decorated_signature); + // Ok(TransactionEnvelope::Tx(TransactionV1Envelope { + // tx: tx.clone(), + // signatures: sigs.try_into()?, + // })) + // } + // _ => Err(Error::UnsupportedTransactionEnvelopeType), + // } + } } pub struct LocalKey { @@ -346,6 +381,10 @@ pub struct SecureStoreEntry { } impl SecureStoreEntry { + pub fn get_public_key(&self) -> Result { + Ok(secure_store::get_public_key(&self.name, self.hd_path)?) + } + pub fn sign_tx_hash(&self, tx_hash: [u8; 32]) -> Result { let hint = SignatureHint( secure_store::get_public_key(&self.name, self.hd_path)?.0[28..].try_into()?, @@ -356,4 +395,10 @@ impl SecureStoreEntry { let signature = Signature(signed_tx_hash.clone().try_into()?); Ok(DecoratedSignature { hint, signature }) } + + pub fn sign_payload(&self, payload: [u8; 32]) -> Result { + let signed_bytes = secure_store::sign_tx_data(&self.name, self.hd_path, &payload)?; + let sig = Ed25519Signature::from_bytes(signed_bytes.as_slice().try_into()?); + Ok(sig) + } } From e3cd18ee3d668b4cd40a0c83eca1463fd0e06030 Mon Sep 17 00:00:00 2001 From: elizabethengelman <4752801+elizabethengelman@users.noreply.github.com> Date: Tue, 30 Sep 2025 13:25:08 -0400 Subject: [PATCH 06/23] Cleanup / remove unsued code --- .../src/commands/contract/arg_parsing.rs | 19 ++--------------- cmd/soroban-cli/src/signer/mod.rs | 21 ------------------- 2 files changed, 2 insertions(+), 38 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs index 65c448c572..8a130226b5 100644 --- a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs +++ b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs @@ -8,7 +8,7 @@ use crate::xdr::{ }; use clap::error::ErrorKind::DisplayHelp; use clap::value_parser; -use ed25519_dalek::{SigningKey, VerifyingKey}; +use ed25519_dalek::SigningKey; use heck::ToKebabCase; use soroban_spec_tools::Spec; use std::collections::HashMap; @@ -301,20 +301,12 @@ fn resolve_address(addr_or_alias: &str, config: &config::Args) -> Result VerifyingKey { - match self { - SignerKey::Local(s) => s.verifying_key(), - SignerKey::Other(_s) => todo!() - } - } - pub async fn matches_verifying_key(&self, needle: &[u8; 32]) -> bool { match self { SignerKey::Local(s) => { @@ -331,12 +323,6 @@ impl SignerKey { } // impl SignerKey { -// pub fn verifying_key(&self) -> VerifyingKey { -// match self { -// SignerKey::Local(s) => s.verifying_key(), -// SignerKey::Other(s) => todo!(), -// } -// } // pub fn sign(&self, msg: &[u8]) -> Result { // match self { // SignerKey::Local(s) => s.sign(msg), @@ -376,7 +362,6 @@ impl SignerKey { // } async fn resolve_signer(addr_or_alias: &str, config: &config::Args) -> Option { - // let key = config.locator.read_key(addr_or_alias).unwrap(); // match key { // config::key::Key::PublicKey(_public) => todo!(), @@ -404,7 +389,7 @@ async fn resolve_signer(addr_or_alias: &str, config: &config::Args) -> Option { - // let tx_hash = transaction_hash(tx, &network.network_passphrase)?; - // self.print - // .infoln(format!("Signing transaction: {}", hex::encode(tx_hash),)); - // let decorated_signature = match &self.kind { - // SignerKind::Local(key) => key.sign_tx_hash(tx_hash)?, - // SignerKind::Lab => Lab::sign_tx_env(tx_env, network, &self.print)?, - // SignerKind::Ledger(ledger) => ledger.sign_transaction_hash(&tx_hash).await?, - // SignerKind::SecureStore(entry) => entry.sign_tx_hash(tx_hash)?, - // }; - // let mut sigs = signatures.clone().into_vec(); - // sigs.push(decorated_signature); - // Ok(TransactionEnvelope::Tx(TransactionV1Envelope { - // tx: tx.clone(), - // signatures: sigs.try_into()?, - // })) - // } - // _ => Err(Error::UnsupportedTransactionEnvelopeType), - // } } } From cdbd99d265502acfef8b5ac4e3a62463b28527d2 Mon Sep 17 00:00:00 2001 From: elizabethengelman <4752801+elizabethengelman@users.noreply.github.com> Date: Tue, 30 Sep 2025 16:38:23 -0400 Subject: [PATCH 07/23] Resolve arg_parsing signer as a SignerKey and signing invoke tx with secure store --- .../src/commands/contract/arg_parsing.rs | 36 +++---------------- cmd/soroban-cli/src/config/mod.rs | 4 +-- cmd/soroban-cli/src/signer/mod.rs | 24 +++++++++---- 3 files changed, 23 insertions(+), 41 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs index 8a130226b5..ce9130fec2 100644 --- a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs +++ b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs @@ -362,36 +362,8 @@ impl SignerKey { // } async fn resolve_signer(addr_or_alias: &str, config: &config::Args) -> Option { - - // match key { - // config::key::Key::PublicKey(_public) => todo!(), - // config::key::Key::MuxedAccount(_muxed_account) => todo!(), - // config::key::Key::Secret(secret) => { - // match secret { - // config::secret::Secret::SecretKey { secret_key } => key.private_key(None), - // config::secret::Secret::SeedPhrase { seed_phrase } => key.private_key(None), - // config::secret::Secret::Ledger => todo!(), - // config::secret::Secret::SecureStore { entry_name } => todo!(), - // } - // }, - // } - - let key = config.locator.read_key(addr_or_alias); - let m = key.unwrap().muxed_account(None).unwrap(); - println!("muxed {:?}", m); - - if let Some(pk) = config - .locator - .read_key(addr_or_alias) - .ok()? - .private_key(None) - .ok() - { - Some(SignerKey::Local(SigningKey::from_bytes(&pk.0))) - } else { - let secret = config.locator.get_secret_key(addr_or_alias).ok()?; - let print = Print::new(false); - let signer = secret.signer(None, print).await.ok()?; // can the hd_path be none here?? - Some(SignerKey::Other(signer)) - } + let secret = config.locator.get_secret_key(addr_or_alias).unwrap(); + let print = Print::new(false); + let signer = secret.signer(None, print).await.ok()?; // can the hd_path be none here?? + Some(SignerKey::Other(signer)) } diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index f03f1cb6c0..737e835c50 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -105,13 +105,13 @@ impl Args { signers: &[SignerKey], ) -> Result, Error> { let network = self.get_network()?; - let source_key = self.key_pair()?; + let source_account = self.source_account().await.unwrap();// handle this! let client = network.rpc_client()?; let latest_ledger = client.get_latest_ledger().await?.sequence; let seq_num = latest_ledger + 60; // ~ 5 min Ok(signer::sign_soroban_authorizations( tx, - &source_key, + &source_account, signers, seq_num, &network.network_passphrase, diff --git a/cmd/soroban-cli/src/signer/mod.rs b/cmd/soroban-cli/src/signer/mod.rs index d85d06d756..b3b9166be6 100644 --- a/cmd/soroban-cli/src/signer/mod.rs +++ b/cmd/soroban-cli/src/signer/mod.rs @@ -9,7 +9,7 @@ use crate::{ WriteXdr, }, }; -use ed25519_dalek::{ed25519::signature::Signer as _, Signature as Ed25519Signature}; +use ed25519_dalek::{ed25519::signature::{Signer as _}, Signature as Ed25519Signature}; use sha2::{Digest, Sha256}; use crate::{config::network::Network, print::Print, utils::transaction_hash}; @@ -46,6 +46,8 @@ pub enum Error { SecureStore(#[from] secure_store::Error), #[error(transparent)] Ledger(#[from] ledger::Error), + #[error(transparent)] + Decode(#[from] stellar_strkey::DecodeError), } fn requires_auth(txn: &Transaction) -> Option { @@ -67,7 +69,7 @@ fn requires_auth(txn: &Transaction) -> Option { // transaction. If unable to sign, return an error. pub async fn sign_soroban_authorizations( raw: &Transaction, - source_key: &ed25519_dalek::SigningKey, + _source_account: &xdr::MuxedAccount, signers: &[SignerKey], signature_expiration_ledger: u32, network_passphrase: &str, @@ -87,9 +89,6 @@ pub async fn sign_soroban_authorizations( let network_id = Hash(Sha256::digest(network_passphrase.as_bytes()).into()); - let verification_key = source_key.verifying_key(); - let source_address = verification_key.as_bytes(); - let mut signed_auths = Vec::with_capacity(body.auth.len()); for raw_auth in body.auth.as_slice().iter() { let mut auth = raw_auth.clone(); @@ -294,7 +293,11 @@ impl Signer { pub async fn get_public_key(&self) -> Result{ match &self.kind { - SignerKind::Local(_local_key) => todo!("local key"), + SignerKind::Local(local_key) => { + + let k = local_key.key.verifying_key().as_bytes().clone(); + Ok(stellar_strkey::ed25519::PublicKey::from_payload(&k)?) + }, SignerKind::Ledger(_ledger) => todo!("ledger key"), SignerKind::Lab => todo!("lab"), SignerKind::SecureStore(secure_store_entry) => secure_store_entry.get_public_key(), @@ -306,7 +309,10 @@ impl Signer { payload: &[u8], ) -> Result { match &self.kind { - SignerKind::Local(_local_key) => todo!("local key"), + SignerKind::Local(local_key) => { + let p = <[u8; 32]>::try_from(payload)?; + local_key.sign_payload(p) + }, SignerKind::Ledger(_ledger) => todo!("ledger"), SignerKind::Lab => todo!("lab"), SignerKind::SecureStore(secure_store_entry) => { @@ -327,6 +333,10 @@ impl LocalKey { let signature = Signature(self.key.sign(&tx_hash).to_bytes().to_vec().try_into()?); Ok(DecoratedSignature { hint, signature }) } + + pub fn sign_payload(&self, payload: [u8; 32]) -> Result { + Ok(self.key.sign(&payload)) + } } pub struct Lab; From bf6dfe89dd5bde0ddd3d0367ef2787c2b2bcf4b8 Mon Sep 17 00:00:00 2001 From: elizabethengelman <4752801+elizabethengelman@users.noreply.github.com> Date: Wed, 1 Oct 2025 12:57:35 -0400 Subject: [PATCH 08/23] Start to remove SignerKey enum we should be able to have resolve_signer (in arg_parsing) always return a Signer instead of a SigningKey --- .../src/commands/contract/arg_parsing.rs | 6 ------ cmd/soroban-cli/src/signer/mod.rs | 13 +++++-------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs index ce9130fec2..d4c707bcbf 100644 --- a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs +++ b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs @@ -8,7 +8,6 @@ use crate::xdr::{ }; use clap::error::ErrorKind::DisplayHelp; use clap::value_parser; -use ed25519_dalek::SigningKey; use heck::ToKebabCase; use soroban_spec_tools::Spec; use std::collections::HashMap; @@ -302,17 +301,12 @@ fn resolve_address(addr_or_alias: &str, config: &config::Args) -> Result bool { match self { - SignerKey::Local(s) => { - // let needle_muxed = xdr::MuxedAccount::Ed25519(xdr::Uint256(*needle)); - needle == s.verifying_key().as_bytes() - } SignerKey::Other(s) => { let signer_pk = s.get_public_key().await.unwrap(); signer_pk.0 == *needle diff --git a/cmd/soroban-cli/src/signer/mod.rs b/cmd/soroban-cli/src/signer/mod.rs index b3b9166be6..6c713ea281 100644 --- a/cmd/soroban-cli/src/signer/mod.rs +++ b/cmd/soroban-cli/src/signer/mod.rs @@ -121,18 +121,15 @@ pub async fn sign_soroban_authorizations( }; let mut signer: Option<&SignerKey> = None; - // let needle_muxed = xdr::MuxedAccount::Ed25519(xdr::Uint256(*needle)); - // println!("NEedle Muxed {:?}", needle_muxed); for s in signers { if s.matches_verifying_key(needle).await { signer = Some(s); } } - let sk = SignerKey::Local(source_key.clone()); //this may not necessarily be a local signer - if needle == source_address { - signer = Some(&sk); - } + // if needle == source_address { + // signer = Some(&sk); + // } if signer.is_none() { return Err(Error::MissingSignerForAddress { @@ -202,12 +199,12 @@ async fn sign_soroban_authorization_entry( let payload = Sha256::digest(preimage); let signature = match signer { - SignerKey::Local(signing_key) => signing_key.sign(&payload), + // SignerKey::Local(signing_key) => signing_key.sign(&payload), SignerKey::Other(signer) => signer.sign_payload(&payload).await?, }; let public_key_vec = match signer { - SignerKey::Local(signing_key) => signing_key.verifying_key().to_bytes().to_vec(), + // SignerKey::Local(signing_key) => signing_key.verifying_key().to_bytes().to_vec(), SignerKey::Other(signer) => signer.get_public_key().await?.0.to_vec(), }; From 49c39ce7db309f5f4f06545269047d3220005dac Mon Sep 17 00:00:00 2001 From: elizabethengelman <4752801+elizabethengelman@users.noreply.github.com> Date: Wed, 1 Oct 2025 13:34:19 -0400 Subject: [PATCH 09/23] Continue removing SignerKey enum in favor of just a Signer --- .../src/commands/contract/arg_parsing.rs | 22 ++++---- cmd/soroban-cli/src/signer/mod.rs | 55 ++++++++++--------- 2 files changed, 39 insertions(+), 38 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs index d4c707bcbf..836fed3f02 100644 --- a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs +++ b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs @@ -2,7 +2,7 @@ use crate::commands::contract::arg_parsing::Error::HelpMessage; use crate::commands::txn_result::TxnResult; use crate::config::{self, sc_address, UnresolvedScAddress}; use crate::print::Print; -use crate::signer::Signer; +use crate::signer::{self, Signer}; use crate::xdr::{ self, Hash, InvokeContractArgs, ScSpecEntry, ScSpecFunctionV0, ScSpecTypeDef, ScVal, ScVec, }; @@ -52,6 +52,8 @@ pub enum Error { HelpMessage(String), #[error("Unsupported ScAddress {0}")] UnsupportedScAddress(String), + #[error(transparent)] + Signer(#[from] signer::Error), } pub type HostFunctionParameters = (String, Spec, InvokeContractArgs, Vec); @@ -300,19 +302,15 @@ fn resolve_address(addr_or_alias: &str, config: &config::Args) -> Result bool { - match self { - SignerKey::Other(s) => { - let signer_pk = s.get_public_key().await.unwrap(); - signer_pk.0 == *needle - } - } + pub async fn sign_payload(&self, payload: &[u8]) -> Result{ + Ok(self.0.sign_payload(payload).await?) + } + pub async fn get_public_key(&self) -> Result<[u8; 32], Error>{ + Ok(self.0.get_public_key().await?) } } @@ -359,5 +357,5 @@ async fn resolve_signer(addr_or_alias: &str, config: &config::Args) -> Option = None; for s in signers { - if s.matches_verifying_key(needle).await { - signer = Some(s); + let pk = s.get_public_key().await.unwrap(); + if needle == &pk { + signer = Some(s); } } @@ -163,7 +164,8 @@ pub async fn sign_soroban_authorizations( &signer.unwrap(), // handle this signature_expiration_ledger, &network_id, - ).await?; + ) + .await?; signed_auths.push(signed); } @@ -198,15 +200,8 @@ async fn sign_soroban_authorization_entry( .to_xdr(Limits::none())?; let payload = Sha256::digest(preimage); - let signature = match signer { - // SignerKey::Local(signing_key) => signing_key.sign(&payload), - SignerKey::Other(signer) => signer.sign_payload(&payload).await?, - }; - - let public_key_vec = match signer { - // SignerKey::Local(signing_key) => signing_key.verifying_key().to_bytes().to_vec(), - SignerKey::Other(signer) => signer.get_public_key().await?.0.to_vec(), - }; + let signature = signer.sign_payload(&payload).await.unwrap(); + let public_key_vec = signer.get_public_key().await.unwrap().to_vec(); let map = ScMap::sorted_from(vec![ ( @@ -246,8 +241,7 @@ pub enum SignerKind { SecureStore(SecureStoreEntry), } -// Instead of using `sign_tx` and `sign_tx_env` directly, it is advised to instead use the sign_with module -// which allows for signing with a local key, lab or a ledger device +// It is advised to use the sign_with module, which handles creating a Signer with the appropriate SignerKind impl Signer { pub async fn sign_tx( &self, @@ -288,28 +282,37 @@ impl Signer { } } - pub async fn get_public_key(&self) -> Result{ + // pub async fn get_raw_public_key(&self) -> Result<[u8; 32], Error> { + // match &self.kind { + // SignerKind::Local(local_key) => Ok(*local_key.key.verifying_key().as_bytes()), + // SignerKind::Ledger(_) => todo!("ledger key"), + // SignerKind::Lab => todo!("lab"), + // SignerKind::SecureStore(secure_store_entry) => { + // let pk = secure_store_entry.get_public_key().await?; + // Ok(*pk.as_bytes()) // assuming `PublicKey` has `.as_bytes() -> &[u8; 32]` + // } + // } + // } + pub async fn get_public_key(&self) -> Result<[u8; 32], Error> { match &self.kind { SignerKind::Local(local_key) => { - - let k = local_key.key.verifying_key().as_bytes().clone(); - Ok(stellar_strkey::ed25519::PublicKey::from_payload(&k)?) - }, + Ok(*local_key.key.verifying_key().as_bytes()) + } SignerKind::Ledger(_ledger) => todo!("ledger key"), SignerKind::Lab => todo!("lab"), - SignerKind::SecureStore(secure_store_entry) => secure_store_entry.get_public_key(), + SignerKind::SecureStore(secure_store_entry) => { + let pk = secure_store_entry.get_public_key()?; + Ok(pk.0) + } } } - pub async fn sign_payload( - &self, - payload: &[u8], - ) -> Result { + pub async fn sign_payload(&self, payload: &[u8]) -> Result { match &self.kind { SignerKind::Local(local_key) => { let p = <[u8; 32]>::try_from(payload)?; local_key.sign_payload(p) - }, + } SignerKind::Ledger(_ledger) => todo!("ledger"), SignerKind::Lab => todo!("lab"), SignerKind::SecureStore(secure_store_entry) => { From a5308f61079b8999c51d9fa259aaa7a83406807a Mon Sep 17 00:00:00 2001 From: elizabethengelman <4752801+elizabethengelman@users.noreply.github.com> Date: Wed, 1 Oct 2025 13:36:55 -0400 Subject: [PATCH 10/23] Remove SignerKey - we can just use a Signer --- .../src/commands/contract/arg_parsing.rs | 59 ++----------------- cmd/soroban-cli/src/config/mod.rs | 4 +- cmd/soroban-cli/src/signer/mod.rs | 43 +++----------- 3 files changed, 13 insertions(+), 93 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs index 836fed3f02..1154cc9d56 100644 --- a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs +++ b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs @@ -56,7 +56,7 @@ pub enum Error { Signer(#[from] signer::Error), } -pub type HostFunctionParameters = (String, Spec, InvokeContractArgs, Vec); +pub type HostFunctionParameters = (String, Spec, InvokeContractArgs, Vec); fn running_cmd() -> String { let mut args: Vec = env::args().collect(); @@ -106,7 +106,7 @@ pub async fn build_host_function_parameters( let func = spec.find_function(function)?; // create parsed_args in same order as the inputs to func let mut parsed_args = Vec::with_capacity(func.inputs.len()); - let mut signers = Vec::::new(); + let mut signers = Vec::::new(); for i in func.inputs.iter() { let name = i.name.to_utf8_string()?; if let Some(mut val) = matches_.get_raw(&name) { @@ -302,60 +302,9 @@ fn resolve_address(addr_or_alias: &str, config: &config::Args) -> Result Result{ - Ok(self.0.sign_payload(payload).await?) - } - - pub async fn get_public_key(&self) -> Result<[u8; 32], Error>{ - Ok(self.0.get_public_key().await?) - } -} - -// impl SignerKey { -// pub fn sign(&self, msg: &[u8]) -> Result { -// match self { -// SignerKey::Local(s) => s.sign(msg), -// SignerKey::Other(s) => s.sign(msg), -// } -// } -// } - - -// pub trait SignerKeyTrait { -// fn verifying_key(&self) -> VerifyingKey; -// fn sign(&self, msg: &[u8]) -> Result; -// } - -// #[derive(thiserror::Error, Debug)] -// pub enum SignerKeyError { -// #[error("secure store error: {0}")] -// SecureStore(String), -// #[error("in-memory sign error: {0}")] -// InMemory(String), -// } - -// pub struct LocalSigner { -// sk: ed25519_dalek::SigningKey, -// } - -// impl LocalSigner { -// pub fn new(sk: ed25519_dalek::SigningKey) -> Self { Self { sk } } -// } - -// impl SignerKeyTrait for LocalSigner { -// fn verifying_key(&self) -> VerifyingKey { self.sk.verifying_key() } -// fn sign(&self, msg: &[u8]) -> Result { -// Ok(signature::Signer::::try_sign(&self.sk, msg) -// .map_err(|e| SignerKeyError::InMemory(e.to_string()))?) -// } -// } - -async fn resolve_signer(addr_or_alias: &str, config: &config::Args) -> Option { +async fn resolve_signer(addr_or_alias: &str, config: &config::Args) -> Option { let secret = config.locator.get_secret_key(addr_or_alias).unwrap(); let print = Print::new(false); let signer = secret.signer(None, print).await.ok()?; // can the hd_path be none here?? - Some(SignerKey(signer)) + Some(signer) } diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index 737e835c50..9f954adae3 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -6,7 +6,7 @@ use std::{ }; use crate::{ - commands::contract::arg_parsing::SignerKey, signer, xdr::{self, SequenceNumber, Transaction, TransactionEnvelope, TransactionV1Envelope, VecM}, Pwd + signer::{self, Signer}, xdr::{self, SequenceNumber, Transaction, TransactionEnvelope, TransactionV1Envelope, VecM}, Pwd }; use network::Network; @@ -102,7 +102,7 @@ impl Args { pub async fn sign_soroban_authorizations( &self, tx: &Transaction, - signers: &[SignerKey], + signers: &[Signer], ) -> Result, Error> { let network = self.get_network()?; let source_account = self.source_account().await.unwrap();// handle this! diff --git a/cmd/soroban-cli/src/signer/mod.rs b/cmd/soroban-cli/src/signer/mod.rs index 51b7c2348d..3161712fae 100644 --- a/cmd/soroban-cli/src/signer/mod.rs +++ b/cmd/soroban-cli/src/signer/mod.rs @@ -1,5 +1,4 @@ use crate::{ - commands::contract::arg_parsing::SignerKey, xdr::{ self, AccountId, DecoratedSignature, Hash, HashIdPreimage, HashIdPreimageSorobanAuthorization, InvokeHostFunctionOp, Limits, Operation, OperationBody, @@ -70,7 +69,7 @@ fn requires_auth(txn: &Transaction) -> Option { pub async fn sign_soroban_authorizations( raw: &Transaction, _source_account: &xdr::MuxedAccount, - signers: &[SignerKey], + signers: &[Signer], signature_expiration_ledger: u32, network_passphrase: &str, ) -> Result, Error> { @@ -120,14 +119,15 @@ pub async fn sign_soroban_authorizations( } }; - let mut signer: Option<&SignerKey> = None; + let mut signer: Option<&Signer> = None; for s in signers { - let pk = s.get_public_key().await.unwrap(); + let pk = s.get_public_key().await?; if needle == &pk { signer = Some(s); } } + // do we need this still? // if needle == source_address { // signer = Some(&sk); // } @@ -141,24 +141,6 @@ pub async fn sign_soroban_authorizations( }); } - // let signer = if let Some(s) = signers - // .iter() - // .find(|s| needle == s.verifying_key().as_bytes()) - // { - // s - // } else if needle == source_address { - // // This is the source address, so we can sign it - // source_key - // } else { - // // We don't have a signer for this address - // return Err(Error::MissingSignerForAddress { - // address: stellar_strkey::Strkey::PublicKeyEd25519( - // stellar_strkey::ed25519::PublicKey(*needle), - // ) - // .to_string(), - // }); - // }; - let signed = sign_soroban_authorization_entry( raw_auth, &signer.unwrap(), // handle this @@ -176,7 +158,7 @@ pub async fn sign_soroban_authorizations( async fn sign_soroban_authorization_entry( raw: &SorobanAuthorizationEntry, - signer: &SignerKey, + signer: &Signer, signature_expiration_ledger: u32, network_id: &Hash, ) -> Result { @@ -200,8 +182,8 @@ async fn sign_soroban_authorization_entry( .to_xdr(Limits::none())?; let payload = Sha256::digest(preimage); - let signature = signer.sign_payload(&payload).await.unwrap(); - let public_key_vec = signer.get_public_key().await.unwrap().to_vec(); + let signature = signer.sign_payload(&payload).await?; + let public_key_vec = signer.get_public_key().await?.to_vec(); let map = ScMap::sorted_from(vec![ ( @@ -282,17 +264,6 @@ impl Signer { } } - // pub async fn get_raw_public_key(&self) -> Result<[u8; 32], Error> { - // match &self.kind { - // SignerKind::Local(local_key) => Ok(*local_key.key.verifying_key().as_bytes()), - // SignerKind::Ledger(_) => todo!("ledger key"), - // SignerKind::Lab => todo!("lab"), - // SignerKind::SecureStore(secure_store_entry) => { - // let pk = secure_store_entry.get_public_key().await?; - // Ok(*pk.as_bytes()) // assuming `PublicKey` has `.as_bytes() -> &[u8; 32]` - // } - // } - // } pub async fn get_public_key(&self) -> Result<[u8; 32], Error> { match &self.kind { SignerKind::Local(local_key) => { From ec2f70d9dcc769252fc775665e8731b99ae97c05 Mon Sep 17 00:00:00 2001 From: elizabethengelman <4752801+elizabethengelman@users.noreply.github.com> Date: Wed, 1 Oct 2025 15:11:11 -0400 Subject: [PATCH 11/23] Clippy & cleanup --- .../src/commands/contract/arg_parsing.rs | 4 +- .../src/commands/contract/deploy/wasm.rs | 3 +- cmd/soroban-cli/src/config/mod.rs | 9 ++-- cmd/soroban-cli/src/signer/mod.rs | 43 ++++++++----------- 4 files changed, 29 insertions(+), 30 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs index 1154cc9d56..25ed2f3ba0 100644 --- a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs +++ b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs @@ -303,8 +303,8 @@ fn resolve_address(addr_or_alias: &str, config: &config::Args) -> Result Option { - let secret = config.locator.get_secret_key(addr_or_alias).unwrap(); + let secret = config.locator.get_secret_key(addr_or_alias).ok()?; let print = Print::new(false); - let signer = secret.signer(None, print).await.ok()?; // can the hd_path be none here?? + let signer = secret.signer(None, print).await.ok()?; Some(signer) } diff --git a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs index 3b93104a89..157ad343bc 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs @@ -253,7 +253,8 @@ impl NetworkRunnable for Cmd { &slop, &entries, config, - ).await? + ) + .await? .2, ) } diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index 9f954adae3..7cedb843a2 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -6,7 +6,9 @@ use std::{ }; use crate::{ - signer::{self, Signer}, xdr::{self, SequenceNumber, Transaction, TransactionEnvelope, TransactionV1Envelope, VecM}, Pwd + signer::{self, Signer}, + xdr::{self, SequenceNumber, Transaction, TransactionEnvelope, TransactionV1Envelope, VecM}, + Pwd, }; use network::Network; @@ -105,7 +107,7 @@ impl Args { signers: &[Signer], ) -> Result, Error> { let network = self.get_network()?; - let source_account = self.source_account().await.unwrap();// handle this! + let source_account = self.source_account().await?; let client = network.rpc_client()?; let latest_ledger = client.get_latest_ledger().await?.sequence; let seq_num = latest_ledger + 60; // ~ 5 min @@ -115,7 +117,8 @@ impl Args { signers, seq_num, &network.network_passphrase, - ).await?) + ) + .await?) } pub fn get_network(&self) -> Result { diff --git a/cmd/soroban-cli/src/signer/mod.rs b/cmd/soroban-cli/src/signer/mod.rs index 3161712fae..4429196c41 100644 --- a/cmd/soroban-cli/src/signer/mod.rs +++ b/cmd/soroban-cli/src/signer/mod.rs @@ -1,12 +1,9 @@ -use crate::{ - xdr::{ - self, AccountId, DecoratedSignature, Hash, HashIdPreimage, - HashIdPreimageSorobanAuthorization, InvokeHostFunctionOp, Limits, Operation, OperationBody, - PublicKey, ScAddress, ScMap, ScSymbol, ScVal, Signature, SignatureHint, - SorobanAddressCredentials, SorobanAuthorizationEntry, SorobanAuthorizedFunction, - SorobanCredentials, Transaction, TransactionEnvelope, TransactionV1Envelope, Uint256, VecM, - WriteXdr, - }, +use crate::xdr::{ + self, AccountId, DecoratedSignature, Hash, HashIdPreimage, HashIdPreimageSorobanAuthorization, + InvokeHostFunctionOp, Limits, Operation, OperationBody, PublicKey, ScAddress, ScMap, ScSymbol, + ScVal, Signature, SignatureHint, SorobanAddressCredentials, SorobanAuthorizationEntry, + SorobanAuthorizedFunction, SorobanCredentials, Transaction, TransactionEnvelope, + TransactionV1Envelope, Uint256, VecM, WriteXdr, }; use ed25519_dalek::{ed25519::signature::Signer as _, Signature as Ed25519Signature}; use sha2::{Digest, Sha256}; @@ -89,7 +86,7 @@ pub async fn sign_soroban_authorizations( let network_id = Hash(Sha256::digest(network_passphrase.as_bytes()).into()); let mut signed_auths = Vec::with_capacity(body.auth.len()); - for raw_auth in body.auth.as_slice().iter() { + for raw_auth in body.auth.as_slice() { let mut auth = raw_auth.clone(); let SorobanAuthorizationEntry { credentials: SorobanCredentials::Address(ref mut credentials), @@ -123,7 +120,7 @@ pub async fn sign_soroban_authorizations( for s in signers { let pk = s.get_public_key().await?; if needle == &pk { - signer = Some(s); + signer = Some(s); } } @@ -132,7 +129,16 @@ pub async fn sign_soroban_authorizations( // signer = Some(&sk); // } - if signer.is_none() { + if let Some(signer) = signer { + let signed = sign_soroban_authorization_entry( + raw_auth, + signer, // handle this + signature_expiration_ledger, + &network_id, + ) + .await?; + signed_auths.push(signed); + } else { return Err(Error::MissingSignerForAddress { address: stellar_strkey::Strkey::PublicKeyEd25519( stellar_strkey::ed25519::PublicKey(*needle), @@ -140,15 +146,6 @@ pub async fn sign_soroban_authorizations( .to_string(), }); } - - let signed = sign_soroban_authorization_entry( - raw_auth, - &signer.unwrap(), // handle this - signature_expiration_ledger, - &network_id, - ) - .await?; - signed_auths.push(signed); } body.auth = signed_auths.try_into()?; @@ -266,9 +263,7 @@ impl Signer { pub async fn get_public_key(&self) -> Result<[u8; 32], Error> { match &self.kind { - SignerKind::Local(local_key) => { - Ok(*local_key.key.verifying_key().as_bytes()) - } + SignerKind::Local(local_key) => Ok(*local_key.key.verifying_key().as_bytes()), SignerKind::Ledger(_ledger) => todo!("ledger key"), SignerKind::Lab => todo!("lab"), SignerKind::SecureStore(secure_store_entry) => { From 27be163f958302def045e0ad90dc3e9136cbd1a7 Mon Sep 17 00:00:00 2001 From: elizabethengelman <4752801+elizabethengelman@users.noreply.github.com> Date: Wed, 1 Oct 2025 17:01:32 -0400 Subject: [PATCH 12/23] Cleanup --- cmd/soroban-cli/src/signer/mod.rs | 33 +++++++++++++++++-------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/cmd/soroban-cli/src/signer/mod.rs b/cmd/soroban-cli/src/signer/mod.rs index 4429196c41..ed04ce86c5 100644 --- a/cmd/soroban-cli/src/signer/mod.rs +++ b/cmd/soroban-cli/src/signer/mod.rs @@ -129,22 +129,25 @@ pub async fn sign_soroban_authorizations( // signer = Some(&sk); // } - if let Some(signer) = signer { - let signed = sign_soroban_authorization_entry( - raw_auth, - signer, // handle this - signature_expiration_ledger, - &network_id, - ) - .await?; - signed_auths.push(signed); - } else { - return Err(Error::MissingSignerForAddress { - address: stellar_strkey::Strkey::PublicKeyEd25519( - stellar_strkey::ed25519::PublicKey(*needle), + match signer { + Some(signer) => { + let signed = sign_soroban_authorization_entry( + raw_auth, + signer, // handle this + signature_expiration_ledger, + &network_id, ) - .to_string(), - }); + .await?; + signed_auths.push(signed); + }, + None => { + return Err(Error::MissingSignerForAddress { + address: stellar_strkey::Strkey::PublicKeyEd25519( + stellar_strkey::ed25519::PublicKey(*needle), + ) + .to_string(), + }); + } } } From eb5ba548f30a1c2eb2be3d96557882da14db32ea Mon Sep 17 00:00:00 2001 From: elizabethengelman <4752801+elizabethengelman@users.noreply.github.com> Date: Wed, 1 Oct 2025 17:40:20 -0400 Subject: [PATCH 13/23] Clippy --- cmd/soroban-cli/src/signer/mod.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/cmd/soroban-cli/src/signer/mod.rs b/cmd/soroban-cli/src/signer/mod.rs index ed04ce86c5..d95cfbce5a 100644 --- a/cmd/soroban-cli/src/signer/mod.rs +++ b/cmd/soroban-cli/src/signer/mod.rs @@ -131,15 +131,15 @@ pub async fn sign_soroban_authorizations( match signer { Some(signer) => { - let signed = sign_soroban_authorization_entry( + let signed_entry = sign_soroban_authorization_entry( raw_auth, signer, // handle this signature_expiration_ledger, &network_id, ) .await?; - signed_auths.push(signed); - }, + signed_auths.push(signed_entry); + } None => { return Err(Error::MissingSignerForAddress { address: stellar_strkey::Strkey::PublicKeyEd25519( @@ -264,6 +264,8 @@ impl Signer { } } + // when we implement this for ledger we'll need it to be awaited + #[allow(clippy::unused_async)] pub async fn get_public_key(&self) -> Result<[u8; 32], Error> { match &self.kind { SignerKind::Local(local_key) => Ok(*local_key.key.verifying_key().as_bytes()), @@ -276,6 +278,8 @@ impl Signer { } } + // when we implement this for ledger we'll need it to be awaited + #[allow(clippy::unused_async)] pub async fn sign_payload(&self, payload: &[u8]) -> Result { match &self.kind { SignerKind::Local(local_key) => { From c8f3d64af456b071238c2b4e88aa4006c4898072 Mon Sep 17 00:00:00 2001 From: elizabethengelman <4752801+elizabethengelman@users.noreply.github.com> Date: Wed, 1 Oct 2025 18:11:03 -0400 Subject: [PATCH 14/23] Allow source account signer to be the auth entry signer --- cmd/soroban-cli/src/config/mod.rs | 16 ++++++++++------ cmd/soroban-cli/src/signer/mod.rs | 12 +++++------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index 7cedb843a2..cbdbecb595 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -6,9 +6,7 @@ use std::{ }; use crate::{ - signer::{self, Signer}, - xdr::{self, SequenceNumber, Transaction, TransactionEnvelope, TransactionV1Envelope, VecM}, - Pwd, + print::Print, signer::{self, Signer}, xdr::{self, SequenceNumber, Transaction, TransactionEnvelope, TransactionV1Envelope, VecM}, Pwd }; use network::Network; @@ -35,7 +33,7 @@ pub enum Error { #[error(transparent)] Secret(#[from] secret::Error), #[error(transparent)] - Config(#[from] locator::Error), + Locator(#[from] locator::Error), #[error(transparent)] Rpc(#[from] soroban_rpc::Error), #[error(transparent)] @@ -79,6 +77,12 @@ impl Args { .await?) } + pub async fn source_signer(&self) -> Result { + let print = Print::new(true); + let secret = &self.source_account.resolve_secret(&self.locator)?; + Ok(secret.signer(None, print).await?) + } + pub fn key_pair(&self) -> Result { let key = &self.source_account.resolve_secret(&self.locator)?; Ok(key.key_pair(self.hd_path())?) @@ -107,13 +111,13 @@ impl Args { signers: &[Signer], ) -> Result, Error> { let network = self.get_network()?; - let source_account = self.source_account().await?; + let source_signer = self.source_signer().await?; let client = network.rpc_client()?; let latest_ledger = client.get_latest_ledger().await?.sequence; let seq_num = latest_ledger + 60; // ~ 5 min Ok(signer::sign_soroban_authorizations( tx, - &source_account, + &source_signer, signers, seq_num, &network.network_passphrase, diff --git a/cmd/soroban-cli/src/signer/mod.rs b/cmd/soroban-cli/src/signer/mod.rs index d95cfbce5a..ac2519cad6 100644 --- a/cmd/soroban-cli/src/signer/mod.rs +++ b/cmd/soroban-cli/src/signer/mod.rs @@ -65,7 +65,7 @@ fn requires_auth(txn: &Transaction) -> Option { // transaction. If unable to sign, return an error. pub async fn sign_soroban_authorizations( raw: &Transaction, - _source_account: &xdr::MuxedAccount, + source_signer: &Signer, signers: &[Signer], signature_expiration_ledger: u32, network_passphrase: &str, @@ -118,16 +118,14 @@ pub async fn sign_soroban_authorizations( let mut signer: Option<&Signer> = None; for s in signers { - let pk = s.get_public_key().await?; - if needle == &pk { + if needle == &s.get_public_key().await? { signer = Some(s); } } - // do we need this still? - // if needle == source_address { - // signer = Some(&sk); - // } + if needle == &source_signer.get_public_key().await? { + signer = Some(source_signer); + } match signer { Some(signer) => { From f76206a516897b64d306d4b8798d01809dc2a355 Mon Sep 17 00:00:00 2001 From: elizabethengelman <4752801+elizabethengelman@users.noreply.github.com> Date: Thu, 2 Oct 2025 11:41:11 -0400 Subject: [PATCH 15/23] Cargo fmt --- cmd/soroban-cli/src/config/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index cbdbecb595..7cf6d8c8ba 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -6,7 +6,10 @@ use std::{ }; use crate::{ - print::Print, signer::{self, Signer}, xdr::{self, SequenceNumber, Transaction, TransactionEnvelope, TransactionV1Envelope, VecM}, Pwd + print::Print, + signer::{self, Signer}, + xdr::{self, SequenceNumber, Transaction, TransactionEnvelope, TransactionV1Envelope, VecM}, + Pwd, }; use network::Network; From 8ca76197e2e20cd7a320b151a7c085cf63d52f42 Mon Sep 17 00:00:00 2001 From: elizabethengelman <4752801+elizabethengelman@users.noreply.github.com> Date: Thu, 2 Oct 2025 14:27:35 -0400 Subject: [PATCH 16/23] Return a nicer error for signing with lab --- cmd/soroban-cli/src/signer/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/soroban-cli/src/signer/mod.rs b/cmd/soroban-cli/src/signer/mod.rs index ac2519cad6..0b74504cb2 100644 --- a/cmd/soroban-cli/src/signer/mod.rs +++ b/cmd/soroban-cli/src/signer/mod.rs @@ -268,7 +268,7 @@ impl Signer { match &self.kind { SignerKind::Local(local_key) => Ok(*local_key.key.verifying_key().as_bytes()), SignerKind::Ledger(_ledger) => todo!("ledger key"), - SignerKind::Lab => todo!("lab"), + SignerKind::Lab => Err(Error::ReturningSignatureFromLab), SignerKind::SecureStore(secure_store_entry) => { let pk = secure_store_entry.get_public_key()?; Ok(pk.0) @@ -285,7 +285,7 @@ impl Signer { local_key.sign_payload(p) } SignerKind::Ledger(_ledger) => todo!("ledger"), - SignerKind::Lab => todo!("lab"), + SignerKind::Lab => Err(Error::ReturningSignatureFromLab), SignerKind::SecureStore(secure_store_entry) => { let p = <[u8; 32]>::try_from(payload)?; secure_store_entry.sign_payload(p) From deb308972fcc6186c5b1d5137e845fbc17053127 Mon Sep 17 00:00:00 2001 From: elizabethengelman <4752801+elizabethengelman@users.noreply.github.com> Date: Tue, 7 Oct 2025 12:04:20 -0400 Subject: [PATCH 17/23] Fix after merging main --- cmd/soroban-cli/src/commands/contract/arg_parsing.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs index e25d5ce243..c9671d3674 100644 --- a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs +++ b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs @@ -75,19 +75,19 @@ pub async fn build_host_function_parameters( spec_entries: &[ScSpecEntry], config: &config::Args, ) -> Result { - build_host_function_parameters_with_filter(contract_id, slop, spec_entries, config, true) + build_host_function_parameters_with_filter(contract_id, slop, spec_entries, config, true).await } -pub fn build_constructor_parameters( +pub async fn build_constructor_parameters( contract_id: &stellar_strkey::Contract, slop: &[OsString], spec_entries: &[ScSpecEntry], config: &config::Args, ) -> Result { - build_host_function_parameters_with_filter(contract_id, slop, spec_entries, config, false) + build_host_function_parameters_with_filter(contract_id, slop, spec_entries, config, false).await } -fn build_host_function_parameters_with_filter( +async fn build_host_function_parameters_with_filter( contract_id: &stellar_strkey::Contract, slop: &[OsString], spec_entries: &[ScSpecEntry], From 697cb5f0498710c9924e8b0af0aa6e8f5b9ffc9f Mon Sep 17 00:00:00 2001 From: elizabethengelman <4752801+elizabethengelman@users.noreply.github.com> Date: Tue, 7 Oct 2025 12:29:36 -0400 Subject: [PATCH 18/23] Wire up signer get_public_key for ledger --- cmd/soroban-cli/src/signer/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/soroban-cli/src/signer/mod.rs b/cmd/soroban-cli/src/signer/mod.rs index 0b74504cb2..f08b2e185c 100644 --- a/cmd/soroban-cli/src/signer/mod.rs +++ b/cmd/soroban-cli/src/signer/mod.rs @@ -267,7 +267,10 @@ impl Signer { pub async fn get_public_key(&self) -> Result<[u8; 32], Error> { match &self.kind { SignerKind::Local(local_key) => Ok(*local_key.key.verifying_key().as_bytes()), - SignerKind::Ledger(_ledger) => todo!("ledger key"), + SignerKind::Ledger(ledger) => { + let pk = ledger.public_key().await?; + Ok(pk.0) + } SignerKind::Lab => Err(Error::ReturningSignatureFromLab), SignerKind::SecureStore(secure_store_entry) => { let pk = secure_store_entry.get_public_key()?; From b9abd64a00a3cde6f7adcc51462d93b4411c0803 Mon Sep 17 00:00:00 2001 From: elizabethengelman <4752801+elizabethengelman@users.noreply.github.com> Date: Tue, 7 Oct 2025 13:37:50 -0400 Subject: [PATCH 19/23] Implement sign payload for ledger --- cmd/soroban-cli/src/signer/ledger.rs | 7 +++++++ cmd/soroban-cli/src/signer/mod.rs | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/cmd/soroban-cli/src/signer/ledger.rs b/cmd/soroban-cli/src/signer/ledger.rs index 534cebec06..85f9e5ce1c 100644 --- a/cmd/soroban-cli/src/signer/ledger.rs +++ b/cmd/soroban-cli/src/signer/ledger.rs @@ -22,6 +22,7 @@ pub enum Error { mod ledger_impl { use super::Error; use crate::xdr::{DecoratedSignature, Hash, Signature, SignatureHint, Transaction}; + use ed25519_dalek::Signature as Ed25519Signature; use sha2::{Digest, Sha256}; use stellar_ledger::{Blob as _, Exchange, LedgerSigner}; @@ -96,6 +97,12 @@ mod ledger_impl { Ok(DecoratedSignature { hint, signature }) } + pub async fn sign_payload(&self, payload: [u8; 32]) -> Result { + let signed_bytes = self.signer.sign_blob(&self.index.into(), &payload).await?; + let sig = Ed25519Signature::from_bytes(signed_bytes.as_slice().try_into()?); + Ok(sig) + } + pub async fn public_key(&self) -> Result { Ok(self.signer.get_public_key(&self.index.into()).await?) } diff --git a/cmd/soroban-cli/src/signer/mod.rs b/cmd/soroban-cli/src/signer/mod.rs index f08b2e185c..4df9739fa8 100644 --- a/cmd/soroban-cli/src/signer/mod.rs +++ b/cmd/soroban-cli/src/signer/mod.rs @@ -287,7 +287,10 @@ impl Signer { let p = <[u8; 32]>::try_from(payload)?; local_key.sign_payload(p) } - SignerKind::Ledger(_ledger) => todo!("ledger"), + SignerKind::Ledger(ledger) => Ok({ + let p = <[u8; 32]>::try_from(payload)?; + ledger.sign_payload(p).await? + }), SignerKind::Lab => Err(Error::ReturningSignatureFromLab), SignerKind::SecureStore(secure_store_entry) => { let p = <[u8; 32]>::try_from(payload)?; From 4bb573c47df49131edc9d8269681e0b3e8047fc3 Mon Sep 17 00:00:00 2001 From: elizabethengelman <4752801+elizabethengelman@users.noreply.github.com> Date: Wed, 15 Oct 2025 10:36:12 -0400 Subject: [PATCH 20/23] Use ledger as source account for invoke contract txs --- 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 a60c676729..0213c3fa2f 100644 --- a/cmd/soroban-cli/src/config/address.rs +++ b/cmd/soroban-cli/src/config/address.rs @@ -129,7 +129,9 @@ impl UnresolvedMuxedAccount { UnresolvedMuxedAccount::AliasOrSecret(alias_or_secret) => { Ok(locator.read_key(alias_or_secret)?.try_into()?) } - UnresolvedMuxedAccount::Ledger(_) => Err(Error::LedgerPrivateKeyRevealNotSupported), + UnresolvedMuxedAccount::Ledger(_) => { + Ok(locator.read_key("ledger")?.try_into()?) + } } } } From 4c2721d3615d619984ca0c49fbed5ab08d5c3046 Mon Sep 17 00:00:00 2001 From: elizabethengelman <4752801+elizabethengelman@users.noreply.github.com> Date: Wed, 15 Oct 2025 11:04:17 -0400 Subject: [PATCH 21/23] Refactor secret.public_key to use a match instead of if block --- cmd/soroban-cli/src/config/secret.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index 54c616dfcd..46a76ba9a5 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -126,13 +126,16 @@ impl Secret { } pub fn public_key(&self, index: Option) -> Result { - if let Secret::SecureStore { entry_name } = self { - Ok(secure_store::get_public_key(entry_name, index)?) - } else { - let key = self.key_pair(index)?; - Ok(stellar_strkey::ed25519::PublicKey::from_payload( - key.verifying_key().as_bytes(), - )?) + match &self { + Secret::SecureStore { entry_name } => { + Ok(secure_store::get_public_key(entry_name, index)?) + }, + _ => { + let key = self.key_pair(index)?; + Ok(stellar_strkey::ed25519::PublicKey::from_payload( + key.verifying_key().as_bytes(), + )?) + } } } From ad133354a560ee06dc9d2700cf5690c1eab3f59f Mon Sep 17 00:00:00 2001 From: elizabethengelman <4752801+elizabethengelman@users.noreply.github.com> Date: Wed, 15 Oct 2025 11:12:02 -0400 Subject: [PATCH 22/23] wip --- cmd/soroban-cli/src/config/key.rs | 1 + cmd/soroban-cli/src/config/secret.rs | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/cmd/soroban-cli/src/config/key.rs b/cmd/soroban-cli/src/config/key.rs index 9d21ad0615..61152b7155 100644 --- a/cmd/soroban-cli/src/config/key.rs +++ b/cmd/soroban-cli/src/config/key.rs @@ -30,6 +30,7 @@ pub enum Key { impl Key { pub fn muxed_account(&self, hd_path: Option) -> Result { let bytes = match self { + // this is what calls public key Key::Secret(secret) => secret.public_key(hd_path)?.0, Key::PublicKey(Public(key)) => key.0, Key::MuxedAccount(MuxedAccount(stellar_strkey::ed25519::MuxedAccount { diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index 46a76ba9a5..928f8c3a0a 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -125,17 +125,27 @@ impl Secret { }) } + + // stopping point, i want to make this async but a lot of things rely on this fn that aren't currently async. it will be a big refactor an im not sure it should be done all at once. pub fn public_key(&self, index: Option) -> Result { match &self { Secret::SecureStore { entry_name } => { Ok(secure_store::get_public_key(entry_name, index)?) }, + Secret::Ledger => { + //TODO: handle unwrap + let hd_path: u32 = index.unwrap_or(0).try_into().unwrap(); + let ledger = ledger::new(hd_path).await.unwrap(); + Ok(ledger.public_key().await.unwrap()) + }, _ => { let key = self.key_pair(index)?; Ok(stellar_strkey::ed25519::PublicKey::from_payload( key.verifying_key().as_bytes(), )?) } + // Secret::SecretKey { secret_key } => todo!(), + // Secret::SeedPhrase { seed_phrase } => todo!(), } } From c89fcb8a2e6b17ed62e267722335c3c492d8437b Mon Sep 17 00:00:00 2001 From: elizabethengelman <4752801+elizabethengelman@users.noreply.github.com> Date: Wed, 15 Oct 2025 15:19:50 -0400 Subject: [PATCH 23/23] Make arg_parsing resolve_address async --- .../src/commands/contract/arg_parsing.rs | 6 ++-- cmd/soroban-cli/src/config/key.rs | 17 +++++++++++ cmd/soroban-cli/src/config/sc_address.rs | 29 +++++++++++++++++++ cmd/soroban-cli/src/config/secret.rs | 17 +++++++---- 4 files changed, 61 insertions(+), 8 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs index c9671d3674..3e6329f76f 100644 --- a/cmd/soroban-cli/src/commands/contract/arg_parsing.rs +++ b/cmd/soroban-cli/src/commands/contract/arg_parsing.rs @@ -144,7 +144,7 @@ async fn build_host_function_parameters_with_filter( i.type_, ScSpecTypeDef::Address | ScSpecTypeDef::MuxedAddress ) { - let addr = resolve_address(&s, config)?; + let addr = resolve_address(&s, config).await?; let signer = resolve_signer(&s, config).await; s = addr; if let Some(signer) = signer { @@ -306,12 +306,12 @@ pub fn output_to_string( Ok(TxnResult::Res(res_str)) } -fn resolve_address(addr_or_alias: &str, config: &config::Args) -> Result { +async fn resolve_address(addr_or_alias: &str, config: &config::Args) -> Result { let sc_address: UnresolvedScAddress = addr_or_alias.parse().unwrap(); let account = match sc_address { UnresolvedScAddress::Resolved(addr) => addr.to_string(), addr @ UnresolvedScAddress::Alias(_) => { - let addr = addr.resolve(&config.locator, &config.get_network()?.network_passphrase)?; + let addr = addr.resolve_async(&config.locator, &config.get_network()?.network_passphrase).await?; match addr { xdr::ScAddress::Account(account) => account.to_string(), contract @ xdr::ScAddress::Contract(_) => contract.to_string(), diff --git a/cmd/soroban-cli/src/config/key.rs b/cmd/soroban-cli/src/config/key.rs index 61152b7155..222a68339a 100644 --- a/cmd/soroban-cli/src/config/key.rs +++ b/cmd/soroban-cli/src/config/key.rs @@ -28,6 +28,23 @@ pub enum Key { } impl Key { + pub async fn muxed_account_async(&self, hd_path: Option) -> Result { + let bytes = match self { + Key::Secret(secret) => secret.public_key_async(hd_path).await?.0, + 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, + })) + } + }; + Ok(xdr::MuxedAccount::Ed25519(xdr::Uint256(bytes))) + } + pub fn muxed_account(&self, hd_path: Option) -> Result { let bytes = match self { // this is what calls public key diff --git a/cmd/soroban-cli/src/config/sc_address.rs b/cmd/soroban-cli/src/config/sc_address.rs index f5ac8ba7a9..4a958c301f 100644 --- a/cmd/soroban-cli/src/config/sc_address.rs +++ b/cmd/soroban-cli/src/config/sc_address.rs @@ -40,6 +40,35 @@ impl FromStr for UnresolvedScAddress { } impl UnresolvedScAddress { + pub async fn resolve_async(self, + locator: &locator::Args, + network_passphrase: &str, + ) -> Result { + let alias = match self { + UnresolvedScAddress::Resolved(addr) => return Ok(addr), + UnresolvedScAddress::Alias(alias) => alias, + }; + let contract = UnresolvedContract::resolve_alias(&alias, locator, network_passphrase); + let key = locator.read_key(&alias); + match (contract, key) { + (Ok(contract), Ok(_)) => { + eprintln!( + "Warning: ScAddress alias {alias} is ambiguous, assuming it is a contract" + ); + Ok(xdr::ScAddress::Contract(stellar_xdr::curr::ContractId( + xdr::Hash(contract.0), + ))) + } + (Ok(contract), _) => Ok(xdr::ScAddress::Contract(stellar_xdr::curr::ContractId( + xdr::Hash(contract.0), + ))), + (_, Ok(key)) => Ok(xdr::ScAddress::Account( + key.muxed_account_async(None).await?.account_id(), + )), + _ => Err(Error::AccountAliasNotFound(alias)), + } + } + pub fn resolve( self, locator: &locator::Args, diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index 928f8c3a0a..6e16d1a771 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -126,18 +126,25 @@ impl Secret { } - // stopping point, i want to make this async but a lot of things rely on this fn that aren't currently async. it will be a big refactor an im not sure it should be done all at once. - pub fn public_key(&self, index: Option) -> Result { + pub async fn public_key_async(&self, index: Option) -> Result { match &self { - Secret::SecureStore { entry_name } => { - Ok(secure_store::get_public_key(entry_name, index)?) - }, Secret::Ledger => { //TODO: handle unwrap let hd_path: u32 = index.unwrap_or(0).try_into().unwrap(); let ledger = ledger::new(hd_path).await.unwrap(); Ok(ledger.public_key().await.unwrap()) }, + _ => { + self.public_key(index) + } + } + } + + pub fn public_key(&self, index: Option) -> Result { + match &self { + Secret::SecureStore { entry_name } => { + Ok(secure_store::get_public_key(entry_name, index)?) + }, _ => { let key = self.key_pair(index)?; Ok(stellar_strkey::ed25519::PublicKey::from_payload(