From 137e209e05a2bd1e00dcf15c57b469a12a1b3a92 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 24 Sep 2024 14:50:13 -0400 Subject: [PATCH] feat: allow source account to be a public key Currently this would fail if not `--build-only` since we rely on source account being a reference to a private key or seed. However, this is will be useful for `--sign-with-lab` which users will likely copy their public keys from their wallet to use on the CLI. --- .../src/commands/contract/deploy/asset.rs | 9 +++---- .../src/commands/contract/deploy/wasm.rs | 22 +++++++-------- .../src/commands/contract/extend.rs | 8 +++--- .../src/commands/contract/install.rs | 27 ++++++++++--------- .../src/commands/contract/invoke.rs | 8 +++--- .../src/commands/contract/restore.rs | 7 +++-- cmd/soroban-cli/src/config/mod.rs | 14 ++++++++++ 7 files changed, 52 insertions(+), 43 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs index 51baa55e9..da7b6b805 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs @@ -97,11 +97,10 @@ impl NetworkRunnable for Cmd { client .verify_network_passphrase(Some(&network.network_passphrase)) .await?; - let key = config.key_pair()?; + let key = config.source_account()?; // Get the account sequence number - let public_strkey = - stellar_strkey::ed25519::PublicKey(key.verifying_key().to_bytes()).to_string(); + let public_strkey = key.to_string(); // TODO: use symbols for the method names (both here and in serve) let account_details = client.get_account(&public_strkey).await?; let sequence: i64 = account_details.seq_num.into(); @@ -141,7 +140,7 @@ fn build_wrap_token_tx( sequence: i64, fee: u32, _network_passphrase: &str, - key: &ed25519_dalek::SigningKey, + key: &stellar_strkey::ed25519::PublicKey, ) -> Result { let contract = ScAddress::Contract(contract_id.clone()); let mut read_write = vec![ @@ -180,7 +179,7 @@ fn build_wrap_token_tx( }; Ok(Transaction { - source_account: MuxedAccount::Ed25519(Uint256(key.verifying_key().to_bytes())), + source_account: MuxedAccount::Ed25519(Uint256(key.0)), fee, seq_num: SequenceNumber(sequence), cond: Preconditions::None, diff --git a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs index 74491249f..84138d432 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs @@ -212,12 +212,10 @@ impl NetworkRunnable for Cmd { client .verify_network_passphrase(Some(&network.network_passphrase)) .await?; - let key = config.key_pair()?; + let key = config.source_account()?; // Get the account sequence number - let public_strkey = - stellar_strkey::ed25519::PublicKey(key.verifying_key().to_bytes()).to_string(); - + let public_strkey = key.to_string(); let account_details = client.get_account(&public_strkey).await?; let sequence: i64 = account_details.seq_num.into(); let (txn, contract_id) = build_create_contract_tx( @@ -274,11 +272,9 @@ fn build_create_contract_tx( fee: u32, network_passphrase: &str, salt: [u8; 32], - key: &ed25519_dalek::SigningKey, + key: &stellar_strkey::ed25519::PublicKey, ) -> Result<(Transaction, Hash), Error> { - let source_account = AccountId(PublicKey::PublicKeyTypeEd25519( - key.verifying_key().to_bytes().into(), - )); + let source_account = AccountId(PublicKey::PublicKeyTypeEd25519(key.0.into())); let contract_id_preimage = ContractIdPreimage::Address(ContractIdPreimageFromAddress { address: ScAddress::Account(source_account), @@ -297,7 +293,7 @@ fn build_create_contract_tx( }), }; let tx = Transaction { - source_account: MuxedAccount::Ed25519(Uint256(key.verifying_key().to_bytes())), + source_account: MuxedAccount::Ed25519(Uint256(key.0)), fee, seq_num: SequenceNumber(sequence), cond: Preconditions::None, @@ -325,8 +321,12 @@ mod tests { 1, "Public Global Stellar Network ; September 2015", [0u8; 32], - &utils::parse_secret_key("SBFGFF27Y64ZUGFAIG5AMJGQODZZKV2YQKAVUUN4HNE24XZXD2OEUVUP") - .unwrap(), + &stellar_strkey::ed25519::PublicKey( + utils::parse_secret_key("SBFGFF27Y64ZUGFAIG5AMJGQODZZKV2YQKAVUUN4HNE24XZXD2OEUVUP") + .unwrap() + .verifying_key() + .to_bytes(), + ), ); assert!(result.is_ok()); diff --git a/cmd/soroban-cli/src/commands/contract/extend.rs b/cmd/soroban-cli/src/commands/contract/extend.rs index 5901c71b1..6666fcce7 100644 --- a/cmd/soroban-cli/src/commands/contract/extend.rs +++ b/cmd/soroban-cli/src/commands/contract/extend.rs @@ -132,17 +132,15 @@ impl NetworkRunnable for Cmd { tracing::trace!(?network); let keys = self.key.parse_keys(&config.locator, &network)?; let client = Client::new(&network.rpc_url)?; - let key = config.key_pair()?; + let key = config.source_account()?; let extend_to = self.ledgers_to_extend(); // Get the account sequence number - let public_strkey = - stellar_strkey::ed25519::PublicKey(key.verifying_key().to_bytes()).to_string(); - let account_details = client.get_account(&public_strkey).await?; + let account_details = client.get_account(&key.to_string()).await?; let sequence: i64 = account_details.seq_num.into(); let tx = Transaction { - source_account: MuxedAccount::Ed25519(Uint256(key.verifying_key().to_bytes())), + source_account: MuxedAccount::Ed25519(Uint256(key.0)), fee: self.fee.fee, seq_num: SequenceNumber(sequence + 1), cond: Preconditions::None, diff --git a/cmd/soroban-cli/src/commands/contract/install.rs b/cmd/soroban-cli/src/commands/contract/install.rs index 98c866271..5a1842b7b 100644 --- a/cmd/soroban-cli/src/commands/contract/install.rs +++ b/cmd/soroban-cli/src/commands/contract/install.rs @@ -126,16 +126,14 @@ impl NetworkRunnable for Cmd { } } - let key = config.key_pair()?; - // Get the account sequence number - let public_strkey = - stellar_strkey::ed25519::PublicKey(key.verifying_key().to_bytes()).to_string(); - let account_details = client.get_account(&public_strkey).await?; + let source_account = config.source_account()?; + + let account_details = client.get_account(&source_account.to_string()).await?; let sequence: i64 = account_details.seq_num.into(); let (tx_without_preflight, hash) = - build_install_contract_code_tx(&contract, sequence + 1, self.fee.fee, &key)?; + build_install_contract_code_tx(&contract, sequence + 1, self.fee.fee, &source_account)?; if self.fee.build_only { return Ok(TxnResult::Txn(tx_without_preflight)); @@ -255,14 +253,12 @@ pub(crate) fn build_install_contract_code_tx( source_code: &[u8], sequence: i64, fee: u32, - key: &ed25519_dalek::SigningKey, + key: &stellar_strkey::ed25519::PublicKey, ) -> Result<(Transaction, Hash), XdrError> { let hash = utils::contract_hash(source_code)?; let op = Operation { - source_account: Some(MuxedAccount::Ed25519(Uint256( - key.verifying_key().to_bytes(), - ))), + source_account: Some(MuxedAccount::Ed25519(Uint256(key.0))), body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { host_function: HostFunction::UploadContractWasm(source_code.try_into()?), auth: VecM::default(), @@ -270,7 +266,7 @@ pub(crate) fn build_install_contract_code_tx( }; let tx = Transaction { - source_account: MuxedAccount::Ed25519(Uint256(key.verifying_key().to_bytes())), + source_account: MuxedAccount::Ed25519(Uint256(key.0)), fee, seq_num: SequenceNumber(sequence), cond: Preconditions::None, @@ -292,8 +288,13 @@ mod tests { b"foo", 300, 1, - &utils::parse_secret_key("SBFGFF27Y64ZUGFAIG5AMJGQODZZKV2YQKAVUUN4HNE24XZXD2OEUVUP") - .unwrap(), + &stellar_strkey::ed25519::PublicKey::from_payload( + utils::parse_secret_key("SBFGFF27Y64ZUGFAIG5AMJGQODZZKV2YQKAVUUN4HNE24XZXD2OEUVUP") + .unwrap() + .verifying_key() + .as_bytes(), + ) + .unwrap(), ); assert!(result.is_ok()); diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index 293867c76..fa90ef9b3 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -216,12 +216,10 @@ impl NetworkRunnable for Cmd { client .verify_network_passphrase(Some(&network.network_passphrase)) .await?; - let key = config.key_pair()?; - // Get the account sequence number - let public_strkey = - stellar_strkey::ed25519::PublicKey(key.verifying_key().to_bytes()).to_string(); - client.get_account(&public_strkey).await? + client + .get_account(&config.source_account()?.to_string()) + .await? }; let sequence: i64 = account_details.seq_num.into(); let AccountId(PublicKey::PublicKeyTypeEd25519(account_id)) = account_details.account_id; diff --git a/cmd/soroban-cli/src/commands/contract/restore.rs b/cmd/soroban-cli/src/commands/contract/restore.rs index e3e8e65ae..cf6a33248 100644 --- a/cmd/soroban-cli/src/commands/contract/restore.rs +++ b/cmd/soroban-cli/src/commands/contract/restore.rs @@ -135,16 +135,15 @@ impl NetworkRunnable for Cmd { tracing::trace!(?network); let entry_keys = self.key.parse_keys(&config.locator, &network)?; let client = Client::new(&network.rpc_url)?; - let key = config.key_pair()?; + let key = config.source_account()?; // Get the account sequence number - let public_strkey = - stellar_strkey::ed25519::PublicKey(key.verifying_key().to_bytes()).to_string(); + let public_strkey = key.to_string(); let account_details = client.get_account(&public_strkey).await?; let sequence: i64 = account_details.seq_num.into(); let tx = Transaction { - source_account: MuxedAccount::Ed25519(Uint256(key.verifying_key().to_bytes())), + source_account: MuxedAccount::Ed25519(Uint256(key.0)), fee: self.fee.fee, seq_num: SequenceNumber(sequence + 1), cond: Preconditions::None, diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index 48eb6dbe8..d331bf662 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -34,6 +34,8 @@ pub enum Error { Rpc(#[from] soroban_rpc::Error), #[error(transparent)] Signer(#[from] signer::Error), + #[error(transparent)] + StellarStrkey(#[from] stellar_strkey::DecodeError), } #[derive(Debug, clap::Args, Clone, Default)] @@ -55,6 +57,18 @@ pub struct Args { } impl Args { + pub fn source_account(&self) -> Result { + if let Ok(secret) = self.locator.read_identity(&self.source_account) { + Ok(stellar_strkey::ed25519::PublicKey( + secret.key_pair(self.hd_path)?.verifying_key().to_bytes(), + )) + } else { + Ok(stellar_strkey::ed25519::PublicKey::from_string( + &self.source_account, + )?) + } + } + pub fn key_pair(&self) -> Result { let key = self.account(&self.source_account)?; Ok(key.key_pair(self.hd_path)?)