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