soroban_cli/signer/
ledger.rs

1use crate::xdr;
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::Error;
24    use crate::xdr::{DecoratedSignature, Hash, Signature, SignatureHint, Transaction};
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    #[allow(clippy::unused_async)]
40    pub async fn new(hd_path: u32) -> Result<Ledger<stellar_ledger::TransportNativeHID>, Error> {
41        let signer = stellar_ledger::native()?;
42        Ok(Ledger {
43            index: hd_path,
44            signer,
45        })
46    }
47
48    #[cfg(feature = "emulator-tests")]
49    pub async fn new(
50        hd_path: u32,
51    ) -> Result<Ledger<stellar_ledger::emulator_test_support::http_transport::Emulator>, Error>
52    {
53        use stellar_ledger::emulator_test_support::ledger as emulator_ledger;
54        // port from SPECULOS_PORT ENV var
55        let host_port: u16 = std::env::var("SPECULOS_PORT")
56            .expect("SPECULOS_PORT env var not set")
57            .parse()
58            .expect("port must be a number");
59        let signer = emulator_ledger(host_port).await;
60
61        Ok(Ledger {
62            index: hd_path,
63            signer,
64        })
65    }
66
67    impl<T: Exchange> Ledger<T> {
68        pub async fn sign_transaction_hash(
69            &self,
70            tx_hash: &[u8; 32],
71        ) -> Result<DecoratedSignature, Error> {
72            let key = self.public_key().await?;
73            let hint = SignatureHint(key.0[28..].try_into()?);
74            let signature = Signature(
75                self.signer
76                    .sign_transaction_hash(self.index, tx_hash)
77                    .await?
78                    .try_into()?,
79            );
80            Ok(DecoratedSignature { hint, signature })
81        }
82
83        pub async fn sign_transaction(
84            &self,
85            tx: Transaction,
86            network_passphrase: &str,
87        ) -> Result<DecoratedSignature, Error> {
88            let network_id = Hash(Sha256::digest(network_passphrase).into());
89            let signature = self
90                .signer
91                .sign_transaction(self.index, tx, network_id)
92                .await?;
93            let key = self.public_key().await?;
94            let hint = SignatureHint(key.0[28..].try_into()?);
95            let signature = Signature(signature.try_into()?);
96            Ok(DecoratedSignature { hint, signature })
97        }
98
99        pub async fn public_key(&self) -> Result<stellar_strkey::ed25519::PublicKey, Error> {
100            Ok(self.signer.get_public_key(&self.index.into()).await?)
101        }
102    }
103}
104
105#[cfg(not(feature = "additional-libs"))]
106mod ledger_impl {
107    use super::Error;
108    use crate::xdr::{DecoratedSignature, Transaction};
109    use std::marker::PhantomData;
110
111    pub type LedgerType = Ledger<GenericExchange>;
112
113    pub trait Exchange {}
114    pub struct Ledger<T: Exchange> {
115        _marker: PhantomData<T>,
116    }
117
118    #[allow(clippy::unused_async)]
119    pub async fn new(_hd_path: u32) -> Result<Ledger<GenericExchange>, Error> {
120        Err(Error::FeatureNotEnabled)
121    }
122
123    impl<T: Exchange> Ledger<T> {
124        #[allow(clippy::unused_async)]
125        pub async fn sign_transaction_hash(
126            &self,
127            _tx_hash: &[u8; 32],
128        ) -> Result<DecoratedSignature, Error> {
129            Err(Error::FeatureNotEnabled)
130        }
131
132        #[allow(clippy::unused_async)]
133        pub async fn sign_transaction(
134            &self,
135            _tx: Transaction,
136            _network_passphrase: &str,
137        ) -> Result<DecoratedSignature, Error> {
138            Err(Error::FeatureNotEnabled)
139        }
140
141        #[allow(clippy::unused_async)]
142        pub async fn public_key(&self) -> Result<stellar_strkey::ed25519::PublicKey, Error> {
143            Err(Error::FeatureNotEnabled)
144        }
145    }
146
147    pub struct GenericExchange {}
148
149    impl Exchange for GenericExchange {}
150
151    impl GenericExchange {}
152}