Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: tx sign and new signer #1590

Merged
merged 26 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
bf88e36
feat: `tx sign` and new traits for signing
willemneal Sep 10, 2024
df58864
refactor(tx): test
willemneal Sep 10, 2024
58630c3
Merge remote-tracking branch 'origin/main' into feat/tx_sign
willemneal Sep 12, 2024
a0cc592
fix: simplify sign_with
willemneal Sep 12, 2024
c705666
Apply suggestions from code review
willemneal Sep 16, 2024
00aaedc
fix: address PR review
willemneal Sep 17, 2024
8745dc5
Merge remote-tracking branch 'origin/main' into feat/tx_sign
willemneal Sep 17, 2024
ada184f
Merge remote-tracking branch 'origin/main' into feat/tx_sign
willemneal Sep 18, 2024
00d989a
fix: remove unneeded arg
willemneal Sep 18, 2024
c36f788
rename Transaction trait to SignTx and collapse traits
leighmcculloch Sep 23, 2024
1470057
rename account to key
leighmcculloch Sep 23, 2024
70837be
update docs
leighmcculloch Sep 23, 2024
bf7f88d
flatten layer of one liner
leighmcculloch Sep 23, 2024
c2a7cc2
undo move transaction_hash, remove trait
leighmcculloch Sep 23, 2024
df299e8
undo change
leighmcculloch Sep 23, 2024
9beed05
rename Stellar error to Signer
leighmcculloch Sep 23, 2024
d3e22cc
move StellarSigner and eliminate trait
leighmcculloch Sep 23, 2024
ded7eba
handle instead of panic on error
leighmcculloch Sep 23, 2024
015435b
move types into signer and replace existing signing logic
leighmcculloch Sep 23, 2024
baf7036
address clippy
leighmcculloch Sep 23, 2024
f3fbff7
lab.stellar.org
leighmcculloch Sep 23, 2024
d56fa09
clippy
leighmcculloch Sep 23, 2024
25102a0
remove unnecessary trim
leighmcculloch Sep 24, 2024
02245e7
fix e2e test run and tweak
leighmcculloch Sep 24, 2024
376c464
remove deadcode
leighmcculloch Sep 24, 2024
1db4b5a
remove sign with lab
leighmcculloch Sep 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions FULL_HELP_DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1291,6 +1291,7 @@ Sign, Simulate, and Send transactions

* `simulate` — Simulate a transaction envelope from stdin
* `hash` — Calculate the hash of a transaction envelope from stdin
* `sign` — Sign a transaction envolope appending the signature to the envelope



Expand Down Expand Up @@ -1326,6 +1327,26 @@ Calculate the hash of a transaction envelope from stdin



## `stellar tx sign`

Sign a transaction envolope appending the signature to the envelope

**Usage:** `stellar tx sign [OPTIONS]`

###### **Options:**

