diff --git a/Cargo.lock b/Cargo.lock index c683baba4..9614fab3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5127,6 +5127,7 @@ dependencies = [ "soroban-spec-rust", "soroban-spec-tools", "soroban-spec-typescript", + "stellar-ledger", "stellar-rpc-client", "stellar-strkey 0.0.11", "stellar-xdr", diff --git a/Cargo.toml b/Cargo.toml index f7185b9ed..856002700 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,6 +59,10 @@ version = "21.4.0" version = "21.2.0" default-features = true +[workspace.dependencies.stellar-ledger] +version = "=21.5.0" +path = "cmd/crates/stellar-ledger" + [workspace.dependencies] stellar-strkey = "0.0.11" sep5 = "0.0.4" diff --git a/cmd/crates/stellar-ledger/src/lib.rs b/cmd/crates/stellar-ledger/src/lib.rs index c74365af2..fef0e8b80 100644 --- a/cmd/crates/stellar-ledger/src/lib.rs +++ b/cmd/crates/stellar-ledger/src/lib.rs @@ -1,10 +1,14 @@ use hd_path::HdPath; -use ledger_transport::{APDUCommand, Exchange}; +use ledger_transport::APDUCommand; +pub use ledger_transport::Exchange; + use ledger_transport_hid::{ hidapi::{HidApi, HidError}, - LedgerHIDError, TransportNativeHID, + LedgerHIDError, }; +pub use ledger_transport_hid::TransportNativeHID; + use soroban_env_host::xdr::{Hash, Transaction}; use std::vec; use stellar_strkey::DecodeError; diff --git a/cmd/soroban-cli/Cargo.toml b/cmd/soroban-cli/Cargo.toml index 1f381eb42..269bb8e3a 100644 --- a/cmd/soroban-cli/Cargo.toml +++ b/cmd/soroban-cli/Cargo.toml @@ -50,6 +50,8 @@ soroban-ledger-snapshot = { workspace = true } stellar-strkey = { workspace = true } soroban-sdk = { workspace = true } soroban-rpc = { workspace = true } +stellar-ledger = { workspace = true } + clap = { workspace = true, features = [ "derive", "env", @@ -113,7 +115,7 @@ async-compression = { version = "0.4.12", features = [ "tokio", "gzip" ] } tempfile = "3.8.1" toml_edit = "0.21.0" rust-embed = { version = "8.2.0", features = ["debug-embed"] } -bollard = { workspace=true } +bollard = { workspace = true } futures-util = "0.3.30" futures = "0.3.30" home = "0.5.9" diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index ff1fb7b72..dbf268e6d 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -1,9 +1,11 @@ use clap::arg; use serde::{Deserialize, Serialize}; use std::{io::Write, str::FromStr}; + use stellar_strkey::ed25519::{PrivateKey, PublicKey}; use crate::print::Print; +use crate::signer::native_ledger; use crate::{ signer::{self, LocalKey, Signer, SignerKind}, utils, @@ -27,6 +29,8 @@ pub enum Error { InvalidAddress(String), #[error(transparent)] Signer(#[from] signer::Error), + #[error("Ledger does not reveal secret key")] + LedgerDoesNotRevealSecretKey, } #[derive(Debug, clap::Args, Clone)] @@ -78,6 +82,7 @@ impl Args { pub enum Secret { SecretKey { secret_key: String }, SeedPhrase { seed_phrase: String }, + Ledger, } impl FromStr for Secret { @@ -92,6 +97,8 @@ impl FromStr for Secret { Ok(Secret::SeedPhrase { seed_phrase: s.to_string(), }) + } else if s == "ledger" { + Ok(Secret::Ledger) } else { Err(Error::InvalidAddress(s.to_string())) } @@ -116,6 +123,7 @@ impl Secret { .private() .0, )?, + Secret::Ledger => panic!("Ledger does not reveal secret key"), }) } @@ -132,6 +140,13 @@ impl Secret { let key = self.key_pair(index)?; SignerKind::Local(LocalKey { key }) } + Secret::Ledger => { + let hd_path: u32 = index + .unwrap_or_default() + .try_into() + .expect("uszie bigger than u32"); + SignerKind::Ledger(native_ledger(hd_path)?) + } }; Ok(Signer { kind, diff --git a/cmd/soroban-cli/src/signer.rs b/cmd/soroban-cli/src/signer.rs index 36ae55053..9112fc7bd 100644 --- a/cmd/soroban-cli/src/signer.rs +++ b/cmd/soroban-cli/src/signer.rs @@ -8,6 +8,7 @@ use soroban_env_host::xdr::{ SorobanAuthorizedFunction, SorobanCredentials, Transaction, TransactionEnvelope, TransactionV1Envelope, Uint256, VecM, WriteXdr, }; +use stellar_ledger::{Exchange, LedgerSigner, TransportNativeHID}; use crate::{config::network::Network, print::Print, utils::transaction_hash}; @@ -24,6 +25,8 @@ pub enum Error { #[error("User cancelled signing, perhaps need to add -y")] UserCancelledSigning, #[error(transparent)] + Ledger(#[from] stellar_ledger::Error), + #[error(transparent)] Xdr(#[from] xdr::Error), #[error("Only Transaction envelope V1 type is supported")] UnsupportedTransactionEnvelopeType, @@ -200,6 +203,7 @@ pub struct Signer { #[allow(clippy::module_name_repetitions)] pub enum SignerKind { Local(LocalKey), + Ledger(Ledger), } impl Signer { @@ -227,6 +231,7 @@ impl Signer { .infoln(format!("Signing transaction: {}", hex::encode(tx_hash),)); let decorated_signature = match &self.kind { SignerKind::Local(key) => key.sign_tx_hash(tx_hash)?, + SignerKind::Ledger(ledger) => todo!("ledger signing"), }; let mut sigs = signatures.into_vec(); sigs.push(decorated_signature); @@ -244,6 +249,18 @@ pub struct LocalKey { pub key: ed25519_dalek::SigningKey, } +pub struct Ledger { + index: u32, + signer: LedgerSigner, +} +pub fn native_ledger(hd_path: u32) -> Result, Error> { + let signer = stellar_ledger::native()?; + Ok(Ledger { + index: hd_path, + signer, + }) +} + impl LocalKey { pub fn sign_tx_hash(&self, tx_hash: [u8; 32]) -> Result { let hint = SignatureHint(self.key.verifying_key().to_bytes()[28..].try_into()?);