From bec2474b4222ab0035cc8f7f7b2fc733aed70c00 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Mon, 29 Jul 2024 09:38:38 -0400 Subject: [PATCH] feat: add `sign_txn_env` to Signer trait `tx sign` should append a signature to the provided transaction envelope and not throw away the original ones. --- FULL_HELP_DOCS.md | 4 +-- cmd/soroban-cli/src/commands/tx/mod.rs | 3 +-- cmd/soroban-cli/src/commands/tx/sign.rs | 10 +++----- cmd/soroban-cli/src/config/mod.rs | 2 +- cmd/soroban-cli/src/config/sign_with.rs | 18 +++++++++---- cmd/soroban-cli/src/signer.rs | 34 +++++++++++++++++++------ 6 files changed, 47 insertions(+), 24 deletions(-) diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index e79a68135..79daa6da4 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -1041,7 +1041,7 @@ Sign, Simulate, and Send transactions * `simulate` — Simulate a transaction envelope from stdin * `hash` — Calculate the hash of a transaction envelope from stdin -* `sign` — Sign a transaction +* `sign` — Sign a transaction envolope appending the signature to the envelope * `send` — Send a transaction envelope to the network @@ -1083,7 +1083,7 @@ Calculate the hash of a transaction envelope from stdin ## `stellar tx sign` -Sign a transaction +Sign a transaction envolope appending the signature to the envelope **Usage:** `stellar tx sign [OPTIONS]` diff --git a/cmd/soroban-cli/src/commands/tx/mod.rs b/cmd/soroban-cli/src/commands/tx/mod.rs index 95d0ecc78..d5d36ead6 100644 --- a/cmd/soroban-cli/src/commands/tx/mod.rs +++ b/cmd/soroban-cli/src/commands/tx/mod.rs @@ -15,7 +15,7 @@ pub enum Cmd { Simulate(simulate::Cmd), /// Calculate the hash of a transaction envelope from stdin Hash(hash::Cmd), - /// Sign a transaction + /// Sign a transaction envolope appending the signature to the envelope Sign(sign::Cmd), /// Send a transaction envelope to the network Send(send::Cmd), @@ -25,7 +25,6 @@ pub enum Cmd { pub enum Error { #[error(transparent)] Simulate(#[from] simulate::Error), - /// An error during hash calculation #[error(transparent)] Hash(#[from] hash::Error), #[error(transparent)] diff --git a/cmd/soroban-cli/src/commands/tx/sign.rs b/cmd/soroban-cli/src/commands/tx/sign.rs index 094d58483..3f0b90139 100644 --- a/cmd/soroban-cli/src/commands/tx/sign.rs +++ b/cmd/soroban-cli/src/commands/tx/sign.rs @@ -1,6 +1,6 @@ use crate::{ config::sign_with, - xdr::{self, Limits, Transaction, TransactionEnvelope, WriteXdr}, + xdr::{self, Limits, TransactionEnvelope, WriteXdr}, }; #[derive(thiserror::Error, Debug)] @@ -24,14 +24,12 @@ impl Cmd { #[allow(clippy::unused_async)] pub async fn run(&self) -> Result<(), Error> { let txn_env = super::xdr::tx_envelope_from_stdin()?; - let envelope = self - .sign_tx(super::xdr::unwrap_envelope_v1(txn_env)?) - .await?; + let envelope = self.sign_tx_env(txn_env).await?; println!("{}", envelope.to_xdr_base64(Limits::none())?.trim()); Ok(()) } - pub async fn sign_tx(&self, tx: Transaction) -> Result { - Ok(self.sign_with.sign(tx).await?) + pub async fn sign_tx_env(&self, tx: TransactionEnvelope) -> Result { + Ok(self.sign_with.sign_txn_env(tx).await?) } } diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index 48859ed0e..7eb79c7e0 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -63,7 +63,7 @@ impl Args { } pub async fn sign(&self, tx: Transaction) -> Result { - Ok(self.sign_with.sign(tx).await?) + Ok(self.sign_with.sign_txn(tx).await?) } pub async fn sign_soroban_authorizations( diff --git a/cmd/soroban-cli/src/config/sign_with.rs b/cmd/soroban-cli/src/config/sign_with.rs index 54b17f4bc..31701ecdc 100644 --- a/cmd/soroban-cli/src/config/sign_with.rs +++ b/cmd/soroban-cli/src/config/sign_with.rs @@ -78,18 +78,26 @@ impl Args { Ok(self.signer()?.get_public_key().await?) } - pub async fn sign(&self, tx: Transaction) -> Result { + pub async fn sign_txn(&self, tx: Transaction) -> Result { let signer = self.signer()?; - self.sign_with_signer(&signer, tx).await + self.sign_tx_env_with_signer(&signer, tx.into()).await } - pub async fn sign_with_signer( + pub async fn sign_txn_env( + &self, + tx: TransactionEnvelope, + ) -> Result { + let signer = self.signer()?; + self.sign_tx_env_with_signer(&signer, tx).await + } + + pub async fn sign_tx_env_with_signer( &self, signer: &(impl Stellar + std::marker::Sync), - tx: Transaction, + tx_env: TransactionEnvelope, ) -> Result { let network = self.get_network()?; - Ok(signer.sign_txn(tx, &network).await?) + Ok(signer.sign_txn_env(tx_env, &network).await?) } pub async fn sign_soroban_authorizations( diff --git a/cmd/soroban-cli/src/signer.rs b/cmd/soroban-cli/src/signer.rs index 09000ed99..f2ac358f0 100644 --- a/cmd/soroban-cli/src/signer.rs +++ b/cmd/soroban-cli/src/signer.rs @@ -28,6 +28,8 @@ pub enum Error { Rpc(#[from] crate::rpc::Error), #[error("User cancelled signing, perhaps need to remove --check")] UserCancelledSigning, + #[error("Only Transaction envelope V1 type is supported")] + UnsupportedTransactionEnvelopeType, } fn requires_auth(txn: &Transaction) -> Option { @@ -81,6 +83,26 @@ pub trait Stellar { signature: Signature(tx_signature.try_into()?), }) } + + async fn sign_txn_env( + &self, + txn_env: TransactionEnvelope, + network: &Network, + ) -> Result { + match txn_env { + TransactionEnvelope::Tx(TransactionV1Envelope { tx, signatures }) => { + let decorated_signature = self.sign_txn(&tx, network).await?; + let mut sigs = signatures.to_vec(); + sigs.push(decorated_signature); + Ok(TransactionEnvelope::Tx(TransactionV1Envelope { + tx, + signatures: sigs.try_into()?, + })) + } + _ => Err(Error::UnsupportedTransactionEnvelopeType), + } + } + /// Sign a Stellar transaction with the given source account /// This is a default implementation that signs the transaction hash and returns a decorated signature /// @@ -89,17 +111,13 @@ pub trait Stellar { /// Returns an error if the source account is not found async fn sign_txn( &self, - txn: Transaction, + txn: &Transaction, Network { network_passphrase, .. }: &Network, - ) -> Result { - let hash = transaction_hash(&txn, network_passphrase)?; - let decorated_signature = self.sign_txn_hash(hash).await?; - Ok(TransactionEnvelope::Tx(TransactionV1Envelope { - tx: txn, - signatures: vec![decorated_signature].try_into()?, - })) + ) -> Result { + let hash = transaction_hash(txn, network_passphrase)?; + self.sign_txn_hash(hash).await } /// Sign a Soroban authorization entries for a given transaction and set the expiration ledger