* `--sign-with-key <SIGN_WITH_KEY>` — Sign with a local key. Can be an identity (--sign-with-key alice), a secret key (--sign-with-key SC36…), or a seed phrase (--sign-with-key "kite urban…"). If using seed phrase, `--hd-path` defaults to the `0` path
* `--hd-path <HD_PATH>` — If using a seed phrase to sign, sets which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0`
* `--yes` — If one of `--sign-with-*` flags is provided, don't ask to confirm to sign a transaction
* `--rpc-url <RPC_URL>` — RPC server endpoint
* `--network-passphrase <NETWORK_PASSPHRASE>` — Network passphrase to sign the transaction sent to the rpc server
* `--network <NETWORK>` — Name of network to use from config
* `--global` — Use global config
* `--config-dir <CONFIG_DIR>` — Location of config directory, default is "."
* `--source-account <SOURCE_ACCOUNT>` — Source account of the transaction. By default will be the account that signs the transaction



## `stellar xdr`

Decode and encode XDR
Expand Down
51 changes: 49 additions & 2 deletions cmd/crates/soroban-test/tests/it/integration/tx.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use soroban_sdk::xdr::{Limits, ReadXdr, TransactionEnvelope, WriteXdr};
use soroban_test::{AssertExt, TestEnv};

use crate::integration::util::{deploy_contract, DeployKind, HELLO_WORLD};
use crate::integration::util::{deploy_contract, deploy_hello, DeployKind, HELLO_WORLD};

#[tokio::test]
async fn txn_simulate() {
async fn simulate() {
let sandbox = &TestEnv::new();
let xdr_base64_build_only = deploy_contract(sandbox, HELLO_WORLD, DeployKind::BuildOnly).await;
let xdr_base64_sim_only = deploy_contract(sandbox, HELLO_WORLD, DeployKind::SimOnly).await;
Expand Down Expand Up @@ -49,3 +49,50 @@ async fn txn_hash() {

assert_eq!(hash.trim(), expected_hash);
}

#[tokio::test]
async fn send() {
let sandbox = &TestEnv::new();
sandbox
.new_assert_cmd("contract")
.arg("install")
.args(["--wasm", HELLO_WORLD.path().as_os_str().to_str().unwrap()])
.assert()
.success();

let xdr_base64 = deploy_contract(sandbox, HELLO_WORLD, DeployKind::SimOnly).await;
println!("{xdr_base64}");
leighmcculloch marked this conversation as resolved.
Show resolved Hide resolved
let tx_env = TransactionEnvelope::from_xdr_base64(&xdr_base64, Limits::none()).unwrap();
let tx_env = sign_manually(sandbox, &tx_env);

println!(
"Transaction to send:\n{}",
tx_env.to_xdr_base64(Limits::none()).unwrap()
);

let rpc_result = send_manually(sandbox, &tx_env).await;

println!("Transaction sent: {rpc_result}");
willemneal marked this conversation as resolved.
Show resolved Hide resolved
}

async fn send_manually(sandbox: &TestEnv, tx_env: &TransactionEnvelope) -> String {
let client = soroban_rpc::Client::new(&sandbox.rpc_url).unwrap();
let res = client.send_transaction_polling(tx_env).await.unwrap();
serde_json::to_string_pretty(&res).unwrap()
}

fn sign_manually(sandbox: &TestEnv, tx_env: &TransactionEnvelope) -> TransactionEnvelope {
TransactionEnvelope::from_xdr_base64(
sandbox
.new_assert_cmd("tx")
.arg("sign")
.arg("--sign-with-key=test")
.arg("--yes")
.write_stdin(tx_env.to_xdr_base64(Limits::none()).unwrap().as_bytes())
.assert()
.success()
.stdout_as_str(),
Limits::none(),
)
.unwrap()
}
1 change: 1 addition & 0 deletions cmd/soroban-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ humantime = "2.1.0"
phf = { version = "0.11.2", features = ["macros"] }
semver = "1.0.0"
glob = "0.3.1"
crossterm = "0.28.1"
willemneal marked this conversation as resolved.
Show resolved Hide resolved

# For hyper-tls
[target.'cfg(unix)'.dependencies]
Expand Down
6 changes: 6 additions & 0 deletions cmd/soroban-cli/src/commands/tx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use clap::Parser;
use super::global;

pub mod hash;
pub mod sign;
pub mod simulate;
pub mod xdr;

Expand All @@ -12,6 +13,8 @@ pub enum Cmd {
Simulate(simulate::Cmd),
/// Calculate the hash of a transaction envelope from stdin
Hash(hash::Cmd),
/// Sign a transaction envolope appending the signature to the envelope
Sign(sign::Cmd),
}

#[derive(thiserror::Error, Debug)]
Expand All @@ -22,13 +25,16 @@ pub enum Error {
/// An error during hash calculation
#[error(transparent)]
Hash(#[from] hash::Error),
#[error(transparent)]
Sign(#[from] sign::Error),
}

impl Cmd {
pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> {
match self {
Cmd::Simulate(cmd) => cmd.run(global_args).await?,
Cmd::Hash(cmd) => cmd.run(global_args)?,
Cmd::Sign(cmd) => cmd.run().await?,
};
Ok(())
}
Expand Down
35 changes: 35 additions & 0 deletions cmd/soroban-cli/src/commands/tx/sign.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use crate::{
config::sign_with,
xdr::{self, Limits, TransactionEnvelope, WriteXdr},
};

#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error(transparent)]
XdrArgs(#[from] super::xdr::Error),
#[error(transparent)]
SignWith(#[from] sign_with::Error),
#[error(transparent)]
Xdr(#[from] xdr::Error),
}

#[derive(Debug, clap::Parser, Clone)]
#[group(skip)]
pub struct Cmd {
#[command(flatten)]
pub sign_with: sign_with::Args,
}

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_env(txn_env).await?;
println!("{}", envelope.to_xdr_base64(Limits::none())?.trim());
Ok(())
}

pub async fn sign_tx_env(&self, tx: TransactionEnvelope) -> Result<TransactionEnvelope, Error> {
Ok(self.sign_with.sign_txn_env(tx).await?)
}
}
8 changes: 8 additions & 0 deletions cmd/soroban-cli/src/config/locator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,14 @@ impl Args {
KeyType::Identity.read_with_global(name, &self.local_config()?)
}

pub fn account(&self, account_str: &str) -> Result<Secret, Error> {
if let Ok(signer) = account_str.parse::<Secret>() {
Ok(signer)
} else {
self.read_identity(account_str)
}
}

pub fn read_network(&self, name: &str) -> Result<Network, Error> {
let res = KeyType::Network.read_with_global(name, &self.local_config()?);
if let Err(Error::ConfigMissing(_, _)) = &res {
Expand Down
1 change: 1 addition & 0 deletions cmd/soroban-cli/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub mod data;
pub mod locator;
pub mod network;
pub mod secret;
pub mod sign_with;
pub mod upgrade_check;

#[derive(thiserror::Error, Debug)]
Expand Down
28 changes: 27 additions & 1 deletion cmd/soroban-cli/src/config/secret.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use serde::{Deserialize, Serialize};
use std::{io::Write, str::FromStr};
use stellar_strkey::ed25519::{PrivateKey, PublicKey};

use crate::utils;
use crate::{
signer::{self, LocalKey},
utils,
};

#[derive(thiserror::Error, Debug)]
pub enum Error {
Expand All @@ -21,6 +24,8 @@ pub enum Error {
Ed25519(#[from] ed25519_dalek::SignatureError),
#[error("Invalid address {0}")]
InvalidAddress(String),
#[error(transparent)]
Stellar(#[from] signer::Error),
}

#[derive(Debug, clap::Args, Clone)]
Expand Down Expand Up @@ -120,6 +125,14 @@ impl Secret {
)?)
}

pub fn signer(&self, index: Option<usize>, prompt: bool) -> Result<StellarSigner, Error> {
match self {
Secret::SecretKey { .. } | Secret::SeedPhrase { .. } => Ok(StellarSigner::Local(
LocalKey::new(self.key_pair(index)?, prompt),
)),
}
}

pub fn key_pair(&self, index: Option<usize>) -> Result<ed25519_dalek::SigningKey, Error> {
Ok(utils::into_signing_key(&self.private_key(index)?))
}
Expand All @@ -140,6 +153,19 @@ impl Secret {
}
}

pub enum StellarSigner {
Local(LocalKey),
}

#[async_trait::async_trait]
impl signer::Blob for StellarSigner {
async fn sign_blob(&self, blob: &[u8]) -> Result<Vec<u8>, signer::types::Error> {
match self {
StellarSigner::Local(signer) => signer.sign_blob(blob).await,
}
}
}

fn read_password() -> Result<String, Error> {
std::io::stdout().flush().map_err(|_| Error::PasswordRead)?;
rpassword::read_password().map_err(|_| Error::PasswordRead)
Expand Down
Loading
Loading