soroban_cli/signer/
ledger.rs

1use crate::xdr::{self, DecoratedSignature, Transaction};
2
3pub use ledger_impl::*;
4
5#[derive(thiserror::Error, Debug)]
6pub enum Error {
7    #[error("Ledger Device keys are not allowed: additional-libs feature must be enabled")]
8    FeatureNotEnabled,
9
10    #[cfg(feature = "additional-libs")]
11    #[error(transparent)]
12    StellarLedger(#[from] stellar_ledger::Error),
13
14    #[error(transparent)]
15    TryFromSlice(#[from] std::array::TryFromSliceError),
16
17    #[error(transparent)]
18    Xdr(#[from] xdr::Error),
19}
20
21#[cfg(feature = "additional-libs")]
22mod ledger_impl {
23    use super::*;
24    use crate::xdr::{Hash, Signature, SignatureHint};
25    use sha2::{Digest, Sha256};
26    use stellar_ledger::{Blob as _, Exchange, LedgerSigner};
27
28    #[cfg(not(feature = "emulator-tests"))]
29    pub type LedgerType = Ledger<stellar_ledger::TransportNativeHID>;
30    #[cfg(feature = "emulator-tests")]
31    pub type LedgerType = Ledger<stellar_ledger::emulator_test_support::http_transport::Emulator>;
32
33    pub struct Ledger<T: Exchange> {
34        pub(crate) index: u32,
35        pub(crate) signer: LedgerSigner<T>,
36    }
37
38    #[cfg(not(feature = "emulator-tests"))]
39    pub async fn new(hd_path: u32) -> Result<Ledger<stellar_ledger::TransportNativeHID>, Error> {
40        let signer = stellar_ledger::native()?;
41        Ok(Ledger {
42            index: hd_path,
43            signer,
44        })
45    }
46
47    #[cfg(feature = "emulator-tests")]
48    pub async fn new(
49        hd_path: u32,
50    ) -> Result<Ledger<stellar_ledger::emulator_test_support::http_transport::Emulator>, Error>
51    {
52        use stellar_ledger::emulator_test_support::ledger as emulator_ledger;
53        // port from SPECULOS_PORT ENV var
54        let host_port: u16 = std::env::var("SPECULOS_PORT")
55            .expect("SPECULOS_PORT env var not set")
56            .parse()
57            .expect("port must be a number");
58        let signer = emulator_ledger(host_port).await;
59
60        Ok(Ledger {
61            index: hd_path,
62            signer,
63        })
64    }
65
66    impl<T: Exchange> Ledger<T> {
67        pub async fn sign_transaction_hash(
68            &self,
69            tx_hash: &[u8; 32],
70        ) -> Result<DecoratedSignature, Error> {
71            let key = self.public_key().await?;
72            let hint = SignatureHint(key.0[28..].try_into()?);
73            let signature = Signature(
74                self.signer
75                    .sign_transaction_hash(self.index, tx_hash)
76                    .await?
77                    .try_into()?,
78            );
79            Ok(DecoratedSignature { hint, signature })
80        }
81
82        pub async fn sign_transaction(
83            &self,
84            tx: Transaction,
85            network_passphrase: &str,
86        ) -> Result<DecoratedSignature, Error> {
87            let network_id = Hash(Sha256::digest(network_passphrase).into());
88            let signature = self
89                .signer
90                .sign_transaction(self.index, tx, network_id)
91                .await?;
92            let key = self.public_key().await?;
93            let hint = SignatureHint(key.0[28..].try_into()?);
94            let signature = Signature(signature.try_into()?);
95            Ok(DecoratedSignature { hint, signature })
96        }
97
98        pub async fn public_key(&self) -> Result<stellar_strkey::ed25519::PublicKey, Error> {
99            Ok(self.signer.get_public_key(&self.index.into()).await?)
100        }
101    }
102}
103
104#[cfg(not(feature = "additional-libs"))]
105mod ledger_impl {
106    use super::{DecoratedSignature, Error, Transaction};
107    use std::marker::PhantomData;
108
109    pub type LedgerType = Ledger<GenericExchange>;
110
111    pub trait Exchange {}
112    pub struct Ledger<T: Exchange> {
113        _marker: PhantomData<T>,
114    }
115
116    #[allow(clippy::unused_async)]
117    pub async fn new(_hd_path: u32) -> Result<Ledger<GenericExchange>, Error> {
118        Err(Error::FeatureNotEnabled)
119    }
120
121    impl<T: Exchange> Ledger<T> {
122        #[allow(clippy::unused_async)]
123        pub async fn sign_transaction_hash(
124            &self,
125            _tx_hash: &[u8; 32],
126        ) -> Result<DecoratedSignature, Error> {
127            Err(Error::FeatureNotEnabled)
128        }
129
130        #[allow(clippy::unused_async)]
131        pub async fn sign_transaction(
132            &self,
133            _tx: Transaction,
134            _network_passphrase: &str,
135        ) -> Result<DecoratedSignature, Error> {
136            Err(Error::FeatureNotEnabled)
137        }
138
139        #[allow(clippy::unused_async)]
140        pub async fn public_key(&self) -> Result<stellar_strkey::ed25519::PublicKey, Error> {
141            Err(Error::FeatureNotEnabled)
142        }
143    }
144
145    pub struct GenericExchange {}
146
147    impl Exchange for GenericExchange {}
148
149    impl GenericExchange {}
150}