soroban_cli/signer/
ledger.rs1use 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 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}