mirror of
https://github.com/trezor/trezor-firmware.git
synced 2026-02-20 00:33:30 +01:00
make ui call generic
This commit is contained in:
@@ -643,6 +643,7 @@ static void _librust_qstrs(void) {
|
||||
MP_QSTR_regulatory__title;
|
||||
MP_QSTR_reject_pairing;
|
||||
MP_QSTR_remaining_shares;
|
||||
MP_QSTR_remote;
|
||||
MP_QSTR_request_bip39;
|
||||
MP_QSTR_request_complete_repaint;
|
||||
MP_QSTR_request_duration;
|
||||
|
||||
@@ -157,3 +157,17 @@ impl Drop for IpcMessage<'_> {
|
||||
unsafe { ffi::ipc_message_free(&mut lowlevel_message) };
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, num_enum::IntoPrimitive, num_enum::FromPrimitive)]
|
||||
#[repr(u16)]
|
||||
pub enum CoreIpcService {
|
||||
Lifecycle = 0,
|
||||
Ui = 1,
|
||||
WireStart = 2,
|
||||
WireContinue = 3,
|
||||
WireEnd = 4,
|
||||
Crypto = 5,
|
||||
Util = 6,
|
||||
#[num_enum(catch_all)]
|
||||
Unknown(u16),
|
||||
}
|
||||
|
||||
@@ -1351,9 +1351,10 @@ extern "C" fn new_process_ipc_message(n_args: usize, args: *const Obj, kwargs: *
|
||||
let obj: Obj = kwargs.get(Qstr::MP_QSTR_data)?;
|
||||
|
||||
let data = unwrap!(unsafe { crate::micropython::buffer::get_buffer(obj) });
|
||||
let remote: u8 = kwargs.get(Qstr::MP_QSTR_remote)?.try_into()?;
|
||||
|
||||
// Pass the slice directly to the trait function
|
||||
let layout = ModelUI::process_ipc_message(data)?;
|
||||
let layout = ModelUI::process_ipc_message(data, remote)?;
|
||||
Ok(layout.into())
|
||||
};
|
||||
unsafe { util::try_with_args_and_kwargs(n_args, args, kwargs, block) }
|
||||
@@ -2281,6 +2282,7 @@ pub static mp_module_trezorui_api: Module = obj_module! {
|
||||
/// def process_ipc_message(
|
||||
/// *,
|
||||
/// data: bytes,
|
||||
/// remote: int,
|
||||
/// ) -> LayoutObj[UiResult]:
|
||||
/// """Process an IPC message by deserializing it and dispatching to the appropriate UI function."""
|
||||
Qstr::MP_QSTR_process_ipc_message => obj_fn_kw!(0, new_process_ipc_message).as_obj(),
|
||||
|
||||
@@ -1320,7 +1320,7 @@ impl FirmwareUI for UIBolt {
|
||||
Err::<RootComponent<Empty, ModelUI>, Error>(Error::NotImplementedError)
|
||||
}
|
||||
|
||||
fn process_ipc_message(_data: &[u8]) -> Result<Gc<LayoutObj>, Error> {
|
||||
fn process_ipc_message(_data: &[u8], _remote: u8) -> Result<Gc<LayoutObj>, Error> {
|
||||
Err(Error::NotImplementedError)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1492,7 +1492,7 @@ impl FirmwareUI for UICaesar {
|
||||
Ok(layout)
|
||||
}
|
||||
|
||||
fn process_ipc_message(_data: &[u8]) -> Result<Gc<LayoutObj>, Error> {
|
||||
fn process_ipc_message(_data: &[u8], _remote: u8) -> Result<Gc<LayoutObj>, Error> {
|
||||
Err(Error::NotImplementedError)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1357,7 +1357,7 @@ impl FirmwareUI for UIDelizia {
|
||||
Ok(flow)
|
||||
}
|
||||
|
||||
fn process_ipc_message(_data: &[u8]) -> Result<Gc<LayoutObj>, Error> {
|
||||
fn process_ipc_message(_data: &[u8], _remote: u8) -> Result<Gc<LayoutObj>, Error> {
|
||||
Err(Error::NotImplementedError)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
ipc::{IpcMessage, RemoteSysTask},
|
||||
ipc::{CoreIpcService, IpcMessage, RemoteSysTask},
|
||||
strutil::TString,
|
||||
ui::{
|
||||
cache::PageCache,
|
||||
@@ -38,8 +38,8 @@ pub struct LongContentScreen<'a> {
|
||||
}
|
||||
|
||||
impl<'a> LongContentScreen<'a> {
|
||||
pub fn new(title: TString<'static>, pages: usize) -> Self {
|
||||
let content = LongContent::new(pages as u16);
|
||||
pub fn new(title: TString<'static>, pages: usize, remote: u8) -> Self {
|
||||
let content = LongContent::new(pages as u16, remote);
|
||||
let mut action_bar = ActionBar::new_cancel_confirm();
|
||||
action_bar.update(content.pager);
|
||||
|
||||
@@ -139,15 +139,17 @@ struct LongContent {
|
||||
cache: PageCache,
|
||||
area: Rect,
|
||||
state: ContentState,
|
||||
remote: u8,
|
||||
}
|
||||
|
||||
impl LongContent {
|
||||
fn new(pages: u16) -> Self {
|
||||
fn new(pages: u16, remote: u8) -> Self {
|
||||
Self {
|
||||
pager: Pager::new(pages),
|
||||
cache: PageCache::new(),
|
||||
area: Rect::zero(),
|
||||
state: ContentState::Uninit,
|
||||
remote,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,8 +198,8 @@ impl LongContent {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let msg = IpcMessage::new(9, &bytes);
|
||||
unwrap!(msg.send(RemoteSysTask::Unknown(2), 6));
|
||||
let msg = IpcMessage::new(idx as u16, &bytes);
|
||||
unwrap!(msg.send(RemoteSysTask::Unknown(self.remote), CoreIpcService::Util.into()));
|
||||
ctx.request_anim_frame();
|
||||
}
|
||||
}
|
||||
@@ -218,14 +220,15 @@ impl Component for LongContent {
|
||||
}
|
||||
|
||||
if let Event::Timer(EventCtx::ANIM_FRAME_TIMER) = event {
|
||||
if let Some(data) = IpcMessage::try_receive(RemoteSysTask::Unknown(2)) {
|
||||
if let Some(message) = IpcMessage::try_receive(RemoteSysTask::Unknown(2)) {
|
||||
debug_assert!(matches!(
|
||||
self.state,
|
||||
ContentState::Uninit | ContentState::Waiting(_)
|
||||
));
|
||||
self.state = match self.state {
|
||||
ContentState::Uninit => {
|
||||
self.cache.init(data.data());
|
||||
debug_assert!(message.id() == 0);
|
||||
self.cache.init(message.data());
|
||||
ctx.request_paint();
|
||||
if self.pager.has_next() {
|
||||
let idx = self.pager.next() as usize;
|
||||
@@ -238,15 +241,17 @@ impl Component for LongContent {
|
||||
ContentState::Waiting(next_page)
|
||||
if self.pager.current() + 1 == next_page as u16 =>
|
||||
{
|
||||
debug_assert!(message.id() == next_page as u16);
|
||||
debug_assert!(self.cache.is_at_head());
|
||||
self.cache.push_head(data.data());
|
||||
self.cache.push_head(message.data());
|
||||
ContentState::Ready
|
||||
}
|
||||
ContentState::Waiting(prev_page)
|
||||
if self.pager.current() == prev_page as u16 + 1 =>
|
||||
{
|
||||
debug_assert!(message.id() == prev_page as u16);
|
||||
debug_assert!(self.cache.is_at_tail());
|
||||
self.cache.push_tail(data.data());
|
||||
self.cache.push_tail(message.data());
|
||||
ContentState::Ready
|
||||
}
|
||||
_ => {
|
||||
|
||||
@@ -124,8 +124,12 @@ impl FirmwareUI for UIEckhart {
|
||||
Ok(layout)
|
||||
}
|
||||
|
||||
fn confirm_long(title: TString<'static>, pages: usize) -> Result<impl LayoutMaybeTrace, Error> {
|
||||
let screen = LongContentScreen::new(title, pages);
|
||||
fn confirm_long(
|
||||
title: TString<'static>,
|
||||
pages: usize,
|
||||
remote: u8,
|
||||
) -> Result<impl LayoutMaybeTrace, Error> {
|
||||
let screen = LongContentScreen::new(title, pages, remote);
|
||||
let layout = RootComponent::new(screen);
|
||||
Ok(layout)
|
||||
}
|
||||
@@ -1744,7 +1748,7 @@ impl FirmwareUI for UIEckhart {
|
||||
Ok(flow)
|
||||
}
|
||||
|
||||
fn process_ipc_message(data: &[u8]) -> Result<Gc<LayoutObj>, Error> {
|
||||
fn process_ipc_message(data: &[u8], remote: u8) -> Result<Gc<LayoutObj>, Error> {
|
||||
// Safe helper to convert archived string to TString using Deref
|
||||
fn tstr_from_archived<const N: usize>(s: &ArchivedStringN<N>) -> TString<'static> {
|
||||
unsafe { StrBuffer::from_ptr_and_len(s.data.as_ptr(), s.len as usize) }.into()
|
||||
@@ -1778,6 +1782,7 @@ impl FirmwareUI for UIEckhart {
|
||||
let layout = Self::confirm_long(
|
||||
tstr_from_archived(title),
|
||||
unwrap!(usize::try_from(pages.to_native())),
|
||||
remote,
|
||||
)?;
|
||||
LayoutObj::new_root(layout)
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ pub trait FirmwareUI {
|
||||
fn confirm_long(
|
||||
title: TString<'static>,
|
||||
pages: usize,
|
||||
remote: u8,
|
||||
) -> Result<impl LayoutMaybeTrace, Error>;
|
||||
|
||||
fn confirm_address(
|
||||
@@ -513,9 +514,10 @@ pub trait FirmwareUI {
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `data` - Byte slice containing the rkyv-encoded TrezorEnum message
|
||||
/// * `app_id` - Application identifier
|
||||
///
|
||||
/// # Returns
|
||||
/// A Result containing the layout object or an error if deserialization
|
||||
/// fails
|
||||
fn process_ipc_message(data: &[u8]) -> Result<Gc<LayoutObj>, Error>;
|
||||
fn process_ipc_message(data: &[u8], extapp_id: u8) -> Result<Gc<LayoutObj>, Error>;
|
||||
}
|
||||
|
||||
@@ -868,6 +868,7 @@ def tutorial() -> LayoutObj[UiResult]:
|
||||
def process_ipc_message(
|
||||
*,
|
||||
data: bytes,
|
||||
remote: int,
|
||||
) -> LayoutObj[UiResult]:
|
||||
"""Process an IPC message by deserializing it and dispatching to the appropriate UI function."""
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ _SERVICE_WIRE_START = const(2)
|
||||
_SERVICE_WIRE_CONTINUE = const(3)
|
||||
_SERVICE_WIRE_END = const(4)
|
||||
_SERVICE_CRYPTO = const(5)
|
||||
_SERVICE_LONG_CONFIRM = const(6)
|
||||
_SERVICE_UTIL = const(6)
|
||||
|
||||
|
||||
def fn_id(service: int, message_id: int) -> int:
|
||||
@@ -88,7 +88,7 @@ async def run(request: ExtAppMessage) -> ExtAppResponse:
|
||||
|
||||
if service == _SERVICE_UI:
|
||||
result = await interact(
|
||||
trezorui_api.process_ipc_message(data=bytes(msg.data)),
|
||||
trezorui_api.process_ipc_message(data=bytes(msg.data), remote=msg.remote),
|
||||
None,
|
||||
raise_on_cancel=None,
|
||||
)
|
||||
|
||||
@@ -13,8 +13,7 @@ use trezor_structs::{DerivationPath, LongString, TrezorCryptoEnum};
|
||||
|
||||
use crate::core_services::services_or_die;
|
||||
use crate::ipc::IpcMessage;
|
||||
use crate::low_level_api::ffi::HDNode;
|
||||
use crate::service::{CoreIpcService, Error};
|
||||
use crate::service::{CoreIpcService, Error, NoUtilHandler};
|
||||
use crate::util::Timeout;
|
||||
pub type ArchivedTrezorCryptoResult = Archived<TrezorCryptoResult>;
|
||||
pub type ArchivedTrezorCryptoEnum = Archived<TrezorCryptoEnum>;
|
||||
@@ -29,7 +28,12 @@ type CryptoResult = Result<TrezorCryptoResult>;
|
||||
fn ipc_crypto_call(value: &TrezorCryptoEnum) -> CryptoResult {
|
||||
let bytes = to_bytes::<Failure>(value).unwrap();
|
||||
let message = IpcMessage::new(0, &bytes);
|
||||
let result = services_or_die().call(CoreIpcService::Crypto, &message, Timeout::max())?;
|
||||
let result = services_or_die().call(
|
||||
CoreIpcService::Crypto,
|
||||
&message,
|
||||
Timeout::max(),
|
||||
&NoUtilHandler {},
|
||||
)?;
|
||||
|
||||
// Safe validation using bytecheck before accessing archived data
|
||||
let archived = rkyv::access::<ArchivedTrezorCryptoResult, Failure>(result.data()).unwrap();
|
||||
|
||||
@@ -67,6 +67,10 @@ impl IpcMessage<'_> {
|
||||
pub fn data(&self) -> &[u8] {
|
||||
self.data
|
||||
}
|
||||
|
||||
pub fn remote(&self) -> RemoteSysTask {
|
||||
self.remote
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IpcMessage<'a> {
|
||||
|
||||
@@ -10,6 +10,49 @@ pub const CORE_SERVICE_REMOTE: RemoteSysTask = RemoteSysTask::CoreApp;
|
||||
|
||||
use trezor_structs::ArchivedUtilEnum;
|
||||
|
||||
// ============================================================================
|
||||
// Trait-based Call Abstraction
|
||||
// ============================================================================
|
||||
/// Context for handling utility messages, contains info from the original request
|
||||
pub struct UtilContext {
|
||||
pub service: u16,
|
||||
pub id: u16,
|
||||
pub remote: RemoteSysTask,
|
||||
}
|
||||
|
||||
/// Result of handling a utility message
|
||||
pub enum UtilHandleResult {
|
||||
/// Continue waiting for more messages
|
||||
Continue,
|
||||
/// Unexpected message received
|
||||
Unexpected,
|
||||
}
|
||||
|
||||
/// Trait for handling utility service messages during IPC calls
|
||||
pub trait UtilHandler {
|
||||
/// Returns true if this handler expects utility messages
|
||||
fn expects_util_messages(&self) -> bool;
|
||||
|
||||
/// Handle an incoming utility enum message
|
||||
/// Returns `UtilHandleResult::Continue` if handled successfully and should keep waiting
|
||||
/// Returns `UtilHandleResult::Unexpected` if the message was not expected
|
||||
fn handle(&self, ctx: &UtilContext, archived: &ArchivedUtilEnum) -> UtilHandleResult;
|
||||
}
|
||||
|
||||
/// No utility message handling - for regular calls
|
||||
pub struct NoUtilHandler;
|
||||
|
||||
impl UtilHandler for NoUtilHandler {
|
||||
fn expects_util_messages(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn handle(&self, _ctx: &UtilContext, _archived: &ArchivedUtilEnum) -> UtilHandleResult {
|
||||
// Should never receive utility messages
|
||||
UtilHandleResult::Unexpected
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(uDebug, Copy, Clone, PartialEq, Eq, num_enum::IntoPrimitive, num_enum::FromPrimitive)]
|
||||
#[repr(u16)]
|
||||
pub enum CoreIpcService {
|
||||
@@ -19,10 +62,9 @@ pub enum CoreIpcService {
|
||||
WireContinue = 3,
|
||||
WireEnd = 4,
|
||||
Crypto = 5,
|
||||
LongConfirm = 6,
|
||||
Util = 6,
|
||||
#[num_enum(catch_all)]
|
||||
Unknown(u16),
|
||||
Ping = 0xffff,
|
||||
}
|
||||
|
||||
pub struct IpcRemote<'a, T> {
|
||||
@@ -38,7 +80,7 @@ pub enum Error<'a> {
|
||||
UnexpectedResponse(IpcMessage<'a>),
|
||||
}
|
||||
|
||||
impl<'a, T: Into<u16>> IpcRemote<'a, T> {
|
||||
impl<'a, T: Into<u16> + Copy> IpcRemote<'a, T> {
|
||||
pub const fn new(inbox: IpcInbox<'a>) -> Self {
|
||||
Self {
|
||||
inbox,
|
||||
@@ -65,25 +107,7 @@ impl<'a, T: Into<u16>> IpcRemote<'a, T> {
|
||||
service: T,
|
||||
message: &IpcMessage,
|
||||
timeout: Timeout,
|
||||
) -> Result<IpcMessage<'a>, Error<'a>> {
|
||||
let service_id = service.into();
|
||||
message
|
||||
.send(self.inbox.remote(), service_id)
|
||||
.map_err(|_| Error::FailedToSend)?;
|
||||
let reply = self.receive(timeout)?;
|
||||
if reply.service() != service_id {
|
||||
Err(Error::UnexpectedService(reply))
|
||||
} else {
|
||||
Ok(reply)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_long(
|
||||
&self,
|
||||
service: T,
|
||||
message: &IpcMessage,
|
||||
timeout: Timeout,
|
||||
long_content: &str,
|
||||
util_handler: &dyn UtilHandler,
|
||||
) -> Result<IpcMessage<'a>, Error<'a>> {
|
||||
let service_id = service.into();
|
||||
message
|
||||
@@ -91,31 +115,21 @@ impl<'a, T: Into<u16>> IpcRemote<'a, T> {
|
||||
.map_err(|_| Error::FailedToSend)?;
|
||||
loop {
|
||||
let reply = self.receive(timeout)?;
|
||||
if reply.service() == 6 {
|
||||
|
||||
if reply.service() == u16::from(CoreIpcService::Util)
|
||||
&& util_handler.expects_util_messages()
|
||||
{
|
||||
let util_ctx = UtilContext {
|
||||
service: reply.service(),
|
||||
id: reply.id(),
|
||||
remote: reply.remote(),
|
||||
};
|
||||
let data = reply.data();
|
||||
|
||||
let archived = unsafe { rkyv::access_unchecked::<ArchivedUtilEnum>(data) };
|
||||
match archived {
|
||||
ArchivedUtilEnum::RequestPage { idx } => {
|
||||
let page_idx = idx.to_native() as usize;
|
||||
|
||||
// Find byte range for the requested char slice
|
||||
let mut chars = long_content.chars();
|
||||
let start_byte = chars
|
||||
.by_ref()
|
||||
.take(page_idx * crate::ui::CHARS_PER_PAGE)
|
||||
.map(|c| c.len_utf8())
|
||||
.sum::<usize>();
|
||||
let slice_len = chars
|
||||
.take(crate::ui::CHARS_PER_PAGE)
|
||||
.map(|c| c.len_utf8())
|
||||
.sum::<usize>();
|
||||
let slice = &long_content.as_bytes()[start_byte..start_byte + slice_len];
|
||||
|
||||
// TODO implement Debug for Api error
|
||||
let _ = IpcMessage::new(10, slice).send(RemoteSysTask::CoreApp, 6);
|
||||
}
|
||||
_ => return Err(Error::UnexpectedResponse(reply)),
|
||||
match util_handler.handle(&util_ctx, archived) {
|
||||
UtilHandleResult::Continue => continue,
|
||||
UtilHandleResult::Unexpected => return Err(Error::UnexpectedResponse(reply)),
|
||||
}
|
||||
} else if reply.service() != service_id {
|
||||
return Err(Error::UnexpectedService(reply));
|
||||
|
||||
@@ -23,18 +23,64 @@ use rkyv::api::low::deserialize;
|
||||
use rkyv::rancor::Failure;
|
||||
use rkyv::to_bytes;
|
||||
pub use trezor_structs::TrezorUiResult;
|
||||
use trezor_structs::{LongString, PropsList, ShortString, TrezorUiEnum};
|
||||
use trezor_structs::{ArchivedUtilEnum, LongString, PropsList, ShortString, TrezorUiEnum};
|
||||
|
||||
use crate::core_services::services_or_die;
|
||||
use crate::error;
|
||||
use crate::ipc::IpcMessage;
|
||||
use crate::service::{CoreIpcService, Error};
|
||||
use crate::service::{
|
||||
CoreIpcService, Error, NoUtilHandler, UtilContext, UtilHandleResult, UtilHandler,
|
||||
};
|
||||
use crate::util::Timeout;
|
||||
|
||||
pub type ArchivedTrezorUiResult = Archived<TrezorUiResult>;
|
||||
pub type ArchivedTrezorUiEnum = Archived<TrezorUiEnum>;
|
||||
|
||||
pub const CHARS_PER_PAGE: usize = 96;
|
||||
|
||||
/// Long content handler - responds to page requests
|
||||
pub struct LongContentHandler<'a>(pub &'a str);
|
||||
|
||||
impl<'a> LongContentHandler<'a> {
|
||||
fn send_page(&self, ctx: &UtilContext, page_idx: usize) {
|
||||
let long_content = self.0;
|
||||
|
||||
// Find byte range for the requested char slice
|
||||
let mut chars = long_content.chars();
|
||||
let start_byte = chars
|
||||
.by_ref()
|
||||
.take(page_idx * crate::ui::CHARS_PER_PAGE)
|
||||
.map(|c| c.len_utf8())
|
||||
.sum::<usize>();
|
||||
let slice_len = chars
|
||||
.take(crate::ui::CHARS_PER_PAGE)
|
||||
.map(|c| c.len_utf8())
|
||||
.sum::<usize>();
|
||||
let slice = &long_content.as_bytes()[start_byte..start_byte + slice_len];
|
||||
|
||||
// Send response with the same service ID as the request
|
||||
let _ = IpcMessage::new(ctx.id, slice).send(ctx.remote, ctx.service);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> UtilHandler for LongContentHandler<'a> {
|
||||
fn expects_util_messages(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn handle(&self, ctx: &UtilContext, archived: &ArchivedUtilEnum) -> UtilHandleResult {
|
||||
match archived {
|
||||
ArchivedUtilEnum::RequestPage { idx } => {
|
||||
let page_idx = idx.to_native() as usize;
|
||||
self.send_page(ctx, page_idx);
|
||||
UtilHandleResult::Continue
|
||||
}
|
||||
// Only RequestPage is allowed for LongContentHandler
|
||||
_ => UtilHandleResult::Unexpected,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Helper Functions
|
||||
// ============================================================================
|
||||
@@ -42,33 +88,39 @@ pub const CHARS_PER_PAGE: usize = 96;
|
||||
type Result<T> = core::result::Result<T, Error<'static>>;
|
||||
type UiResult = Result<TrezorUiResult>;
|
||||
|
||||
/// Send a UI enum over IPC and get the response
|
||||
fn ipc_ui_call(value: &TrezorUiEnum) -> UiResult {
|
||||
/// Send a UI enum over IPC with optional long content
|
||||
fn ipc_ui_call(value: &TrezorUiEnum, util_handler: &dyn UtilHandler) -> UiResult {
|
||||
let bytes = to_bytes::<Failure>(value).unwrap();
|
||||
let message = IpcMessage::new(0, &bytes);
|
||||
let result = services_or_die().call(CoreIpcService::Ui, &message, Timeout::max())?;
|
||||
|
||||
// Safe validation using bytecheck before accessing archived data
|
||||
let archived = rkyv::access::<ArchivedTrezorUiResult, Failure>(result.data()).unwrap();
|
||||
let deserialized = deserialize::<TrezorUiResult, Failure>(archived).unwrap();
|
||||
Ok(deserialized)
|
||||
}
|
||||
|
||||
fn ipc_ui_long_call(value: &TrezorUiEnum, long_content: &str) -> UiResult {
|
||||
let bytes = to_bytes::<Failure>(value).unwrap();
|
||||
let message = IpcMessage::new(0, &bytes);
|
||||
let result =
|
||||
services_or_die().call_long(CoreIpcService::Ui, &message, Timeout::max(), long_content)?;
|
||||
|
||||
services_or_die().call(CoreIpcService::Ui, &message, Timeout::max(), util_handler)?;
|
||||
// Safe validation using bytecheck before accessing archived data
|
||||
let archived = rkyv::access::<ArchivedTrezorUiResult, Failure>(result.data()).unwrap();
|
||||
let deserialized = deserialize::<TrezorUiResult, Failure>(archived).unwrap();
|
||||
Ok(deserialized)
|
||||
}
|
||||
|
||||
/// Send a UI enum over IPC and get the response
|
||||
// fn ipc_ui_call(value: &TrezorUiEnum) -> UiResult {
|
||||
// ipc_ui_call_with_content(value, &NoUtilHandler)
|
||||
// }
|
||||
|
||||
// fn ipc_ui_call_with_content(value: &TrezorUiEnum, util_handler: &dyn UtilHandler) -> UiResult {
|
||||
// let bytes = to_bytes::<Failure>(value).unwrap();
|
||||
// let message = IpcMessage::new(0, &bytes);
|
||||
|
||||
// let result = services_or_die().call(CoreIpcService::Ui, &message, Timeout::max(), util_handler)?;
|
||||
|
||||
// // Safe validation using bytecheck before accessing archived data
|
||||
// let archived = rkyv::access::<ArchivedTrezorUiResult, Failure>(result.data()).unwrap();
|
||||
// let deserialized = deserialize::<TrezorUiResult, Failure>(archived).unwrap();
|
||||
// Ok(deserialized)
|
||||
// }
|
||||
|
||||
/// Send a UI call and expect a boolean confirmation result
|
||||
fn ipc_ui_call_confirm(value: TrezorUiEnum) -> UiResult {
|
||||
match ipc_ui_call(&value) {
|
||||
match ipc_ui_call(&value, &NoUtilHandler {}) {
|
||||
Ok(TrezorUiResult::Confirmed) => Ok(TrezorUiResult::Confirmed),
|
||||
Ok(_) => Ok(TrezorUiResult::Cancelled),
|
||||
Err(e) => {
|
||||
@@ -80,10 +132,14 @@ fn ipc_ui_call_confirm(value: TrezorUiEnum) -> UiResult {
|
||||
|
||||
/// Send a UI call that doesn't expect a meaningful response
|
||||
fn ipc_ui_call_void(value: TrezorUiEnum) -> Result<()> {
|
||||
ipc_ui_call(&value)?;
|
||||
ipc_ui_call(&value, &NoUtilHandler {})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// fn ipc_ui_long_call(value: &TrezorUiEnum, long_content: &str) -> UiResult {
|
||||
// ipc_ui_call_with_content(value, &LongContentHandler(long_content))
|
||||
// }
|
||||
|
||||
// ============================================================================
|
||||
// Public API Functions
|
||||
// ============================================================================
|
||||
@@ -105,7 +161,7 @@ pub fn confirm_long_value(title: &str, content: &str) -> UiResult {
|
||||
pages: (content.chars().count() as usize + CHARS_PER_PAGE - 1) / CHARS_PER_PAGE,
|
||||
};
|
||||
|
||||
match ipc_ui_long_call(&value, content) {
|
||||
match ipc_ui_call(&value, &LongContentHandler(content)) {
|
||||
Ok(TrezorUiResult::Confirmed) => Ok(TrezorUiResult::Confirmed),
|
||||
Ok(_) => Ok(TrezorUiResult::Cancelled),
|
||||
Err(e) => {
|
||||
@@ -149,7 +205,7 @@ pub fn request_string(prompt: &str) -> UiResult {
|
||||
let value = TrezorUiEnum::RequestString {
|
||||
prompt: ShortString::from_str(prompt).unwrap(),
|
||||
};
|
||||
let result = ipc_ui_call(&value)?;
|
||||
let result = ipc_ui_call(&value, &NoUtilHandler {})?;
|
||||
match result {
|
||||
TrezorUiResult::String(_) => Ok(result),
|
||||
_ => Ok(TrezorUiResult::Cancelled),
|
||||
@@ -166,7 +222,7 @@ pub fn request_number(title: &str, content: &str, initial: u32, min: u32, max: u
|
||||
max,
|
||||
};
|
||||
|
||||
let result = ipc_ui_call(&value)?;
|
||||
let result = ipc_ui_call(&value, &NoUtilHandler {})?;
|
||||
match result {
|
||||
TrezorUiResult::Integer(_) => Ok(result),
|
||||
_ => Ok(TrezorUiResult::Cancelled),
|
||||
@@ -177,20 +233,9 @@ pub fn show_public_key(key: &str) -> UiResult {
|
||||
let value = TrezorUiEnum::ShowPublicKey {
|
||||
key: LongString::from_str(key).unwrap(),
|
||||
};
|
||||
let result = ipc_ui_call(&value)?;
|
||||
let result = ipc_ui_call(&value, &NoUtilHandler {})?;
|
||||
match result {
|
||||
TrezorUiResult::Confirmed => Ok(result),
|
||||
_ => Ok(TrezorUiResult::Cancelled),
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a ping message (for testing)
|
||||
pub fn ping(msg: &str) -> Result<()> {
|
||||
let ping = IpcMessage::new(0, msg.as_bytes());
|
||||
let resp = services_or_die().call(CoreIpcService::Ping, &ping, Timeout::max())?;
|
||||
if resp.data() == msg.as_bytes() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::UnexpectedResponse(resp))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user