receive event

This commit is contained in:
Lukas Bielesch
2026-02-12 11:19:52 +01:00
parent c33815e4c3
commit c1e28ebb07
17 changed files with 161 additions and 66 deletions

View File

@@ -586,6 +586,7 @@ fn generate_ipc_bindings() {
.allowlist_function("ipc_send")
.allowlist_function("ipc_message_free")
.allowlist_function("ipc_try_receive")
.allowlist_function("ipc_has_message")
.allowlist_function("ipc_register")
.allowlist_function("ipc_unregister")
.allowlist_type("ipc_message_t")

View File

@@ -427,6 +427,7 @@ static void _librust_qstrs(void) {
MP_QSTR_instructions_verb;
MP_QSTR_interface;
MP_QSTR_ipc_cb;
MP_QSTR_ipc_event;
MP_QSTR_is_connectable;
MP_QSTR_is_connected;
MP_QSTR_is_data;

View File

@@ -122,6 +122,10 @@ impl<'a> IpcMessage<'a> {
}
}
pub fn has_message(remote: RemoteSysTask) -> bool {
unsafe { ffi::ipc_has_message(remote.into()) }
}
pub fn try_receive(remote: RemoteSysTask) -> Option<Self> {
let mut lowlevel_message = ffi::ipc_message_t {
remote: remote.into(),

View File

@@ -1507,6 +1507,9 @@ pub static mp_module_trezorui_api: Module = obj_module! {
/// def ble_event(self, event: int, data: bytes) -> LayoutState | None:
/// """Receive a BLE events."""
///
/// def ipc_event(self) -> LayoutState | None:
/// """Signal an incoming IPC message."""
///
/// if utils.USE_POWER_MANAGER:
/// def pm_event(self, flags: int) -> LayoutState | None:
/// """Receive a power management event with packed flags."""

View File

@@ -15,6 +15,7 @@ use crate::{
#[cfg(feature = "ble")]
use crate::ui::event::BLEEvent;
use crate::ui::event::IpcEvent;
#[cfg(feature = "button")]
use crate::ui::event::ButtonEvent;
#[cfg(feature = "power_manager")]
@@ -327,6 +328,7 @@ pub enum Event {
Touch(TouchEvent),
#[cfg(feature = "ble")]
BLE(BLEEvent),
IPC(IpcEvent),
#[cfg(feature = "power_manager")]
PM(PMEvent),
USBWire,

View File

@@ -0,0 +1,14 @@
use crate::error::Error;
#[derive(Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "debug", derive(ufmt::derive::uDebug))]
pub enum IpcEvent {
Received,
}
impl IpcEvent {
pub fn new() -> Result<Self, Error> {
let result = Self::Received;
Ok(result)
}
}

View File

@@ -8,11 +8,14 @@ pub mod usb;
#[cfg(feature = "ble")]
mod ble;
mod ipc;
#[cfg(feature = "power_manager")]
mod power_manager;
#[cfg(feature = "ble")]
pub use ble::BLEEvent;
pub use ipc::IpcEvent;
#[cfg(feature = "button")]
pub use button::{ButtonEvent, PhysicalButton};
#[cfg(feature = "power_manager")]

View File

@@ -14,6 +14,8 @@ use num_traits::ToPrimitive;
#[cfg(feature = "ble")]
use crate::ui::event::BLEEvent;
use crate::ui::event::IpcEvent;
#[cfg(feature = "button")]
use crate::{
trezorhal::button::{PhysicalButton, PhysicalButtonEvent},
@@ -412,7 +414,8 @@ impl LayoutObj {
Qstr::MP_QSTR_button_event => obj_fn_var!(3, 3, ui_layout_button_event).as_obj(),
Qstr::MP_QSTR_progress_event => obj_fn_var!(3, 3, ui_layout_progress_event).as_obj(),
Qstr::MP_QSTR_usb_event => obj_fn_var!(2, 2, ui_layout_usb_event).as_obj(),
Qstr::MP_QSTR_ble_event => obj_fn_var!(3, 3, ui_layout_ble_event).as_obj(),
Qstr::MP_QSTR_ble_event => obj_fn_var!(2, 3, ui_layout_ble_event).as_obj(),
Qstr::MP_QSTR_ipc_event => obj_fn_var!(1, 1, ui_layout_ipc_event).as_obj(),
Qstr::MP_QSTR_pm_event => obj_fn_2!(ui_layout_pm_event).as_obj(),
Qstr::MP_QSTR_timer => obj_fn_2!(ui_layout_timer).as_obj(),
Qstr::MP_QSTR_paint => obj_fn_1!(ui_layout_paint).as_obj(),
@@ -574,6 +577,20 @@ extern "C" fn ui_layout_ble_event(_n_args: usize, _args: *const Obj) -> Obj {
Obj::const_none()
}
extern "C" fn ui_layout_ipc_event(n_args: usize, args: *const Obj) -> Obj {
let block = |args: &[Obj], _kwargs: &Map| {
if args.len() != 1 {
return Err(Error::TypeError);
}
let this: Gc<LayoutObj> = args[0].try_into()?;
let event = IpcEvent::new()?;
let msg = this.inner_mut().obj_event(Event::IPC(event))?;
Ok(msg)
};
unsafe { util::try_with_args_and_kwargs(n_args, args, &Map::EMPTY, block) }
}
#[cfg(feature = "power_manager")]
extern "C" fn ui_layout_pm_event(this: Obj, flags: Obj) -> Obj {
let block = || {

View File

@@ -8,6 +8,7 @@ use crate::{
text::{layout::LayoutFit, LineBreaking},
Component, Event, EventCtx, Never, TextLayout,
},
event::IpcEvent,
geometry::Rect,
shape::Renderer,
util::Pager,
@@ -60,16 +61,16 @@ where
}
}
fn switch_next(&mut self, ctx: &mut EventCtx) {
fn switch_next(&mut self) {
debug_assert!(!self.content.pager.is_last());
self.content.switch_next(ctx);
self.content.switch_next();
self.hint.update(self.content.pager());
self.action_bar.update(self.content.pager());
}
fn switch_prev(&mut self, ctx: &mut EventCtx) {
fn switch_prev(&mut self) {
debug_assert!(!self.content.pager.is_first());
self.content.switch_prev(ctx);
self.content.switch_prev();
self.hint.update(self.content.pager());
self.action_bar.update(self.content.pager());
}
@@ -106,12 +107,12 @@ where
ActionBarMsg::Cancelled => return Some(LongContentScreenMsg::Cancelled),
ActionBarMsg::Next => {
debug_assert!(!self.content.pager.is_last());
self.switch_next(ctx);
self.switch_next();
return None;
}
ActionBarMsg::Prev => {
debug_assert!(!self.content.pager.is_first());
self.switch_prev(ctx);
self.switch_prev();
return None;
}
}
@@ -171,7 +172,7 @@ where
self.pager
}
fn switch_next(&mut self, ctx: &mut EventCtx) {
fn switch_next(&mut self) {
debug_assert!(!self.pager.is_last());
debug_assert!(matches!(self.state, ContentState::Ready));
self.pager.goto_next();
@@ -179,12 +180,12 @@ where
if self.pager.has_next() && self.cache.is_at_head() {
let next = self.pager.next() as usize;
self.request_page(ctx, next);
self.request_page(next);
self.state = ContentState::Waiting(next);
}
}
fn switch_prev(&mut self, ctx: &mut EventCtx) {
fn switch_prev(&mut self) {
debug_assert!(!self.pager.is_first());
debug_assert!(matches!(self.state, ContentState::Ready));
self.pager.goto_prev();
@@ -192,12 +193,12 @@ where
if self.pager.has_prev() && self.cache.is_at_tail() {
let prev = self.pager.prev() as usize;
self.request_page(ctx, prev);
self.request_page(prev);
self.state = ContentState::Waiting(prev);
}
}
fn request_page(&self, ctx: &mut EventCtx, idx: usize) {
fn request_page(&self, idx: usize) {
let data = UtilEnum::RequestPage { idx };
let mut arena = [MaybeUninit::<u8>::uninit(); 200];
@@ -211,7 +212,6 @@ where
.unwrap();
(self.request_cb)(&bytes, idx as u16);
ctx.request_anim_frame();
}
}
@@ -229,10 +229,10 @@ where
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
if matches!(event, Event::Attach(AttachType::Initial)) {
debug_assert!(self.state == ContentState::Uninit);
self.request_page(ctx, 0);
self.request_page(0);
}
if let Event::Timer(EventCtx::ANIM_FRAME_TIMER) = event {
if let Event::IPC(IpcEvent::Received) = event {
if let Some(message) = IpcMessage::try_receive(RemoteSysTask::Unknown(2)) {
debug_assert!(matches!(
self.state,
@@ -245,7 +245,7 @@ where
ctx.request_paint();
if self.pager.has_next() {
let idx = self.pager.next() as usize;
self.request_page(ctx, idx);
self.request_page(idx);
ContentState::Waiting(idx)
} else {
ContentState::Ready
@@ -272,7 +272,7 @@ where
}
}
} else {
ctx.request_anim_frame();
unimplemented!("Unexpected IPC message received");
}
}

View File

@@ -31,7 +31,7 @@ typedef struct {
// Function code with flags (IPC_FN_xxx)
uint32_t fn;
// Pointer to the message payload data
const void *data;
const void* data;
// Size of the payload data
size_t size;
} ipc_message_t;
@@ -62,7 +62,7 @@ bool ipc_init(void);
* @return true if the buffer was successfully registered
*
*/
bool ipc_register(systask_id_t remote, void *buffer, size_t size);
bool ipc_register(systask_id_t remote, void* buffer, size_t size);
/**
* @brief Unregisters the IPC message buffer for the given task ID.
@@ -72,6 +72,14 @@ bool ipc_register(systask_id_t remote, void *buffer, size_t size);
*/
void ipc_unregister(systask_id_t remote);
/**
* @brief Checks if there are any pending IPC messages from the specified task.
*
* @param remote The remote task ID to check for messages.
* @return true if there are pending messages, false otherwise
*/
bool ipc_has_message(systask_id_t remote);
/**
* @brief Attempts to receive an IPC message without blocking.
*
@@ -79,7 +87,7 @@ void ipc_unregister(systask_id_t remote);
* message.
* @return true if a message was received and stored in `msg`
*/
bool ipc_try_receive(ipc_message_t *msg);
bool ipc_try_receive(ipc_message_t* msg);
/**
* @brief Releases resources associated with a received IPC message.
@@ -89,7 +97,7 @@ bool ipc_try_receive(ipc_message_t *msg);
*
* @param msg Pointer to the freed `ipc_message_t` structure.
*/
void ipc_message_free(ipc_message_t *msg);
void ipc_message_free(ipc_message_t* msg);
/**
* @brief Sends an IPC message to the specified destination task.
@@ -105,5 +113,5 @@ void ipc_message_free(ipc_message_t *msg);
*
* @return true if the message was successfully sent
*/
bool ipc_send(systask_id_t remote, uint32_t fn, const void *data,
bool ipc_send(systask_id_t remote, uint32_t fn, const void* data,
size_t data_size);

View File

@@ -43,9 +43,9 @@ typedef struct {
} ipc_queue_item_t;
typedef struct {
uint8_t *ptr;
uint8_t *wptr;
uint8_t *rptr;
uint8_t* ptr;
uint8_t* wptr;
uint8_t* rptr;
size_t size;
} ipc_queue_t;
@@ -63,7 +63,7 @@ static ipc_driver_t g_ipc_driver = {
static const syshandle_vmt_t g_ipc_handle_vmt;
bool ipc_init(void) {
ipc_driver_t *drv = &g_ipc_driver;
ipc_driver_t* drv = &g_ipc_driver;
if (drv->initialized) {
return true;
@@ -73,7 +73,7 @@ bool ipc_init(void) {
for (systask_id_t task_id = 0; task_id < SYSTASK_MAX_TASKS; task_id++) {
syshandle_t handle = SYSHANDLE_IPC0 + task_id;
void *context = (void *)(uintptr_t)task_id;
void* context = (void*)(uintptr_t)task_id;
if (!syshandle_register(handle, &g_ipc_handle_vmt, context)) {
return false;
}
@@ -83,8 +83,8 @@ bool ipc_init(void) {
return true;
}
ipc_queue_t *ipc_queue(systask_id_t target, systask_id_t origin) {
ipc_driver_t *drv = &g_ipc_driver;
ipc_queue_t* ipc_queue(systask_id_t target, systask_id_t origin) {
ipc_driver_t* drv = &g_ipc_driver;
if (!drv->initialized) {
return NULL;
@@ -97,9 +97,9 @@ ipc_queue_t *ipc_queue(systask_id_t target, systask_id_t origin) {
return &drv->queue[target][origin];
}
bool ipc_register(systask_id_t remote, void *buffer, size_t size) {
bool ipc_register(systask_id_t remote, void* buffer, size_t size) {
systask_id_t target = systask_id(systask_active());
ipc_queue_t *queue = ipc_queue(target, remote);
ipc_queue_t* queue = ipc_queue(target, remote);
if (queue == NULL) {
return false;
@@ -123,16 +123,16 @@ bool ipc_register(systask_id_t remote, void *buffer, size_t size) {
void ipc_unregister(systask_id_t remote) {
systask_id_t target = systask_id(systask_active());
ipc_queue_t *queue = ipc_queue(target, remote);
ipc_queue_t* queue = ipc_queue(target, remote);
if (queue != NULL) {
memset(queue, 0, sizeof(ipc_queue_t));
}
}
bool ipc_try_receive(ipc_message_t *msg) {
bool ipc_has_message(systask_id_t remote) {
systask_id_t target = systask_id(systask_active());
ipc_queue_t *queue = ipc_queue(target, msg->remote);
ipc_queue_t* queue = ipc_queue(target, remote);
if (queue == NULL || queue->ptr == NULL) {
// Invalid target or no queue registered
@@ -144,13 +144,23 @@ bool ipc_try_receive(ipc_message_t *msg) {
return false;
}
ipc_queue_item_t *item = (ipc_queue_item_t *)queue->rptr;
ipc_queue_item_t* item = (ipc_queue_item_t*)queue->rptr;
if (queue->wptr - queue->rptr - sizeof(ipc_queue_item_t) <
ALIGN_UP(item->size, IPC_DATA_ALIGNMENT)) {
// Invalid item size
return false;
}
return true;
}
bool ipc_try_receive(ipc_message_t* msg) {
if (!ipc_has_message(msg->remote)) {
return false;
}
ipc_queue_t* queue = ipc_queue(systask_id(systask_active()), msg->remote);
ipc_queue_item_t* item = (ipc_queue_item_t*)queue->rptr;
msg->fn = item->fn;
msg->data = item->data;
@@ -163,21 +173,21 @@ bool ipc_try_receive(ipc_message_t *msg) {
return true;
}
void ipc_message_free(ipc_message_t *msg) {
void ipc_message_free(ipc_message_t* msg) {
systask_id_t target = systask_id(systask_active());
ipc_queue_t *queue = ipc_queue(target, msg->remote);
ipc_queue_t* queue = ipc_queue(target, msg->remote);
if (queue == NULL || queue->ptr == NULL) {
// Invalid target or no queue registered
return;
}
ipc_queue_item_t *item = (ipc_queue_item_t *)queue->ptr;
ipc_queue_item_t *new_wptr = item;
ipc_queue_item_t* item = (ipc_queue_item_t*)queue->ptr;
ipc_queue_item_t* new_wptr = item;
while (item < (ipc_queue_item_t *)queue->wptr) {
size_t remaining_size = (uint8_t *)queue->wptr - (uint8_t *)item;
while (item < (ipc_queue_item_t*)queue->wptr) {
size_t remaining_size = (uint8_t*)queue->wptr - (uint8_t*)item;
if (remaining_size < sizeof(ipc_queue_item_t) ||
item->size > remaining_size - sizeof(ipc_queue_item_t)) {
@@ -195,25 +205,25 @@ void ipc_message_free(ipc_message_t *msg) {
// Move to next item
size_t item_size =
ALIGN_UP(sizeof(ipc_queue_item_t) + item->size, IPC_DATA_ALIGNMENT);
item = (ipc_queue_item_t *)((uint8_t *)item + item_size);
item = (ipc_queue_item_t*)((uint8_t*)item + item_size);
if (advance_wptr) {
new_wptr = item;
}
}
queue->wptr = (uint8_t *)new_wptr;
queue->wptr = (uint8_t*)new_wptr;
if (queue->wptr < queue->rptr) {
queue->rptr = queue->wptr;
}
}
bool ipc_send(systask_id_t remote, uint32_t fn, const void *data,
bool ipc_send(systask_id_t remote, uint32_t fn, const void* data,
size_t data_size) {
systask_id_t origin = systask_id(systask_active());
ipc_queue_t *queue = ipc_queue(remote, origin);
ipc_queue_t* queue = ipc_queue(remote, origin);
if (queue == NULL || queue->ptr == NULL) {
// Invalid target or no queue registered
@@ -241,7 +251,7 @@ bool ipc_send(systask_id_t remote, uint32_t fn, const void *data,
.size = data_size,
};
ipc_queue_item_t *item = (ipc_queue_item_t *)queue->wptr;
ipc_queue_item_t* item = (ipc_queue_item_t*)queue->wptr;
ipc_memcpy(item, &item_hdr, sizeof(item_hdr));
if (data_size > 0) {
@@ -253,13 +263,13 @@ bool ipc_send(systask_id_t remote, uint32_t fn, const void *data,
return true;
}
static void on_task_created(void *context, systask_id_t task_id) {
static void on_task_created(void* context, systask_id_t task_id) {
systask_id_t origin = (systask_id_t)(uintptr_t)context;
ipc_queue_t *queue = ipc_queue(task_id, origin);
ipc_queue_t* queue = ipc_queue(task_id, origin);
memset(queue, 0, sizeof(ipc_queue_t));
}
static void on_event_poll(void *context, bool read_awaited,
static void on_event_poll(void* context, bool read_awaited,
bool write_awaited) {
systask_id_t origin = (systask_id_t)(uintptr_t)context;
@@ -271,13 +281,13 @@ static void on_event_poll(void *context, bool read_awaited,
}
}
static bool on_check_read_ready(void *context, systask_id_t task_id,
void *param) {
static bool on_check_read_ready(void* context, systask_id_t task_id,
void* param) {
systask_id_t origin = (systask_id_t)(uintptr_t)context;
UNUSED(param);
ipc_queue_t *queue = ipc_queue(task_id, origin);
ipc_queue_t* queue = ipc_queue(task_id, origin);
return (queue != NULL && queue->rptr < queue->wptr);
}

View File

@@ -44,6 +44,17 @@ STATIC mp_obj_t mod_trezorio_ipc_send(mp_obj_t remote_obj, mp_obj_t fn_obj,
STATIC MP_DEFINE_CONST_FUN_OBJ_3(mod_trezorio_ipc_send_obj,
mod_trezorio_ipc_send);
/// def ipc_has_message(remote: int) -> bool:
/// """
/// Checks if there are pending IPC messages from the specified remote task.
/// """
STATIC mp_obj_t mod_trezorio_ipc_has_message(mp_obj_t remote_obj) {
systask_id_t remote = (systask_id_t)mp_obj_get_int(remote_obj);
return mp_obj_new_bool(ipc_has_message(remote));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_ipc_has_message_obj,
mod_trezorio_ipc_has_message);
/// class IpcMessage(NamedTuple):
/// """
/// IPC message structure.

View File

@@ -41,7 +41,7 @@ uint32_t last_touch_sample_time = 0;
#define CHECK_PARAM_RANGE(value, minimum, maximum) \
if (value < minimum || value > maximum) { \
const char *msg = (#value " is out of range"); \
const char* msg = (#value " is out of range"); \
mp_raise_ValueError((mp_rom_error_text_t)msg); \
}
@@ -139,6 +139,8 @@ STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = {
#ifdef USE_IPC
{MP_ROM_QSTR(MP_QSTR_IPC2_EVENT), MP_ROM_INT(SYSHANDLE_IPC2)},
{MP_ROM_QSTR(MP_QSTR_ipc_send), MP_ROM_PTR(&mod_trezorio_ipc_send_obj)},
{MP_ROM_QSTR(MP_QSTR_ipc_has_message),
MP_ROM_PTR(&mod_trezorio_ipc_has_message_obj)},
#endif
{MP_ROM_QSTR(MP_QSTR_USB), MP_ROM_PTR(&mod_trezorio_USB_type)},
{MP_ROM_QSTR(MP_QSTR_USBIF), MP_ROM_PTR(&mod_trezorio_USBIF_type)},
@@ -158,7 +160,7 @@ STATIC MP_DEFINE_CONST_DICT(mp_module_trezorio_globals,
const mp_obj_module_t mp_module_trezorio = {
.base = {&mp_type_module},
.globals = (mp_obj_dict_t *)&mp_module_trezorio_globals,
.globals = (mp_obj_dict_t*)&mp_module_trezorio_globals,
};
MP_REGISTER_MODULE(MP_QSTR_trezorio, mp_module_trezorio);

View File

@@ -9,6 +9,13 @@ def ipc_send(remote: int, fn: int, data: AnyBytes) -> None:
"""
# upymod/modtrezorio/modtrezorio-ipc.h
def ipc_has_message(remote: int) -> bool:
"""
Checks if there are pending IPC messages from the specified remote task.
"""
# upymod/modtrezorio/modtrezorio-ipc.h
class IpcMessage(NamedTuple):
"""

View File

@@ -40,6 +40,8 @@ class LayoutObj(Generic[T]):
if utils.USE_BLE:
def ble_event(self, event: int, data: bytes) -> LayoutState | None:
"""Receive a BLE events."""
def ipc_event(self) -> LayoutState | None:
"""Signal an incoming IPC message."""
if utils.USE_POWER_MANAGER:
def pm_event(self, flags: int) -> LayoutState | None:
"""Receive a power management event with packed flags."""

View File

@@ -430,6 +430,7 @@ class Layout(Generic[T]):
yield self._handle_touch_events()
if utils.USE_BLE:
yield self._handle_ble_events()
yield self._handle_ipc_events()
if utils.USE_POWER_MANAGER:
yield self._handle_power_manager()
@@ -529,6 +530,15 @@ class Layout(Generic[T]):
except Shutdown:
return
async def _handle_ipc_events(self) -> None:
try:
while True:
if io.ipc_has_message(2):
self._event(self.layout.ipc_event)
await loop.sleep(10)
except Shutdown:
return
if utils.USE_POWER_MANAGER:
def _handle_power_manager(self) -> Generator[Any, int, None]:

View File

@@ -6,19 +6,19 @@ use alloc::string::ToString;
use trezor_app_sdk::{Result, crypto, log, ui, util};
pub fn get_public_key(msg: EthereumGetPublicKey) -> Result<EthereumPublicKey> {
// let long_string: &str = "asdfghjklqwertyuiopzxcvbnmASDFGHJKLQWERTYUIOPZXCVBNM0123456789\
// asdfghjklqwertyuiopzxcvbnmASDFGHJKLQWERTYUIOPZXCVBNM0123456789\
// asdfghjklqwertyuiopzxcvbnmASDFGHJKLQWERTYUIOPZXCVBNM0123456789\
// asdfghjklqwertyuiopzxcvbnmASDFGHJKLQWERTYUIOPZXCVBNM0123456789\
// asdfghjklqwertyuiopzxcvbnmASDFGHJKLQWERTYUIOPZXCVBNM0123456789\
// asdfghjklqwertyuiopzxcvbnmASDFGHJKLQWERTYUIOPZXCVBNM0123456789";
// log::info!(
// "string chars: {}, string bytes: {}",
// long_string.chars().count(),
// long_string.len()
// );
let long_string: &str = "asdfghjklqwertyuiopzxcvbnmASDFGHJKLQWERTYUIOPZXCVBNM0123456789\
asdfghjklqwertyuiopzxcvbnmASDFGHJKLQWERTYUIOPZXCVBNM0123456789\
asdfghjklqwertyuiopzxcvbnmASDFGHJKLQWERTYUIOPZXCVBNM0123456789\
asdfghjklqwertyuiopzxcvbnmASDFGHJKLQWERTYUIOPZXCVBNM0123456789\
asdfghjklqwertyuiopzxcvbnmASDFGHJKLQWERTYUIOPZXCVBNM0123456789\
asdfghjklqwertyuiopzxcvbnmASDFGHJKLQWERTYUIOPZXCVBNM0123456789";
log::info!(
"string chars: {}, string bytes: {}",
long_string.chars().count(),
long_string.len()
);
// ui::confirm_long_value("title", long_string)?;
ui::confirm_long_value("title", long_string)?;
// TODO: Implement Ethereum public key retrieval"
let mut public_key = EthereumPublicKey::default();