coins_ledger/protocol.rs
1use crate::{Ledger, LedgerError};
2
3/// A Ledger Recovery. A the device to perform
4/// some operation. Protocols are run in a task, and may send multiple
5/// commands to the device, and receive multiple responses. The protocol has
6/// exclusive access to the transport while it is running.
7///
8/// The protocol may fail, and the [`LedgerProtocol::recover`] function will
9/// be invoked. The protocol may also fail to recover, in which case future
10/// uses of the device may fail.
11pub trait LedgerProtocol {
12 /// The output of the protocol.
13 type Output;
14
15 /// Run the protocol. This sends commands to the device, and receives
16 /// responses. The transport is locked while this function is running.
17 /// If the protocol fails, the app may be in an undefined state, and
18 /// the [`LedgerProtocol::recover`] function will be invoked.
19 ///
20 fn execute(&mut self, transport: &mut Ledger) -> Result<Self::Output, LedgerError>;
21
22 /// Run recovery if the protocol fails.
23 ///
24 /// This is invoked after the protocol fails. This function should attempt
25 /// to recover the app on the device to a known state.
26 ///
27 /// Multi-APDU protocols MUST override this function. The recommended
28 /// implementation is to retrieve a pubkey from the device twice.
29 fn recover(&self, _transport: &mut Ledger) -> Result<(), LedgerError> {
30 Ok(())
31 }
32
33 /// Run the protocol. This sends commands to the device, and receives
34 /// responses. The transport is locked while this function is running.
35 fn run(&mut self, transport: &mut Ledger) -> Result<Self::Output, LedgerError> {
36 match self.execute(transport) {
37 Ok(output) => Ok(output),
38 Err(e) => {
39 // TODO: make less ugly
40 #[cfg(target_arch = "wasm32")]
41 log::error!("Protocol failed, running recovery: {}", e);
42 #[cfg(not(target_arch = "wasm32"))]
43 tracing::error!(err = %e, "Protocol failed, running recovery.");
44
45 if let Err(e) = self.recover(transport) {
46 #[cfg(target_arch = "wasm32")]
47 log::error!("Recovery failed: {}", e);
48 #[cfg(not(target_arch = "wasm32"))]
49 tracing::error!(err = %e, "Recovery failed.");
50 }
51
52 Err(e)
53 }
54 }
55 }
56}