use crate::{
error::{Error, Result},
messages::TrezorMessage,
protos, Trezor,
};
use std::fmt;
pub use protos::{
button_request::ButtonRequestType, pin_matrix_request::PinMatrixRequestType, Features,
};
#[cfg(feature = "bitcoin")]
pub use protos::InputScriptType;
pub enum WordCount {
W12 = 12,
W18 = 18,
W24 = 24,
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum InteractionType {
Button,
PinMatrix,
Passphrase,
PassphraseState,
}
pub type ResultHandler<'a, T, R> = dyn Fn(&'a mut Trezor, R) -> Result<T>;
pub struct ButtonRequest<'a, T, R: TrezorMessage> {
pub message: protos::ButtonRequest,
pub client: &'a mut Trezor,
pub result_handler: Box<ResultHandler<'a, T, R>>,
}
impl<'a, T, R: TrezorMessage> fmt::Debug for ButtonRequest<'a, T, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.message, f)
}
}
impl<'a, T, R: TrezorMessage> ButtonRequest<'a, T, R> {
pub fn request_type(&self) -> ButtonRequestType {
self.message.code()
}
pub fn ack(self) -> Result<TrezorResponse<'a, T, R>> {
let req = protos::ButtonAck::new();
self.client.call(req, self.result_handler)
}
}
pub struct PinMatrixRequest<'a, T, R: TrezorMessage> {
pub message: protos::PinMatrixRequest,
pub client: &'a mut Trezor,
pub result_handler: Box<ResultHandler<'a, T, R>>,
}
impl<'a, T, R: TrezorMessage> fmt::Debug for PinMatrixRequest<'a, T, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.message, f)
}
}
impl<'a, T, R: TrezorMessage> PinMatrixRequest<'a, T, R> {
pub fn request_type(&self) -> PinMatrixRequestType {
self.message.type_()
}
pub fn ack_pin(self, pin: String) -> Result<TrezorResponse<'a, T, R>> {
let mut req = protos::PinMatrixAck::new();
req.set_pin(pin);
self.client.call(req, self.result_handler)
}
}
pub struct PassphraseRequest<'a, T, R: TrezorMessage> {
pub message: protos::PassphraseRequest,
pub client: &'a mut Trezor,
pub result_handler: Box<ResultHandler<'a, T, R>>,
}
impl<'a, T, R: TrezorMessage> fmt::Debug for PassphraseRequest<'a, T, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.message, f)
}
}
impl<'a, T, R: TrezorMessage> PassphraseRequest<'a, T, R> {
pub fn on_device(&self) -> bool {
self.message._on_device()
}
pub fn ack_passphrase(self, passphrase: String) -> Result<TrezorResponse<'a, T, R>> {
let mut req = protos::PassphraseAck::new();
req.set_passphrase(passphrase);
self.client.call(req, self.result_handler)
}
pub fn ack(self, on_device: bool) -> Result<TrezorResponse<'a, T, R>> {
let mut req = protos::PassphraseAck::new();
if on_device {
req.set_on_device(on_device);
}
self.client.call(req, self.result_handler)
}
}
#[derive(Debug)]
pub enum TrezorResponse<'a, T, R: TrezorMessage> {
Ok(T),
Failure(protos::Failure),
ButtonRequest(ButtonRequest<'a, T, R>),
PinMatrixRequest(PinMatrixRequest<'a, T, R>),
PassphraseRequest(PassphraseRequest<'a, T, R>),
}
impl<'a, T, R: TrezorMessage> fmt::Display for TrezorResponse<'a, T, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TrezorResponse::Ok(ref _m) => f.write_str("Ok"),
TrezorResponse::Failure(ref m) => write!(f, "Failure: {:?}", m),
TrezorResponse::ButtonRequest(ref r) => write!(f, "ButtonRequest: {:?}", r),
TrezorResponse::PinMatrixRequest(ref r) => write!(f, "PinMatrixRequest: {:?}", r),
TrezorResponse::PassphraseRequest(ref r) => write!(f, "PassphraseRequest: {:?}", r),
}
}
}
impl<'a, T, R: TrezorMessage> TrezorResponse<'a, T, R> {
pub fn ok(self) -> Result<T> {
match self {
TrezorResponse::Ok(m) => Ok(m),
TrezorResponse::Failure(m) => Err(Error::FailureResponse(m)),
TrezorResponse::ButtonRequest(_) => {
Err(Error::UnexpectedInteractionRequest(InteractionType::Button))
}
TrezorResponse::PinMatrixRequest(_) => {
Err(Error::UnexpectedInteractionRequest(InteractionType::PinMatrix))
}
TrezorResponse::PassphraseRequest(_) => {
Err(Error::UnexpectedInteractionRequest(InteractionType::Passphrase))
}
}
}
pub fn button_request(self) -> Result<ButtonRequest<'a, T, R>> {
match self {
TrezorResponse::ButtonRequest(r) => Ok(r),
TrezorResponse::Ok(_) => Err(Error::UnexpectedMessageType(R::MESSAGE_TYPE)),
TrezorResponse::Failure(m) => Err(Error::FailureResponse(m)),
TrezorResponse::PinMatrixRequest(_) => {
Err(Error::UnexpectedInteractionRequest(InteractionType::PinMatrix))
}
TrezorResponse::PassphraseRequest(_) => {
Err(Error::UnexpectedInteractionRequest(InteractionType::Passphrase))
}
}
}
pub fn pin_matrix_request(self) -> Result<PinMatrixRequest<'a, T, R>> {
match self {
TrezorResponse::PinMatrixRequest(r) => Ok(r),
TrezorResponse::Ok(_) => Err(Error::UnexpectedMessageType(R::MESSAGE_TYPE)),
TrezorResponse::Failure(m) => Err(Error::FailureResponse(m)),
TrezorResponse::ButtonRequest(_) => {
Err(Error::UnexpectedInteractionRequest(InteractionType::Button))
}
TrezorResponse::PassphraseRequest(_) => {
Err(Error::UnexpectedInteractionRequest(InteractionType::Passphrase))
}
}
}
pub fn passphrase_request(self) -> Result<PassphraseRequest<'a, T, R>> {
match self {
TrezorResponse::PassphraseRequest(r) => Ok(r),
TrezorResponse::Ok(_) => Err(Error::UnexpectedMessageType(R::MESSAGE_TYPE)),
TrezorResponse::Failure(m) => Err(Error::FailureResponse(m)),
TrezorResponse::ButtonRequest(_) => {
Err(Error::UnexpectedInteractionRequest(InteractionType::Button))
}
TrezorResponse::PinMatrixRequest(_) => {
Err(Error::UnexpectedInteractionRequest(InteractionType::PinMatrix))
}
}
}
}
pub fn handle_interaction<T, R: TrezorMessage>(resp: TrezorResponse<'_, T, R>) -> Result<T> {
match resp {
TrezorResponse::Ok(res) => Ok(res),
TrezorResponse::Failure(_) => resp.ok(), TrezorResponse::ButtonRequest(req) => handle_interaction(req.ack()?),
TrezorResponse::PinMatrixRequest(_) => Err(Error::UnsupportedNetwork),
TrezorResponse::PassphraseRequest(req) => handle_interaction({
let on_device = req.on_device();
req.ack(!on_device)?
}),
}
}
pub struct EntropyRequest<'a> {
pub client: &'a mut Trezor,
}
impl<'a> EntropyRequest<'a> {
pub fn ack_entropy(self, entropy: Vec<u8>) -> Result<TrezorResponse<'a, (), protos::Success>> {
if entropy.len() != 32 {
return Err(Error::InvalidEntropy)
}
let mut req = protos::EntropyAck::new();
req.set_entropy(entropy);
self.client.call(req, Box::new(|_, _| Ok(())))
}
}