From 4425706388d06a4256884a59a27a81b05bd3312c Mon Sep 17 00:00:00 2001 From: Nico Burniske Date: Tue, 5 May 2026 09:16:40 -0400 Subject: [PATCH 1/3] ql-api: init --- Cargo.lock | 383 +++++++++++++++++++++++++++++++++++++++- Cargo.toml | 1 + ql-api/Cargo.toml | 20 +++ ql-api/src/app_store.rs | 73 ++++++++ ql-api/src/benchmark.rs | 72 ++++++++ ql-api/src/bootstrap.rs | 22 +++ ql-api/src/codec.rs | 90 ++++++++++ ql-api/src/lib.rs | 36 ++++ 8 files changed, 692 insertions(+), 5 deletions(-) create mode 100644 ql-api/Cargo.toml create mode 100644 ql-api/src/app_store.rs create mode 100644 ql-api/src/benchmark.rs create mode 100644 ql-api/src/bootstrap.rs create mode 100644 ql-api/src/codec.rs create mode 100644 ql-api/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index c1e30d37..71803e2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,12 +26,40 @@ dependencies = [ "memchr", ] +[[package]] +name = "allo-isolate" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "449e356a4864c017286dbbec0e12767ea07efba29e3b7d984194c2a7ff3c4550" +dependencies = [ + "anyhow", + "atomic", + "backtrace", +] + [[package]] name = "android-tzdata" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" +[[package]] +name = "android_log-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d" + +[[package]] +name = "android_logger" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb4e440d04be07da1f1bf44fb4495ebd58669372fe0cffa6e48595ac5bd88a3" +dependencies = [ + "android_log-sys", + "env_filter 0.1.4", + "log", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -91,6 +119,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + [[package]] name = "async-channel" version = "2.5.0" @@ -103,6 +137,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "atomic" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" + [[package]] name = "autocfg" version = "1.5.0" @@ -175,6 +215,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "build-target" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "832133bbabbbaa9fbdba793456a2827627a7d2b8fb96032fa1e7666d7895832b" + [[package]] name = "bumpalo" version = "3.19.0" @@ -224,6 +270,12 @@ dependencies = [ "syn", ] +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.10.1" @@ -259,6 +311,33 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + [[package]] name = "colorchoice" version = "1.0.5" @@ -287,6 +366,16 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + [[package]] name = "consts" version = "1.0.0" @@ -340,6 +429,28 @@ dependencies = [ "typenum", ] +[[package]] +name = "dart-sys" +version = "4.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57967e4b200d767d091b961d6ab42cc7d0cc14fe9e052e75d0d3cf9eb732d895" +dependencies = [ + "cc", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "dcbor" version = "0.23.3" @@ -354,6 +465,17 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "delegate-attr" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51aac4c99b2e6775164b412ea33ae8441b2fde2dbf05a20bc0052a63d08c475b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "diatomic-waker" version = "0.2.3" @@ -376,6 +498,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" +[[package]] +name = "env_filter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" +dependencies = [ + "log", + "regex", +] + [[package]] name = "env_filter" version = "1.0.1" @@ -394,7 +526,7 @@ checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a" dependencies = [ "anstream", "anstyle", - "env_filter", + "env_filter 1.0.1", "jiff", "log", ] @@ -443,23 +575,101 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "flutter_rust_bridge" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde126295b2acc5f0a712e265e91b6fdc0ed38767496483e592ae7134db83725" +dependencies = [ + "allo-isolate", + "android_logger", + "anyhow", + "build-target", + "bytemuck", + "byteorder", + "console_error_panic_hook", + "dart-sys", + "delegate-attr", + "flutter_rust_bridge_macros", + "futures", + "js-sys", + "lazy_static", + "log", + "oslog", + "portable-atomic", + "threadpool", + "tokio", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "flutter_rust_bridge_macros" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f0420326b13675321b194928bb7830043b68cf8b810e1c651285c747abb080" +dependencies = [ + "hex", + "md-5", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-lite" @@ -474,6 +684,46 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + [[package]] name = "generator" version = "0.8.8" @@ -538,6 +788,12 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "hashbrown" version = "0.15.5" @@ -587,6 +843,12 @@ dependencies = [ "uuid", ] +[[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" @@ -791,6 +1053,15 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.29" @@ -819,6 +1090,16 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + [[package]] name = "memchr" version = "2.7.5" @@ -902,6 +1183,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "object" version = "0.36.7" @@ -929,6 +1220,17 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ce411919553d3f9fa53a0880544cda985a112117a0444d5ff1e870a893d6ea" +[[package]] +name = "oslog" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d2043d1f61d77cb2f4b1f7b7b2295f40507f5f8e9d1c8bf10a1ca5f97a3969" +dependencies = [ + "cc", + "dashmap", + "log", +] + [[package]] name = "parking" version = "2.2.1" @@ -938,6 +1240,19 @@ dependencies = [ "loom", ] +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link 0.2.1", +] + [[package]] name = "paste" version = "1.0.15" @@ -1050,6 +1365,17 @@ dependencies = [ "syn", ] +[[package]] +name = "ql-api" +version = "0.1.0" +dependencies = [ + "bytes", + "ciborium", + "flutter_rust_bridge", + "ql-rpc", + "serde", +] + [[package]] name = "ql-fsm" version = "0.1.0" @@ -1166,6 +1492,15 @@ dependencies = [ "rand_core", ] +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" version = "1.12.3" @@ -1283,6 +1618,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde" version = "1.0.228" @@ -1428,6 +1769,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + [[package]] name = "tinyvec" version = "1.10.0" @@ -1656,6 +2006,19 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.100" @@ -1688,6 +2051,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "windows-core" version = "0.61.2" diff --git a/Cargo.toml b/Cargo.toml index 83ac135d..14dfbaae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ resolver = "2" members = [ "backup-shard", "btp", + "ql-api", "ql-fsm", "ql-rpc", "ql-runtime", diff --git a/ql-api/Cargo.toml b/ql-api/Cargo.toml new file mode 100644 index 00000000..9188a3a8 --- /dev/null +++ b/ql-api/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "ql-api" +version = "0.1.0" +edition = "2021" +description = "KeyOS QLv2 RPC protocol" +license = "Proprietary" + + +[dependencies] +ql-rpc = { workspace = true } +bytes = { workspace = true } +ciborium = "0.2.2" +flutter_rust_bridge = { version = "=2.11.1", optional = true } +serde = { version = "1.0", features = ["derive"] } + +[features] +frb = ["flutter_rust_bridge"] + +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(frb_expand)'] } diff --git a/ql-api/src/app_store.rs b/ql-api/src/app_store.rs new file mode 100644 index 00000000..92faf038 --- /dev/null +++ b/ql-api/src/app_store.rs @@ -0,0 +1,73 @@ +use ql_rpc::{Download, Request, RouteId}; + +use crate::{codec::rpc, Error, Route}; + +rpc! { + #[derive(Copy)] + pub struct AppVersion(pub u32, pub u32, pub u32); +} + +// +// LIST APPS +// + +pub struct ListApps; + +rpc! { + pub struct ListAppsRequest { + // + } +} + +rpc! { + pub struct ListAppsResponse { + pub apps: Vec, + } +} + +rpc! { + pub struct AppDetails { + pub name: String, + pub icon: String, // SVG? + pub developer_id: Vec, + pub app_id: Vec, + } +} + +impl Request for ListApps { + const ROUTE: RouteId = Route::ListApps.id(); + type Error = Error; + type Request = ListAppsRequest; + type Response = ListAppsResponse; +} + +// +// APP DOWNLOAD +// +// download a single tar file that contains: +// - app.elf +// - manifest.json + +pub struct AppDownload; + +rpc! { + pub struct AppDownloadRequest { + pub version: Option, + } +} + +rpc! { + pub struct AppDownloadHeader { + pub version: String, + } +} + +impl Download for AppDownload { + const ROUTE: RouteId = Route::AppDownload.id(); + + type Error = Error; + + type Request = AppDownloadRequest; + + type ResponseHeader = AppDownloadHeader; +} diff --git a/ql-api/src/benchmark.rs b/ql-api/src/benchmark.rs new file mode 100644 index 00000000..0b6504d4 --- /dev/null +++ b/ql-api/src/benchmark.rs @@ -0,0 +1,72 @@ +use ql_rpc::{request::Request, Download, RouteId, Subscription}; + +use crate::{codec::rpc, Error, Route}; + +// Echo request-response benchmark +pub struct Echo; + +impl Request for Echo { + type Error = Error; + type Request = EchoRequest; + type Response = EchoResponse; + + const ROUTE: RouteId = Route::Echo.id(); +} + +rpc! { + pub struct EchoRequest { + pub message: String, + } +} + +rpc! { + pub struct EchoResponse { + pub message: String, + } +} + +// Byte-stream subscription benchmark +pub struct BytesBenchmark; + +impl Subscription for BytesBenchmark { + type Error = Error; + type Event = BenchmarkEvent; + type Request = BenchmarkRequest; + + const ROUTE: ql_rpc::RouteId = Route::BytesBenchmark.id(); +} + +rpc! { + pub struct BenchmarkRequest { + pub length: u32, + } +} + +rpc! { + pub struct BenchmarkEvent { + pub bytes: Vec, + } +} + +// Raw-body download benchmark +pub struct DownloadBenchmark; + +impl Download for DownloadBenchmark { + type Error = Error; + type Request = DownloadBenchmarkRequest; + type ResponseHeader = DownloadBenchmarkHeader; + + const ROUTE: RouteId = Route::DownloadBenchmark.id(); +} + +rpc! { + pub struct DownloadBenchmarkRequest { + pub length: u64, + } +} + +rpc! { + pub struct DownloadBenchmarkHeader { + pub hash: Vec, + } +} diff --git a/ql-api/src/bootstrap.rs b/ql-api/src/bootstrap.rs new file mode 100644 index 00000000..9f050d4b --- /dev/null +++ b/ql-api/src/bootstrap.rs @@ -0,0 +1,22 @@ +use crate::{codec::rpc, Error, Route}; + +pub struct BootstrapQlv1; + +impl ql_rpc::Request for BootstrapQlv1 { + const ROUTE: ql_rpc::RouteId = Route::BootstrapQlv1.id(); + type Error = Error; + type Request = BootstrapRequest; + type Response = BootstrapResponse; +} + +rpc! { + pub struct BootstrapRequest { + pub xid_document: Vec, + } +} + +rpc! { + pub struct BootstrapResponse { + pub xid_document: Vec, + } +} diff --git a/ql-api/src/codec.rs b/ql-api/src/codec.rs new file mode 100644 index 00000000..7d5323f2 --- /dev/null +++ b/ql-api/src/codec.rs @@ -0,0 +1,90 @@ +use std::io; + +use bytes::{Buf, BufMut}; +use serde::{de::DeserializeOwned, Serialize}; + +use crate::Error; + +macro_rules! impl_codec { + ($ty:ty) => { + impl ql_rpc::RpcCodec for $ty { + type Error = crate::Error; + + fn encode_value(&self, out: &mut B) { + $crate::codec::encode_cbor(self, out); + } + + fn decode_value(bytes: &mut B) -> Result { + $crate::codec::decode_cbor(bytes) + } + } + }; +} + +pub(crate) use impl_codec; + +macro_rules! rpc { + ($(#[$attr:meta])* $vis:vis struct $name:ident;) => { + compile_error!("rpc! does not support unit structs"); + }; + ($(#[$attr:meta])* $vis:vis struct $name:ident $($body:tt)+) => { + #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] + $(#[$attr])* + #[cfg_attr(feature = "frb", flutter_rust_bridge::frb(non_opaque))] + $vis struct $name $($body)+ + + $crate::codec::impl_codec!($name); + }; +} + +pub(crate) use rpc; + +pub fn encode_cbor(value: &T, out: &mut B) +where + T: Serialize, + B: BufMut + ?Sized, +{ + ciborium::ser::into_writer(value, BufMutWriter(out)) + .expect("CBOR serialization to BufMut failed"); +} + +pub fn decode_cbor(bytes: &mut B) -> Result +where + T: DeserializeOwned, + B: Buf, +{ + let value = ciborium::de::from_reader(BufReader(bytes))?; + bytes.advance(bytes.remaining()); + Ok(value) +} + +struct BufMutWriter<'a, B: BufMut + ?Sized>(&'a mut B); + +impl io::Write for BufMutWriter<'_, B> { + fn write(&mut self, data: &[u8]) -> io::Result { + if self.0.remaining_mut() < data.len() { + return Err(io::ErrorKind::WriteZero.into()); + } + + self.0.put_slice(data); + Ok(data.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +struct BufReader<'a, B: Buf>(&'a mut B); + +impl io::Read for BufReader<'_, B> { + fn read(&mut self, out: &mut [u8]) -> io::Result { + let len = out.len().min(self.0.remaining()); + if len == 0 { + return Ok(0); + } + + self.0.copy_to_slice(&mut out[..len]); + Ok(len) + } +} diff --git a/ql-api/src/lib.rs b/ql-api/src/lib.rs new file mode 100644 index 00000000..c280eb4e --- /dev/null +++ b/ql-api/src/lib.rs @@ -0,0 +1,36 @@ +// 1. list catalog of apps (request/response) +// 2. get app by id +// 3. download version? + +mod codec; +mod app_store; +mod benchmark; +mod bootstrap; + +pub use app_store::*; +pub use benchmark::*; +pub use bootstrap::*; + +pub type Error = ciborium::de::Error; + +#[repr(u32)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Route { + // setup + BootstrapQlv1 = 100, + + // app store + ListApps = 200, + AppDownload = 201, + + // debug + Echo = 1000, + BytesBenchmark = 1001, + DownloadBenchmark = 1002, +} + +impl Route { + pub const fn id(self) -> ql_rpc::RouteId { + ql_rpc::RouteId::from_u32(self as u32) + } +} From 04722f2ba5ecfc9248db34d520a4be548458a61b Mon Sep 17 00:00:00 2001 From: Nico Burniske Date: Thu, 14 May 2026 09:28:33 -0400 Subject: [PATCH 2/3] ql-api: migrate all legacy apis --- ql-api/src/app_store.rs | 19 +++-- ql-api/src/backup.rs | 167 +++++++++++++++++++++++++++++++++++++++ ql-api/src/benchmark.rs | 27 +++---- ql-api/src/bitcoin.rs | 78 ++++++++++++++++++ ql-api/src/bootstrap.rs | 22 ------ ql-api/src/codec.rs | 34 -------- ql-api/src/firmware.rs | 87 ++++++++++++++++++++ ql-api/src/fx.rs | 84 ++++++++++++++++++++ ql-api/src/lib.rs | 78 ++++++++++++++---- ql-api/src/macros.rs | 81 +++++++++++++++++++ ql-api/src/onboarding.rs | 30 +++++++ ql-api/src/scv.rs | 44 +++++++++++ ql-api/src/status.rs | 52 ++++++++++++ ql-api/src/timezone.rs | 20 +++++ 14 files changed, 725 insertions(+), 98 deletions(-) create mode 100644 ql-api/src/backup.rs create mode 100644 ql-api/src/bitcoin.rs delete mode 100644 ql-api/src/bootstrap.rs create mode 100644 ql-api/src/firmware.rs create mode 100644 ql-api/src/fx.rs create mode 100644 ql-api/src/macros.rs create mode 100644 ql-api/src/onboarding.rs create mode 100644 ql-api/src/scv.rs create mode 100644 ql-api/src/status.rs create mode 100644 ql-api/src/timezone.rs diff --git a/ql-api/src/app_store.rs b/ql-api/src/app_store.rs index 92faf038..629b6a80 100644 --- a/ql-api/src/app_store.rs +++ b/ql-api/src/app_store.rs @@ -1,6 +1,6 @@ -use ql_rpc::{Download, Request, RouteId}; +use ql_rpc::{Download, Request}; -use crate::{codec::rpc, Error, Route}; +use crate::{route, Error}; rpc! { #[derive(Copy)] @@ -11,8 +11,6 @@ rpc! { // LIST APPS // -pub struct ListApps; - rpc! { pub struct ListAppsRequest { // @@ -34,8 +32,7 @@ rpc! { } } -impl Request for ListApps { - const ROUTE: RouteId = Route::ListApps.id(); +impl Request for route::ListApps { type Error = Error; type Request = ListAppsRequest; type Response = ListAppsResponse; @@ -48,8 +45,6 @@ impl Request for ListApps { // - app.elf // - manifest.json -pub struct AppDownload; - rpc! { pub struct AppDownloadRequest { pub version: Option, @@ -62,12 +57,16 @@ rpc! { } } -impl Download for AppDownload { - const ROUTE: RouteId = Route::AppDownload.id(); +rpc! { + pub struct AppDownloadPartHeader {} +} +impl Download for route::AppDownload { type Error = Error; type Request = AppDownloadRequest; type ResponseHeader = AppDownloadHeader; + + type PartHeader = AppDownloadPartHeader; } diff --git a/ql-api/src/backup.rs b/ql-api/src/backup.rs new file mode 100644 index 00000000..e2c32a46 --- /dev/null +++ b/ql-api/src/backup.rs @@ -0,0 +1,167 @@ +use ql_rpc::{Download, Notification, Request, Upload}; + +use crate::{route, Error}; + +rpc! { + pub struct Shard(pub Vec); +} + +rpc! { + pub struct SeedFingerprint(pub [u8; 32]); +} + +rpc! { + pub struct BackupShardRequest { + pub shard: Shard, + } +} + +rpc! { + pub enum BackupShardResponse { + Success, + Error { error: String }, + } +} + +impl Request for route::BackupShard { + type Error = Error; + type Request = BackupShardRequest; + type Response = BackupShardResponse; +} + +rpc! { + pub struct RestoreShardRequest { + pub seed_fingerprint: SeedFingerprint, + pub timestamp: Option, + } +} + +rpc! { + pub enum RestoreShardResponse { + Success { shard: Shard }, + Error { error: String }, + NotFound, + } +} + +impl Request for route::RestoreShard { + type Error = Error; + type Request = RestoreShardRequest; + type Response = RestoreShardResponse; +} + +rpc! { + pub struct EnvoyMagicBackupEnabledRequest {} +} + +rpc! { + pub struct EnvoyMagicBackupEnabledResponse { + pub enabled: bool, + } +} + +impl Request for route::EnvoyMagicBackupEnabled { + type Error = Error; + type Request = EnvoyMagicBackupEnabledRequest; + type Response = EnvoyMagicBackupEnabledResponse; +} + +rpc! { + pub struct PassportMagicBackupEnabledPayload { + pub enabled: bool, + pub seed_fingerprint: SeedFingerprint, + } +} + +impl Notification for route::PassportMagicBackupEnabled { + type Error = Error; + type Payload = PassportMagicBackupEnabledPayload; +} + +rpc! { + pub struct PassportMagicBackupStatusRequest { + pub seed_fingerprint: SeedFingerprint, + pub timestamp: Option, + } +} + +rpc! { + pub struct PassportMagicBackupStatusResponse { + pub shard_backup_found: bool, + } +} + +impl Request for route::PassportMagicBackupStatus { + type Error = Error; + type Request = PassportMagicBackupStatusRequest; + type Response = PassportMagicBackupStatusResponse; +} + +rpc! { + pub struct UploadMagicBackupRequest { + pub seed_fingerprint: SeedFingerprint, + pub total_size: Option, + pub hash: [u8; 32], + } +} + +rpc! { + pub enum UploadMagicBackupResult { + Success, + Error { error: String }, + } +} + +rpc! { + pub struct UploadMagicBackupPartHeader {} +} + +impl Upload for route::UploadMagicBackup { + type Error = Error; + type Request = UploadMagicBackupRequest; + type PartHeader = UploadMagicBackupPartHeader; + type Response = UploadMagicBackupResult; +} + +rpc! { + pub struct DownloadMagicBackupRequest { + pub seed_fingerprint: SeedFingerprint, + } +} + +rpc! { + pub enum DownloadMagicBackupHeader { + Found(BackupMetadata), + NotFound, + Error { error: String }, + } +} + +rpc! { + pub struct BackupMetadata { + pub total_size: Option, + } +} + +rpc! { + pub struct DownloadMagicBackupPartHeader {} +} + +impl Download for route::DownloadMagicBackup { + type Error = Error; + type Request = DownloadMagicBackupRequest; + type ResponseHeader = DownloadMagicBackupHeader; + type PartHeader = DownloadMagicBackupPartHeader; +} + +rpc! { + pub enum RestoreMagicBackupResult { + Success, + Error { error: String }, + } +} + +impl Notification for route::RestoreMagicBackupComplete { + type Error = Error; + type Payload = RestoreMagicBackupResult; +} diff --git a/ql-api/src/benchmark.rs b/ql-api/src/benchmark.rs index 0b6504d4..3409899c 100644 --- a/ql-api/src/benchmark.rs +++ b/ql-api/src/benchmark.rs @@ -1,16 +1,12 @@ -use ql_rpc::{request::Request, Download, RouteId, Subscription}; +use ql_rpc::{request::Request, Download, Subscription}; -use crate::{codec::rpc, Error, Route}; +use crate::{route, Error}; // Echo request-response benchmark -pub struct Echo; - -impl Request for Echo { +impl Request for route::Echo { type Error = Error; type Request = EchoRequest; type Response = EchoResponse; - - const ROUTE: RouteId = Route::Echo.id(); } rpc! { @@ -26,14 +22,10 @@ rpc! { } // Byte-stream subscription benchmark -pub struct BytesBenchmark; - -impl Subscription for BytesBenchmark { +impl Subscription for route::BytesBenchmark { type Error = Error; type Event = BenchmarkEvent; type Request = BenchmarkRequest; - - const ROUTE: ql_rpc::RouteId = Route::BytesBenchmark.id(); } rpc! { @@ -49,14 +41,11 @@ rpc! { } // Raw-body download benchmark -pub struct DownloadBenchmark; - -impl Download for DownloadBenchmark { +impl Download for route::DownloadBenchmark { type Error = Error; type Request = DownloadBenchmarkRequest; type ResponseHeader = DownloadBenchmarkHeader; - - const ROUTE: RouteId = Route::DownloadBenchmark.id(); + type PartHeader = DownloadBenchmarkPartHeader; } rpc! { @@ -70,3 +59,7 @@ rpc! { pub hash: Vec, } } + +rpc! { + pub struct DownloadBenchmarkPartHeader {} +} diff --git a/ql-api/src/bitcoin.rs b/ql-api/src/bitcoin.rs new file mode 100644 index 00000000..9235247d --- /dev/null +++ b/ql-api/src/bitcoin.rs @@ -0,0 +1,78 @@ +use ql_rpc::{Notification, Request}; + +use crate::{route, Error}; + +rpc! { + pub struct SignPsbtRequest { + pub account_id: String, + pub psbt: Vec, + } +} + +rpc! { + pub enum SignPsbtResponse { + Signed { psbt: Vec }, + Rejected, + Error { error: String }, + } +} + +impl Request for route::SignPsbt { + type Error = Error; + type Request = SignPsbtRequest; + type Response = SignPsbtResponse; +} + +rpc! { + pub struct BroadcastTransactionRequest { + pub account_id: String, + pub psbt: Vec, + } +} + +rpc! { + pub enum BroadcastTransactionResponse { + Broadcast { txid: String }, + Error { error: String }, + } +} + +impl Request for route::BroadcastTransaction { + type Error = Error; + type Request = BroadcastTransactionRequest; + type Response = BroadcastTransactionResponse; +} + +rpc! { + pub struct AccountUpdatePayload { + pub account_id: String, + pub update: Vec, + } +} + +impl Notification for route::AccountUpdate { + type Error = Error; + type Payload = AccountUpdatePayload; +} + +rpc! { + pub struct ActiveSeedFingerprint { + pub fingerprint: String, + pub has_passphrase: bool, + } +} + +impl Notification for route::PassportActiveSeedFingerprint { + type Error = Error; + type Payload = ActiveSeedFingerprint; +} + +rpc! { + pub struct PassportActiveSeedFingerprintRequest {} +} + +impl Request for route::GetPassportActiveSeedFingerprint { + type Error = Error; + type Request = PassportActiveSeedFingerprintRequest; + type Response = ActiveSeedFingerprint; +} diff --git a/ql-api/src/bootstrap.rs b/ql-api/src/bootstrap.rs deleted file mode 100644 index 9f050d4b..00000000 --- a/ql-api/src/bootstrap.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::{codec::rpc, Error, Route}; - -pub struct BootstrapQlv1; - -impl ql_rpc::Request for BootstrapQlv1 { - const ROUTE: ql_rpc::RouteId = Route::BootstrapQlv1.id(); - type Error = Error; - type Request = BootstrapRequest; - type Response = BootstrapResponse; -} - -rpc! { - pub struct BootstrapRequest { - pub xid_document: Vec, - } -} - -rpc! { - pub struct BootstrapResponse { - pub xid_document: Vec, - } -} diff --git a/ql-api/src/codec.rs b/ql-api/src/codec.rs index 7d5323f2..eda25685 100644 --- a/ql-api/src/codec.rs +++ b/ql-api/src/codec.rs @@ -5,40 +5,6 @@ use serde::{de::DeserializeOwned, Serialize}; use crate::Error; -macro_rules! impl_codec { - ($ty:ty) => { - impl ql_rpc::RpcCodec for $ty { - type Error = crate::Error; - - fn encode_value(&self, out: &mut B) { - $crate::codec::encode_cbor(self, out); - } - - fn decode_value(bytes: &mut B) -> Result { - $crate::codec::decode_cbor(bytes) - } - } - }; -} - -pub(crate) use impl_codec; - -macro_rules! rpc { - ($(#[$attr:meta])* $vis:vis struct $name:ident;) => { - compile_error!("rpc! does not support unit structs"); - }; - ($(#[$attr:meta])* $vis:vis struct $name:ident $($body:tt)+) => { - #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] - $(#[$attr])* - #[cfg_attr(feature = "frb", flutter_rust_bridge::frb(non_opaque))] - $vis struct $name $($body)+ - - $crate::codec::impl_codec!($name); - }; -} - -pub(crate) use rpc; - pub fn encode_cbor(value: &T, out: &mut B) where T: Serialize, diff --git a/ql-api/src/firmware.rs b/ql-api/src/firmware.rs new file mode 100644 index 00000000..bf0b49e0 --- /dev/null +++ b/ql-api/src/firmware.rs @@ -0,0 +1,87 @@ +use ql_rpc::{Download, Notification, Request}; + +use crate::{route, Error}; + +rpc! { + pub struct FirmwareUpdateCheckRequest { + pub current_version: String, + } +} + +rpc! { + pub enum FirmwareUpdateCheckResponse { + Available(FirmwareUpdateAvailable), + NotAvailable, + } +} + +rpc! { + pub struct FirmwareUpdateAvailable { + pub version: String, + pub changelog: String, + pub timestamp: u32, + pub total_size: u32, + pub patch_count: u8, + } +} + +impl Request for route::FirmwareUpdateCheck { + type Error = Error; + type Request = FirmwareUpdateCheckRequest; + type Response = FirmwareUpdateCheckResponse; +} + +rpc! { + pub struct FirmwareDownloadRequest { + pub current_version: String, + } +} + +rpc! { + pub enum FirmwareDownloadHeader { + Available(FirmwareUpdateAvailable), + NotAvailable, + Error { error: String }, + } +} + +rpc! { + pub struct FirmwareDownloadPartHeader { + pub patch_name: String, + pub size_bytes: u64, + } +} + +impl Download for route::FirmwareDownload { + type Error = Error; + type Request = FirmwareDownloadRequest; + type ResponseHeader = FirmwareDownloadHeader; + type PartHeader = FirmwareDownloadPartHeader; +} + +rpc! { + pub enum FirmwareInstallEvent { + Installing, + Rebooting, + Success { + installed_version: String, + }, + Error { + error: String, + stage: InstallErrorStage, + }, + } +} + +rpc! { + pub enum InstallErrorStage { + Download, + Verify, + Install, + } +} + +impl Notification for route::FirmwareInstallStatus { + type Error = Error; + type Payload = FirmwareInstallEvent; +} diff --git a/ql-api/src/fx.rs b/ql-api/src/fx.rs new file mode 100644 index 00000000..260cf3d4 --- /dev/null +++ b/ql-api/src/fx.rs @@ -0,0 +1,84 @@ +use ql_rpc::{Notification, Request, Subscription}; + +use crate::{route, Error}; + +rpc! { + #[PartialEq] + pub struct ExchangeRate { + pub currency_code: String, + pub rate: f32, + pub timestamp: u64, + } +} + +rpc! { + #[PartialEq] + pub struct PricePoint { + pub rate: f32, + pub timestamp: u64, + } +} + +rpc! { + pub struct ExchangeRateHistoryRequest { + pub currency_code: String, + } +} + +rpc! { + #[PartialEq] + pub struct ExchangeRateHistory { + pub history: Vec, + pub currency_code: String, + } +} + +impl Notification for route::ExchangeRateUpdate { + type Error = Error; + type Payload = ExchangeRate; +} + +rpc! { + pub struct ExchangeRateSubscriptionRequest { + pub currency_code: String, + } +} + +impl Subscription for route::ExchangeRateSubscription { + type Error = Error; + type Request = ExchangeRateSubscriptionRequest; + type Event = ExchangeRate; +} + +impl Request for route::ExchangeRateHistory { + type Error = Error; + type Request = ExchangeRateHistoryRequest; + type Response = ExchangeRateHistory; +} + +rpc! { + pub struct PassportFiatPreferencePayload { + pub currency_code: String, + } +} + +impl Notification for route::PassportFiatPreference { + type Error = Error; + type Payload = PassportFiatPreferencePayload; +} + +rpc! { + pub struct PassportFiatPreferenceRequest {} +} + +rpc! { + pub struct PassportFiatPreferenceResponse { + pub currency_code: String, + } +} + +impl Request for route::PassportFiatPreferenceRequest { + type Error = Error; + type Request = PassportFiatPreferenceRequest; + type Response = PassportFiatPreferenceResponse; +} diff --git a/ql-api/src/lib.rs b/ql-api/src/lib.rs index c280eb4e..591ab2c2 100644 --- a/ql-api/src/lib.rs +++ b/ql-api/src/lib.rs @@ -2,35 +2,83 @@ // 2. get app by id // 3. download version? -mod codec; +#[macro_use] +mod macros; + mod app_store; +mod backup; mod benchmark; -mod bootstrap; +mod bitcoin; +mod codec; +mod firmware; +mod fx; +mod onboarding; +mod scv; +mod status; +mod timezone; pub use app_store::*; +pub use backup::*; pub use benchmark::*; -pub use bootstrap::*; +pub use bitcoin::*; +pub use firmware::*; +pub use fx::*; +pub use onboarding::*; +pub use scv::*; +pub use status::*; +pub use timezone::*; pub type Error = ciborium::de::Error; -#[repr(u32)] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum Route { - // setup - BootstrapQlv1 = 100, - +routes! { // app store ListApps = 200, AppDownload = 201, + // status and presence + DeviceStatus = 300, + EnvoyStatus = 301, + Timezone = 303, + + // status and presence + PassportDetails = 402, + PassportDetailsUpdated = 403, + + // security and onboarding + SecurityCheck = 500, + OnboardingStatus = 501, + + // firmware + FirmwareUpdateCheck = 600, + FirmwareDownload = 601, + FirmwareInstallStatus = 602, + + // market data + ExchangeRateUpdate = 700, + ExchangeRateSubscription = 701, + ExchangeRateHistory = 702, + PassportFiatPreference = 703, + PassportFiatPreferenceRequest = 704, + + // bitcoin and wallet + SignPsbt = 800, + BroadcastTransaction = 801, + AccountUpdate = 802, + PassportActiveSeedFingerprint = 803, + GetPassportActiveSeedFingerprint = 804, + + // backup + BackupShard = 900, + RestoreShard = 901, + EnvoyMagicBackupEnabled = 902, + PassportMagicBackupEnabled = 903, + PassportMagicBackupStatus = 904, + UploadMagicBackup = 905, + DownloadMagicBackup = 906, + RestoreMagicBackupComplete = 907, + // debug Echo = 1000, BytesBenchmark = 1001, DownloadBenchmark = 1002, } - -impl Route { - pub const fn id(self) -> ql_rpc::RouteId { - ql_rpc::RouteId::from_u32(self as u32) - } -} diff --git a/ql-api/src/macros.rs b/ql-api/src/macros.rs new file mode 100644 index 00000000..8fba9c97 --- /dev/null +++ b/ql-api/src/macros.rs @@ -0,0 +1,81 @@ +macro_rules! impl_codec { + ($ty:ty) => { + impl ql_rpc::RpcCodec for $ty { + type Error = crate::Error; + + fn encode_value(&self, out: &mut B) { + $crate::codec::encode_cbor(self, out); + } + + fn decode_value( + bytes: &mut B, + ) -> Result::Error> { + $crate::codec::decode_cbor(bytes) + } + } + }; +} + +macro_rules! routes { + ($($route:ident = $id:literal,)*) => { + #[repr(u32)] + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub enum Route { + $($route = $id,)* + } + + impl Route { + pub const fn id(self) -> ql_rpc::RouteId { + ql_rpc::RouteId::from_u32(self as u32) + } + } + + pub mod route { + $( + #[derive(Debug)] + pub struct $route; + + impl ql_rpc::Route for $route { + const ROUTE: ql_rpc::RouteId = super::Route::$route.id(); + } + )* + } + }; +} + +macro_rules! rpc { + ($(#[$attr:meta])* $vis:vis struct $name:ident;) => { + compile_error!("rpc! does not support unit structs"); + }; + (#[PartialEq] $(#[$attr:meta])* $vis:vis struct $name:ident $($body:tt)+) => { + rpc_item!(PartialEq; $(#[$attr])* $vis struct $name $($body)+); + }; + ($(#[$attr:meta])* $vis:vis struct $name:ident $($body:tt)+) => { + rpc_item!(Eq; $(#[$attr])* $vis struct $name $($body)+); + }; + (#[PartialEq] $(#[$attr:meta])* $vis:vis enum $name:ident $($body:tt)+) => { + rpc_item!(PartialEq; $(#[$attr])* $vis enum $name $($body)+); + }; + ($(#[$attr:meta])* $vis:vis enum $name:ident $($body:tt)+) => { + rpc_item!(Eq; $(#[$attr])* $vis enum $name $($body)+); + }; +} + +macro_rules! rpc_item { + (PartialEq; $(#[$attr:meta])* $vis:vis $kind:ident $name:ident $($body:tt)+) => { + #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] + $(#[$attr])* + #[cfg_attr(feature = "frb", flutter_rust_bridge::frb(non_opaque))] + $vis $kind $name $($body)+ + + impl_codec!($name); + }; + (Eq; $(#[$attr:meta])* $vis:vis $kind:ident $name:ident $($body:tt)+) => { + #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] + $(#[$attr])* + #[cfg_attr(feature = "frb", flutter_rust_bridge::frb(non_opaque))] + $vis $kind $name $($body)+ + + impl_codec!($name); + }; +} diff --git a/ql-api/src/onboarding.rs b/ql-api/src/onboarding.rs new file mode 100644 index 00000000..24ce0263 --- /dev/null +++ b/ql-api/src/onboarding.rs @@ -0,0 +1,30 @@ +use ql_rpc::Notification; + +use crate::{route, Error}; + +rpc! { + pub enum OnboardingState { + SecurityChecked, + SecurityCheckFailed, + FirmwareUpdateScreen, + SecuringDevice, + DeviceSecured, + WalletCreationScreen, + CreatingWallet, + WalletCreated, + MagicBackupScreen, + CreatingMagicBackup, + MagicBackupCreated, + CreatingManualBackup, + CreatingKeycardBackup, + WritingDownSeedWords, + ConnectingWallet, + WalletConnected, + Completed, + } +} + +impl Notification for route::OnboardingStatus { + type Error = Error; + type Payload = OnboardingState; +} diff --git a/ql-api/src/scv.rs b/ql-api/src/scv.rs new file mode 100644 index 00000000..e4d60761 --- /dev/null +++ b/ql-api/src/scv.rs @@ -0,0 +1,44 @@ +use ql_rpc::Duplex; + +use crate::{route, Error}; + +rpc! { + pub struct ChallengeRequest { + pub data: Vec, + } +} + +rpc! { + pub enum ChallengeResponseResult { + Success { data: Vec }, + Error { error: String }, + } +} + +rpc! { + pub enum VerificationResult { + Success, + Error { error: String }, + Failure, + } +} + +rpc! { + pub enum SecurityCheckRemoteEvent { + ChallengeRequest(ChallengeRequest), + VerificationResult(VerificationResult), + } +} + +rpc! { + pub enum SecurityCheckPassportEvent { + Start, + ChallengeResponse(ChallengeResponseResult), + } +} + +impl Duplex for route::SecurityCheck { + type Error = Error; + type InitiatorEvent = SecurityCheckPassportEvent; + type ResponderEvent = SecurityCheckRemoteEvent; +} diff --git a/ql-api/src/status.rs b/ql-api/src/status.rs new file mode 100644 index 00000000..8a1ba269 --- /dev/null +++ b/ql-api/src/status.rs @@ -0,0 +1,52 @@ +use ql_rpc::{Notification, Request}; + +use crate::{route, Error}; + +rpc! { + pub enum PassportModel { + Gen1, + Gen2, + Prime, + } +} + +rpc! { + pub struct PassportFirmwareVersion(pub String); +} + +rpc! { + pub struct PassportSerial(pub String); +} + +rpc! { + pub enum PassportColor { + Light, + Dark, + } +} + +rpc! { + pub struct PassportDetailsRequest {} +} + +rpc! { + pub struct PassportDetailsResponse { + pub device_name: String, + pub model: PassportModel, + pub firmware_version: PassportFirmwareVersion, + pub serial: PassportSerial, + pub color: PassportColor, + pub onboarding_complete: bool, + } +} + +impl Request for route::PassportDetails { + type Error = Error; + type Request = PassportDetailsRequest; + type Response = PassportDetailsResponse; +} + +impl Notification for route::PassportDetailsUpdated { + type Error = Error; + type Payload = PassportDetailsResponse; +} diff --git a/ql-api/src/timezone.rs b/ql-api/src/timezone.rs new file mode 100644 index 00000000..4a44b924 --- /dev/null +++ b/ql-api/src/timezone.rs @@ -0,0 +1,20 @@ +use ql_rpc::Request; + +use crate::{route, Error}; + +rpc! { + pub struct TimezoneRequest {} +} + +rpc! { + pub struct TimezoneResponse { + pub offset_minutes: i32, + pub zone: String, + } +} + +impl Request for route::Timezone { + type Error = Error; + type Request = TimezoneRequest; + type Response = TimezoneResponse; +} From 01aea9fcd73937e7f333fd4d69136fd05165c517 Mon Sep 17 00:00:00 2001 From: Nico Burniske Date: Wed, 20 May 2026 09:56:20 -0400 Subject: [PATCH 3/3] ql-api: current time --- ql-api/src/lib.rs | 5 +++-- ql-api/src/{timezone.rs => time.rs} | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) rename ql-api/src/{timezone.rs => time.rs} (54%) diff --git a/ql-api/src/lib.rs b/ql-api/src/lib.rs index 591ab2c2..90c24e7e 100644 --- a/ql-api/src/lib.rs +++ b/ql-api/src/lib.rs @@ -15,7 +15,7 @@ mod fx; mod onboarding; mod scv; mod status; -mod timezone; +mod time; pub use app_store::*; pub use backup::*; @@ -26,7 +26,7 @@ pub use fx::*; pub use onboarding::*; pub use scv::*; pub use status::*; -pub use timezone::*; +pub use time::*; pub type Error = ciborium::de::Error; @@ -39,6 +39,7 @@ routes! { DeviceStatus = 300, EnvoyStatus = 301, Timezone = 303, + CurrentTime = 304, // status and presence PassportDetails = 402, diff --git a/ql-api/src/timezone.rs b/ql-api/src/time.rs similarity index 54% rename from ql-api/src/timezone.rs rename to ql-api/src/time.rs index 4a44b924..112bd550 100644 --- a/ql-api/src/timezone.rs +++ b/ql-api/src/time.rs @@ -18,3 +18,19 @@ impl Request for route::Timezone { type Request = TimezoneRequest; type Response = TimezoneResponse; } + +rpc! { + pub struct CurrentTimeRequest {} +} + +rpc! { + pub struct CurrentTimeResponse { + pub server_time_millis: u64, + } +} + +impl Request for route::CurrentTime { + type Error = Error; + type Request = CurrentTimeRequest; + type Response = CurrentTimeResponse; +}