What changed, and why it matters
This commit adds a new graphical simulator for the BitBox02 hardware wallet. It is a development and testing tool, not a change to the firmware that runs on real devices. There is no indication this commit fixes or introduces a security vulnerability.
No security action required. Treat as a normal development/testing feature addition. Review the new simulator's main.rs and any future commits that wire it into CI or production workflows.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit introduces test/simulator-graphical, a GUI simulator built in Rust using winit/glutin/femtovg. It adds a new simulator-graphical Cargo feature, exposes some existing internal modules under that feature, and adjusts build/test configuration. The changes are additive and gated behind the new simulator feature. No security-relevant bug fixes or unsafe code changes affecting production firmware are present in the diff.
Changed components
test/simulator-graphicalsrc/rust/bitbox02src/rust/bitbox02-rustsrc/rust/bitbox02-rust-csrc/rust/bitbox02-syssrc/ui/event.hsrc/ui/screen_process.hsrc/ui/components/status.cInspect captured patch +5127 / −53
diff --git a/.cargo/config.toml b/.cargo/config.toml
deleted file mode 100644
index bd7d967..0000000
--- a/.cargo/config.toml
+++ /dev/null
@@ -1,20 +0,0 @@
-[source.crates-io]
-replace-with = "vendored-sources"
-
-[source."git+https://github.com/probe-rs/rtt-target.git?rev=117d9519a5d3b1f4bc024bc05f9e3c5dec0a57f5"]
-git = "https://github.com/probe-rs/rtt-target.git"
-rev = "117d9519a5d3b1f4bc024bc05f9e3c5dec0a57f5"
-replace-with = "vendored-sources"
-
-[source."git+https://github.com/BitBoxSwiss/rust-bip32-ed25519?tag=v0.2.1"]
-git = "https://github.com/BitBoxSwiss/rust-bip32-ed25519"
-tag = "v0.2.1"
-replace-with = "vendored-sources"
-
-[source."git+https://github.com/BitBoxSwiss/rust-bip39.git?branch=bitbox-20250922"]
-git = "https://github.com/BitBoxSwiss/rust-bip39.git"
-branch = "bitbox-20250922"
-replace-with = "vendored-sources"
-
-[source.vendored-sources]
-directory = "external/vendor"
diff --git a/.github/workflows/ci-common.yml b/.github/workflows/ci-common.yml
index da0a12e..9f2603c 100644
--- a/.github/workflows/ci-common.yml
+++ b/.github/workflows/ci-common.yml
@@ -201,6 +201,7 @@ jobs:
- factory-setup
- firmware-debug
- simulator
+ - simulator-graphical
runs-on: ubuntu-22.04
container:
image: ${{ inputs.container-repo }}:${{ inputs.container-version }}
diff --git a/BUILD.md b/BUILD.md
index 0c246e9..f8fffb6 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -161,12 +161,22 @@ make bootloader
### Build the simulator
-The Multi edition firmware can be built as a simulator for linux-amd64. To build it, run:
+The Multi edition firmware can be built as a simulator for linux and macos. To
+build it, run:
```sh
make simulator
```
+### Build the graphical simulator
+
+The Multi edition firmware can be built as a graphical simulator for linux and
+macos. To build it, run:
+
+```sh
+make simulator-graphical
+```
+
### Flash instructions
#### Connect J-Link probe
@@ -236,17 +246,28 @@ To view the results, open `build/docs/html/index.html` in a web browser.
Run it with:
```sh
-./build-build/bin/simulator
+./build-build-noasan/bin/simulator
```
-This launches a server simulating the firmware. The send_message tool can connect to it with:
+or the following if you built the graphical one:
+
+```sh
+./build-build-noasan/bin/simulator-graphical --preseed
+```
+
+This launches a server simulating the firmware. The send_message tool can
+connect to it with:
./py/send_message.py --simulator
-If you choose to create a wallet by restoring a mnemonic, the simulator will automatically use this
-mnemonic:
+Both simulators can load the following seed:
+
+ boring mistake dish oyster truth pigeon viable emerge sort crash wire
+ portion cannon couple enact box walk height pull today solid off enable
+ tide
- boring mistake dish oyster truth pigeon viable emerge sort crash wire portion cannon couple enact box walk height pull today solid off enable tide
+The graphical simulator does it with the flag `--preseed`. The original
+simulator loads it if you restore from mnemonic.
#### Debugging using the J-Link probe and GDB
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9eed3f5..bacec07 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ customers cannot upgrade their bootloader, its changes are recorded separately.
### [Unreleased]
- Fix bug that BLE was turned off when iOS device is unlocked
+- simulator-graphical: a new simulator with a graphical user interface
### v9.24.0
- Change title when entering recovery words to `1 of 24`, `2 of 24`, etc.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bbd6e19..341c015 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -369,6 +369,7 @@ else()
include(CTest)
add_subdirectory(test/unit-test)
add_subdirectory(test/simulator)
+ add_subdirectory(test/simulator-graphical)
if(COVERAGE)
find_program(GCOVR gcovr)
if(NOT GCOVR STREQUAL "GCOVR-NOTFOUND")
diff --git a/Makefile b/Makefile
index 0a5dd55..0a6d6cd 100644
--- a/Makefile
+++ b/Makefile
@@ -108,6 +108,8 @@ rust-docs: | build
$(MAKE) -C build rust-docs
simulator: | build-build-noasan
$(MAKE) -C build-build-noasan simulator
+simulator-graphical: | build-build-noasan
+ $(MAKE) -C build-build-noasan simulator-graphical
run-simulator: | simulator
./build-build-noasan/bin/simulator
unit-test: | build-build
@@ -189,3 +191,7 @@ prepare-tidy: | build build-build
$(MAKE) -C build-build rust-cbindgen
clean:
rm -rf build build-build build-debug build-build-noasan
+
+# When you vendor rust libs avoid duplicates
+vendor-rust-deps:
+ (cd external; ./vendor-rust.sh)
diff --git a/README.md b/README.md
index c54f602..3b4a647 100644
--- a/README.md
+++ b/README.md
@@ -59,6 +59,14 @@ messages. The encryption protocol used is Noise with out-of-band authentication.
The instructions for setting up a development environment can be found in [BUILD.md](BUILD.md).
Please also read our [CONTRIBUTING.md](CONTRIBUTING.md) before filing issues and pull requests.
+### Simulator
+
+To test without hardware there are two kinds of simulators. One with GUI and
+one without. See more details in [BUILD.md](BUILD.md#build-the-simulator).
+
+<p align="center"><img src="./doc/bb02-simulator-graphical.png" /></p>
+
+
## Installing/upgrading firmware
There are two main ways to install or upgrade firmware on a BitBox02:
diff --git a/doc/bb02-simulator-graphical.png b/doc/bb02-simulator-graphical.png
new file mode 100644
index 0000000..c8fe2ae
Binary files /dev/null and b/doc/bb02-simulator-graphical.png differ
diff --git a/src/rust/.cargo/config.toml b/src/rust/.cargo/config.toml
new file mode 100644
index 0000000..1c2583b
--- /dev/null
+++ b/src/rust/.cargo/config.toml
@@ -0,0 +1,20 @@
+[source.crates-io]
+replace-with = "vendored-sources"
+
+[source."git+https://github.com/probe-rs/rtt-target.git?rev=117d9519a5d3b1f4bc024bc05f9e3c5dec0a57f5"]
+git = "https://github.com/probe-rs/rtt-target.git"
+rev = "117d9519a5d3b1f4bc024bc05f9e3c5dec0a57f5"
+replace-with = "vendored-sources"
+
+[source."git+https://github.com/BitBoxSwiss/rust-bip32-ed25519?tag=v0.2.1"]
+git = "https://github.com/BitBoxSwiss/rust-bip32-ed25519"
+tag = "v0.2.1"
+replace-with = "vendored-sources"
+
+[source."git+https://github.com/BitBoxSwiss/rust-bip39.git?branch=bitbox-20250922"]
+git = "https://github.com/BitBoxSwiss/rust-bip39.git"
+branch = "bitbox-20250922"
+replace-with = "vendored-sources"
+
+[source.vendored-sources]
+directory = "../../external/vendor"
diff --git a/src/rust/README.md b/src/rust/README.md
index 2bec49f..232c0a5 100644
--- a/src/rust/README.md
+++ b/src/rust/README.md
@@ -38,5 +38,16 @@ We generate one header file `rust.h` and ever product specific function is `#ifd
# Vendoring
-Run the vendoring script `vendor.sh` to vendor dependencies from crates.io. The
-script will ensure that also rust std libs dependencies are vendored.
+Run the vendoring script `vendor-rust.sh` to vendor dependencies from
+crates.io. The script will ensure that also rust std libs dependencies are
+vendored.
+
+When the toolchain (and standard libraries) are updated they usually depend on
+newer versions of crates. Update our lockfile to the same versions to avoid
+vendoringing duplicate versions. You can use:
+
+```
+cargo update <package> --precise <version>
+```
+
+to specify specific versions to up/downgrade to.
diff --git a/src/rust/bitbox02-rust-c/Cargo.toml b/src/rust/bitbox02-rust-c/Cargo.toml
index cb35f5d..3961a80 100644
--- a/src/rust/bitbox02-rust-c/Cargo.toml
+++ b/src/rust/bitbox02-rust-c/Cargo.toml
@@ -90,6 +90,14 @@ testing = ["bitbox02-rust/testing", "bitbox02/testing"]
# Active when the Rust code is compiled to be linked into the C unit tests and simulator.
c-unit-testing = ["bitbox02-rust/c-unit-testing", "bitbox02/c-unit-testing", "util/c-unit-testing"]
+simulator-graphical = [
+ "app-bitcoin",
+ "app-litecoin",
+ "app-ethereum",
+ "app-cardano",
+ "firmware",
+]
+
app-ethereum = [
# enable this feature in the deps
"bitbox02-rust/app-ethereum",
diff --git a/src/rust/bitbox02-rust-c/src/async_usb.rs b/src/rust/bitbox02-rust-c/src/async_usb.rs
index b5499ac..348ce09 100644
--- a/src/rust/bitbox02-rust-c/src/async_usb.rs
+++ b/src/rust/bitbox02-rust-c/src/async_usb.rs
@@ -11,6 +11,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
+#![allow(clippy::missing_safety_doc)]
use bitbox02_rust::async_usb::{on_next_request, spawn, waiting_for_next_request};
use bitbox02_rust::hww::process_packet;
diff --git a/src/rust/bitbox02-rust-c/src/lib.rs b/src/rust/bitbox02-rust-c/src/lib.rs
index aef5a40..08a2cce 100644
--- a/src/rust/bitbox02-rust-c/src/lib.rs
+++ b/src/rust/bitbox02-rust-c/src/lib.rs
@@ -22,11 +22,11 @@ extern crate std;
mod alloc;
#[cfg(feature = "firmware")]
-mod async_usb;
+pub mod async_usb;
#[cfg(feature = "firmware")]
mod der;
#[cfg(feature = "firmware")]
-mod workflow;
+pub mod workflow;
// Expose C interface defined in bitbox_aes
#[cfg(feature = "firmware")]
@@ -39,7 +39,11 @@ extern crate util;
// handler will print the available information on the screen and over RTT. If we compile with
// `panic=abort` this code will never get executed.
#[cfg_attr(feature = "bootloader", allow(unused_variables))]
-#[cfg(not(any(feature = "testing", feature = "c-unit-testing")))]
+#[cfg(not(any(
+ feature = "testing",
+ feature = "c-unit-testing",
+ feature = "simulator-graphical"
+)))]
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
#[cfg(feature = "firmware")]
diff --git a/src/rust/bitbox02-rust-c/src/workflow.rs b/src/rust/bitbox02-rust-c/src/workflow.rs
index ad903df..22952da 100644
--- a/src/rust/bitbox02-rust-c/src/workflow.rs
+++ b/src/rust/bitbox02-rust-c/src/workflow.rs
@@ -19,6 +19,7 @@
// TODO: figure out how to deal with the static muts below.
// https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html
#![allow(static_mut_refs)]
+#![allow(clippy::missing_safety_doc)]
extern crate alloc;
diff --git a/src/rust/bitbox02-rust/Cargo.toml b/src/rust/bitbox02-rust/Cargo.toml
index 9e60548..8aea87c 100644
--- a/src/rust/bitbox02-rust/Cargo.toml
+++ b/src/rust/bitbox02-rust/Cargo.toml
@@ -107,4 +107,6 @@ testing = [
c-unit-testing = []
+simulator-graphical = []
+
firmware = []
diff --git a/src/rust/bitbox02-rust/src/general/screen.rs b/src/rust/bitbox02-rust/src/general/screen.rs
index 1aedafb..0fe778a 100644
--- a/src/rust/bitbox02-rust/src/general/screen.rs
+++ b/src/rust/bitbox02-rust/src/general/screen.rs
@@ -14,10 +14,10 @@
use core::time::Duration;
-use bitbox02::{delay, ug_clear_buffer, ug_font_select_9x9, ug_put_string, ug_send_buffer};
+use bitbox02::{delay, screen_clear, ug_font_select_9x9, ug_put_string, ug_send_buffer};
pub fn print_debug_internal(duration: Duration, msg: &str) {
- ug_clear_buffer();
+ screen_clear();
ug_font_select_9x9();
ug_put_string(0, 0, msg, false);
ug_send_buffer();
diff --git a/src/rust/bitbox02-rust/src/workflow/unlock.rs b/src/rust/bitbox02-rust/src/workflow/unlock.rs
index 8bccc21..98cd629 100644
--- a/src/rust/bitbox02-rust/src/workflow/unlock.rs
+++ b/src/rust/bitbox02-rust/src/workflow/unlock.rs
@@ -142,10 +142,10 @@ pub async fn unlock_bip39(hal: &mut impl crate::hal::Hal, seed: &[u8]) {
&mnemonic_passphrase,
// for the simulator, we don't yield at all, otherwise unlock becomes very slow in the
// simulator.
- #[cfg(feature = "c-unit-testing")]
+ #[cfg(any(feature = "c-unit-testing", feature = "simulator-graphical"))]
async || {},
// we yield every time to keep the processing time per iteration to a minimum.
- #[cfg(not(feature = "c-unit-testing"))]
+ #[cfg(not(any(feature = "c-unit-testing", feature = "simulator-graphical")))]
futures_lite::future::yield_now,
),
)
diff --git a/src/rust/bitbox02-sys/build.rs b/src/rust/bitbox02-sys/build.rs
index 52f32bf..79ca6b9 100644
--- a/src/rust/bitbox02-sys/build.rs
+++ b/src/rust/bitbox02-sys/build.rs
@@ -41,7 +41,10 @@ const ALLOWLIST_VARS: &[&str] = &[
"MEMORY_SPI_BLE_FIRMWARE_1_ADDR",
"MEMORY_SPI_BLE_FIRMWARE_2_ADDR",
"MEMORY_SPI_BLE_FIRMWARE_MAX_SIZE",
+ "SCREEN_HEIGHT",
+ "SCREEN_WIDTH",
"SD_MAX_FILE_SIZE",
+ "SLIDER_POSITION_TWO_THIRD",
"XPUB_ENCODED_LEN",
];
@@ -49,11 +52,15 @@ const ALLOWLIST_TYPES: &[&str] = &[
"buffer_t",
"component_t",
"confirm_params_t",
+ "delay_t",
+ "event_slider_data_t",
+ "event_types",
"secp256k1_ecdsa_s2c_opening",
"secp256k1_ecdsa_signature",
"secp256k1_pubkey",
"securechip_error_t",
"trinary_input_string_params_t",
+ "UG_COLOR",
];
const ALLOWLIST_FNS: &[&str] = &[
@@ -74,11 +81,13 @@ const ALLOWLIST_FNS: &[&str] = &[
"delay_is_elapsed",
"delay_ms",
"delay_us",
+ "emit_event",
"empty_create",
"fake_memory_factoryreset",
"fake_securechip_event_counter_reset",
"fake_securechip_event_counter",
"gmtime",
+ "hww_setup",
"keystore_bip39_mnemonic_to_seed",
"keystore_get_bip39_word",
"keystore_secp256k1_nonce_commit",
@@ -120,12 +129,16 @@ const ALLOWLIST_FNS: &[&str] = &[
"printf",
"progress_create",
"progress_set",
+ "queue_hww_queue",
+ "queue_pull",
"random_32_bytes_mcu",
"random_32_bytes",
"random_fake_reset",
"reboot_to_bootloader",
"reset_ble",
"reset_reset",
+ "screen_clear",
+ "screen_init",
"screen_print_debug",
"screen_process_waiting_switch_to_lockscreen",
"screen_process_waiting_switch_to_logo",
@@ -133,6 +146,7 @@ const ALLOWLIST_FNS: &[&str] = &[
"screen_rotate",
"screen_saver_disable",
"screen_saver_enable",
+ "screen_splash",
"sd_card_inserted",
"sd_erase_file_in_subdir",
"sd_format",
@@ -168,11 +182,16 @@ const ALLOWLIST_FNS: &[&str] = &[
"ui_screen_stack_pop",
"ui_screen_stack_push",
"unlock_animation_create",
+ "usb_packet_process",
+ "usb_processing_hww",
+ "usb_processing_init",
+ "usb_processing_process",
"usb_processing_timeout_reset",
"util_format_datetime",
];
const RUSTIFIED_ENUMS: &[&str] = &[
+ "event_types",
"keystore_error_t",
"keystore_secp256k1_pubkey_format",
"memory_result_t",
diff --git a/src/rust/bitbox02-sys/wrapper.h b/src/rust/bitbox02-sys/wrapper.h
index 936c634..6f6aaef 100644
--- a/src/rust/bitbox02-sys/wrapper.h
+++ b/src/rust/bitbox02-sys/wrapper.h
@@ -57,6 +57,12 @@
#if defined(TESTING)
#include <fake_memory.h>
+ #include <hww.h>
+ #include <touch/gestures.h>
+ #include <ui/event.h>
+ #include <ui/event_handler.h>
+ #include <usb/usb_packet.h>
+ #include <usb/usb_processing.h>
#endif
#if !defined(TESTING)
diff --git a/src/rust/bitbox02/Cargo.toml b/src/rust/bitbox02/Cargo.toml
index 25f87d0..222270f 100644
--- a/src/rust/bitbox02/Cargo.toml
+++ b/src/rust/bitbox02/Cargo.toml
@@ -42,6 +42,8 @@ testing = ["util/testing"]
unit-testing = []
# Active when the Rust code is compiled to be linked into the C unit tests and simulator.
c-unit-testing = []
+# Active when building for the graphical simulator
+simulator-graphical = []
app-ethereum = []
app-bitcoin = []
diff --git a/src/rust/bitbox02/src/delay.rs b/src/rust/bitbox02/src/delay.rs
index dc9905d..a31d3af 100644
--- a/src/rust/bitbox02/src/delay.rs
+++ b/src/rust/bitbox02/src/delay.rs
@@ -16,12 +16,20 @@ use core::pin::Pin;
use core::task::{Context, Poll};
use core::time::Duration;
-#[cfg(not(any(feature = "testing", feature = "c-unit-testing")))]
+#[cfg(not(any(
+ feature = "testing",
+ feature = "c-unit-testing",
+ feature = "simulator-graphical"
+)))]
struct DelayInner {
bitbox02_delay: bitbox02_sys::delay_t,
}
-#[cfg(any(feature = "testing", feature = "c-unit-testing"))]
+#[cfg(any(
+ feature = "testing",
+ feature = "c-unit-testing",
+ feature = "simulator-graphical"
+))]
struct DelayInner {
thread_handle: Option<std::thread::JoinHandle<()>>,
done: std::sync::Arc<std::sync::atomic::AtomicBool>,
@@ -32,7 +40,11 @@ pub struct Delay {
}
impl Delay {
- #[cfg(not(any(feature = "testing", feature = "c-unit-testing")))]
+ #[cfg(not(any(
+ feature = "testing",
+ feature = "c-unit-testing",
+ feature = "simulator-graphical"
+ )))]
pub fn from_ms(ms: u32) -> Delay {
let mut delay = Delay {
inner: DelayInner {
@@ -42,7 +54,11 @@ impl Delay {
unsafe { bitbox02_sys::delay_init_ms(&mut delay.inner.bitbox02_delay as *mut _, ms) }
delay
}
- #[cfg(any(feature = "testing", feature = "c-unit-testing"))]
+ #[cfg(any(
+ feature = "testing",
+ feature = "c-unit-testing",
+ feature = "simulator-graphical"
+ ))]
pub fn from_ms(ms: u32) -> Delay {
let (thread_handle, done) = if ms == 0 {
(
@@ -70,7 +86,11 @@ impl Delay {
}
}
-#[cfg(not(any(feature = "testing", feature = "c-unit-testing")))]
+#[cfg(not(any(
+ feature = "testing",
+ feature = "c-unit-testing",
+ feature = "simulator-graphical"
+)))]
impl Future for Delay {
type Output = ();
@@ -83,7 +103,11 @@ impl Future for Delay {
}
}
-#[cfg(any(feature = "testing", feature = "c-unit-testing"))]
+#[cfg(any(
+ feature = "testing",
+ feature = "c-unit-testing",
+ feature = "simulator-graphical"
+))]
impl Future for Delay {
type Output = ();
fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
@@ -98,7 +122,11 @@ impl Future for Delay {
}
}
-#[cfg(not(any(feature = "testing", feature = "c-unit-testing")))]
+#[cfg(not(any(
+ feature = "testing",
+ feature = "c-unit-testing",
+ feature = "simulator-graphical"
+)))]
impl Drop for Delay {
fn drop(&mut self) {
unsafe { bitbox02_sys::delay_cancel(&self.inner.bitbox02_delay as *const _) }
diff --git a/src/rust/bitbox02/src/event.rs b/src/rust/bitbox02/src/event.rs
new file mode 100644
index 0000000..7bf0330
--- /dev/null
+++ b/src/rust/bitbox02/src/event.rs
@@ -0,0 +1,20 @@
+// Copyright 2025 Shift Crypto AG
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+pub use bitbox02_sys::event_slider_data_t;
+pub use bitbox02_sys::event_t;
+pub use bitbox02_sys::event_types;
+
+pub fn emit_event(event: &event_t) {
+ unsafe { bitbox02_sys::emit_event(event as *const _) }
+}
diff --git a/src/rust/bitbox02/src/hww.rs b/src/rust/bitbox02/src/hww.rs
new file mode 100644
index 0000000..7e5c571
--- /dev/null
+++ b/src/rust/bitbox02/src/hww.rs
@@ -0,0 +1,16 @@
+// Copyright 2025 Shift Crypto AG
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+pub fn setup() {
+ unsafe { bitbox02_sys::hww_setup() };
+}
diff --git a/src/rust/bitbox02/src/lib.rs b/src/rust/bitbox02/src/lib.rs
index b7f93d3..56efecc 100644
--- a/src/rust/bitbox02/src/lib.rs
+++ b/src/rust/bitbox02/src/lib.rs
@@ -16,7 +16,7 @@
// This crate contains safe wrappers around C functions provided by bitbox02_sys.
#![no_std]
-#[cfg(any(test, feature = "c-unit-testing"))]
+#[cfg(any(test, feature = "c-unit-testing", feature = "simulator-graphical"))]
#[allow(unused_imports)]
#[macro_use]
extern crate std;
@@ -31,19 +31,31 @@ extern crate alloc;
use alloc::string::String;
-#[cfg(feature = "testing")]
+#[cfg(any(feature = "testing", feature = "simulator-graphical"))]
pub mod testing;
pub mod delay;
+#[cfg(feature = "simulator-graphical")]
+pub mod event;
+#[cfg(feature = "simulator-graphical")]
+pub mod hww;
pub mod keystore;
pub mod memory;
+#[cfg(feature = "simulator-graphical")]
+pub mod queue;
pub mod random;
+#[cfg(feature = "simulator-graphical")]
+pub mod screen;
pub mod screen_saver;
pub mod sd;
pub mod secp256k1;
pub mod securechip;
+#[cfg(feature = "simulator-graphical")]
+pub mod smarteeprom;
pub mod spi_mem;
pub mod ui;
+#[cfg(feature = "simulator-graphical")]
+pub mod usb_packet;
pub mod usb_processing;
use core::ffi::c_int;
@@ -65,8 +77,8 @@ pub fn ug_put_string(x: i16, y: i16, input: &str, inverted: bool) {
}
}
-pub fn ug_clear_buffer() {
- unsafe { bitbox02_sys::UG_ClearBuffer() }
+pub fn screen_clear() {
+ unsafe { bitbox02_sys::screen_clear() }
}
pub fn ug_send_buffer() {
diff --git a/src/rust/bitbox02/src/queue.rs b/src/rust/bitbox02/src/queue.rs
new file mode 100644
index 0000000..d41dc3e
--- /dev/null
+++ b/src/rust/bitbox02/src/queue.rs
@@ -0,0 +1,22 @@
+// Copyright 2025 Shift Crypto AG
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+pub fn pull_hww() -> Option<[u8; 64]> {
+ let hww_data = unsafe { bitbox02_sys::queue_pull(bitbox02_sys::queue_hww_queue()) };
+ if hww_data.is_null() {
+ return None;
+ }
+ let mut data: [u8; 64] = [0; 64];
+ unsafe { core::ptr::copy_nonoverlapping(hww_data, data.as_mut_ptr(), 64) }
+ Some(data)
+}
diff --git a/src/rust/bitbox02/src/screen.rs b/src/rust/bitbox02/src/screen.rs
new file mode 100644
index 0000000..cd39f25
--- /dev/null
+++ b/src/rust/bitbox02/src/screen.rs
@@ -0,0 +1,52 @@
+// Copyright 2025 Shift Crypto AG
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+use crate::ui::ugui::UG_COLOR;
+pub use bitbox02_sys::{SCREEN_HEIGHT, SCREEN_WIDTH};
+use util::cell::SyncCell;
+
+type PixelFn = fn(i16, i16, UG_COLOR);
+type MirrorFn = fn(bool);
+type ClearFn = fn();
+
+static PIXEL_FN: SyncCell<Option<PixelFn>> = SyncCell::new(None);
+static MIRROR_FN: SyncCell<Option<MirrorFn>> = SyncCell::new(None);
+static CLEAR_FN: SyncCell<Option<ClearFn>> = SyncCell::new(None);
+
+unsafe extern "C" fn _pixel_fn(x: i16, y: i16, c: UG_COLOR) {
+ PIXEL_FN.read().as_ref().unwrap()(x, y, c);
+}
+
+unsafe extern "C" fn _clear_fn() {
+ CLEAR_FN.read().as_ref().unwrap()();
+}
+
+unsafe extern "C" fn _mirror_fn(mirror: bool) {
+ MIRROR_FN.read().as_ref().unwrap()(mirror);
+}
+
+/// Can only be called once
+pub fn init(pixel_fn: PixelFn, mirror_fn: MirrorFn, clear_fn: ClearFn) {
+ PIXEL_FN.write(Some(pixel_fn));
+ MIRROR_FN.write(Some(mirror_fn));
+ CLEAR_FN.write(Some(clear_fn));
+ unsafe { bitbox02_sys::screen_init(Some(_pixel_fn), Some(_mirror_fn), Some(_clear_fn)) }
+}
+
+pub fn splash() {
+ unsafe { bitbox02_sys::screen_splash() }
+}
+
+pub fn process() {
+ unsafe { bitbox02_sys::screen_process() }
+}
diff --git a/src/rust/bitbox02/src/sd.rs b/src/rust/bitbox02/src/sd.rs
index 95afbee..6694c5a 100644
--- a/src/rust/bitbox02/src/sd.rs
+++ b/src/rust/bitbox02/src/sd.rs
@@ -19,6 +19,11 @@ use alloc::vec::Vec;
use crate::util::str_to_cstr_vec;
use bitbox02_sys::SD_MAX_FILE_SIZE;
+#[cfg(any(feature = "testing", feature = "simulator-graphical"))]
+pub fn format() -> bool {
+ unsafe { bitbox02_sys::sd_format() }
+}
+
pub fn sdcard_inserted() -> bool {
unsafe { bitbox02_sys::sd_card_inserted() }
}
@@ -105,9 +110,7 @@ mod tests {
#[test]
fn test_list_write_read_erase() {
- unsafe {
- bitbox02_sys::sd_format();
- }
+ format();
assert_eq!(list_subdir(None), Ok(vec![]));
assert_eq!(list_subdir(Some("dir1")), Ok(vec![]));
diff --git a/src/rust/bitbox02/src/smarteeprom.rs b/src/rust/bitbox02/src/smarteeprom.rs
new file mode 100644
index 0000000..cc959ee
--- /dev/null
+++ b/src/rust/bitbox02/src/smarteeprom.rs
@@ -0,0 +1,20 @@
+// Copyright 2025 Shift Crypto AG
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+pub fn bb02_config() {
+ unsafe { bitbox02_sys::smarteeprom_bb02_config() };
+}
+
+pub fn init() {
+ unsafe { bitbox02_sys::bitbox02_smarteeprom_init() };
+}
diff --git a/src/rust/bitbox02/src/ui.rs b/src/rust/bitbox02/src/ui.rs
index e69b695..1eb7f70 100644
--- a/src/rust/bitbox02/src/ui.rs
+++ b/src/rust/bitbox02/src/ui.rs
@@ -26,6 +26,8 @@ mod ui;
pub use ui::*;
+pub mod ugui;
+
pub fn screen_process_waiting_switch_to_logo() {
unsafe { bitbox02_sys::screen_process_waiting_switch_to_logo() }
}
diff --git a/src/rust/bitbox02/src/ui/ugui.rs b/src/rust/bitbox02/src/ui/ugui.rs
new file mode 100644
index 0000000..ceae3fd
--- /dev/null
+++ b/src/rust/bitbox02/src/ui/ugui.rs
@@ -0,0 +1,15 @@
+// Copyright 2025 Shift Crypto AG
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+pub use bitbox02_sys::UG_COLOR;
diff --git a/src/rust/bitbox02/src/usb_packet.rs b/src/rust/bitbox02/src/usb_packet.rs
new file mode 100644
index 0000000..36f588c
--- /dev/null
+++ b/src/rust/bitbox02/src/usb_packet.rs
@@ -0,0 +1,18 @@
+// Copyright 2025 Shift Crypto AG
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+pub use bitbox02_sys::USB_FRAME;
+
+pub fn process(packet: &[u8; 64]) -> bool {
+ unsafe { bitbox02_sys::usb_packet_process(packet.as_ptr() as *const _) }
+}
diff --git a/src/rust/bitbox02/src/usb_processing.rs b/src/rust/bitbox02/src/usb_processing.rs
index f24561b..0b52c04 100644
--- a/src/rust/bitbox02/src/usb_processing.rs
+++ b/src/rust/bitbox02/src/usb_processing.rs
@@ -18,3 +18,13 @@ pub fn timeout_reset(value: i16) {
bitbox02_sys::usb_processing_timeout_reset(value);
}
}
+
+#[cfg(feature = "simulator-graphical")]
+pub fn init() {
+ unsafe { bitbox02_sys::usb_processing_init() }
+}
+
+#[cfg(feature = "simulator-graphical")]
+pub fn process_hww() {
+ unsafe { bitbox02_sys::usb_processing_process(bitbox02_sys::usb_processing_hww()) }
+}
diff --git a/src/ui/components/status.c b/src/ui/components/status.c
index d7ce208..8840567 100644
--- a/src/ui/components/status.c
+++ b/src/ui/components/status.c
@@ -21,7 +21,11 @@
#include <stdbool.h>
#include <string.h>
-#define STATUS_DEFAULT_DELAY 500 // counts
+#if defined(TESTING)
+ #define STATUS_DEFAULT_DELAY 100 // counts
+#else
+ #define STATUS_DEFAULT_DELAY 500 // counts
+#endif
typedef struct {
bool status;
diff --git a/src/ui/event.h b/src/ui/event.h
index 8e8cf92..a7bb6a6 100644
--- a/src/ui/event.h
+++ b/src/ui/event.h
@@ -25,7 +25,7 @@ typedef struct {
int32_t velocity;
} event_slider_data_t;
-enum {
+enum event_types {
EVENT_SLIDE,
EVENT_SLIDE_RELEASED,
EVENT_LONG_TAP,
diff --git a/src/ui/screen_process.h b/src/ui/screen_process.h
index 7f245bf..bbf724f 100644
--- a/src/ui/screen_process.h
+++ b/src/ui/screen_process.h
@@ -46,9 +46,15 @@ void screen_process_waiting_switch_to_lockscreen(void);
void screen_process(void);
/**
- * Period of screen updates.
* The screen is refreshed every SCREEN_FRAME_RATE event loops cycles.
+ *
+ * In the simulator screen_process is called with a fixed frame rate so there a rate dividor isn't
+ * necessary.
*/
-#define SCREEN_FRAME_RATE 30
+#if defined(TESTING)
+ #define SCREEN_FRAME_RATE 1
+#else
+ #define SCREEN_FRAME_RATE 30
+#endif
#endif
diff --git a/test/simulator-graphical/.gitignore b/test/simulator-graphical/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/test/simulator-graphical/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/test/simulator-graphical/CMakeLists.txt b/test/simulator-graphical/CMakeLists.txt
new file mode 100644
index 0000000..87b0f70
--- /dev/null
+++ b/test/simulator-graphical/CMakeLists.txt
@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: Apache-2.0
+
+#-----------------------------------------------------------------------------
+# Simulator Next Generation
+#
+
+set(RUST_BINARY_DIR ${CMAKE_BINARY_DIR}/src/rust)
+
+set(RUSTFLAGS "-L${CMAKE_ARCHIVE_OUTPUT_DIRECTORY} -lstatic=fatfs -lstatic=secp256k1 --cfg=rust_secp_no_symbol_renaming")
+
+if(SANITIZE_ADDRESS OR SANITIZE_UNDEFINED)
+ string(APPEND RUSTFLAGS " -Zexternal-clangrt -Cdefault-linker-libraries=y")
+ # When building with address sanitizer it is important that llvm/clang is
+ # the same version as rustcs llvm. On macos this can be achieved by
+ # installing llvm from brew and then prepend the path to its binary dir to
+ # PATH. By default rust uses `cc` as linker, but this will pick the system llvm.
+ if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+ string(APPEND RUSTFLAGS " -Clinker=clang")
+ endif()
+endif()
+if(SANITIZE_ADDRESS)
+ string(APPEND RUSTFLAGS " -Clink-arg=-fsanitize=address -Zsanitizer=address")
+ set(CFLAGS "-fsanitize=address")
+endif()
+if(SANITIZE_UNDEFINED)
+ string(APPEND RUSTFLAGS " -Clink-arg=-fsanitize=undefined")
+ string(APPEND CFLAGS " -fsanitize=undefined")
+endif()
+
+execute_process(COMMAND rustc --print host-tuple OUTPUT_VARIABLE LLVM_HOST_TUPLE OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+# Supplying llvm host tuple is necessary for address sanitization and doesn't
+# hurt during regular builds.
+add_custom_command(
+ OUTPUT dummy ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/simulator-graphical
+ COMMAND
+ ${CMAKE_COMMAND} -E env
+ MACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}
+ RUSTFLAGS=${RUSTFLAGS}
+ CFLAGS=${CFLAGS}
+ RUSTC_BOOTSTRAP=1
+ cargo build --manifest-path ${CMAKE_CURRENT_SOURCE_DIR}/Cargo.toml --target-dir ${RUST_BINARY_DIR} --target=${LLVM_HOST_TUPLE} --release
+ COMMAND
+ ${CMAKE_COMMAND} -E copy
+ ${RUST_BINARY_DIR}/${LLVM_HOST_TUPLE}/release/simulator-graphical
+ ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/simulator-graphical
+ DEPENDS rust-cbindgen secp256k1 fatfs
+ WORKING_DIRECTORY ${RUST_BINARY_DIR}
+)
+
+add_custom_target(simulator-graphical
+ DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/simulator-graphical
+)
diff --git a/test/simulator-graphical/Cargo.lock b/test/simulator-graphical/Cargo.lock
new file mode 100644
index 0000000..492cf0a
--- /dev/null
+++ b/test/simulator-graphical/Cargo.lock
@@ -0,0 +1,3854 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "ab_glyph"
+version = "0.2.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2"
+dependencies = [
+ "ab_glyph_rasterizer",
+ "owned_ttf_parser",
+]
+
+[[package]]
+name = "ab_glyph_rasterizer"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
+
+[[package]]
+name = "adler2"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
+
+[[package]]
+name = "aead"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
+dependencies = [
+ "crypto-common",
+ "generic-array",
+]
+
+[[package]]
+name = "ahash"
+version = "0.8.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
+dependencies = [
+ "cfg-if",
+ "getrandom 0.3.3",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "aligned-vec"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b"
+dependencies = [
+ "equator",
+]
+
+[[package]]
+name = "android-activity"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046"
+dependencies = [
+ "android-properties",
+ "bitflags 2.9.4",
+ "cc",
+ "cesu8",
+ "jni",
+ "jni-sys",
+ "libc",
+ "log",
+ "ndk",
+ "ndk-context",
+ "ndk-sys",
+ "num_enum",
+ "thiserror",
+]
+
+[[package]]
+name = "android-properties"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
+
+[[package]]
+name = "anstream"
+version = "0.6.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
+dependencies = [
+ "windows-sys 0.60.2",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
+dependencies = [
+ "anstyle",
+ "once_cell_polyfill",
+ "windows-sys 0.60.2",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
+
+[[package]]
+name = "arbitrary"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
+
+[[package]]
+name = "arg_enum_proc_macro"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "arrayref"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
+
+[[package]]
+name = "as-raw-xcb-connection"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b"
+
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
+[[package]]
+name = "autocfg"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+
+[[package]]
+name = "av1-grain"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f3efb2ca85bc610acfa917b5aaa36f3fcbebed5b3182d7f877b02531c4b80c8"
+dependencies = [
+ "anyhow",
+ "arrayvec",
+ "log",
+ "nom",
+ "num-rational",
+ "v_frame",
+]
+
+[[package]]
+name = "avif-serialize"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f"
+dependencies = [
+ "arrayvec",
+]
+
+[[package]]
+name = "bare-metal"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
+dependencies = [
+ "rustc_version 0.2.3",
+]
+
+[[package]]
+name = "base58ck"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f"
+dependencies = [
+ "bitcoin-internals",
+ "bitcoin_hashes",
+]
+
+[[package]]
+name = "bech32"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d"
+
+[[package]]
+name = "binascii"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72"
+
+[[package]]
+name = "bip32-ed25519"
+version = "0.2.1"
+source = "git+https://github.com/BitBoxSwiss/rust-bip32-ed25519?tag=v0.2.1#385d8a907f694aab7ca3f61ad603804c72ac520a"
+dependencies = [
+ "curve25519-dalek",
+ "digest",
+ "hmac",
+ "zeroize",
+]
+
+[[package]]
+name = "bip39"
+version = "2.2.0"
+source = "git+https://github.com/BitBoxSwiss/rust-bip39.git?branch=bitbox-20250922#d69f68c837ee7962a26619316fb7a725e2e8d44c"
+dependencies = [
+ "bitcoin_hashes",
+ "zeroize",
+]
+
+[[package]]
+name = "bit_field"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
+
+[[package]]
+name = "bitbox-aes"
+version = "0.1.0"
+dependencies = [
+ "bitcoin",
+ "ctaes",
+ "util",
+ "zeroize",
+]
+
+[[package]]
+name = "bitbox02"
+version = "0.1.0"
+dependencies = [
+ "bip39",
+ "bitbox02-sys",
+ "bitcoin",
+ "hex",
+ "util",
+ "zeroize",
+]
+
+[[package]]
+name = "bitbox02-noise"
+version = "0.1.0"
+dependencies = [
+ "noise-protocol",
+ "noise-rust-crypto",
+ "x25519-dalek",
+]
+
+[[package]]
+name = "bitbox02-rust"
+version = "0.1.0"
+dependencies = [
+ "binascii",
+ "bip32-ed25519",
+ "bip39",
+ "bitbox-aes",
+ "bitbox02",
+ "bitbox02-noise",
+ "bitcoin",
+ "bitcoin_hashes",
+ "blake2",
+ "crc",
+ "digest",
+ "ed25519-dalek",
+ "erc20_params",
+ "futures-lite",
+ "hex",
+ "hex_lit",
+ "hmac",
+ "keccak",
+ "minicbor",
+ "miniscript",
+ "num-bigint",
+ "num-traits",
+ "prost",
+ "sha2",
+ "sha3",
+ "streaming-silent-payments",
+ "util",
+ "zeroize",
+]
+
+[[package]]
+name = "bitbox02-rust-c"
+version = "0.1.0"
+dependencies = [
+ "bip39",
+ "bitbox-aes",
+ "bitbox02",
+ "bitbox02-noise",
+ "bitbox02-rust",
+ "cortex-m",
+ "der",
+ "hex",
+ "util",
+ "zeroize",
+]
+
+[[package]]
+name = "bitbox02-sys"
+version = "0.1.0"
+dependencies = [
+ "cc",
+ "util",
+]
+
+[[package]]
+name = "bitcoin"
+version = "0.32.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda569d741b895131a88ee5589a467e73e9c4718e958ac9308e4f7dc44b6945"
+dependencies = [
+ "base58ck",
+ "bech32",
+ "bitcoin-internals",
+ "bitcoin-io",
+ "bitcoin-units",
+ "bitcoin_hashes",
+ "hex-conservative",
+ "hex_lit",
+ "secp256k1",
+]
+
+[[package]]
+name = "bitcoin-internals"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2"
+
+[[package]]
+name = "bitcoin-io"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "340e09e8399c7bd8912f495af6aa58bea0c9214773417ffaa8f6460f93aaee56"
+
+[[package]]
+name = "bitcoin-units"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb54da0b28892f3c52203a7191534033e051b6f4b52bc15480681b57b7e036f5"
+dependencies = [
+ "bitcoin-internals",
+]
+
+[[package]]
+name = "bitcoin_hashes"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16"
+dependencies = [
+ "bitcoin-io",
+ "hex-conservative",
+]
+
+[[package]]
+name = "bitfield"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
+
+[[package]]
+name = "bitstream-io"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2"
+
+[[package]]
+name = "blake2"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
+dependencies = [
+ "digest",
+]
+
+[[package]]
+name = "block-buffer"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "block2"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f"
+dependencies = [
+ "objc2 0.5.2",
+]
+
+[[package]]
+name = "built"
+version = "0.7.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b"
+
+[[package]]
+name = "bumpalo"
+version = "3.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
+
+[[package]]
+name = "bytemuck"
+version = "1.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4"
+dependencies = [
+ "bytemuck_derive",
+]
+
+[[package]]
+name = "bytemuck_derive"
+version = "1.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "byteorder-lite"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
+
+[[package]]
+name = "bytes"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad1f8e949d755f9d79112b5bb46938e0ef9d3804a0b16dfab13aafcaa5f0fa72"
+
+[[package]]
+name = "calloop"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec"
+dependencies = [
+ "bitflags 2.9.4",
+ "log",
+ "polling",
+ "rustix 0.38.44",
+ "slab",
+ "thiserror",
+]
+
+[[package]]
+name = "calloop-wayland-source"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20"
+dependencies = [
+ "calloop",
+ "rustix 0.38.44",
+ "wayland-backend",
+ "wayland-client",
+]
+
+[[package]]
+name = "cc"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8"
+dependencies = [
+ "jobserver",
+ "libc",
+ "shlex",
+]
+
+[[package]]
+name = "cesu8"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
+
+[[package]]
+name = "cfg-expr"
+version = "0.15.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02"
+dependencies = [
+ "smallvec",
+ "target-lexicon",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "cfg_aliases"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+
+[[package]]
+name = "cgl"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "chacha20"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
+dependencies = [
+ "cfg-if",
+ "cipher",
+ "cpufeatures",
+]
+
+[[package]]
+name = "chacha20poly1305"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
+dependencies = [
+ "aead",
+ "chacha20",
+ "cipher",
+ "poly1305",
+ "zeroize",
+]
+
+[[package]]
+name = "cipher"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
+dependencies = [
+ "crypto-common",
+ "inout",
+ "zeroize",
+]
+
+[[package]]
+name = "clap"
+version = "4.5.48"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.48"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
+
+[[package]]
+name = "color_quant"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
+
+[[package]]
+name = "combine"
+version = "4.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
+dependencies = [
+ "bytes",
+ "memchr",
+]
+
+[[package]]
+name = "concurrent-queue"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "core-graphics"
+version = "0.23.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "core-graphics-types",
+ "foreign-types",
+ "libc",
+]
+
+[[package]]
+name = "core-graphics-types"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "libc",
+]
+
+[[package]]
+name = "core_maths"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30"
+dependencies = [
+ "libm",
+]
+
+[[package]]
+name = "cortex-m"
+version = "0.7.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9"
+dependencies = [
+ "bare-metal",
+ "bitfield",
+ "critical-section",
+ "embedded-hal",
+ "volatile-register",
+]
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crc"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675"
+dependencies = [
+ "crc-catalog",
+]
+
+[[package]]
+name = "crc-catalog"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
+
+[[package]]
+name = "crc32fast"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "critical-section"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
+[[package]]
+name = "crunchy"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
+
+[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "ctaes"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4cbca3712c15d1e2dcc77f4eea8c838fb98f54ffe272d13a72516d488b098ce"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "cursor-icon"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f"
+
+[[package]]
+name = "curve25519-dalek"
+version = "4.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "curve25519-dalek-derive",
+ "digest",
+ "fiat-crypto",
+ "rustc_version 0.4.0",
+ "subtle",
+]
+
+[[package]]
+name = "curve25519-dalek-derive"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "der"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+ "subtle",
+]
+
+[[package]]
+name = "dispatch"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
+
+[[package]]
+name = "dispatch2"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
+dependencies = [
+ "bitflags 2.9.4",
+ "objc2 0.6.3",
+]
+
+[[package]]
+name = "dlib"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
+dependencies = [
+ "libloading",
+]
+
+[[package]]
+name = "downcast-rs"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
+
+[[package]]
+name = "dpi"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
+
+[[package]]
+name = "ed25519"
+version = "2.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
+dependencies = [
+ "signature",
+]
+
+[[package]]
+name = "ed25519-dalek"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9"
+dependencies = [
+ "curve25519-dalek",
+ "ed25519",
+ "sha2",
+ "signature",
+ "subtle",
+]
+
+[[package]]
+name = "either"
+version = "1.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
+
+[[package]]
+name = "embedded-hal"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
+dependencies = [
+ "nb 0.1.3",
+ "void",
+]
+
+[[package]]
+name = "equator"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc"
+dependencies = [
+ "equator-macro",
+]
+
+[[package]]
+name = "equator-macro"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "erc20_params"
+version = "0.1.0"
+dependencies = [
+ "hex",
+]
+
+[[package]]
+name = "errno"
+version = "0.3.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
+dependencies = [
+ "libc",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "exr"
+version = "1.73.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0"
+dependencies = [
+ "bit_field",
+ "half",
+ "lebe",
+ "miniz_oxide",
+ "rayon-core",
+ "smallvec",
+ "zune-inflate",
+]
+
+[[package]]
+name = "fax"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab"
+dependencies = [
+ "fax_derive",
+]
+
+[[package]]
+name = "fax_derive"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "fdeflate"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
+dependencies = [
+ "simd-adler32",
+]
+
+[[package]]
+name = "femtovg"
+version = "0.19.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2f4a24ac814ea9887af726cf0e41c76d1614bc5ad051a1d04aa205e9627ee87"
+dependencies = [
+ "bitflags 2.9.4",
+ "bytemuck",
+ "fnv",
+ "glow",
+ "image",
+ "imgref",
+ "itertools 0.14.0",
+ "log",
+ "lru",
+ "rgb",
+ "rustybuzz",
+ "slotmap",
+ "ttf-parser",
+ "unicode-bidi",
+ "unicode-segmentation",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "fiat-crypto"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f69037fe1b785e84986b4f2cbcf647381876a00671d25ceef715d7812dd7e1dd"
+
+[[package]]
+name = "flate2"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc5a4e564e38c699f2880d3fda590bedc2e69f3f84cd48b457bd892ce61d0aa9"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "foreign-types"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
+dependencies = [
+ "foreign-types-macros",
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-macros"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
+
+[[package]]
+name = "futures-core"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
+
+[[package]]
+name = "futures-lite"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "gethostname"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc257fdb4038301ce4b9cd1b3b51704509692bb3ff716a410cbd07925d9dae55"
+dependencies = [
+ "rustix 1.1.2",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.11.1+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi",
+ "wasi 0.14.7+wasi-0.2.4",
+]
+
+[[package]]
+name = "gif"
+version = "0.13.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b"
+dependencies = [
+ "color_quant",
+ "weezl",
+]
+
+[[package]]
+name = "gl_generator"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d"
+dependencies = [
+ "khronos_api",
+ "log",
+ "xml-rs",
+]
+
+[[package]]
+name = "glow"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5e5ea60d70410161c8bf5da3fdfeaa1c72ed2c15f8bbb9d19fe3a4fad085f08"
+dependencies = [
+ "js-sys",
+ "slotmap",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "glutin"
+version = "0.32.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12124de845cacfebedff80e877bb37b5b75c34c5a4c89e47e1cdd67fb6041325"
+dependencies = [
+ "bitflags 2.9.4",
+ "cfg_aliases",
+ "cgl",
+ "dispatch2",
+ "glutin_egl_sys",
+ "glutin_glx_sys",
+ "glutin_wgl_sys",
+ "libloading",
+ "objc2 0.6.3",
+ "objc2-app-kit 0.3.2",
+ "objc2-core-foundation",
+ "objc2-foundation 0.3.2",
+ "once_cell",
+ "raw-window-handle",
+ "wayland-sys",
+ "windows-sys 0.52.0",
+ "x11-dl",
+]
+
+[[package]]
+name = "glutin-winit"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85edca7075f8fc728f28cb8fbb111a96c3b89e930574369e3e9c27eb75d3788f"
+dependencies = [
+ "cfg_aliases",
+ "glutin",
+ "raw-window-handle",
+ "winit",
+]
+
+[[package]]
+name = "glutin_egl_sys"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c4680ba6195f424febdc3ba46e7a42a0e58743f2edb115297b86d7f8ecc02d2"
+dependencies = [
+ "gl_generator",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "glutin_glx_sys"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a7bb2938045a88b612499fbcba375a77198e01306f52272e692f8c1f3751185"
+dependencies = [
+ "gl_generator",
+ "x11-dl",
+]
+
+[[package]]
+name = "glutin_wgl_sys"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e"
+dependencies = [
+ "gl_generator",
+]
+
+[[package]]
+name = "half"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9"
+dependencies = [
+ "cfg-if",
+ "crunchy",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "hermit-abi"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "hex-conservative"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd"
+dependencies = [
+ "arrayvec",
+]
+
+[[package]]
+name = "hex_lit"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd"
+
+[[package]]
+name = "hmac"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
+dependencies = [
+ "digest",
+]
+
+[[package]]
+name = "image"
+version = "0.25.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "529feb3e6769d234375c4cf1ee2ce713682b8e76538cb13f9fc23e1400a591e7"
+dependencies = [
+ "bytemuck",
+ "byteorder-lite",
+ "color_quant",
+ "exr",
+ "gif",
+ "image-webp",
+ "moxcms",
+ "num-traits",
+ "png",
+ "qoi",
+ "ravif",
+ "rayon",
+ "rgb",
+ "tiff",
+ "zune-core",
+ "zune-jpeg",
+]
+
+[[package]]
+name = "image-webp"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3"
+dependencies = [
+ "byteorder-lite",
+ "quick-error",
+]
+
+[[package]]
+name = "imgref"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8"
+
+[[package]]
+name = "indexmap"
+version = "2.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "inout"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "interpolate_name"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
+
+[[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itertools"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "jni"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
+dependencies = [
+ "cesu8",
+ "cfg-if",
+ "combine",
+ "jni-sys",
+ "log",
+ "thiserror",
+ "walkdir",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
+
+[[package]]
+name = "jobserver"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305"
+dependencies = [
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "keccak"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654"
+dependencies = [
+ "cpufeatures",
+]
+
+[[package]]
+name = "khronos_api"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
+
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
+[[package]]
+name = "lebe"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8"
+
+[[package]]
+name = "libc"
+version = "0.2.171"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
+
+[[package]]
+name = "libfuzzer-sys"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404"
+dependencies = [
+ "arbitrary",
+ "cc",
+]
+
+[[package]]
+name = "libloading"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
+dependencies = [
+ "cfg-if",
+ "windows-link",
+]
+
+[[package]]
+name = "libm"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
+
+[[package]]
+name = "libredox"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
+dependencies = [
+ "bitflags 2.9.4",
+ "libc",
+ "redox_syscall 0.5.18",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
+
+[[package]]
+name = "log"
+version = "0.4.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
+
+[[package]]
+name = "loop9"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062"
+dependencies = [
+ "imgref",
+]
+
+[[package]]
+name = "lru"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfe949189f46fabb938b3a9a0be30fdd93fd8a09260da863399a8cf3db756ec8"
+
+[[package]]
+name = "matchers"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
+dependencies = [
+ "regex-automata",
+]
+
+[[package]]
+name = "maybe-rayon"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519"
+dependencies = [
+ "cfg-if",
+ "rayon",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
+
+[[package]]
+name = "memmap2"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "minicbor"
+version = "0.24.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29be4f60e41fde478b36998b88821946aafac540e53591e76db53921a0cc225b"
+dependencies = [
+ "minicbor-derive",
+]
+
+[[package]]
+name = "minicbor-derive"
+version = "0.15.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd2209fff77f705b00c737016a48e73733d7fbccb8b007194db148f03561fb70"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "miniscript"
+version = "12.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "487906208f38448e186e3deb02f2b8ef046a9078b0de00bdb28bf4fb9b76951c"
+dependencies = [
+ "bech32",
+ "bitcoin",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
+dependencies = [
+ "adler2",
+ "simd-adler32",
+]
+
+[[package]]
+name = "moxcms"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cc7d85f3d741164e8972ad355e26ac6e51b20fcae5f911c7da8f2d8bbbb3f33"
+dependencies = [
+ "num-traits",
+ "pxfm",
+]
+
+[[package]]
+name = "nb"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
+dependencies = [
+ "nb 1.1.0",
+]
+
+[[package]]
+name = "nb"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
+
+[[package]]
+name = "ndk"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4"
+dependencies = [
+ "bitflags 2.9.4",
+ "jni-sys",
+ "log",
+ "ndk-sys",
+ "num_enum",
+ "raw-window-handle",
+ "thiserror",
+]
+
+[[package]]
+name = "ndk-context"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
+
+[[package]]
+name = "ndk-sys"
+version = "0.6.0+11769913"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873"
+dependencies = [
+ "jni-sys",
+]
+
+[[package]]
+name = "new_debug_unreachable"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
+
+[[package]]
+name = "noise-protocol"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2473d39689a839f5a363aaef7d99f76d5611bf352286682b25a6644fec18b1d3"
+dependencies = [
+ "arrayvec",
+]
+
+[[package]]
+name = "noise-rust-crypto"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4c6159f60beb3bbbcdc266bc789bfc6c37fdad7d7ca7152d3e049ef5af633f0"
+dependencies = [
+ "chacha20poly1305",
+ "noise-protocol",
+ "sha2",
+ "zeroize",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+]
+
+[[package]]
+name = "noop_proc_macro"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.50.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "num-bigint"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
+dependencies = [
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-derive"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
+dependencies = [
+ "num-bigint",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_enum"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a"
+dependencies = [
+ "num_enum_derive",
+ "rustversion",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "objc-sys"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310"
+
+[[package]]
+name = "objc2"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804"
+dependencies = [
+ "objc-sys",
+ "objc2-encode",
+]
+
+[[package]]
+name = "objc2"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05"
+dependencies = [
+ "objc2-encode",
+]
+
+[[package]]
+name = "objc2-app-kit"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff"
+dependencies = [
+ "bitflags 2.9.4",
+ "block2",
+ "libc",
+ "objc2 0.5.2",
+ "objc2-core-data",
+ "objc2-core-image",
+ "objc2-foundation 0.2.2",
+ "objc2-quartz-core",
+]
+
+[[package]]
+name = "objc2-app-kit"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c"
+dependencies = [
+ "bitflags 2.9.4",
+ "objc2 0.6.3",
+ "objc2-core-foundation",
+ "objc2-foundation 0.3.2",
+]
+
+[[package]]
+name = "objc2-cloud-kit"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009"
+dependencies = [
+ "bitflags 2.9.4",
+ "block2",
+ "objc2 0.5.2",
+ "objc2-core-location",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-contacts"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889"
+dependencies = [
+ "block2",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-core-data"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef"
+dependencies = [
+ "bitflags 2.9.4",
+ "block2",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-core-foundation"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
+dependencies = [
+ "bitflags 2.9.4",
+ "dispatch2",
+ "objc2 0.6.3",
+]
+
+[[package]]
+name = "objc2-core-image"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80"
+dependencies = [
+ "block2",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+ "objc2-metal",
+]
+
+[[package]]
+name = "objc2-core-location"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781"
+dependencies = [
+ "block2",
+ "objc2 0.5.2",
+ "objc2-contacts",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-encode"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
+
+[[package]]
+name = "objc2-foundation"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
+dependencies = [
+ "bitflags 2.9.4",
+ "block2",
+ "dispatch",
+ "libc",
+ "objc2 0.5.2",
+]
+
+[[package]]
+name = "objc2-foundation"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
+dependencies = [
+ "bitflags 2.9.4",
+ "objc2 0.6.3",
+ "objc2-core-foundation",
+]
+
+[[package]]
+name = "objc2-link-presentation"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398"
+dependencies = [
+ "block2",
+ "objc2 0.5.2",
+ "objc2-app-kit 0.2.2",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-metal"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6"
+dependencies = [
+ "bitflags 2.9.4",
+ "block2",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-quartz-core"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a"
+dependencies = [
+ "bitflags 2.9.4",
+ "block2",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+ "objc2-metal",
+]
+
+[[package]]
+name = "objc2-symbols"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc"
+dependencies = [
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-ui-kit"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f"
+dependencies = [
+ "bitflags 2.9.4",
+ "block2",
+ "objc2 0.5.2",
+ "objc2-cloud-kit",
+ "objc2-core-data",
+ "objc2-core-image",
+ "objc2-core-location",
+ "objc2-foundation 0.2.2",
+ "objc2-link-presentation",
+ "objc2-quartz-core",
+ "objc2-symbols",
+ "objc2-uniform-type-identifiers",
+ "objc2-user-notifications",
+]
+
+[[package]]
+name = "objc2-uniform-type-identifiers"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe"
+dependencies = [
+ "block2",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-user-notifications"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3"
+dependencies = [
+ "bitflags 2.9.4",
+ "block2",
+ "objc2 0.5.2",
+ "objc2-core-location",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+
+[[package]]
+name = "once_cell_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
+
+[[package]]
+name = "opaque-debug"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
+
+[[package]]
+name = "orbclient"
+version = "0.3.48"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba0b26cec2e24f08ed8bb31519a9333140a6599b867dac464bb150bdb796fd43"
+dependencies = [
+ "libredox",
+]
+
+[[package]]
+name = "owned_ttf_parser"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b"
+dependencies = [
+ "ttf-parser",
+]
+
+[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
+
+[[package]]
+name = "pin-project"
+version = "1.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
+
+[[package]]
+name = "png"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0"
+dependencies = [
+ "bitflags 2.9.4",
+ "crc32fast",
+ "fdeflate",
+ "flate2",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "polling"
+version = "3.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218"
+dependencies = [
+ "cfg-if",
+ "concurrent-queue",
+ "hermit-abi",
+ "pin-project-lite",
+ "rustix 1.1.2",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "poly1305"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
+dependencies = [
+ "cpufeatures",
+ "opaque-debug",
+ "universal-hash",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "proc-macro-crate"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983"
+dependencies = [
+ "toml_edit 0.23.6",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "profiling"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773"
+dependencies = [
+ "profiling-procmacros",
+]
+
+[[package]]
+name = "profiling-procmacros"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b"
+dependencies = [
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "prost"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13db3d3fde688c61e2446b4d843bc27a7e8af269a69440c0308021dc92333cc"
+dependencies = [
+ "bytes",
+ "prost-derive",
+]
+
+[[package]]
+name = "prost-derive"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18bec9b0adc4eba778b33684b7ba3e7137789434769ee3ce3930463ef904cfca"
+dependencies = [
+ "anyhow",
+ "itertools 0.12.1",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pxfm"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83f9b339b02259ada5c0f4a389b7fb472f933aa17ce176fd2ad98f28bb401fde"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "qoi"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "quick-error"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
+
+[[package]]
+name = "quick-xml"
+version = "0.37.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r-efi"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom 0.2.16",
+]
+
+[[package]]
+name = "rav1e"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9"
+dependencies = [
+ "arbitrary",
+ "arg_enum_proc_macro",
+ "arrayvec",
+ "av1-grain",
+ "bitstream-io",
+ "built",
+ "cfg-if",
+ "interpolate_name",
+ "itertools 0.12.1",
+ "libc",
+ "libfuzzer-sys",
+ "log",
+ "maybe-rayon",
+ "new_debug_unreachable",
+ "noop_proc_macro",
+ "num-derive",
+ "num-traits",
+ "once_cell",
+ "paste",
+ "profiling",
+ "rand",
+ "rand_chacha",
+ "simd_helpers",
+ "system-deps",
+ "thiserror",
+ "v_frame",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "ravif"
+version = "0.11.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5825c26fddd16ab9f515930d49028a630efec172e903483c94796cfe31893e6b"
+dependencies = [
+ "avif-serialize",
+ "imgref",
+ "loop9",
+ "quick-error",
+ "rav1e",
+ "rayon",
+ "rgb",
+]
+
+[[package]]
+name = "raw-window-handle"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
+
+[[package]]
+name = "rayon"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
+dependencies = [
+ "bitflags 2.9.4",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
+
+[[package]]
+name = "rgb"
+version = "0.8.52"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
+dependencies = [
+ "semver 0.9.0",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
+dependencies = [
+ "semver 1.0.20",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
+dependencies = [
+ "bitflags 2.9.4",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.4.15",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "rustix"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
+dependencies = [
+ "bitflags 2.9.4",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.11.0",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+
+[[package]]
+name = "rustybuzz"
+version = "0.20.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702"
+dependencies = [
+ "bitflags 2.9.4",
+ "bytemuck",
+ "core_maths",
+ "log",
+ "smallvec",
+ "ttf-parser",
+ "unicode-bidi-mirroring",
+ "unicode-ccc",
+ "unicode-properties",
+ "unicode-script",
+]
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "scoped-tls"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
+
+[[package]]
+name = "sctk-adwaita"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec"
+dependencies = [
+ "ab_glyph",
+ "log",
+ "memmap2",
+ "smithay-client-toolkit",
+ "tiny-skia",
+]
+
+[[package]]
+name = "secp256k1"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e0cc0f1cf93f4969faf3ea1c7d8a9faed25918d96affa959720823dfe86d4f3"
+dependencies = [
+ "bitcoin_hashes",
+ "secp256k1-sys",
+]
+
+[[package]]
+name = "secp256k1-sys"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1433bd67156263443f14d603720b082dd3121779323fce20cba2aa07b874bc1b"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "semver"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
+dependencies = [
+ "semver-parser",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
+
+[[package]]
+name = "semver-parser"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
+
+[[package]]
+name = "serde"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sha3"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
+dependencies = [
+ "digest",
+ "keccak",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "signature"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
+dependencies = [
+ "digest",
+]
+
+[[package]]
+name = "simd-adler32"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
+
+[[package]]
+name = "simd_helpers"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6"
+dependencies = [
+ "quote",
+]
+
+[[package]]
+name = "simulator-graphical"
+version = "0.1.0"
+dependencies = [
+ "bitbox-aes",
+ "bitbox02",
+ "bitbox02-rust",
+ "bitbox02-rust-c",
+ "clap",
+ "critical-section",
+ "femtovg",
+ "glutin",
+ "glutin-winit",
+ "hex",
+ "image",
+ "tracing",
+ "tracing-subscriber",
+ "util",
+ "winit",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
+
+[[package]]
+name = "slotmap"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+
+[[package]]
+name = "smithay-client-toolkit"
+version = "0.19.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016"
+dependencies = [
+ "bitflags 2.9.4",
+ "calloop",
+ "calloop-wayland-source",
+ "cursor-icon",
+ "libc",
+ "log",
+ "memmap2",
+ "rustix 0.38.44",
+ "thiserror",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-csd-frame",
+ "wayland-cursor",
+ "wayland-protocols",
+ "wayland-protocols-wlr",
+ "wayland-scanner",
+ "xkeysym",
+]
+
+[[package]]
+name = "smol_str"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "streaming-silent-payments"
+version = "0.1.0"
+dependencies = [
+ "bitbox02",
+ "bitcoin",
+]
+
+[[package]]
+name = "strict-num"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "subtle"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
+
+[[package]]
+name = "syn"
+version = "2.0.98"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "system-deps"
+version = "6.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
+dependencies = [
+ "cfg-expr",
+ "heck",
+ "pkg-config",
+ "toml",
+ "version-compare",
+]
+
+[[package]]
+name = "target-lexicon"
+version = "0.12.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
+
+[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "tiff"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f"
+dependencies = [
+ "fax",
+ "flate2",
+ "half",
+ "quick-error",
+ "weezl",
+ "zune-jpeg",
+]
+
+[[package]]
+name = "tiny-skia"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab"
+dependencies = [
+ "arrayref",
+ "arrayvec",
+ "bytemuck",
+ "cfg-if",
+ "log",
+ "tiny-skia-path",
+]
+
+[[package]]
+name = "tiny-skia-path"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93"
+dependencies = [
+ "arrayref",
+ "bytemuck",
+ "strict-num",
+]
+
+[[package]]
+name = "toml"
+version = "0.8.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime 0.6.11",
+ "toml_edit 0.22.27",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.22.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime 0.6.11",
+ "winnow",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.23.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3effe7c0e86fdff4f69cdd2ccc1b96f933e24811c5441d44904e8683e27184b"
+dependencies = [
+ "indexmap",
+ "toml_datetime 0.7.2",
+ "toml_parser",
+ "winnow",
+]
+
+[[package]]
+name = "toml_parser"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627"
+dependencies = [
+ "winnow",
+]
+
+[[package]]
+name = "tracing"
+version = "0.1.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
+dependencies = [
+ "log",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
+dependencies = [
+ "matchers",
+ "nu-ansi-term",
+ "once_cell",
+ "regex-automata",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+]
+
+[[package]]
+name = "ttf-parser"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
+dependencies = [
+ "core_maths",
+]
+
+[[package]]
+name = "typenum"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
+
+[[package]]
+name = "unicode-bidi"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
+
+[[package]]
+name = "unicode-bidi-mirroring"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe"
+
+[[package]]
+name = "unicode-ccc"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
+
+[[package]]
+name = "unicode-properties"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
+
+[[package]]
+name = "unicode-script"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fb421b350c9aff471779e262955939f565ec18b86c15364e6bdf0d662ca7c1f"
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
+
+[[package]]
+name = "universal-hash"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
+dependencies = [
+ "crypto-common",
+ "subtle",
+]
+
+[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
+[[package]]
+name = "util"
+version = "0.1.0"
+dependencies = [
+ "bitcoin",
+ "cortex-m",
+ "critical-section",
+ "hex",
+ "num-bigint",
+ "sha2",
+]
+
+[[package]]
+name = "v_frame"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2"
+dependencies = [
+ "aligned-vec",
+ "num-traits",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "valuable"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
+
+[[package]]
+name = "vcell"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
+
+[[package]]
+name = "version-compare"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b"
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
+name = "void"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
+
+[[package]]
+name = "volatile-register"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de437e2a6208b014ab52972a27e59b33fa2920d3e00fe05026167a1c509d19cc"
+dependencies = [
+ "vcell",
+]
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.1+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+
+[[package]]
+name = "wasi"
+version = "0.14.7+wasi-0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c"
+dependencies = [
+ "wasip2",
+]
+
+[[package]]
+name = "wasip2"
+version = "1.0.1+wasi-0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
+dependencies = [
+ "wit-bindgen",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19"
+dependencies = [
+ "bumpalo",
+ "log",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.54"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "once_cell",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "wayland-backend"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35"
+dependencies = [
+ "cc",
+ "downcast-rs",
+ "rustix 1.1.2",
+ "scoped-tls",
+ "smallvec",
+ "wayland-sys",
+]
+
+[[package]]
+name = "wayland-client"
+version = "0.31.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d"
+dependencies = [
+ "bitflags 2.9.4",
+ "rustix 1.1.2",
+ "wayland-backend",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-csd-frame"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e"
+dependencies = [
+ "bitflags 2.9.4",
+ "cursor-icon",
+ "wayland-backend",
+]
+
+[[package]]
+name = "wayland-cursor"
+version = "0.31.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "447ccc440a881271b19e9989f75726d60faa09b95b0200a9b7eb5cc47c3eeb29"
+dependencies = [
+ "rustix 1.1.2",
+ "wayland-client",
+ "xcursor",
+]
+
+[[package]]
+name = "wayland-protocols"
+version = "0.32.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901"
+dependencies = [
+ "bitflags 2.9.4",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols-plasma"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a07a14257c077ab3279987c4f8bb987851bf57081b93710381daea94f2c2c032"
+dependencies = [
+ "bitflags 2.9.4",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols-wlr"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec"
+dependencies = [
+ "bitflags 2.9.4",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-scanner"
+version = "0.31.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3"
+dependencies = [
+ "proc-macro2",
+ "quick-xml",
+ "quote",
+]
+
+[[package]]
+name = "wayland-sys"
+version = "0.31.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142"
+dependencies = [
+ "dlib",
+ "log",
+ "once_cell",
+ "pkg-config",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "web-time"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "weezl"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
+dependencies = [
+ "windows-targets 0.53.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm 0.52.6",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.53.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
+dependencies = [
+ "windows-link",
+ "windows_aarch64_gnullvm 0.53.1",
+ "windows_aarch64_msvc 0.53.1",
+ "windows_i686_gnu 0.53.1",
+ "windows_i686_gnullvm 0.53.1",
+ "windows_i686_msvc 0.53.1",
+ "windows_x86_64_gnu 0.53.1",
+ "windows_x86_64_gnullvm 0.53.1",
+ "windows_x86_64_msvc 0.53.1",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
+
+[[package]]
+name = "winit"
+version = "0.30.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c66d4b9ed69c4009f6321f762d6e61ad8a2389cd431b97cb1e146812e9e6c732"
+dependencies = [
+ "ahash",
+ "android-activity",
+ "atomic-waker",
+ "bitflags 2.9.4",
+ "block2",
+ "bytemuck",
+ "calloop",
+ "cfg_aliases",
+ "concurrent-queue",
+ "core-foundation",
+ "core-graphics",
+ "cursor-icon",
+ "dpi",
+ "js-sys",
+ "libc",
+ "memmap2",
+ "ndk",
+ "objc2 0.5.2",
+ "objc2-app-kit 0.2.2",
+ "objc2-foundation 0.2.2",
+ "objc2-ui-kit",
+ "orbclient",
+ "percent-encoding",
+ "pin-project",
+ "raw-window-handle",
+ "redox_syscall 0.4.1",
+ "rustix 0.38.44",
+ "sctk-adwaita",
+ "smithay-client-toolkit",
+ "smol_str",
+ "tracing",
+ "unicode-segmentation",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-protocols-plasma",
+ "web-sys",
+ "web-time",
+ "windows-sys 0.52.0",
+ "x11-dl",
+ "x11rb",
+ "xkbcommon-dl",
+]
+
+[[package]]
+name = "winnow"
+version = "0.7.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "wit-bindgen"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
+
+[[package]]
+name = "x11-dl"
+version = "2.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f"
+dependencies = [
+ "libc",
+ "once_cell",
+ "pkg-config",
+]
+
+[[package]]
+name = "x11rb"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414"
+dependencies = [
+ "as-raw-xcb-connection",
+ "gethostname",
+ "libc",
+ "libloading",
+ "once_cell",
+ "rustix 1.1.2",
+ "x11rb-protocol",
+]
+
+[[package]]
+name = "x11rb-protocol"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
+
+[[package]]
+name = "x25519-dalek"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96"
+dependencies = [
+ "curve25519-dalek",
+ "rand_core",
+]
+
+[[package]]
+name = "xcursor"
+version = "0.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b"
+
+[[package]]
+name = "xkbcommon-dl"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5"
+dependencies = [
+ "bitflags 2.9.4",
+ "dlib",
+ "log",
+ "once_cell",
+ "xkeysym",
+]
+
+[[package]]
+name = "xkeysym"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
+
+[[package]]
+name = "xml-rs"
+version = "0.8.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7"
+
+[[package]]
+name = "zerocopy"
+version = "0.8.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
+dependencies = [
+ "zeroize_derive",
+]
+
+[[package]]
+name = "zeroize_derive"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "zune-core"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
+
+[[package]]
+name = "zune-inflate"
+version = "0.2.54"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
+dependencies = [
+ "simd-adler32",
+]
+
+[[package]]
+name = "zune-jpeg"
+version = "0.4.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713"
+dependencies = [
+ "zune-core",
+]
diff --git a/test/simulator-graphical/Cargo.toml b/test/simulator-graphical/Cargo.toml
new file mode 100644
index 0000000..8bd3a07
--- /dev/null
+++ b/test/simulator-graphical/Cargo.toml
@@ -0,0 +1,21 @@
+[package]
+name = "simulator-graphical"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+bitbox02-rust = { path="../../src/rust/bitbox02-rust", features=["simulator-graphical"] }
+bitbox02-rust-c = { path="../../src/rust/bitbox02-rust-c", features=["simulator-graphical"] }
+bitbox02 = { path="../../src/rust/bitbox02", features=["simulator-graphical"] }
+bitbox-aes = { path="../../src/rust/bitbox-aes"}
+winit = "0.30.12"
+tracing = {version = "0.1.41", features=["log"]}
+image = "0.25.8"
+tracing-subscriber = {version = "0.3.20", features=["env-filter"]}
+femtovg = "0.19.1"
+glutin = "0.32.3"
+glutin-winit = "0.5.0"
+clap = { version = "4.5.48", features = ["derive"] }
+hex = "0.4.3"
+util = { path = "../../src/rust/util" }
+critical-section = {version = "1.2", features = ["std"]}
diff --git a/test/simulator-graphical/bg.png b/test/simulator-graphical/bg.png
new file mode 100644
index 0000000..905f5fc
Binary files /dev/null and b/test/simulator-graphical/bg.png differ
diff --git a/test/simulator-graphical/src/main.rs b/test/simulator-graphical/src/main.rs
new file mode 100644
index 0000000..6f0bd5e
--- /dev/null
+++ b/test/simulator-graphical/src/main.rs
@@ -0,0 +1,825 @@
+// Copyright 2025 Shift Crypto AG
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+use clap::Parser;
+use femtovg::{Canvas, ImageFlags, ImageId, ImageSource, Paint, Path, renderer::OpenGl};
+use image::{DynamicImage, GenericImage, Rgba, RgbaImage};
+
+use std::collections::VecDeque;
+use std::error::Error;
+use std::io::{Read, Write};
+use std::net::{TcpListener, TcpStream};
+use std::num::NonZeroU32;
+use std::rc::Rc;
+use std::sync::{
+ Arc, LazyLock, Mutex,
+ atomic::{AtomicBool, AtomicU32, Ordering},
+ mpsc,
+ mpsc::TryRecvError,
+};
+use std::task::Poll::Ready;
+use std::thread;
+use std::time::Duration;
+
+use winit::application::ApplicationHandler;
+use winit::dpi::{LogicalSize, PhysicalSize};
+use winit::event::WindowEvent;
+use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
+use winit::raw_window_handle::HasWindowHandle;
+use winit::window::{Window, WindowId};
+
+use glutin::config::ConfigTemplateBuilder;
+use glutin::context::{
+ ContextApi, ContextAttributesBuilder, NotCurrentGlContext, PossiblyCurrentContext,
+};
+use glutin::display::{GetGlDisplay, GlDisplay};
+use glutin::surface::{GlSurface, Surface, SurfaceAttributesBuilder, WindowSurface};
+use glutin_winit::DisplayBuilder;
+
+use tracing::{debug, error, info};
+use tracing_subscriber::{EnvFilter, filter::LevelFilter, fmt, prelude::*};
+
+use bitbox02::ui::ugui::UG_COLOR;
+
+static BG: &[u8; 325362] = include_bytes!("../bg.png");
+
+const MARGIN: usize = 20;
+const PADDING_TOP_BOTTOM: usize = 22;
+const PADDING_LEFT: usize = 60;
+const PADDING_RIGHT: usize = 35;
+const SCREEN_WIDTH: usize = bitbox02::screen::SCREEN_WIDTH as usize;
+const SCREEN_HEIGHT: usize = bitbox02::screen::SCREEN_HEIGHT as usize;
+const WINDOW_LOGICAL_WIDTH_ORIGINAL: usize =
+ SCREEN_WIDTH + 2 * MARGIN + PADDING_LEFT + PADDING_RIGHT;
+const WINDOW_LOGICAL_HEIGHT_ORIGINAL: usize = SCREEN_HEIGHT + 2 * MARGIN + 2 * PADDING_TOP_BOTTOM;
+
+pub fn handle_stream_reader(
+ mut stream: TcpStream,
+ inbound_in: mpsc::Sender<[u8; 64]>,
+ counter: Arc<AtomicU32>,
+) {
+ let mut buf = [0; 1024];
+ while let Ok(len) = stream.read(&mut buf) {
+ if len == 0 {
+ // Client disconnected
+ break;
+ }
+ for i in 0..len / 64 {
+ let mut data: [u8; 64] = [0; 64];
+ //debug!("rx [{}]", hex::encode(&data[..]));
+ data.copy_from_slice(&buf[64 * i..64 * (i + 1)]);
+ inbound_in.send(data).unwrap();
+ }
+ }
+ drop(inbound_in);
+ info!("TCP Stream: Disconnected read");
+ counter.fetch_sub(1, Ordering::SeqCst);
+}
+
+pub fn handle_stream_writer(
+ mut stream: TcpStream,
+ outbound_out: mpsc::Receiver<[u8; 64]>,
+ counter: Arc<AtomicU32>,
+) {
+ for item in outbound_out.iter() {
+ //debug!("tx [{}]", hex::encode(item));
+ if stream.write_all(&item[..]).is_err() {
+ // Client disconnected while writing
+ break;
+ }
+ }
+ drop(outbound_out);
+ info!("TCP Stream: Disconnected write");
+ counter.fetch_sub(1, Ordering::SeqCst);
+}
+
+/// Screen frame buffer
+static SCREEN_FB: LazyLock<Mutex<DynamicImage>> = LazyLock::new(|| {
+ Mutex::new(DynamicImage::ImageRgba8(RgbaImage::new(
+ SCREEN_WIDTH as u32,
+ SCREEN_HEIGHT as u32,
+ )))
+});
+
+static MIRROR: AtomicBool = AtomicBool::new(false);
+
+fn pixel_fn(x: i16, y: i16, c: UG_COLOR) {
+ if x < 0 || x >= SCREEN_WIDTH as i16 {
+ return;
+ }
+ if y < 0 || y >= SCREEN_HEIGHT as i16 {
+ return;
+ }
+ let x = x as u32;
+ let y = y as u32;
+ let mut screen = SCREEN_FB.lock().unwrap();
+
+ if c != 0 {
+ screen.put_pixel(x, y, Rgba([0xff, 0xff, 0xff, 0xff]));
+ }
+}
+
+fn clear_fn() {
+ let mut screen = SCREEN_FB.lock().unwrap();
+ if let DynamicImage::ImageRgba8(rgba) = &mut *screen {
+ for pixel in rgba.pixels_mut() {
+ *pixel = Rgba([0, 0, 0, 0]);
+ }
+ }
+}
+
+fn mirror_fn(_: bool) {
+ MIRROR.fetch_xor(true, Ordering::Relaxed);
+}
+
+static ACCEPTING_CONNECTIONS: AtomicBool = AtomicBool::new(false);
+
+fn init_hww(preseed: bool) -> bool {
+ bitbox02::screen::init(pixel_fn, mirror_fn, clear_fn);
+ bitbox02::screen::splash();
+
+ // BitBox02 simulation initialization
+ bitbox02::usb_processing::init();
+ info!("USB setup success");
+
+ bitbox02::hww::setup();
+ info!("HWW setup success");
+
+ if !bitbox02::sd::format() {
+ error!("ERROR, sd card setup failed");
+ return false;
+ }
+
+ info!("Sd card setup: success");
+
+ bitbox02::testing::mock_memory();
+ info!("Memory setup: success");
+
+ if preseed {
+ let mnemonic = "boring mistake dish oyster truth pigeon viable emerge sort crash wire portion cannon couple enact box walk height pull today solid off enable tide";
+ let seed = bitbox02_rust::bip39::mnemonic_to_seed(&mnemonic).unwrap();
+ bitbox02_rust::keystore::encrypt_and_store_seed(
+ &mut bitbox02_rust::hal::BitBox02Hal::new(),
+ &seed,
+ "",
+ )
+ .unwrap();
+ bitbox02::memory::set_initialized().unwrap();
+ }
+
+ bitbox02::smarteeprom::bb02_config();
+ bitbox02::smarteeprom::init();
+
+ true
+}
+
+#[derive(Debug)]
+struct Slider {
+ active: bool,
+ position: u16,
+ position_start: u16,
+ sliding: bool,
+ velocity_history: VecDeque<i32>,
+}
+
+impl Default for Slider {
+ fn default() -> Self {
+ Slider {
+ active: false,
+ position: 0,
+ position_start: 0,
+ sliding: false,
+ velocity_history: VecDeque::new(),
+ }
+ }
+}
+
+#[derive(Debug)]
+struct SliderUpdate {
+ active: bool,
+ position: u16,
+}
+
+impl SliderUpdate {
+ fn new(active: bool, position: u16) -> SliderUpdate {
+ SliderUpdate { active, position }
+ }
+}
+
+struct App {
+ window: Option<Rc<Window>>,
+ surface: Option<Surface<WindowSurface>>,
+ gl_context: Option<PossiblyCurrentContext>,
+ canvas: Option<Canvas<OpenGl>>,
+ bg: Option<ImageId>,
+ screen: Option<ImageId>,
+ slider_top: Slider,
+ slider_bottom: Slider,
+ pinch: bool,
+ mouse_last_x: i32,
+ mouse_last_y: i32,
+ outbound_in: Option<mpsc::Sender<[u8; 64]>>,
+ inbound_out: Option<mpsc::Receiver<[u8; 64]>>,
+ orientation_task: Option<util::bb02_async::Task<'static, bool>>,
+}
+
+impl Default for App {
+ fn default() -> App {
+ App {
+ window: Default::default(),
+ surface: Default::default(),
+ gl_context: Default::default(),
+ canvas: Default::default(),
+ bg: Default::default(),
+ screen: Default::default(),
+ slider_top: Default::default(),
+ slider_bottom: Default::default(),
+ pinch: false,
+ mouse_last_x: 0,
+ mouse_last_y: 0,
+ outbound_in: Default::default(),
+ inbound_out: Default::default(),
+ orientation_task: Default::default(),
+ }
+ }
+}
+
+impl App {
+ fn create_window(
+ &mut self,
+ event_loop: &ActiveEventLoop,
+ _: Option<String>,
+ ) -> Result<WindowId, Box<dyn Error>> {
+ let width = (WINDOW_LOGICAL_WIDTH_ORIGINAL) as u32;
+ let height = (WINDOW_LOGICAL_HEIGHT_ORIGINAL) as u32;
+ let w_attr = Window::default_attributes()
+ .with_inner_size(LogicalSize::new(width, height))
+ .with_title("Graphical BitBox02 Simulator");
+
+ let (window, gl_config) = {
+ let template = ConfigTemplateBuilder::new();
+ let (window, gl_config) = DisplayBuilder::new()
+ .with_window_attributes(Some(w_attr))
+ .build(event_loop, template, |mut configs| configs.next().unwrap())?;
+ (Rc::new(window.unwrap()), gl_config)
+ };
+
+ let raw_window_handle = Some(window.window_handle().unwrap().as_ref().clone());
+
+ let gl_display = gl_config.display();
+
+ let context_attributes = ContextAttributesBuilder::new().build(raw_window_handle);
+ let fallback_context_attributes = ContextAttributesBuilder::new()
+ .with_context_api(ContextApi::Gles(None))
+ .build(raw_window_handle);
+ let mut not_current_gl_context = Some(unsafe {
+ gl_display
+ .create_context(&gl_config, &context_attributes)
+ .unwrap_or_else(|_| {
+ gl_display
+ .create_context(&gl_config, &fallback_context_attributes)
+ .expect("failed to create context")
+ })
+ });
+
+ let (width, height): (u32, u32) = window.inner_size().into();
+ let raw_window_handle = window.window_handle().unwrap().as_ref().clone();
+ let attrs = SurfaceAttributesBuilder::<glutin::surface::WindowSurface>::new().build(
+ raw_window_handle,
+ NonZeroU32::new(width).unwrap(),
+ NonZeroU32::new(height).unwrap(),
+ );
+
+ let surface = unsafe {
+ gl_config
+ .display()
+ .create_window_surface(&gl_config, &attrs)
+ .unwrap()
+ };
+
+ let gl_context = not_current_gl_context
+ .take()
+ .unwrap()
+ .make_current(&surface)
+ .unwrap();
+
+ let renderer =
+ unsafe { OpenGl::new_from_function_cstr(|s| gl_display.get_proc_address(s).cast()) }
+ .expect("Cannot create renderer");
+
+ let mut canvas = Canvas::new(renderer).expect("Cannot create canvas");
+ canvas.set_size(width, height, window.scale_factor() as f32);
+
+ let window_id = window.id();
+ info!("Created window {window_id:?}");
+ let bg_orig = image::load_from_memory(BG).unwrap();
+ debug!("image: {} {}", bg_orig.width(), bg_orig.height());
+
+ let bg_id = canvas
+ .create_image(
+ ImageSource::try_from(&bg_orig).unwrap(),
+ ImageFlags::NEAREST,
+ )
+ .unwrap();
+ let screen_id = canvas
+ .create_image(
+ ImageSource::try_from(&*SCREEN_FB.lock().unwrap()).unwrap(),
+ ImageFlags::NEAREST,
+ )
+ .unwrap();
+
+ canvas.save();
+ canvas.reset();
+ canvas.restore();
+
+ self.window.replace(window);
+ self.surface.replace(surface);
+ self.gl_context.replace(gl_context);
+ self.canvas.replace(canvas);
+ self.bg.replace(bg_id);
+ self.screen.replace(screen_id);
+ Ok(window_id)
+ }
+}
+
+fn emit_slider_event(slider: &mut Slider, updated: SliderUpdate, slider_source: u8) {
+ let diff = updated.position as i16 - slider.position as i16;
+ let updated_data = bitbox02::event::event_slider_data_t {
+ source: slider_source,
+ diff: diff,
+ position: updated.position as u16,
+ velocity: slider.velocity_history.iter().sum(),
+ };
+ if !updated.active && slider.active {
+ debug!("tap {:?}", updated_data);
+ let event = bitbox02::event::event_t {
+ id: bitbox02::event::event_types::EVENT_SHORT_TAP as u8,
+ data: updated_data,
+ };
+ bitbox02::event::emit_event(&event);
+ debug!("slide released {:?}", updated_data);
+ slider.sliding = false;
+ slider.velocity_history.clear();
+ let event = bitbox02::event::event_t {
+ id: bitbox02::event::event_types::EVENT_SLIDE_RELEASED as u8,
+ data: updated_data,
+ };
+ bitbox02::event::emit_event(&event);
+ }
+ if updated.active && !slider.active {
+ debug!("cont {:?}", updated_data);
+ let event = bitbox02::event::event_t {
+ data: updated_data,
+ id: bitbox02::event::event_types::EVENT_CONTINUOUS_TAP as u8,
+ };
+ bitbox02::event::emit_event(&event);
+ slider.position_start = updated.position;
+ }
+ if updated.active
+ && slider.active
+ && (i32::abs(slider.position_start as i32 - updated.position as i32) > 10 || slider.sliding)
+ {
+ debug!("slide {:?}", updated_data);
+ slider.velocity_history.push_back(diff as i32);
+ if slider.velocity_history.len() > 30 {
+ slider.velocity_history.pop_front();
+ }
+ slider.sliding = true;
+ let event = bitbox02::event::event_t {
+ id: bitbox02::event::event_types::EVENT_SLIDE as u8,
+ data: updated_data,
+ };
+ bitbox02::event::emit_event(&event);
+ }
+ slider.active = updated.active;
+ slider.position = updated.position;
+}
+
+#[derive(Debug)]
+enum UserEvent {
+ WakeUp,
+ NewConnection(mpsc::Sender<[u8; 64]>, mpsc::Receiver<[u8; 64]>),
+}
+
+impl ApplicationHandler<UserEvent> for App {
+ fn window_event(&mut self, event_loop: &ActiveEventLoop, _: WindowId, event: WindowEvent) {
+ match event {
+ WindowEvent::CloseRequested => {
+ event_loop.exit();
+ return;
+ }
+ WindowEvent::RedrawRequested => {
+ //info!("{event:?}");
+ if let Some(surface) = &mut self.surface
+ && let Some(canvas) = &mut self.canvas
+ && let Some(gl_context) = &mut self.gl_context
+ && let Some(bg_id) = &mut self.bg
+ && let Some(screen_id) = &mut self.screen
+ && let Some(window) = &mut self.window
+ {
+ let dpi_factor = window.scale_factor() as f32;
+ let window_size = window.inner_size();
+ let width_stretch_factor =
+ window_size.width as f32 / WINDOW_LOGICAL_WIDTH_ORIGINAL as f32;
+ let height_stretch_factor =
+ window_size.height as f32 / WINDOW_LOGICAL_HEIGHT_ORIGINAL as f32;
+ canvas.set_size(window_size.width, window_size.height, dpi_factor);
+
+ // fill background
+ let mut bg = Path::new();
+ bg.rect(
+ 0f32,
+ 0f32,
+ window_size.width as f32,
+ window_size.height as f32,
+ );
+ canvas.fill_path(
+ &bg,
+ &Paint::color(femtovg::Color::rgba(0xf5, 0xf5, 0xf5, 0xff)),
+ );
+
+ let bitbox_x = MARGIN as f32 * width_stretch_factor;
+ let bitbox_y = MARGIN as f32 * height_stretch_factor;
+ let bitbox_width =
+ (PADDING_LEFT + SCREEN_WIDTH + PADDING_RIGHT) as f32 * width_stretch_factor;
+ let bitbox_height =
+ (2 * PADDING_TOP_BOTTOM + SCREEN_HEIGHT) as f32 * height_stretch_factor;
+
+ let mut bitbox_path = Path::new();
+ bitbox_path.rect(bitbox_x, bitbox_y, bitbox_width, bitbox_height);
+
+ canvas.fill_path(
+ &bitbox_path,
+ &Paint::image(
+ bg_id.clone(),
+ bitbox_x,
+ bitbox_y,
+ bitbox_width,
+ bitbox_height,
+ 0f32,
+ 1f32,
+ ),
+ );
+
+ let screen_x = (MARGIN + PADDING_LEFT) as f32 * width_stretch_factor;
+ let screen_y = (MARGIN + PADDING_TOP_BOTTOM) as f32 * height_stretch_factor;
+ let screen_width = SCREEN_WIDTH as f32 * width_stretch_factor;
+ let screen_height = SCREEN_HEIGHT as f32 * height_stretch_factor;
+ let mut screen_path = Path::new();
+ screen_path.rect(screen_x, screen_y, screen_width, screen_height);
+ let paint = if MIRROR.load(Ordering::Relaxed) {
+ Paint::image(
+ screen_id.clone(),
+ screen_x + screen_width,
+ screen_y + screen_height,
+ screen_width,
+ screen_height,
+ std::f32::consts::PI,
+ 1f32,
+ )
+ } else {
+ Paint::image(
+ screen_id.clone(),
+ screen_x,
+ screen_y,
+ screen_width,
+ screen_height,
+ 0f32,
+ 1f32,
+ )
+ };
+ canvas.fill_path(&screen_path, &paint);
+
+ canvas.flush_to_surface(&());
+ surface.swap_buffers(gl_context).unwrap();
+ }
+ }
+ WindowEvent::Resized(PhysicalSize { width, height }) => {
+ if let Some(surface) = &mut self.surface
+ && let Some(gl_context) = &mut self.gl_context
+ {
+ surface.resize(
+ gl_context,
+ width.try_into().unwrap(),
+ height.try_into().unwrap(),
+ )
+ }
+ }
+ WindowEvent::CursorMoved { position, .. } => {
+ //debug!("{position:?}");
+ let Some(window) = &mut self.window else {
+ return;
+ };
+ let (x, y) = (position.x, position.y);
+ let window_size = window.inner_size();
+ let width_scale_factor = (window_size.width) as f32
+ / (2 * MARGIN + PADDING_LEFT + SCREEN_WIDTH + PADDING_RIGHT) as f32;
+
+ let height_scale_factor = (window_size.height) as f32
+ / (2 * MARGIN + 2 * PADDING_TOP_BOTTOM + SCREEN_HEIGHT) as f32;
+
+ let (x, y) = (
+ (x as f32 / width_scale_factor) as i32,
+ (y as f32 / height_scale_factor) as i32,
+ );
+ let xrel = x - self.mouse_last_x;
+ let yrel = y - self.mouse_last_y;
+ // Ignore if mouse didn't move long enough
+ if xrel == 0 && yrel == 0 {
+ return;
+ }
+ debug!("x={x}, y={y}, xrel={xrel}, yrel={yrel}");
+ self.mouse_last_x = x;
+ self.mouse_last_y = y;
+
+ let slider_pos = (x - ((MARGIN + PADDING_LEFT / 2) as i32)) * 255
+ / (SCREEN_WIDTH + PADDING_LEFT / 2 + PADDING_RIGHT) as i32;
+ debug!("slider_pos = {slider_pos}");
+ if slider_pos >= 0 && slider_pos <= 255 {
+ // Check top slider hit box
+ if y >= MARGIN as i32 / 2 && y < MARGIN as i32 {
+ emit_slider_event(
+ &mut self.slider_top,
+ SliderUpdate::new(true, slider_pos as u16),
+ 1,
+ );
+ }
+ // Check if moved out upwards
+ if y < MARGIN as i32 / 2 && self.slider_top.active {
+ let prev_pos = self.slider_top.position;
+ emit_slider_event(
+ &mut self.slider_top,
+ SliderUpdate::new(false, prev_pos),
+ 1,
+ );
+ }
+
+ // Check bottom slider hit box
+ if y >= (MARGIN + 2 * PADDING_TOP_BOTTOM + SCREEN_HEIGHT) as i32
+ && y < (MARGIN + 2 * PADDING_TOP_BOTTOM + SCREEN_HEIGHT + MARGIN / 2) as i32
+ {
+ emit_slider_event(
+ &mut self.slider_bottom,
+ SliderUpdate::new(true, slider_pos as u16),
+ 0,
+ );
+ }
+ // Check if moved out downwards
+ if y > (MARGIN + 2 * PADDING_TOP_BOTTOM + SCREEN_HEIGHT + MARGIN / 2) as i32
+ && self.slider_bottom.active
+ {
+ let prev_pos = self.slider_bottom.position;
+ emit_slider_event(
+ &mut self.slider_bottom,
+ SliderUpdate::new(false, prev_pos),
+ 0,
+ );
+ }
+ }
+
+ // Check "pinch hit box"
+ if x > (MARGIN + PADDING_LEFT + SCREEN_WIDTH + PADDING_RIGHT) as i32
+ && y > (MARGIN + PADDING_TOP_BOTTOM + SCREEN_HEIGHT / 3) as i32
+ && y < (MARGIN + PADDING_TOP_BOTTOM + SCREEN_HEIGHT * 2 / 3) as i32
+ {
+ if !self.pinch {
+ self.pinch = true;
+ let prev_pos = self.slider_top.position;
+ emit_slider_event(
+ &mut self.slider_top,
+ SliderUpdate::new(false, prev_pos),
+ 1,
+ );
+ let prev_pos = self.slider_bottom.position;
+ emit_slider_event(
+ &mut self.slider_bottom,
+ SliderUpdate::new(false, prev_pos),
+ 0,
+ );
+ emit_slider_event(&mut self.slider_top, SliderUpdate::new(true, 255), 1);
+ emit_slider_event(&mut self.slider_bottom, SliderUpdate::new(true, 255), 0);
+ }
+ } else if self.pinch {
+ self.pinch = false;
+ let prev_pos = self.slider_top.position;
+ emit_slider_event(&mut self.slider_top, SliderUpdate::new(false, prev_pos), 1);
+ let prev_pos = self.slider_bottom.position;
+ emit_slider_event(
+ &mut self.slider_bottom,
+ SliderUpdate::new(false, prev_pos),
+ 0,
+ );
+ }
+ }
+
+ _ => debug!("{event:?}"),
+ }
+ }
+
+ // Since the firmware code is very non-threadsafe all firmware code is called from user events
+ // in the main thread.
+ fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: UserEvent) {
+ match event {
+ UserEvent::WakeUp => {
+ // Read data from TCP client
+ let mut inbound_out = self.inbound_out.take();
+ let mut disconnected = false;
+ if let Some(inbound_out) = &mut inbound_out {
+ loop {
+ match inbound_out.try_recv() {
+ Ok(data) => {
+ bitbox02::usb_packet::process(&data);
+ }
+ Err(TryRecvError::Disconnected) => {
+ // Drop the outbound channel
+ let _ = self.outbound_in.take();
+ disconnected = true;
+ break;
+ }
+ Err(TryRecvError::Empty) => {
+ break;
+ }
+ }
+ }
+ }
+ if !disconnected {
+ self.inbound_out = inbound_out;
+ }
+ // Send data to TCP Client
+ loop {
+ if let Some(data) = bitbox02::queue::pull_hww() {
+ if let Some(outbound_in) = &mut self.outbound_in {
+ if outbound_in.send(data).is_err() {
+ info!("writer thread died and closed channel");
+ let _ = self.outbound_in.take();
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ // Business logic
+ unsafe { bitbox02_rust_c::workflow::rust_workflow_spin() }
+ bitbox02_rust::async_usb::spin();
+ bitbox02::usb_processing::process_hww();
+ bitbox02::screen::process();
+
+ if let Some(ref mut task) = self.orientation_task {
+ if let Ready(_orientation) = util::bb02_async::spin(task) {
+ ACCEPTING_CONNECTIONS.store(true, Ordering::Relaxed);
+ self.orientation_task = None;
+ }
+ }
+
+ if let Some(window) = &self.window
+ && let Some(canvas) = &mut self.canvas
+ && let Some(screen_id) = self.screen.clone()
+ {
+ // TODO: We should only update texture and redraw in case screen actually changed.
+ // Update opengl texture from "screen_process"
+ let screen_fb = &*SCREEN_FB.lock().unwrap();
+ canvas
+ .update_image(screen_id, ImageSource::try_from(screen_fb).unwrap(), 0, 0)
+ .unwrap();
+
+ window.request_redraw();
+ }
+ }
+ UserEvent::NewConnection(outbound_in, inbound_out) => {
+ self.outbound_in.replace(outbound_in);
+ self.inbound_out.replace(inbound_out);
+ info!("Accepted connection")
+ }
+ }
+ }
+
+ fn resumed(&mut self, event_loop: &ActiveEventLoop) {
+ self.create_window(event_loop, None)
+ .expect("failed to create initial window");
+ self.orientation_task = Some(Box::pin(
+ bitbox02_rust::workflow::orientation_screen::orientation_screen(),
+ ));
+ }
+}
+
+#[derive(Debug)]
+struct AppError(String);
+
+impl Error for AppError {}
+
+impl std::fmt::Display for AppError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.0)
+ }
+}
+
+impl AppError {
+ pub fn new(msg: &str) -> AppError {
+ AppError(msg.into())
+ }
+}
+
+#[derive(Parser)]
+#[command(version, about, long_about = None)]
+struct Args {
+ /// Pre seed the simulated bitbox with empty password and the following bip39 seed phrase "boring mistake dish oyster truth pigeon viable emerge sort crash wire portion cannon couple enact box walk height pull today solid off enable tide"
+ #[arg(long)]
+ preseed: bool,
+}
+
+pub fn main() -> Result<(), Box<dyn Error>> {
+ // Enable debug output with environment variable RUST_LOG=debug
+ let filter = EnvFilter::builder()
+ .with_default_directive(LevelFilter::INFO.into())
+ .from_env_lossy();
+ tracing_subscriber::registry()
+ .with(fmt::layer())
+ .with(filter)
+ .init();
+
+ let args = Args::parse();
+
+ if !init_hww(args.preseed) {
+ return Err(Box::new(AppError::new("Failed to init hww")));
+ }
+ let event_loop = EventLoop::<UserEvent>::with_user_event().build()?;
+ event_loop.set_control_flow(ControlFlow::Poll);
+
+ // "Accept incoming connections" thread
+ thread::spawn({
+ let el_proxy = event_loop.create_proxy();
+ let Ok(listener) = TcpListener::bind("0.0.0.0:15423") else {
+ return Err(Box::new(AppError::new("Failed to bind to address!")));
+ };
+ move || {
+ // Use this counter to ensure that we only connect to a single client.
+ let counter = Arc::new(AtomicU32::new(0));
+
+ for stream in listener.incoming() {
+ if ACCEPTING_CONNECTIONS.load(Ordering::Relaxed)
+ && counter.compare_exchange(0, 2, Ordering::Acquire, Ordering::Relaxed) == Ok(0)
+ {
+ let Ok(stream) = stream else {
+ error!("Error with stream");
+ continue;
+ };
+ let Ok(stream_clone) = stream.try_clone() else {
+ error!("Error with cloning stream");
+ continue;
+ };
+ // Channel for communicating from HWW to client
+ let (outbound_in, outbound_out) = mpsc::channel();
+ // Channel for communicating from Client to HWW
+ let (inbound_in, inbound_out) = mpsc::channel();
+ // Inform the main event loop about the new connection
+ if el_proxy
+ .send_event(UserEvent::NewConnection(outbound_in, inbound_out))
+ .is_err()
+ {
+ // Event loop has quit
+ return;
+ }
+ thread::spawn({
+ let counter = Arc::clone(&counter);
+ move || handle_stream_reader(stream_clone, inbound_in, counter)
+ });
+ thread::spawn({
+ let counter = Arc::clone(&counter);
+ move || handle_stream_writer(stream, outbound_out, counter)
+ });
+ } else {
+ info!("Busy, won't accept new clients",);
+ }
+ }
+ }
+ });
+
+ // Wake up main event loop
+ thread::spawn({
+ let el_proxy = event_loop.create_proxy();
+ move || {
+ loop {
+ if el_proxy.send_event(UserEvent::WakeUp).is_err() {
+ // Event loop has quit
+ return;
+ }
+ std::thread::sleep(Duration::from_micros(5000));
+ }
+ }
+ });
+
+ let mut app = App::default();
+ event_loop.run_app(&mut app)?;
+
+ Ok(())
+}
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.