soroban_cli/signer/
ledger.rs1use 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 ed25519_dalek::Signature as Ed25519Signature;
26 use sha2::{Digest, Sha256};
27 use stellar_ledger::{Blob as _, Exchange, LedgerSigner};
28
29 #[cfg(not(feature = "emulator-tests"))]
30 pub type LedgerType = Ledger<stellar_ledger::TransportNativeHID>;
31 #[cfg(feature = "emulator-tests")]
32 pub type LedgerType = Ledger<stellar_ledger::emulator_test_support::http_transport::Emulator>;
33
34 pub struct LedgerEntry {
39 pub hd_path: u32,
40 pub public_key: Option<stellar_strkey::ed25519::PublicKey>,
41 }
42
43 impl LedgerEntry {
44 pub async fn sign_tx_hash(&self, tx_hash: [u8; 32]) -> Result<DecoratedSignature, Error> {
45 let live = new(self.hd_path).await?;
46 let key = match self.public_key {
47 Some(pk) => pk,
48 None => live.public_key().await?,
49 };
50 let hint = SignatureHint(key.0[28..].try_into()?);
51 let signature = Signature(
52 live.signer
53 .sign_transaction_hash(live.index, &tx_hash)
54 .await?
55 .try_into()?,
56 );
57 Ok(DecoratedSignature { hint, signature })
58 }
59
60 pub async fn sign_payload(&self, payload: [u8; 32]) -> Result<Ed25519Signature, Error> {
61 let live = new(self.hd_path).await?;
62 let bytes = live
63 .signer
64 .sign_transaction_hash(live.index, &payload)
65 .await?;
66 Ok(Ed25519Signature::from_bytes(bytes.as_slice().try_into()?))
67 }
68 }
69
70 pub struct Ledger<T: Exchange> {
71 pub(crate) index: u32,
72 pub(crate) signer: LedgerSigner<T>,
73 }
74
75 #[cfg(not(feature = "emulator-tests"))]
76 #[allow(clippy::unused_async)]
77 pub async fn new(hd_path: u32) -> Result<Ledger<stellar_ledger::TransportNativeHID>, Error> {
78 let signer = stellar_ledger::native()?;
79 Ok(Ledger {
80 index: hd_path,
81 signer,
82 })
83 }
84
85 #[cfg(feature = "emulator-tests")]
86 pub async fn new(
87 hd_path: u32,
88 ) -> Result<Ledger<stellar_ledger::emulator_test_support::http_transport::Emulator>, Error>
89 {
90 use stellar_ledger::emulator_test_support::ledger as emulator_ledger;
91 let host_port: u16 = std::env::var("SPECULOS_PORT")
93 .expect("SPECULOS_PORT env var not set")
94 .parse()
95 .expect("port must be a number");
96 let signer = emulator_ledger(host_port).await;
97
98 Ok(Ledger {
99 index: hd_path,
100 signer,
101 })
102 }
103
104 impl<T: Exchange> Ledger<T> {
105 pub async fn sign_transaction(
106 &self,
107 tx: Transaction,
108 network_passphrase: &str,
109 ) -> Result<DecoratedSignature, Error> {
110 let network_id = Hash(Sha256::digest(network_passphrase).into());
111 let signature = self
112 .signer
113 .sign_transaction(self.index, tx, network_id)
114 .await?;
115 let key = self.public_key().await?;
116 let hint = SignatureHint(key.0[28..].try_into()?);
117 let signature = Signature(signature.try_into()?);
118 Ok(DecoratedSignature { hint, signature })
119 }
120
121 pub async fn public_key(&self) -> Result<stellar_strkey::ed25519::PublicKey, Error> {
122 Ok(self.signer.get_public_key(&self.index.into()).await?)
123 }
124 }
125}
126
127#[cfg(not(feature = "additional-libs"))]
128mod ledger_impl {
129 use super::Error;
130 use crate::xdr::{DecoratedSignature, Transaction};
131 use ed25519_dalek::Signature as Ed25519Signature;
132 use std::marker::PhantomData;
133
134 pub type LedgerType = Ledger<GenericExchange>;
135
136 pub trait Exchange {}
137 pub struct Ledger<T: Exchange> {
138 _marker: PhantomData<T>,
139 }
140
141 pub struct LedgerEntry {
142 pub hd_path: u32,
143 pub public_key: Option<stellar_strkey::ed25519::PublicKey>,
144 }
145
146 impl LedgerEntry {
147 #[allow(clippy::unused_async)]
148 pub async fn sign_tx_hash(&self, _tx_hash: [u8; 32]) -> Result<DecoratedSignature, Error> {
149 Err(Error::FeatureNotEnabled)
150 }
151
152 #[allow(clippy::unused_async)]
153 pub async fn sign_payload(&self, _payload: [u8; 32]) -> Result<Ed25519Signature, Error> {
154 Err(Error::FeatureNotEnabled)
155 }
156 }
157
158 #[allow(clippy::unused_async)]
159 pub async fn new(_hd_path: u32) -> Result<Ledger<GenericExchange>, Error> {
160 Err(Error::FeatureNotEnabled)
161 }
162
163 impl<T: Exchange> Ledger<T> {
164 #[allow(clippy::unused_async)]
165 pub async fn sign_transaction(
166 &self,
167 _tx: Transaction,
168 _network_passphrase: &str,
169 ) -> Result<DecoratedSignature, Error> {
170 Err(Error::FeatureNotEnabled)
171 }
172
173 #[allow(clippy::unused_async)]
174 pub async fn public_key(&self) -> Result<stellar_strkey::ed25519::PublicKey, Error> {
175 Err(Error::FeatureNotEnabled)
176 }
177 }
178
179 pub struct GenericExchange {}
180
181 impl Exchange for GenericExchange {}
182
183 impl GenericExchange {}
184}