From f43956ba6d16394703f8138c51f9091c1d9ef792 Mon Sep 17 00:00:00 2001 From: coreyphillips Date: Fri, 25 Sep 2026 10:02:41 -0400 Subject: [PATCH 1/4] feat(wake): add a wake gateway client module Adds src/modules/wake, the device side of the wake push gateway, as a port of the gateway's wake-proto crate: - key pair, device secret and install id generation; - wake_prepare_registration builds the 12-line message every identity signs and checks each field the way the gateway does; wake_sign_pubky_proof signs it with the pubky key and refuses any other message; - wake_decrypt_push finds the wake in any delivered APNs or FCM shape and decrypts legacy (v0) and v1 envelopes; a wake_fallback marker is returned without decrypting; - register, ack, presence, topics, unregister, info and topic listing, as pure build and parse functions around a thin reqwest executor that does not follow redirects. The AES key is SHA256(SHA256(S33 || label)) over the compressed ECDH point, computed as two plain sha256 passes rather than the sha256d type, whose display is byte-reversed. IVs must be 12 or 16 bytes. The tests assert the wake-proto golden vectors byte for byte (copied from wake commit 6037343), including rebuilding the v0 and v1 envelopes and the ln proof, and run every pushes.json case through the parser. Adds aes-gcm 0.10.3, already in the lockfile, and the serde_json raw_value feature so payloads come back as the producer sent them. register_device and test_notification are unchanged. --- CHANGELOG.md | 4 + Cargo.lock | 1 + Cargo.toml | 3 +- src/lib.rs | 227 +++ src/modules/mod.rs | 1 + src/modules/wake/README.md | 146 ++ src/modules/wake/client.rs | 516 +++++ src/modules/wake/crypto.rs | 424 ++++ src/modules/wake/errors.rs | 73 + src/modules/wake/mod.rs | 15 + src/modules/wake/push.rs | 145 ++ src/modules/wake/registration.rs | 364 ++++ .../test_vectors/legacy_registration.json | 14 + src/modules/wake/test_vectors/pushes.json | 322 +++ .../wake/test_vectors/registration_v1.json | 45 + src/modules/wake/test_vectors/v0_client.json | 15 + src/modules/wake/test_vectors/v0_server.json | 21 + .../wake/test_vectors/v1_envelope.json | 18 + src/modules/wake/tests.rs | 1779 +++++++++++++++++ src/modules/wake/types.rs | 187 ++ 20 files changed, 4319 insertions(+), 1 deletion(-) create mode 100644 src/modules/wake/README.md create mode 100644 src/modules/wake/client.rs create mode 100644 src/modules/wake/crypto.rs create mode 100644 src/modules/wake/errors.rs create mode 100644 src/modules/wake/mod.rs create mode 100644 src/modules/wake/push.rs create mode 100644 src/modules/wake/registration.rs create mode 100644 src/modules/wake/test_vectors/legacy_registration.json create mode 100644 src/modules/wake/test_vectors/pushes.json create mode 100644 src/modules/wake/test_vectors/registration_v1.json create mode 100644 src/modules/wake/test_vectors/v0_client.json create mode 100644 src/modules/wake/test_vectors/v0_server.json create mode 100644 src/modules/wake/test_vectors/v1_envelope.json create mode 100644 src/modules/wake/tests.rs create mode 100644 src/modules/wake/types.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 9577b40..27b5a38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## Unreleased + +- Add a `wake` module for the wake push gateway: device key, secret and install id generation, a signed registration (`wake_prepare_registration`, `wake_sign_pubky_proof`, `wake_register`), decryption of legacy (v0) and v1 envelopes from any delivered push (`wake_decrypt_push`), and the device routes for acks, presence, topics and unregistering. The v1 wire format is experimental until the apps adopt it. `register_device` and `test_notification` are unchanged. + ## 0.5.16 - 2026-09-14 - Serialize Jade discovery, connection setup, and teardown. A disconnect now cancels connection attempts that are in progress or queued, and a transport opened by a cancelled attempt is closed instead of leaked. A scan started while another lifecycle operation is running returns `DeviceBusy`. diff --git a/Cargo.lock b/Cargo.lock index 5366796..8bda522 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -591,6 +591,7 @@ dependencies = [ name = "bitkitcore" version = "0.5.18" dependencies = [ + "aes-gcm", "android_logger", "async-trait", "base64 0.22.1", diff --git a/Cargo.toml b/Cargo.toml index 2c97c1e..89dbbf5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ path = "src/lib.rs" [dependencies] uniffi = { version = "0.29.4", features = [ "cli", "bindgen" ] } -serde_json = "1.0.114" +serde_json = { version = "1.0.114", features = ["raw_value"] } serde = { version = "^1.0.209", features = ["derive"] } tokio = { version = "1.40.0", features = ["rt", "rt-multi-thread", "macros", "time", "sync"] } bitcoin = "0.32.4" @@ -39,6 +39,7 @@ bip39 = "2.0" bdk = { version = "0.30.2", features = ["all-keys"] } boltz-client = { version = "0.4.1", default-features = false, features = ["electrum", "ws"] } base64 = "0.22" +aes-gcm = "0.10.3" minicbor = { version = "2", features = ["alloc"] } ur = "0.5.2" log = "0.4" diff --git a/src/lib.rs b/src/lib.rs index 6aefd74..bf0c8a9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,6 +71,11 @@ pub use crate::modules::ur::{ passport_parse_account_export, ur_encode_crypto_psbt, PassportAccount, PassportAccountExport, UrDecoder, UrDecoderStatus, UrError, UrPayload, }; +use crate::modules::wake::{ + WakeAckOutcome, WakeDeviceSecret, WakeEnvelope, WakeEnvironment, WakeError, WakeIdentityProof, + WakeKeyPair, WakePlatform, WakeRegistration, WakeRegistrationRequest, WakeServerInfo, + WakeTopic, +}; pub use crate::onchain::WordCount; use crate::onchain::{ broadcast_raw_tx, get_account_info, get_address_info, get_transaction_detail, @@ -2293,6 +2298,228 @@ pub async fn pubky_session_list( }) } +// ============================================================================ +// Wake Functions +// ============================================================================ + +/// Generate the secp256k1 key pair wakes are encrypted to. +#[uniffi::export] +pub fn wake_generate_keypair() -> Result { + crate::modules::wake::wake_generate_keypair() +} + +/// Generate a device secret (`wkd_...`), the bearer credential of the device routes. +#[uniffi::export] +pub fn wake_generate_device_secret() -> Result { + crate::modules::wake::wake_generate_device_secret() +} + +/// Generate an install id (22 base64url characters), stable for the life of the install. +#[uniffi::export] +pub fn wake_generate_install_id() -> String { + crate::modules::wake::wake_generate_install_id() +} + +/// Validate a registration and build the message every identity signs. +/// `timestamp` defaults to now. +#[uniffi::export] +#[allow(clippy::too_many_arguments)] +pub fn wake_prepare_registration( + audience: String, + app: String, + install_id: String, + platform: WakePlatform, + environment: WakeEnvironment, + push_token: String, + encryption_public_key: String, + secret_sha256: String, + identities: Vec, + topics: Vec, + timestamp: Option, +) -> Result { + crate::modules::wake::wake_prepare_registration( + audience, + app, + install_id, + platform, + environment, + push_token, + encryption_public_key, + secret_sha256, + identities, + topics, + timestamp, + ) +} + +/// Sign a registration message with a pubky secret key (hex). Returns 128 hex characters. +#[uniffi::export] +pub fn wake_sign_pubky_proof(secret_key_hex: String, message: String) -> Result { + crate::modules::wake::wake_sign_pubky_proof(secret_key_hex, message) +} + +/// Find and decrypt the wake in a delivered push (the APNs payload or the FCM data map as JSON). +#[uniffi::export] +pub fn wake_decrypt_push( + secret_key_hex: String, + push_json: String, +) -> Result { + crate::modules::wake::wake_decrypt_push(secret_key_hex, push_json) +} + +/// Decrypt a legacy (v0) envelope from its four fields. +#[uniffi::export] +pub fn wake_decrypt_v0( + secret_key_hex: String, + cipher: String, + iv: String, + tag: String, + public_key: String, +) -> Result { + crate::modules::wake::wake_decrypt_v0(secret_key_hex, cipher, iv, tag, public_key) +} + +/// Decrypt a v1 container given as JSON. +#[uniffi::export] +pub fn wake_decrypt_v1( + secret_key_hex: String, + container_json: String, +) -> Result { + crate::modules::wake::wake_decrypt_v1(secret_key_hex, container_json) +} + +/// Read the gateway's audience, clock and version. +#[uniffi::export] +pub async fn wake_server_info(gateway_url: String) -> Result { + let rt = ensure_runtime(); + rt.spawn(async move { crate::modules::wake::wake_server_info(gateway_url).await }) + .await + .unwrap_or_else(|e| { + Err(WakeError::RequestFailed { + reason: format!("Runtime error: {}", e), + }) + }) +} + +/// Register the device, or update its registration, with one proof per identity. +#[uniffi::export] +pub async fn wake_register( + gateway_url: String, + request: WakeRegistrationRequest, + proofs: Vec, +) -> Result { + let rt = ensure_runtime(); + rt.spawn(async move { crate::modules::wake::wake_register(gateway_url, request, proofs).await }) + .await + .unwrap_or_else(|e| { + Err(WakeError::RequestFailed { + reason: format!("Runtime error: {}", e), + }) + }) +} + +/// Acknowledge a wake by the id from its envelope. +#[uniffi::export] +pub async fn wake_ack( + gateway_url: String, + device_secret: String, + wake_id: String, + outcome: WakeAckOutcome, +) -> Result<(), WakeError> { + let rt = ensure_runtime(); + rt.spawn(async move { + crate::modules::wake::wake_ack(gateway_url, device_secret, wake_id, outcome).await + }) + .await + .unwrap_or_else(|e| { + Err(WakeError::RequestFailed { + reason: format!("Runtime error: {}", e), + }) + }) +} + +/// Mark the app as in use for `ttl_secs` (clamped to 15..=300). Returns the expiry, unix seconds. +#[uniffi::export] +pub async fn wake_set_presence( + gateway_url: String, + device_secret: String, + ttl_secs: u32, +) -> Result { + let rt = ensure_runtime(); + rt.spawn(async move { + crate::modules::wake::wake_set_presence(gateway_url, device_secret, ttl_secs).await + }) + .await + .unwrap_or_else(|e| { + Err(WakeError::RequestFailed { + reason: format!("Runtime error: {}", e), + }) + }) +} + +/// End presence early. +#[uniffi::export] +pub async fn wake_clear_presence( + gateway_url: String, + device_secret: String, +) -> Result<(), WakeError> { + let rt = ensure_runtime(); + rt.spawn( + async move { crate::modules::wake::wake_clear_presence(gateway_url, device_secret).await }, + ) + .await + .unwrap_or_else(|e| { + Err(WakeError::RequestFailed { + reason: format!("Runtime error: {}", e), + }) + }) +} + +/// Replace the device's topics. Returns the stored topics; unknown names are dropped. +#[uniffi::export] +pub async fn wake_set_topics( + gateway_url: String, + device_secret: String, + topics: Vec, +) -> Result, WakeError> { + let rt = ensure_runtime(); + rt.spawn(async move { + crate::modules::wake::wake_set_topics(gateway_url, device_secret, topics).await + }) + .await + .unwrap_or_else(|e| { + Err(WakeError::RequestFailed { + reason: format!("Runtime error: {}", e), + }) + }) +} + +/// Unregister the device and revoke its secret. +#[uniffi::export] +pub async fn wake_unregister(gateway_url: String, device_secret: String) -> Result<(), WakeError> { + let rt = ensure_runtime(); + rt.spawn(async move { crate::modules::wake::wake_unregister(gateway_url, device_secret).await }) + .await + .unwrap_or_else(|e| { + Err(WakeError::RequestFailed { + reason: format!("Runtime error: {}", e), + }) + }) +} + +/// List the topics devices can subscribe to. +#[uniffi::export] +pub async fn wake_list_topics(gateway_url: String) -> Result, WakeError> { + let rt = ensure_runtime(); + rt.spawn(async move { crate::modules::wake::wake_list_topics(gateway_url).await }) + .await + .unwrap_or_else(|e| { + Err(WakeError::RequestFailed { + reason: format!("Runtime error: {}", e), + }) + }) +} + // ============================================================================ // Trezor Hardware Wallet Functions // ============================================================================ diff --git a/src/modules/mod.rs b/src/modules/mod.rs index 5b2ff2a..5ce1565 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -10,3 +10,4 @@ pub mod scanner; pub mod seedqr; pub mod trezor; pub mod ur; +pub mod wake; diff --git a/src/modules/wake/README.md b/src/modules/wake/README.md new file mode 100644 index 0000000..dea6937 --- /dev/null +++ b/src/modules/wake/README.md @@ -0,0 +1,146 @@ +# Wake Module + +Device side of the wake push gateway: generating the device's keys and +credentials, preparing and sending a signed registration, decrypting the +wakes that arrive in pushes, and the device routes of the gateway's v1 API +(acks, presence, topics, unregister). + +> The v1 wire format is experimental until the Bitkit apps adopt it. The +> legacy (v0) envelope is the one deployed apps already decrypt, and is +> stable. + +The module is a port of the gateway's `wake-proto` crate, which bitkit-core +cannot depend on. It carries no database state: the host keeps the key pair, +the device secret and the install id. + +## Available Methods + +| Method | What it does | +|---|---| +| `wakeGenerateKeypair` | secp256k1 key pair wakes are encrypted to | +| `wakeGenerateDeviceSecret` | `wkd_` bearer secret and its SHA-256 | +| `wakeGenerateInstallId` | 22-character install id | +| `wakePrepareRegistration` | validates a registration and builds the message every identity signs | +| `wakeSignPubkyProof` | signs that message with the pubky key (only registration messages) | +| `wakeRegister` | `POST /v1/devices` with one proof per identity | +| `wakeDecryptPush` | finds and decrypts the wake in an APNs payload or FCM data map | +| `wakeDecryptV0`, `wakeDecryptV1` | decrypt one envelope directly | +| `wakeAck` | `POST /v1/acks` | +| `wakeSetPresence`, `wakeClearPresence` | `PUT` / `DELETE /v1/presence` | +| `wakeSetTopics` | `PUT /v1/devices/self/topics` | +| `wakeUnregister` | `DELETE /v1/devices/self` | +| `wakeServerInfo` | `GET /v1/info` (the audience to register with) | +| `wakeListTopics` | `GET /v1/topics` | + +Grants and peer sends are not exposed yet. + +## Registering + +Keep the encryption secret key and the device secret where the notification +extension can read them (the shared keychain on iOS). Register again whenever +the push token, the key or the topics change; a newer registration from the +same install replaces the older one. + +```swift +import BitkitCore +import LDKNode + +func registerForWakes(node: Node, installId: String, pubkySecretHex: String, pubkyZ32: String, + pushToken: String) async throws { + let gateway = "https://wake.example" + let info = try await wakeServerInfo(gatewayUrl: gateway) + let keys = try wakeGenerateKeypair() // store both, reuse across launches + let secret = try wakeGenerateDeviceSecret() // store the token + let nodeId = node.nodeId() + + let request = try wakePrepareRegistration( + audience: info.audience, + app: "bitkit", + installId: installId, // wakeGenerateInstallId() once per install + platform: .apns, + environment: .production, + pushToken: pushToken, // lowercase hex on APNs + encryptionPublicKey: keys.publicKeyHex, + secretSha256: secret.sha256Hex, + identities: ["ln:\(nodeId)", "pk:\(pubkyZ32)"], + topics: ["blocktank.*"], + timestamp: nil + ) + let proofs = [ + WakeIdentityProof(identity: "ln:\(nodeId)", + signature: node.signMessage(msg: Array(request.message.utf8))), + WakeIdentityProof(identity: "pk:\(pubkyZ32)", + signature: try wakeSignPubkyProof(secretKeyHex: pubkySecretHex, message: request.message)), + ] + let registration = try await wakeRegister(gatewayUrl: gateway, request: request, proofs: proofs) + print("registered \(registration.deviceId), unknown topics: \(registration.unknownTopics)") +} +``` + +The request must reach the gateway within 600 seconds of `timestamp`. + +## Handling a push + +```swift +let envelope = try wakeDecryptPush(secretKeyHex: keys.secretKeyHex, pushJson: payloadJson) +if envelope.fallback { + // A visible fallback alert: nothing to decrypt, the wake was not handled in time. +} else if let id = envelope.id { + // v1: handle envelope.topic / envelope.payloadJson, then acknowledge. + try await wakeAck(gatewayUrl: gateway, deviceSecret: secret.token, wakeId: id, outcome: .handled) +} else { + // v0 (legacy): no id and nothing to acknowledge. +} +``` + +`pushJson` is the APNs `userInfo` dictionary or the FCM data map, serialized +as JSON. Four shapes are recognised: the legacy APNs `aps.alert.payload`, the +legacy flat FCM data (`cipher`, `iv`, `tag`, `publicKey`), a v1 `wake` object +(APNs) and a v1 `wake` JSON string (FCM). A `wake_fallback` marker returns +`fallback = true` without reading the key. + +Acknowledging `.handled` cancels the fallback alert. `.noHandler` and +`.failed` are recorded, and the fallback still fires. + +## Crypto and wire compatibility + +- `S33 = compressed(d * P)` from `bitcoin::secp256k1::ecdh::shared_secret_point`, + i.e. `(0x02 | y & 1) || x`. +- `key = SHA256(SHA256(S33 || label))`, two plain SHA-256 passes (not the + byte-reversed `sha256d` display form). +- AES-256-GCM, no AAD, 16-byte tag. The IV must be 12 or 16 bytes. +- v0: label `bitkit-notifications`, 16-byte IV, plaintext + `{"source","type","payload","createdAt"}`; the envelope's topic is + `source.type`. +- v1: label `wake-v1`, 12-byte IV, container `{"v":1,...}`, plaintext + `{"v":1,"id","topic","urgency","deadline","payload"}`. Any other `v` is + `UnsupportedEnvelope`. +- The registration message is the gateway's 12-line preimage: identities and + topics are deduplicated and sorted bytewise, and a newline in any field, or + an empty list entry or a comma in one, is rejected. +- `payloadJson` is the producer's payload exactly as encrypted. + +Error messages never contain keys, secrets or decrypted plaintext. + +## Test vectors + +`test_vectors/*.json` are copied byte for byte from `crates/wake-proto/vectors` +of the wake repository at commit +`6037343ad39603ce436e2a551cac9db877ede9c6`. The tests assert them exactly: +the v0 server and client envelopes with their shared points and keys, the v1 +envelope, the registration preimage with both proofs, the legacy signature +(used to check the test signer), and every push shape in `pushes.json`. + +To update them, copy the files from a newer wake commit, change the hash +above and run `cargo test wake`. + +## Tests + +```bash +cargo test wake +WAKE_GATEWAY_URL=http://127.0.0.1:9010 cargo test wake -- --ignored # against a running gateway +``` + +The ignored test registers with an `ln:` proof (signed by a local stand-in for +ldk-node `signMessage`) and a `pk:` proof, then sets and clears presence, +lists and sets topics, and unregisters. diff --git a/src/modules/wake/client.rs b/src/modules/wake/client.rs new file mode 100644 index 0000000..14116bb --- /dev/null +++ b/src/modules/wake/client.rs @@ -0,0 +1,516 @@ +//! The device side of the gateway's v1 HTTP API. +//! +//! Every call is a pure `build_*` function that turns the arguments into an +//! [`HttpRequest`], a thin executor that sends it, and a pure `parse_*` +//! function that maps the [`HttpResponse`] to a result, so everything but +//! the network is tested without a gateway. + +use std::time::Duration; + +use reqwest::header::{ACCEPT, CONTENT_TYPE}; +use reqwest::Method; +use serde::de::DeserializeOwned; +use serde::{Deserialize, Serialize}; +use url::Url; + +use super::crypto::parse_wake_id; +use super::errors::{json_error, WakeError}; +use super::registration::{is_valid_device_secret, RegistrationFields}; +use super::types::{ + WakeAckOutcome, WakeIdentityProof, WakeRegistration, WakeRegistrationRequest, WakeServerInfo, + WakeTopic, +}; + +const CONNECT_TIMEOUT: Duration = Duration::from_secs(10); +const REQUEST_TIMEOUT: Duration = Duration::from_secs(15); + +/// A request to the gateway. `bearer` is a device secret, so this type has +/// no `Debug`. +pub(crate) struct HttpRequest { + pub(crate) method: Method, + pub(crate) url: Url, + pub(crate) bearer: Option, + /// JSON body. + pub(crate) body: Option, +} + +/// A response from the gateway. +pub(crate) struct HttpResponse { + pub(crate) status: u16, + pub(crate) body: String, +} + +impl HttpResponse { + fn is_success(&self) -> bool { + (200..300).contains(&self.status) + } +} + +// ----- Wire bodies ----- + +#[derive(Serialize)] +struct RegisterBody<'a> { + app: &'a str, + install_id: &'a str, + platform: &'a str, + environment: &'a str, + push_token: &'a str, + encryption_key: &'a str, + secret_sha256: &'a str, + identities: &'a [String], + topics: &'a [String], + ts: u64, + proofs: Vec>, +} + +#[derive(Serialize)] +struct ProofBody<'a> { + identity: &'a str, + sig: &'a str, +} + +#[derive(Serialize)] +struct AckBody<'a> { + id: &'a str, + outcome: &'a str, +} + +#[derive(Serialize)] +struct PresenceBody { + ttl_secs: u32, +} + +#[derive(Serialize)] +struct TopicsBody<'a> { + topics: &'a [String], +} + +#[derive(Deserialize)] +struct ErrorBody { + error: String, + message: String, +} + +#[derive(Deserialize)] +struct InfoResponse { + audience: String, + server_time: u64, + version: String, +} + +#[derive(Deserialize)] +struct RegisterResponse { + device_id: String, + identities: Vec, + topics: Vec, + unknown_topics: Vec, + server_time: u64, +} + +#[derive(Deserialize)] +struct PresenceResponse { + expires_at: u64, +} + +#[derive(Deserialize)] +struct SetTopicsResponse { + topics: Vec, +} + +#[derive(Deserialize)] +struct TopicsResponse { + topics: Vec, +} + +#[derive(Deserialize)] +struct TopicWire { + name: String, + description: String, + urgency: String, + deadline_secs: u32, + peer: bool, + alertable: bool, + display: Option, +} + +#[derive(Deserialize)] +struct DisplayWire { + title: String, + body: String, +} + +// ----- Builders ----- + +/// `gateway_url` with the path ending in `/`, so relative routes append to +/// any path prefix the gateway is mounted under. +fn base_url(gateway_url: &str) -> Result { + let mut url = Url::parse(gateway_url) + .map_err(|_| WakeError::invalid_input("gateway url is not a valid url"))?; + if !matches!(url.scheme(), "http" | "https") { + return Err(WakeError::invalid_input( + "gateway url must be http or https", + )); + } + if !url.path().ends_with('/') { + let path = format!("{}/", url.path()); + url.set_path(&path); + } + url.set_query(None); + url.set_fragment(None); + Ok(url) +} + +fn request( + method: Method, + gateway_url: &str, + route: &str, + bearer: Option<&str>, + body: Option, +) -> Result { + let url = base_url(gateway_url)? + .join(route) + .map_err(|_| WakeError::invalid_input("gateway url cannot be joined"))?; + let bearer = match bearer { + Some(token) if !is_valid_device_secret(token) => { + return Err(WakeError::invalid_input("malformed device secret")) + } + other => other.map(str::to_string), + }; + Ok(HttpRequest { + method, + url, + bearer, + body, + }) +} + +fn to_json(value: &T) -> String { + // Bodies hold only strings, integers and string lists. + serde_json::to_string(value).expect("request body serialization cannot fail") +} + +/// `GET /v1/info` +pub(crate) fn build_server_info(gateway_url: &str) -> Result { + request(Method::GET, gateway_url, "v1/info", None, None) +} + +/// `GET /v1/topics` +pub(crate) fn build_list_topics(gateway_url: &str) -> Result { + request(Method::GET, gateway_url, "v1/topics", None, None) +} + +/// `POST /v1/devices`. The fields must still be the ones `message` was +/// built from, or no proof could verify. +pub(crate) fn build_register( + gateway_url: &str, + registration: &WakeRegistrationRequest, + proofs: &[WakeIdentityProof], +) -> Result { + let signed = RegistrationFields { + audience: ®istration.audience, + app: ®istration.app, + install_id: ®istration.install_id, + platform: registration.platform, + environment: registration.environment, + push_token: ®istration.push_token, + encryption_key: ®istration.encryption_public_key, + secret_sha256: ®istration.secret_sha256, + identities: ®istration.identities, + topics: ®istration.topics, + ts: registration.timestamp, + } + .preimage()?; + if signed != registration.message { + return Err(WakeError::invalid_input( + "the registration changed after its message was prepared", + )); + } + let body = RegisterBody { + app: ®istration.app, + install_id: ®istration.install_id, + platform: registration.platform.as_str(), + environment: registration.environment.as_str(), + push_token: ®istration.push_token, + encryption_key: ®istration.encryption_public_key, + secret_sha256: ®istration.secret_sha256, + identities: ®istration.identities, + topics: ®istration.topics, + ts: registration.timestamp, + proofs: proofs + .iter() + .map(|proof| ProofBody { + identity: &proof.identity, + sig: &proof.signature, + }) + .collect(), + }; + request( + Method::POST, + gateway_url, + "v1/devices", + None, + Some(to_json(&body)), + ) +} + +/// `POST /v1/acks` +pub(crate) fn build_ack( + gateway_url: &str, + device_secret: &str, + wake_id: &str, + outcome: WakeAckOutcome, +) -> Result { + let id = parse_wake_id(wake_id)?; + let body = AckBody { + id: &id, + outcome: outcome.as_str(), + }; + request( + Method::POST, + gateway_url, + "v1/acks", + Some(device_secret), + Some(to_json(&body)), + ) +} + +/// `PUT /v1/presence`. The gateway clamps `ttl_secs` to 15..=300. +pub(crate) fn build_set_presence( + gateway_url: &str, + device_secret: &str, + ttl_secs: u32, +) -> Result { + request( + Method::PUT, + gateway_url, + "v1/presence", + Some(device_secret), + Some(to_json(&PresenceBody { ttl_secs })), + ) +} + +/// `DELETE /v1/presence` +pub(crate) fn build_clear_presence( + gateway_url: &str, + device_secret: &str, +) -> Result { + request( + Method::DELETE, + gateway_url, + "v1/presence", + Some(device_secret), + None, + ) +} + +/// `PUT /v1/devices/self/topics` +pub(crate) fn build_set_topics( + gateway_url: &str, + device_secret: &str, + topics: &[String], +) -> Result { + request( + Method::PUT, + gateway_url, + "v1/devices/self/topics", + Some(device_secret), + Some(to_json(&TopicsBody { topics })), + ) +} + +/// `DELETE /v1/devices/self` +pub(crate) fn build_unregister( + gateway_url: &str, + device_secret: &str, +) -> Result { + request( + Method::DELETE, + gateway_url, + "v1/devices/self", + Some(device_secret), + None, + ) +} + +// ----- Parsers ----- + +/// The error of a non-2xx response: the gateway's +/// `{"error":"","message":"..."}`, or code `unknown` for any other +/// body (a proxy error page, say). +fn rejection(response: &HttpResponse) -> WakeError { + match serde_json::from_str::(&response.body) { + Ok(body) => WakeError::GatewayRejected { + status: response.status, + code: body.error, + message: body.message, + }, + Err(_) => WakeError::GatewayRejected { + status: response.status, + code: "unknown".to_string(), + message: "the response carries no wake error body".to_string(), + }, + } +} + +fn success_json(response: &HttpResponse) -> Result { + if !response.is_success() { + return Err(rejection(response)); + } + serde_json::from_str(&response.body).map_err(|e| WakeError::invalid_response(json_error(&e))) +} + +/// For routes that answer 204. +pub(crate) fn parse_no_content(response: &HttpResponse) -> Result<(), WakeError> { + if response.is_success() { + Ok(()) + } else { + Err(rejection(response)) + } +} + +pub(crate) fn parse_server_info(response: &HttpResponse) -> Result { + let info: InfoResponse = success_json(response)?; + Ok(WakeServerInfo { + audience: info.audience, + server_time: info.server_time, + version: info.version, + }) +} + +pub(crate) fn parse_list_topics(response: &HttpResponse) -> Result, WakeError> { + let topics: TopicsResponse = success_json(response)?; + Ok(topics + .topics + .into_iter() + .map(|topic| { + let (title, body) = match topic.display { + Some(display) => (Some(display.title), Some(display.body)), + None => (None, None), + }; + WakeTopic { + name: topic.name, + description: topic.description, + urgency: topic.urgency, + deadline_secs: topic.deadline_secs, + alertable: topic.alertable, + title, + body, + peer: topic.peer, + } + }) + .collect()) +} + +pub(crate) fn parse_register(response: &HttpResponse) -> Result { + let registered: RegisterResponse = success_json(response)?; + Ok(WakeRegistration { + device_id: registered.device_id, + identities: registered.identities, + topics: registered.topics, + unknown_topics: registered.unknown_topics, + server_time: registered.server_time, + }) +} + +/// The presence expiry, unix seconds. +pub(crate) fn parse_set_presence(response: &HttpResponse) -> Result { + let presence: PresenceResponse = success_json(response)?; + Ok(presence.expires_at) +} + +/// The stored topics; unknown exact names are dropped by the gateway. +pub(crate) fn parse_set_topics(response: &HttpResponse) -> Result, WakeError> { + let topics: SetTopicsResponse = success_json(response)?; + Ok(topics.topics) +} + +// ----- Executor ----- + +async fn send(request: HttpRequest) -> Result { + let failed = |e: reqwest::Error| WakeError::RequestFailed { + reason: e.to_string(), + }; + // A redirect would turn a POST into a GET; surface it as an error instead. + let client = reqwest::Client::builder() + .connect_timeout(CONNECT_TIMEOUT) + .timeout(REQUEST_TIMEOUT) + .redirect(reqwest::redirect::Policy::none()) + .build() + .map_err(failed)?; + let mut builder = client + .request(request.method, request.url) + .header(ACCEPT, "application/json"); + if let Some(token) = request.bearer { + builder = builder.bearer_auth(token); + } + if let Some(body) = request.body { + builder = builder.header(CONTENT_TYPE, "application/json").body(body); + } + let response = builder.send().await.map_err(failed)?; + let status = response.status().as_u16(); + let body = response.text().await.map_err(failed)?; + Ok(HttpResponse { status, body }) +} + +/// Reads the gateway's audience and clock. +pub async fn wake_server_info(gateway_url: String) -> Result { + parse_server_info(&send(build_server_info(&gateway_url)?).await?) +} + +/// Registers the device, or updates its registration. +pub async fn wake_register( + gateway_url: String, + request: WakeRegistrationRequest, + proofs: Vec, +) -> Result { + parse_register(&send(build_register(&gateway_url, &request, &proofs)?).await?) +} + +/// Acknowledges a wake by the id from its envelope. +pub async fn wake_ack( + gateway_url: String, + device_secret: String, + wake_id: String, + outcome: WakeAckOutcome, +) -> Result<(), WakeError> { + let request = build_ack(&gateway_url, &device_secret, &wake_id, outcome)?; + parse_no_content(&send(request).await?) +} + +/// Marks the app as in use for `ttl_secs` and returns when that expires. +pub async fn wake_set_presence( + gateway_url: String, + device_secret: String, + ttl_secs: u32, +) -> Result { + let request = build_set_presence(&gateway_url, &device_secret, ttl_secs)?; + parse_set_presence(&send(request).await?) +} + +/// Ends presence early. +pub async fn wake_clear_presence( + gateway_url: String, + device_secret: String, +) -> Result<(), WakeError> { + parse_no_content(&send(build_clear_presence(&gateway_url, &device_secret)?).await?) +} + +/// Replaces the device's topics and returns the stored ones. +pub async fn wake_set_topics( + gateway_url: String, + device_secret: String, + topics: Vec, +) -> Result, WakeError> { + let request = build_set_topics(&gateway_url, &device_secret, &topics)?; + parse_set_topics(&send(request).await?) +} + +/// Unregisters the device and revokes its secret. +pub async fn wake_unregister(gateway_url: String, device_secret: String) -> Result<(), WakeError> { + parse_no_content(&send(build_unregister(&gateway_url, &device_secret)?).await?) +} + +/// Lists the topics devices can subscribe to. +pub async fn wake_list_topics(gateway_url: String) -> Result, WakeError> { + parse_list_topics(&send(build_list_topics(&gateway_url)?).await?) +} diff --git a/src/modules/wake/crypto.rs b/src/modules/wake/crypto.rs new file mode 100644 index 0000000..5585a0a --- /dev/null +++ b/src/modules/wake/crypto.rs @@ -0,0 +1,424 @@ +//! Envelope decryption, ported from wake-proto `crypto`. +//! +//! The gateway encrypts every attempt with a fresh ephemeral key and IV: +//! - `S33 = compressed(d_eph * P_device)`, i.e. `(0x02 | y&1) || x`; +//! - `key = SHA256(SHA256(S33 || label))`; +//! - AES-256-GCM with no AAD and a 16-byte tag. +//! +//! v0 (the legacy bitkit-notification-server envelope) uses the label +//! `bitkit-notifications` and a 16-byte IV; v1 uses `wake-v1` and a 12-byte IV. + +use aes_gcm::aead::consts::U16; +use aes_gcm::aead::generic_array::GenericArray; +use aes_gcm::aead::{AeadInPlace, KeyInit}; +use aes_gcm::aes::Aes256; +use aes_gcm::{Aes256Gcm, AesGcm}; +use base64::engine::general_purpose::STANDARD; +use base64::Engine; +use bitcoin::hashes::{sha256, Hash}; +use bitcoin::secp256k1::{ecdh, PublicKey, Secp256k1, SecretKey}; +use rand::rngs::OsRng; +use rand::RngCore; +use serde::Deserialize; +use serde_json::value::RawValue; + +use super::errors::{json_error, WakeError}; +use super::types::{WakeEnvelope, WakeKeyPair}; + +const TAG_LEN: usize = 16; + +/// Draws after which key generation reports the random source as degenerate. +/// A working generator's draw is out of range with probability about 2^-128. +const MAX_REJECTED_DRAWS: usize = 64; + +/// AES-256-GCM with a 16-byte nonce, as the legacy server used. +type Aes256Gcm16 = AesGcm; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum Scheme { + V0, + V1, +} + +impl Scheme { + pub(crate) const fn label(self) -> &'static str { + match self { + Scheme::V0 => "bitkit-notifications", + Scheme::V1 => "wake-v1", + } + } +} + +/// An encrypted envelope: `cipher` is padded standard base64 of the +/// ciphertext, `iv`, `tag` and the ephemeral `publicKey` are hex. +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct Container { + scheme: Scheme, + cipher: Vec, + iv: Vec, + tag: [u8; TAG_LEN], + public_key: [u8; 33], +} + +impl Container { + /// Builds a container from its wire strings. Hex may be upper or lower + /// case; the IV must be 12 or 16 bytes whatever the scheme, because that + /// is what the apps accept. + pub(crate) fn from_fields( + scheme: Scheme, + cipher: &str, + iv: &str, + tag: &str, + public_key: &str, + ) -> Result { + let cipher = STANDARD + .decode(cipher) + .map_err(|_| WakeError::invalid_input("cipher is not padded standard base64"))?; + let iv = hex::decode(iv).map_err(|_| WakeError::invalid_input("iv is not hex"))?; + let tag = hex::decode(tag) + .ok() + .and_then(|t| <[u8; TAG_LEN]>::try_from(t).ok()) + .ok_or_else(|| WakeError::invalid_input("tag must be 16 hex bytes"))?; + let public_key = hex::decode(public_key) + .ok() + .and_then(|k| <[u8; 33]>::try_from(k).ok()) + .ok_or_else(|| WakeError::invalid_input("publicKey must be 33 hex bytes"))?; + if iv.len() != 12 && iv.len() != 16 { + return Err(WakeError::invalid_input("iv must be 12 or 16 bytes")); + } + Ok(Container { + scheme, + cipher, + iv, + tag, + public_key, + }) + } + + /// Parses container JSON. A missing `v` means v0, `"v":1` means v1, and + /// any other `v` (`null` included) is unsupported. Unknown keys are + /// ignored, as the apps ignore them. + pub(crate) fn from_json(json: &str) -> Result { + let wire: ContainerWire = serde_json::from_str(json) + .map_err(|_| WakeError::invalid_input("malformed container json"))?; + let scheme = match wire.v { + None => Scheme::V0, + Some(v) if v.as_u64() == Some(1) => Scheme::V1, + Some(_) => return Err(WakeError::unsupported("unsupported envelope version")), + }; + Container::from_fields(scheme, &wire.cipher, &wire.iv, &wire.tag, &wire.public_key) + } + + pub(crate) fn scheme(&self) -> Scheme { + self.scheme + } + + /// Decrypts with the device's secret key and returns the plaintext bytes. + pub(crate) fn decrypt(&self, device_secret: &SecretKey) -> Result, WakeError> { + let point = shared_point(device_secret, &self.public_key)?; + let key = derive_key(&point, self.scheme.label()); + open(&key, &self.iv, &self.cipher, &self.tag) + } + + /// Decrypts and parses the plaintext of this container's scheme. + pub(crate) fn open_envelope( + &self, + device_secret: &SecretKey, + ) -> Result { + let plaintext = self.decrypt(device_secret)?; + match self.scheme { + Scheme::V0 => legacy_envelope(&plaintext), + Scheme::V1 => v1_envelope(&plaintext), + } + } +} + +#[derive(Deserialize)] +struct ContainerWire { + /// `None` only when the key is absent; `"v":null` is `Some(Null)`. + #[serde(default, deserialize_with = "present")] + v: Option, + cipher: String, + iv: String, + tag: String, + #[serde(rename = "publicKey")] + public_key: String, +} + +fn present<'de, D: serde::Deserializer<'de>>( + deserializer: D, +) -> Result, D::Error> { + serde_json::Value::deserialize(deserializer).map(Some) +} + +/// `S33 = compressed(secret * public)`, the shared point both sides hash +/// into the AES key. +pub(crate) fn shared_point(secret: &SecretKey, public: &[u8; 33]) -> Result<[u8; 33], WakeError> { + if public[0] != 0x02 && public[0] != 0x03 { + return Err(WakeError::invalid_key("public key must be compressed")); + } + let point = PublicKey::from_slice(public) + .map_err(|_| WakeError::invalid_key("public key is not on the curve"))?; + let xy = ecdh::shared_secret_point(&point, secret); + let mut out = [0u8; 33]; + out[0] = 0x02 | (xy[63] & 1); + out[1..].copy_from_slice(&xy[..32]); + Ok(out) +} + +/// `SHA256(SHA256(S33 || label))`, the AES-256 key of both schemes. +/// +/// Two plain SHA-256 passes. Not the `sha256d` hash type, whose hex display +/// is byte-reversed. +pub(crate) fn derive_key(shared_point: &[u8; 33], label: &str) -> [u8; 32] { + let mut input = Vec::with_capacity(shared_point.len() + label.len()); + input.extend_from_slice(shared_point); + input.extend_from_slice(label.as_bytes()); + let first = sha256::Hash::hash(&input); + sha256::Hash::hash(first.as_byte_array()).to_byte_array() +} + +fn open( + key: &[u8; 32], + iv: &[u8], + ciphertext: &[u8], + tag: &[u8; TAG_LEN], +) -> Result, WakeError> { + let mut buffer = ciphertext.to_vec(); + let tag = GenericArray::from_slice(tag); + match iv.len() { + 12 => Aes256Gcm::new(GenericArray::from_slice(key)).decrypt_in_place_detached( + GenericArray::from_slice(iv), + b"", + &mut buffer, + tag, + ), + 16 => Aes256Gcm16::new(GenericArray::from_slice(key)).decrypt_in_place_detached( + GenericArray::from_slice(iv), + b"", + &mut buffer, + tag, + ), + _ => return Err(WakeError::invalid_input("iv must be 12 or 16 bytes")), + } + .map_err(|_| WakeError::DecryptionFailed { + reason: "authentication failed".to_string(), + })?; + Ok(buffer) +} + +/// The legacy plaintext: +/// `{"source":S,"type":T,"payload":,"createdAt":"..."}`. +#[derive(Deserialize)] +struct LegacyPlaintext { + source: String, + #[serde(rename = "type")] + kind: String, + #[serde(default)] + payload: Option>, + #[serde(rename = "createdAt")] + created_at: String, +} + +/// The v1 plaintext: +/// `{"v":1,"id":"","topic":T,"urgency":U,"deadline":N,"payload":}`. +#[derive(Deserialize)] +struct V1Plaintext { + v: u64, + id: String, + topic: String, + urgency: Urgency, + deadline: u64, + #[serde(default)] + payload: Option>, +} + +#[derive(Deserialize)] +#[serde(rename_all = "snake_case")] +enum Urgency { + Background, + Alert, + TimeSensitive, +} + +impl Urgency { + const fn as_str(&self) -> &'static str { + match self { + Urgency::Background => "background", + Urgency::Alert => "alert", + Urgency::TimeSensitive => "time_sensitive", + } + } +} + +fn malformed_plaintext(e: serde_json::Error) -> WakeError { + WakeError::invalid_input(format!("malformed plaintext, {}", json_error(&e))) +} + +fn legacy_envelope(plaintext: &[u8]) -> Result { + let legacy: LegacyPlaintext = serde_json::from_slice(plaintext).map_err(malformed_plaintext)?; + Ok(WakeEnvelope { + version: 0, + id: None, + topic: format!("{}.{}", legacy.source, legacy.kind), + urgency: None, + deadline: None, + payload_json: legacy.payload.map(|p| p.get().to_string()), + created_at: Some(legacy.created_at), + fallback: false, + }) +} + +fn v1_envelope(plaintext: &[u8]) -> Result { + let wire: V1Plaintext = serde_json::from_slice(plaintext).map_err(malformed_plaintext)?; + let id = parse_wake_id(&wire.id)?; + if wire.v != 1 { + return Err(WakeError::unsupported("unsupported plaintext version")); + } + Ok(WakeEnvelope { + version: 1, + id: Some(id), + topic: wire.topic, + urgency: Some(wire.urgency.as_str().to_string()), + deadline: Some(wire.deadline), + payload_json: wire.payload.map(|p| p.get().to_string()), + created_at: None, + fallback: false, + }) +} + +/// A wake id in its canonical hyphenated form. +pub(crate) fn parse_wake_id(id: &str) -> Result { + uuid::Uuid::parse_str(id) + .map(|id| id.to_string()) + .map_err(|_| WakeError::invalid_input("wake id is not a uuid")) +} + +/// Parses a 32-byte secp256k1 secret key written as hex. +pub(crate) fn parse_secret_key(secret_key_hex: &str) -> Result { + let bytes = parse_hex_32(secret_key_hex) + .ok_or_else(|| WakeError::invalid_key("secret key must be 32 hex bytes"))?; + SecretKey::from_slice(&bytes).map_err(|_| WakeError::invalid_key("secret key out of range")) +} + +pub(crate) fn parse_hex_32(s: &str) -> Option<[u8; 32]> { + hex::decode(s).ok()?.try_into().ok() +} + +/// Fills `dest` from the operating system's random number generator. +pub(crate) fn os_random(dest: &mut [u8]) -> Result<(), WakeError> { + OsRng + .try_fill_bytes(dest) + .map_err(|_| WakeError::invalid_key("the system random number generator failed")) +} + +/// Draws secret keys from `fill` until one is in range, at most +/// `MAX_REJECTED_DRAWS` times, so a broken source fails instead of looping. +pub(crate) fn generate_keypair_with( + mut fill: impl FnMut(&mut [u8]) -> Result<(), WakeError>, +) -> Result { + for _ in 0..MAX_REJECTED_DRAWS { + let mut candidate = [0u8; 32]; + fill(&mut candidate)?; + if let Ok(secret) = SecretKey::from_slice(&candidate) { + let public = PublicKey::from_secret_key(&Secp256k1::signing_only(), &secret); + return Ok(WakeKeyPair { + secret_key_hex: hex::encode(secret.secret_bytes()), + public_key_hex: hex::encode(public.serialize()), + }); + } + } + Err(WakeError::invalid_key("entropy source is degenerate")) +} + +/// Generates the device's push encryption key pair. +pub fn wake_generate_keypair() -> Result { + generate_keypair_with(os_random) +} + +/// Decrypts a legacy (v0) envelope given its four wire fields. +pub fn wake_decrypt_v0( + secret_key_hex: String, + cipher: String, + iv: String, + tag: String, + public_key: String, +) -> Result { + let secret = parse_secret_key(&secret_key_hex)?; + Container::from_fields(Scheme::V0, &cipher, &iv, &tag, &public_key)?.open_envelope(&secret) +} + +/// Decrypts a v1 container given as JSON. +pub fn wake_decrypt_v1( + secret_key_hex: String, + container_json: String, +) -> Result { + let secret = parse_secret_key(&secret_key_hex)?; + let container = Container::from_json(&container_json)?; + if container.scheme() != Scheme::V1 { + return Err(WakeError::unsupported("expected a v1 container")); + } + container.open_envelope(&secret) +} + +/// Encrypts like the gateway, with an explicit ephemeral key and IV, so tests +/// can rebuild the golden envelopes. Reusing a (key, IV) pair breaks GCM. +#[cfg(test)] +pub(crate) fn seal_deterministic( + scheme: Scheme, + device_public: &[u8; 33], + plaintext: &[u8], + ephemeral: &SecretKey, + iv: &[u8], +) -> Container { + let point = shared_point(ephemeral, device_public).unwrap(); + let key = derive_key(&point, scheme.label()); + let mut buffer = plaintext.to_vec(); + let tag = match iv.len() { + 12 => Aes256Gcm::new(GenericArray::from_slice(&key)).encrypt_in_place_detached( + GenericArray::from_slice(iv), + b"", + &mut buffer, + ), + 16 => Aes256Gcm16::new(GenericArray::from_slice(&key)).encrypt_in_place_detached( + GenericArray::from_slice(iv), + b"", + &mut buffer, + ), + other => panic!("iv of {other} bytes"), + } + .unwrap(); + Container { + scheme, + cipher: buffer, + iv: iv.to_vec(), + tag: tag.into(), + public_key: PublicKey::from_secret_key(&Secp256k1::signing_only(), ephemeral).serialize(), + } +} + +#[cfg(test)] +impl Container { + /// Compact container JSON in the wire key order. + pub(crate) fn to_json(&self) -> String { + let version = match self.scheme { + Scheme::V0 => "", + Scheme::V1 => "\"v\":1,", + }; + format!( + "{{{version}\"cipher\":\"{}\",\"iv\":\"{}\",\"tag\":\"{}\",\"publicKey\":\"{}\"}}", + STANDARD.encode(&self.cipher), + hex::encode(&self.iv), + hex::encode(self.tag), + hex::encode(self.public_key) + ) + } + + pub(crate) fn tag_mut(&mut self) -> &mut [u8; TAG_LEN] { + &mut self.tag + } + + pub(crate) fn public_key_mut(&mut self) -> &mut [u8; 33] { + &mut self.public_key + } +} diff --git a/src/modules/wake/errors.rs b/src/modules/wake/errors.rs new file mode 100644 index 0000000..90d74de --- /dev/null +++ b/src/modules/wake/errors.rs @@ -0,0 +1,73 @@ +/// Errors of the wake module. Messages never contain secrets, push tokens or +/// decrypted plaintext. +#[derive(uniffi::Error, thiserror::Error, Debug, Clone, PartialEq, Eq)] +#[non_exhaustive] +pub enum WakeError { + #[error("Invalid input: {reason}")] + InvalidInput { reason: String }, + + #[error("Invalid key: {reason}")] + InvalidKey { reason: String }, + + #[error("Decryption failed: {reason}")] + DecryptionFailed { reason: String }, + + #[error("Unsupported envelope: {reason}")] + UnsupportedEnvelope { reason: String }, + + #[error("Request failed: {reason}")] + RequestFailed { reason: String }, + + #[error("Gateway rejected the request with HTTP {status} ({code}): {message}")] + GatewayRejected { + status: u16, + code: String, + message: String, + }, + + #[error("Invalid response: {reason}")] + InvalidResponse { reason: String }, +} + +impl WakeError { + pub(crate) fn invalid_input(reason: impl Into) -> Self { + WakeError::InvalidInput { + reason: reason.into(), + } + } + + pub(crate) fn invalid_key(reason: impl Into) -> Self { + WakeError::InvalidKey { + reason: reason.into(), + } + } + + pub(crate) fn unsupported(reason: impl Into) -> Self { + WakeError::UnsupportedEnvelope { + reason: reason.into(), + } + } + + pub(crate) fn invalid_response(reason: impl Into) -> Self { + WakeError::InvalidResponse { + reason: reason.into(), + } + } +} + +/// The category and position of a JSON error, without its message: serde_json +/// messages quote the offending value, which may be decrypted plaintext. +pub(crate) fn json_error(e: &serde_json::Error) -> String { + use serde_json::error::Category; + let kind = match e.classify() { + Category::Io => "io error", + Category::Syntax => "syntax error", + Category::Data => "unexpected type or value", + Category::Eof => "unexpected end of input", + }; + format!( + "invalid json: {kind} at line {} column {}", + e.line(), + e.column() + ) +} diff --git a/src/modules/wake/mod.rs b/src/modules/wake/mod.rs new file mode 100644 index 0000000..ef8922a --- /dev/null +++ b/src/modules/wake/mod.rs @@ -0,0 +1,15 @@ +mod client; +mod crypto; +mod errors; +mod push; +mod registration; +#[cfg(test)] +mod tests; +mod types; + +pub use client::*; +pub use crypto::*; +pub use errors::*; +pub use push::*; +pub use registration::*; +pub use types::*; diff --git a/src/modules/wake/push.rs b/src/modules/wake/push.rs new file mode 100644 index 0000000..19bb64e --- /dev/null +++ b/src/modules/wake/push.rs @@ -0,0 +1,145 @@ +//! Finds the wake in a delivered push, ported from wake-proto +//! `push::parse_delivered`. + +use serde::Deserialize; +use serde_json::{Map, Value}; + +use super::crypto::{parse_secret_key, parse_wake_id, Container, Scheme}; +use super::errors::{json_error, WakeError}; +use super::types::WakeEnvelope; + +/// A wake as the device receives it. +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) enum Delivered { + /// A legacy envelope: APNs `aps.alert.payload`, or the flat FCM data + /// fields `cipher`, `iv`, `tag` and `publicKey`. + V0(Container), + /// A v1 envelope: the APNs `wake` object or the FCM `wake` JSON string. + V1(Container), + /// A fallback alert's `wake_fallback` marker; there is nothing to decrypt. + Fallback { id: String, topic: String }, +} + +/// Finds the wake in a push. +/// +/// `push` is the JSON the app receives: +/// - APNs: the notification dictionary, carrying `aps.alert.payload` (v0), +/// a `wake` object (v1) or a `wake_fallback` object; +/// - FCM: the data map, carrying flat `cipher`, `iv`, `tag` and `publicKey` +/// strings (v0), or `wake` or `wake_fallback` as JSON strings. +/// +/// A whole FCM `messages:send` body is accepted too: the map read is +/// `message.android.data` when present, else `message.data`. +pub(crate) fn parse_delivered(push: &str) -> Result { + let root: Value = + serde_json::from_str(push).map_err(|e| WakeError::invalid_input(json_error(&e)))?; + let mut map = root + .as_object() + .ok_or_else(|| WakeError::invalid_input("push is not a JSON object"))?; + // The legacy FCM data map has a string `message` (the alert text), so + // only an object marks a `messages:send` body. + if let Some(message) = map.get("message").filter(|m| m.is_object()) { + map = message + .pointer("/android/data") + .or_else(|| message.get("data")) + .and_then(Value::as_object) + .ok_or_else(|| WakeError::unsupported("FCM message carries no data"))?; + } + if let Some(marker) = map.get("wake_fallback") { + return parse_fallback(marker); + } + if let Some(wake) = map.get("wake") { + let container = match wake { + Value::String(text) => Container::from_json(text)?, + Value::Object(_) => Container::from_json(&wake.to_string())?, + _ => return Err(WakeError::invalid_input("wake is not a container")), + }; + expect_scheme(&container, Scheme::V1)?; + return Ok(Delivered::V1(container)); + } + if let Some(payload) = map.get("aps").and_then(|aps| aps.pointer("/alert/payload")) { + if !payload.is_object() { + return Err(WakeError::invalid_input( + "aps.alert.payload is not a container", + )); + } + let container = Container::from_json(&payload.to_string())?; + expect_scheme(&container, Scheme::V0)?; + return Ok(Delivered::V0(container)); + } + if map.contains_key("cipher") { + return legacy_fields(map).map(Delivered::V0); + } + Err(WakeError::unsupported("push carries no wake")) +} + +fn parse_fallback(marker: &Value) -> Result { + #[derive(Deserialize)] + struct Marker { + v: Value, + id: String, + topic: String, + } + let marker: Marker = match marker { + Value::String(text) => serde_json::from_str(text), + other => Marker::deserialize(other), + } + .map_err(|e| WakeError::invalid_input(json_error(&e)))?; + let id = parse_wake_id(&marker.id)?; + if marker.v.as_u64() != Some(1) { + return Err(WakeError::unsupported("unsupported envelope version")); + } + Ok(Delivered::Fallback { + id, + topic: marker.topic, + }) +} + +fn legacy_fields(map: &Map) -> Result { + let field = |key: &str| { + map.get(key).and_then(Value::as_str).ok_or_else(|| { + WakeError::invalid_input("legacy data needs cipher, iv, tag and publicKey strings") + }) + }; + Container::from_fields( + Scheme::V0, + field("cipher")?, + field("iv")?, + field("tag")?, + field("publicKey")?, + ) +} + +fn expect_scheme(container: &Container, scheme: Scheme) -> Result<(), WakeError> { + if container.scheme() == scheme { + Ok(()) + } else { + Err(WakeError::unsupported( + "container version does not match where it was found", + )) + } +} + +/// Finds and decrypts the wake in a delivered push. A fallback alert returns +/// `fallback = true` without decrypting, so the key is only read for +/// envelopes. +pub fn wake_decrypt_push( + secret_key_hex: String, + push_json: String, +) -> Result { + match parse_delivered(&push_json)? { + Delivered::V0(container) | Delivered::V1(container) => { + container.open_envelope(&parse_secret_key(&secret_key_hex)?) + } + Delivered::Fallback { id, topic } => Ok(WakeEnvelope { + version: 1, + id: Some(id), + topic, + urgency: None, + deadline: None, + payload_json: None, + created_at: None, + fallback: true, + }), + } +} diff --git a/src/modules/wake/registration.rs b/src/modules/wake/registration.rs new file mode 100644 index 0000000..adb3c68 --- /dev/null +++ b/src/modules/wake/registration.rs @@ -0,0 +1,364 @@ +//! Registration preimage and credentials, ported from wake-proto +//! `registration`, `tokens` and `api`. +//! +//! Every identity signs the same 12-line UTF-8 preimage, lines joined by a +//! single `\n` with no trailing newline: +//! +//! ```text +//! wake-register-v1 +//! aud: +//! app: +//! install: +//! platform: +//! environment: +//! push_token: +//! encryption_key:<66 hex> +//! secret_sha256:<64 hex> +//! identities: +//! topics: +//! ts: +//! ``` + +use std::time::{SystemTime, UNIX_EPOCH}; + +use base64::engine::general_purpose::URL_SAFE_NO_PAD; +use base64::Engine; +use bitcoin::hashes::{sha256, Hash}; +use bitcoin::secp256k1::PublicKey; +use rand::rngs::OsRng; +use rand::RngCore; + +use super::crypto::{os_random, parse_hex_32}; +use super::errors::WakeError; +use super::types::{WakeDeviceSecret, WakeEnvironment, WakePlatform, WakeRegistrationRequest}; + +/// First line of the preimage, and its domain separator. +pub(crate) const PREIMAGE_TAG: &str = "wake-register-v1"; + +const DEVICE_SECRET_PREFIX: &str = "wkd_"; +const MAX_TOPICS: usize = 32; +const MAX_TOPIC_LEN: usize = 64; + +/// The fields the registration preimage covers. +pub(crate) struct RegistrationFields<'a> { + pub(crate) audience: &'a str, + pub(crate) app: &'a str, + pub(crate) install_id: &'a str, + pub(crate) platform: WakePlatform, + pub(crate) environment: WakeEnvironment, + pub(crate) push_token: &'a str, + pub(crate) encryption_key: &'a str, + pub(crate) secret_sha256: &'a str, + pub(crate) identities: &'a [String], + pub(crate) topics: &'a [String], + pub(crate) ts: u64, +} + +impl RegistrationFields<'_> { + /// Builds the preimage. Fails if any field contains a newline, or any + /// identity or topic is empty or contains a comma, since each would make + /// the preimage ambiguous (`[""]` and `[]` both give `topics:`). + pub(crate) fn preimage(&self) -> Result { + let scalars = [ + self.audience, + self.app, + self.install_id, + self.push_token, + self.encryption_key, + self.secret_sha256, + ]; + if scalars.iter().any(|f| f.contains('\n')) { + return Err(WakeError::invalid_input( + "a registration field contains a newline", + )); + } + let identities = sorted_list(self.identities)?; + let topics = sorted_list(self.topics)?; + Ok(format!( + "{PREIMAGE_TAG}\naud:{}\napp:{}\ninstall:{}\nplatform:{}\nenvironment:{}\n\ + push_token:{}\nencryption_key:{}\nsecret_sha256:{}\nidentities:{identities}\n\ + topics:{topics}\nts:{}", + self.audience, + self.app, + self.install_id, + self.platform.as_str(), + self.environment.as_str(), + self.push_token, + self.encryption_key, + self.secret_sha256, + self.ts, + )) + } +} + +fn sorted_list(items: &[String]) -> Result { + if items + .iter() + .any(|i| i.is_empty() || i.contains(',') || i.contains('\n')) + { + return Err(WakeError::invalid_input( + "a list entry is empty or contains a comma or newline", + )); + } + let mut sorted: Vec<&str> = items.iter().map(String::as_str).collect(); + sorted.sort_unstable(); + sorted.dedup(); + Ok(sorted.join(",")) +} + +/// Checks every field the way the gateway does, so a request it would reject +/// as `invalid_request` fails here, before anything is signed. +fn validate(fields: &RegistrationFields<'_>) -> Result<(), WakeError> { + if fields.audience.is_empty() || fields.app.is_empty() { + return Err(WakeError::invalid_input( + "audience and app must not be empty", + )); + } + if !is_valid_install_id(fields.install_id) { + return Err(WakeError::invalid_input( + "install_id must be 16 to 64 characters of [A-Za-z0-9_-]", + )); + } + if fields.platform == WakePlatform::Fcm && fields.environment != WakeEnvironment::Production { + return Err(WakeError::invalid_input( + "fcm devices are always production", + )); + } + if !is_valid_push_token(fields.platform, fields.push_token) { + return Err(WakeError::invalid_input(match fields.platform { + WakePlatform::Apns => "an apns push token is 64 to 200 lowercase hex characters", + WakePlatform::Fcm => "an fcm push token is 100 to 4096 characters of [0-9A-Za-z_:-]", + })); + } + parse_public_key_hex(fields.encryption_key)?; + if !is_lower_hex(fields.secret_sha256, 32) { + return Err(WakeError::invalid_input( + "secret_sha256 must be 64 lowercase hex characters", + )); + } + let schemes = fields + .identities + .iter() + .map(|identity| identity_scheme(identity)) + .collect::, _>>()?; + if !(1..=2).contains(&schemes.len()) || (schemes.len() == 2 && schemes[0] == schemes[1]) { + return Err(WakeError::invalid_input( + "identities must be one ln: and/or one pk: identity", + )); + } + if fields.topics.len() > MAX_TOPICS { + return Err(WakeError::invalid_input("at most 32 topics")); + } + if let Some(topic) = fields.topics.iter().find(|t| !is_valid_topic_filter(t)) { + return Err(WakeError::invalid_input(format!( + "{topic:?} is not a topic name or a .* pattern" + ))); + } + Ok(()) +} + +/// `^[A-Za-z0-9_-]{16,64}$` +fn is_valid_install_id(s: &str) -> bool { + (16..=64).contains(&s.len()) + && s.bytes() + .all(|b| b.is_ascii_alphanumeric() || b == b'_' || b == b'-') +} + +/// APNs: even-length lowercase hex, 64 to 200 characters. FCM: +/// `^[0-9A-Za-z_:-]{100,4096}$`. +fn is_valid_push_token(platform: WakePlatform, token: &str) -> bool { + match platform { + WakePlatform::Apns => { + // `is_lower_hex` needs exactly twice `len / 2` characters, so odd + // lengths fail it. + (64..=200).contains(&token.len()) && is_lower_hex(token, token.len() / 2) + } + WakePlatform::Fcm => { + (100..=4096).contains(&token.len()) + && token + .bytes() + .all(|b| b.is_ascii_alphanumeric() || matches!(b, b'_' | b':' | b'-')) + } + } +} + +/// Exactly `bytes` bytes written as lowercase hex. +fn is_lower_hex(s: &str, bytes: usize) -> bool { + s.len() == bytes * 2 + && s.bytes() + .all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b)) +} + +/// A compressed key written as 66 lowercase hex characters that lies on the +/// curve. +fn parse_public_key_hex(s: &str) -> Result { + if !is_lower_hex(s, 33) || !(s.starts_with("02") || s.starts_with("03")) { + return Err(WakeError::invalid_key( + "a public key is 66 lowercase hex characters starting with 02 or 03", + )); + } + let bytes = hex::decode(s).map_err(|_| WakeError::invalid_key("public key is not hex"))?; + PublicKey::from_slice(&bytes) + .map_err(|_| WakeError::invalid_key("public key is not on the curve")) +} + +/// The scheme (`"ln"` or `"pk"`) of a canonical identity: +/// - `ln:` + 66 lowercase hex, a compressed secp256k1 key on the curve; +/// - `pk:` + 52 canonical z-base-32 characters, a canonical, non-weak +/// Ed25519 point. +fn identity_scheme(identity: &str) -> Result<&'static str, WakeError> { + let invalid = || WakeError::invalid_input("identities must be canonical ln: or pk: identities"); + if let Some(key) = identity.strip_prefix("ln:") { + parse_public_key_hex(key).map_err(|_| invalid())?; + return Ok("ln"); + } + let z32 = identity.strip_prefix("pk:").ok_or_else(invalid)?; + if z32.len() != 52 { + return Err(invalid()); + } + let key = pubky::PublicKey::try_from_z32(z32).map_err(|_| invalid())?; + let point = key.verifying_key(); + let canonical = key.z32() == z32 + && point.to_edwards().compress().to_bytes() == point.to_bytes() + && !point.is_weak(); + if !canonical { + return Err(invalid()); + } + Ok("pk") +} + +/// `^[a-z][a-z0-9_-]*(\.[A-Za-z][A-Za-z0-9_-]*)+$` names and `.*` +/// patterns, at most 64 characters. +fn is_valid_topic_filter(s: &str) -> bool { + if s.len() > MAX_TOPIC_LEN { + return false; + } + match s.strip_suffix(".*") { + Some(namespace) => is_namespace(namespace), + None => is_namespace(s) && s.contains('.'), + } +} + +/// `^[a-z][a-z0-9_-]*(\.[A-Za-z][A-Za-z0-9_-]*)*$` +fn is_namespace(s: &str) -> bool { + let mut segments = s.split('.'); + let first_ok = segments.next().is_some_and(|first| { + let mut bytes = first.bytes(); + bytes.next().is_some_and(|b| b.is_ascii_lowercase()) + && bytes.all(|b| b.is_ascii_lowercase() || b.is_ascii_digit() || b == b'_' || b == b'-') + }); + first_ok + && segments.all(|segment| { + let mut bytes = segment.bytes(); + bytes.next().is_some_and(|b| b.is_ascii_alphabetic()) + && bytes.all(|b| b.is_ascii_alphanumeric() || b == b'_' || b == b'-') + }) +} + +/// Encodes 32 bytes as a device secret: `wkd_` + 43 base64url characters. +pub(crate) fn device_secret_from_bytes(bytes: &[u8; 32]) -> WakeDeviceSecret { + let token = format!("{DEVICE_SECRET_PREFIX}{}", URL_SAFE_NO_PAD.encode(bytes)); + let sha256_hex = hex::encode(sha256::Hash::hash(token.as_bytes()).to_byte_array()); + WakeDeviceSecret { token, sha256_hex } +} + +/// Checks `wkd_` + 43 canonical base64url characters (32 bytes). +pub(crate) fn is_valid_device_secret(token: &str) -> bool { + token + .strip_prefix(DEVICE_SECRET_PREFIX) + .filter(|body| body.len() == 43) + .and_then(|body| URL_SAFE_NO_PAD.decode(body).ok()) + .is_some_and(|bytes| bytes.len() == 32) +} + +/// A fresh install id: 16 random bytes as 22 base64url characters. +pub(crate) fn install_id_from_bytes(bytes: &[u8; 16]) -> String { + URL_SAFE_NO_PAD.encode(bytes) +} + +/// Generates a device secret. Keep the token on the device; register its hash. +pub fn wake_generate_device_secret() -> Result { + let mut bytes = [0u8; 32]; + os_random(&mut bytes)?; + Ok(device_secret_from_bytes(&bytes)) +} + +/// Generates an install id, stable for the life of the install. +pub fn wake_generate_install_id() -> String { + let mut bytes = [0u8; 16]; + OsRng.fill_bytes(&mut bytes); + install_id_from_bytes(&bytes) +} + +/// Validates a registration and builds the message every identity signs. +/// `timestamp` defaults to now and must be within the gateway's skew window +/// (600 s) when the request is sent. +#[allow(clippy::too_many_arguments)] +pub fn wake_prepare_registration( + audience: String, + app: String, + install_id: String, + platform: WakePlatform, + environment: WakeEnvironment, + push_token: String, + encryption_public_key: String, + secret_sha256: String, + identities: Vec, + topics: Vec, + timestamp: Option, +) -> Result { + let timestamp = match timestamp { + Some(ts) => ts, + None => SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(|_| WakeError::invalid_input("the system clock is before 1970"))? + .as_secs(), + }; + let fields = RegistrationFields { + audience: &audience, + app: &app, + install_id: &install_id, + platform, + environment, + push_token: &push_token, + encryption_key: &encryption_public_key, + secret_sha256: &secret_sha256, + identities: &identities, + topics: &topics, + ts: timestamp, + }; + let message = fields.preimage()?; + validate(&fields)?; + Ok(WakeRegistrationRequest { + audience, + app, + install_id, + platform, + environment, + push_token, + encryption_public_key, + secret_sha256, + identities, + topics, + timestamp, + message, + }) +} + +/// Signs a registration message with a pubky Ed25519 secret key and returns +/// the 128-character lowercase hex signature. Only registration messages +/// are signed, so the key cannot be used here to sign anything else. +pub fn wake_sign_pubky_proof(secret_key_hex: String, message: String) -> Result { + if !message + .strip_prefix(PREIMAGE_TAG) + .is_some_and(|rest| rest.starts_with('\n')) + { + return Err(WakeError::invalid_input( + "only wake registration messages can be signed", + )); + } + let seed = parse_hex_32(&secret_key_hex) + .ok_or_else(|| WakeError::invalid_key("secret key must be 32 hex bytes"))?; + let keypair = pubky::Keypair::from_secret(&seed); + Ok(hex::encode(keypair.sign(message.as_bytes()).to_bytes())) +} diff --git a/src/modules/wake/test_vectors/legacy_registration.json b/src/modules/wake/test_vectors/legacy_registration.json new file mode 100644 index 0000000..e0e8325 --- /dev/null +++ b/src/modules/wake/test_vectors/legacy_registration.json @@ -0,0 +1,14 @@ +{ + "description": "Legacy POST /api/device signature: the node signs \"bitkit-notifications\" + deviceToken + isoTimestamp as a Lightning signed message (RFC 6979). Both encodings verify on the legacy route.", + "node_secret_key": "3333333333333333333333333333333333333333333333333333333333333333", + "node_id": "023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b1", + "device_token": "00fc13adff785122b4ad28809a3420982341241421348097878e577c991de8f0", + "iso_timestamp": "2024-01-01T00:00:00.000Z", + "message": "bitkit-notifications00fc13adff785122b4ad28809a3420982341241421348097878e577c991de8f02024-01-01T00:00:00.000Z", + "message_hash": "d71b311e68f324dfd117c3f3f868c4ba43b288351123765e58f8254b40fc7d2d", + "recovery_header": 31, + "signature_zbase32": "d7pcmmefqgy3qqi69fdsi3f6z7uot9m3bbe8baeciprhckdrscizyr7ks5yat84yn83zayon894qyijsxc6s5ufshs68ppa8sh1j5nwo", + "signature_hex": "1f5ac5ad057181973abef9476ae4bebf6708fd79085070e10cab49c62864b32b7013aab6c1889f4011f37c02023ff4e055367b3d6dccb6e5bc76b707b7249d8a90", + "device_secret_key": "2222222222222222222222222222222222222222222222222222222222222222", + "request_body": "{\"deviceToken\":\"00fc13adff785122b4ad28809a3420982341241421348097878e577c991de8f0\",\"features\":[\"blocktank.incomingHtlc\",\"blocktank.mutualClose\",\"blocktank.orderPaymentConfirmed\",\"blocktank.cjitPaymentArrived\",\"blocktank.wakeToTimeout\"],\"isProduction\":true,\"isoTimestamp\":\"2024-01-01T00:00:00.000Z\",\"nodeId\":\"023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b1\",\"publicKey\":\"02466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27\",\"signature\":\"d7pcmmefqgy3qqi69fdsi3f6z7uot9m3bbe8baeciprhckdrscizyr7ks5yat84yn83zayon894qyijsxc6s5ufshs68ppa8sh1j5nwo\"}" +} diff --git a/src/modules/wake/test_vectors/pushes.json b/src/modules/wake/test_vectors/pushes.json new file mode 100644 index 0000000..b2799e0 --- /dev/null +++ b/src/modules/wake/test_vectors/pushes.json @@ -0,0 +1,322 @@ +{ + "description": "Every provider request shape, byte-exact. APNs headers are listed in order; FCM bodies are POSTed to {fcm.base_url}/v1/projects/{project}/messages:send. Cases with device_secret_key decrypt to plaintext. inputs.collapse_key is the producer's collapse_key; topics with collapse = \"topic\" (every peer topic) pass none, so their background and FCM v1 pushes carry no collapse id.", + "collapse_rule": "collapse(x) = x when it is 1 to 64 visible ASCII characters (0x21 to 0x7e), else the 64 hex characters of sha256(x)", + "cases": [ + { + "name": "apns_v0", + "provider": "apns", + "inputs": { + "apns_topic": "to.bitkit", + "apns_id": "01a0c450-6c00-7000-8000-0000000000d1", + "expiration": 1790000060, + "container": "{\"cipher\":\"mvdDR3nQxKFTs/N1fQ5z38Zw079ynGfhKTULDq9Mzm96yPl4T4HRyvGLxpqEz9Kq/zeJnUbNzNNPFA2Kh5bPISCuuna+L375NLJgKEN65Aqu6q8KLXECOLhlgdXTEt38xl946MINUfY45uWdtO9h\",\"iv\":\"000102030405060708090a0b0c0d0e0f\",\"tag\":\"2bacdc4bd3ba62cdc3376e302dd70a5f\",\"publicKey\":\"034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa\"}" + }, + "headers": [ + [ + "apns-topic", + "to.bitkit" + ], + [ + "apns-id", + "01a0c450-6c00-7000-8000-0000000000d1" + ], + [ + "apns-expiration", + "1790000060" + ], + [ + "apns-push-type", + "alert" + ], + [ + "apns-priority", + "10" + ] + ], + "body": "{\"aps\":{\"badge\":0,\"sound\":\"ping.aiff\",\"alert\":{\"title\":\"Background Operation Request\",\"body\":\"Please open your wallet.\",\"payload\":{\"cipher\":\"mvdDR3nQxKFTs/N1fQ5z38Zw079ynGfhKTULDq9Mzm96yPl4T4HRyvGLxpqEz9Kq/zeJnUbNzNNPFA2Kh5bPISCuuna+L375NLJgKEN65Aqu6q8KLXECOLhlgdXTEt38xl946MINUfY45uWdtO9h\",\"iv\":\"000102030405060708090a0b0c0d0e0f\",\"tag\":\"2bacdc4bd3ba62cdc3376e302dd70a5f\",\"publicKey\":\"034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa\"}},\"content-available\":1,\"mutable-content\":1}}", + "body_len": 498, + "device_secret_key": "2222222222222222222222222222222222222222222222222222222222222222", + "plaintext": "{\"source\":\"blocktank\",\"type\":\"incomingHtlc\",\"payload\":{\"orderId\":\"abc\"},\"createdAt\":\"2024-01-01T00:00:00.000Z\"}" + }, + { + "name": "apns_v1_background", + "provider": "apns", + "inputs": { + "apns_topic": "to.bitkit", + "apns_id": "01a0c450-6c00-7000-8000-0000000000d2", + "expiration": 1790000060, + "wake_id": "01a0c450-6c00-7000-8000-000000000001", + "topic": "blocktank.incomingHtlc", + "collapse_key": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "container": "{\"v\":1,\"cipher\":\"bXIadw8XhVApCI59oieKY3P/h33PZz6NSdVFABqcg9CSgNhH5L+odPp6fRlxsbx/A37B0ZTxkuZgONflwXB4AyprdoQGEuDEbM8NSRT1j9uAzrfBR098QxyA5eA4nsKbginEF+oPHY97X0yzHZwxV7qd69X4SKve9PaXn4li383A1ZTqlcXJhtAWA7U5YHiFFrbiHAoQgUr7D01/z0vPKtHvryG+GD0rtFnrIzNwrwt6MH9hGO8gYHp1xPRO0bs432EnkOUO7rRv0JwD6q1VQ+GJrtEp/n8p5tW3QzOd8cexBUGoQM+Q6HlNHFRROjBM1fTIsaZj\",\"iv\":\"000102030405060708090a0b\",\"tag\":\"4521e07642272d9b8aed5157c331ad7a\",\"publicKey\":\"035ab4689e400a4a160cf01cd44730845a54768df8547dcdf073d964f109f18c30\"}" + }, + "headers": [ + [ + "apns-topic", + "to.bitkit" + ], + [ + "apns-id", + "01a0c450-6c00-7000-8000-0000000000d2" + ], + [ + "apns-expiration", + "1790000060" + ], + [ + "apns-push-type", + "background" + ], + [ + "apns-priority", + "5" + ], + [ + "apns-collapse-id", + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ] + ], + "body": "{\"aps\":{\"content-available\":1},\"wake\":{\"v\":1,\"cipher\":\"bXIadw8XhVApCI59oieKY3P/h33PZz6NSdVFABqcg9CSgNhH5L+odPp6fRlxsbx/A37B0ZTxkuZgONflwXB4AyprdoQGEuDEbM8NSRT1j9uAzrfBR098QxyA5eA4nsKbginEF+oPHY97X0yzHZwxV7qd69X4SKve9PaXn4li383A1ZTqlcXJhtAWA7U5YHiFFrbiHAoQgUr7D01/z0vPKtHvryG+GD0rtFnrIzNwrwt6MH9hGO8gYHp1xPRO0bs432EnkOUO7rRv0JwD6q1VQ+GJrtEp/n8p5tW3QzOd8cexBUGoQM+Q6HlNHFRROjBM1fTIsaZj\",\"iv\":\"000102030405060708090a0b\",\"tag\":\"4521e07642272d9b8aed5157c331ad7a\",\"publicKey\":\"035ab4689e400a4a160cf01cd44730845a54768df8547dcdf073d964f109f18c30\"}}", + "body_len": 540, + "device_secret_key": "5555555555555555555555555555555555555555555555555555555555555555", + "plaintext": "{\"v\":1,\"id\":\"01a0c450-6c00-7000-8000-000000000001\",\"topic\":\"blocktank.incomingHtlc\",\"urgency\":\"background\",\"deadline\":1790000060,\"payload\":{\"lspId\":\"blocktank-lsp\",\"paymentHash\":\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\"}}" + }, + { + "name": "apns_v1_background_topic_collapse", + "provider": "apns", + "inputs": { + "apns_topic": "to.bitkit", + "apns_id": "01a0c450-6c00-7000-8000-0000000000d6", + "expiration": 1790000600, + "wake_id": "01a0c450-6c00-7000-8000-000000000004", + "topic": "paykit.sync", + "collapse_key": null, + "container": "{\"v\":1,\"cipher\":\"nMvhVZYC5nYbEsJ9PMETt49AfmbSzDKo5KDW7OCsyeEZmRP3PZHvTkQcQ3UGYV8d4hsdMaffBIiXd9mrYmxKmr5YeB/NwOuYYUHOT2CdRBUzDwXzwlCBvHQyjBwhSyhxSlwGEWXy4bok+/AEdFLcYgGPfevEhMK+GmGc45s9PR7//n2T1A==\",\"iv\":\"000102030405060708090a0b\",\"tag\":\"eccbf52e56ed92385a2b5ae895a352ea\",\"publicKey\":\"02666bdf2025e32f41088899f2bcb4bf6983187f380e72fc7dee115b1f9957cc72\"}" + }, + "headers": [ + [ + "apns-topic", + "to.bitkit" + ], + [ + "apns-id", + "01a0c450-6c00-7000-8000-0000000000d6" + ], + [ + "apns-expiration", + "1790000600" + ], + [ + "apns-push-type", + "background" + ], + [ + "apns-priority", + "5" + ] + ], + "body": "{\"aps\":{\"content-available\":1},\"wake\":{\"v\":1,\"cipher\":\"nMvhVZYC5nYbEsJ9PMETt49AfmbSzDKo5KDW7OCsyeEZmRP3PZHvTkQcQ3UGYV8d4hsdMaffBIiXd9mrYmxKmr5YeB/NwOuYYUHOT2CdRBUzDwXzwlCBvHQyjBwhSyhxSlwGEWXy4bok+/AEdFLcYgGPfevEhMK+GmGc45s9PR7//n2T1A==\",\"iv\":\"000102030405060708090a0b\",\"tag\":\"eccbf52e56ed92385a2b5ae895a352ea\",\"publicKey\":\"02666bdf2025e32f41088899f2bcb4bf6983187f380e72fc7dee115b1f9957cc72\"}}", + "body_len": 392, + "device_secret_key": "5555555555555555555555555555555555555555555555555555555555555555", + "plaintext": "{\"v\":1,\"id\":\"01a0c450-6c00-7000-8000-000000000004\",\"topic\":\"paykit.sync\",\"urgency\":\"background\",\"deadline\":1790000600,\"payload\":null}" + }, + { + "name": "apns_v1_alert", + "provider": "apns", + "inputs": { + "apns_topic": "to.bitkit", + "apns_id": "01a0c450-6c00-7000-8000-0000000000d3", + "expiration": 1790086400, + "wake_id": "01a0c450-6c00-7000-8000-000000000002", + "topic": "blocktank.mutualClose", + "collapse_key": null, + "title": "Spending Balance Expired", + "body": "Open Bitkit to move funds from spending to savings", + "interruption_level": "active", + "container": "{\"v\":1,\"cipher\":\"/2jwGSI3YmZGs/MT/ImtkUPt18COPSJjr5+aEYM2cWSHXGAQRlkSnjMKtwn/cGcS5jBCgNizbCDdWOwKrjVM8aO8G1+0rjOq/c4APXHNHQfO7kyPF8E54uvX2BKg09vDks3mP9VyfEOB4DoCyXJbLBiWJOr3ue+zIJ9Le34Z2q5YMx5VM0cp4OFPB6s8vSo9wpz8hDZebvNPYECnfZ0Y\",\"iv\":\"000102030405060708090a0b\",\"tag\":\"24acda5546c94f893d9691094c96fb31\",\"publicKey\":\"02a1474ca48f28ec81e0c4bade52a4ccdf9ace80b438382464f14f7ba15ea3dc58\"}" + }, + "headers": [ + [ + "apns-topic", + "to.bitkit" + ], + [ + "apns-id", + "01a0c450-6c00-7000-8000-0000000000d3" + ], + [ + "apns-expiration", + "1790086400" + ], + [ + "apns-push-type", + "alert" + ], + [ + "apns-priority", + "10" + ], + [ + "apns-collapse-id", + "01a0c450-6c00-7000-8000-000000000002" + ] + ], + "body": "{\"aps\":{\"alert\":{\"title\":\"Spending Balance Expired\",\"body\":\"Open Bitkit to move funds from spending to savings\"},\"sound\":\"default\",\"mutable-content\":1,\"interruption-level\":\"active\",\"thread-id\":\"blocktank.mutualClose\"},\"wake\":{\"v\":1,\"cipher\":\"/2jwGSI3YmZGs/MT/ImtkUPt18COPSJjr5+aEYM2cWSHXGAQRlkSnjMKtwn/cGcS5jBCgNizbCDdWOwKrjVM8aO8G1+0rjOq/c4APXHNHQfO7kyPF8E54uvX2BKg09vDks3mP9VyfEOB4DoCyXJbLBiWJOr3ue+zIJ9Le34Z2q5YMx5VM0cp4OFPB6s8vSo9wpz8hDZebvNPYECnfZ0Y\",\"iv\":\"000102030405060708090a0b\",\"tag\":\"24acda5546c94f893d9691094c96fb31\",\"publicKey\":\"02a1474ca48f28ec81e0c4bade52a4ccdf9ace80b438382464f14f7ba15ea3dc58\"}}", + "body_len": 611, + "device_secret_key": "5555555555555555555555555555555555555555555555555555555555555555", + "plaintext": "{\"v\":1,\"id\":\"01a0c450-6c00-7000-8000-000000000002\",\"topic\":\"blocktank.mutualClose\",\"urgency\":\"alert\",\"deadline\":1790086400,\"payload\":{\"lspId\":\"blocktank-lsp\"}}" + }, + { + "name": "apns_v1_time_sensitive", + "provider": "apns", + "inputs": { + "apns_topic": "to.bitkit", + "apns_id": "01a0c450-6c00-7000-8000-0000000000d4", + "expiration": 1790000060, + "wake_id": "01a0c450-6c00-7000-8000-000000000003", + "topic": "blocktank.incomingHtlc", + "collapse_key": "incomingHtlc:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "title": "Incoming Payment", + "body": "Open Bitkit now to receive your payment", + "interruption_level": "time-sensitive", + "container": "{\"v\":1,\"cipher\":\"N8vLNLPyiSvIytusmyeLOes0HH5PiRqiWP1PaZMnU7tzjAXeWkjOfPTHmd81o1zXjtqfj08qV6+nhebRqoFKATvSzGoUeKnhp8AGmAgNBjNklfgJx8GkKjFyaj00975BlBI4s3kfElILeqjis+6KhBHx/gcn+YHIpwyuA91bHEY5C07Nl0hy6jTGesU3h5pxBrSq+JvWz8EBKsVCAWNFAxW/FfRZWUWyFgtjwdl6b1gTM2MLQZ4lqTbftYTJQ7R++g4d/J9NdoDXHvF9gf95nI73NYlEqMFKUUmvMVy8l6eHOP3WFx9e4z/MAlY9O6id2MZrXugm8HoqPQ==\",\"iv\":\"000102030405060708090a0b\",\"tag\":\"08e2e6ca1df355ca8907d3ba21245464\",\"publicKey\":\"03652d1b5efa70cb3b83a53cdf536172d205001f0dd54ca766a6a4ef766a48e6fb\"}" + }, + "headers": [ + [ + "apns-topic", + "to.bitkit" + ], + [ + "apns-id", + "01a0c450-6c00-7000-8000-0000000000d4" + ], + [ + "apns-expiration", + "1790000060" + ], + [ + "apns-push-type", + "alert" + ], + [ + "apns-priority", + "10" + ], + [ + "apns-collapse-id", + "abda80501930fdfd4ae15e1b29c5df0a047946cf41cfd257312306e4a9d8d8a5" + ] + ], + "body": "{\"aps\":{\"alert\":{\"title\":\"Incoming Payment\",\"body\":\"Open Bitkit now to receive your payment\"},\"sound\":\"default\",\"mutable-content\":1,\"interruption-level\":\"time-sensitive\",\"thread-id\":\"blocktank.incomingHtlc\"},\"wake\":{\"v\":1,\"cipher\":\"N8vLNLPyiSvIytusmyeLOes0HH5PiRqiWP1PaZMnU7tzjAXeWkjOfPTHmd81o1zXjtqfj08qV6+nhebRqoFKATvSzGoUeKnhp8AGmAgNBjNklfgJx8GkKjFyaj00975BlBI4s3kfElILeqjis+6KhBHx/gcn+YHIpwyuA91bHEY5C07Nl0hy6jTGesU3h5pxBrSq+JvWz8EBKsVCAWNFAxW/FfRZWUWyFgtjwdl6b1gTM2MLQZ4lqTbftYTJQ7R++g4d/J9NdoDXHvF9gf95nI73NYlEqMFKUUmvMVy8l6eHOP3WFx9e4z/MAlY9O6id2MZrXugm8HoqPQ==\",\"iv\":\"000102030405060708090a0b\",\"tag\":\"08e2e6ca1df355ca8907d3ba21245464\",\"publicKey\":\"03652d1b5efa70cb3b83a53cdf536172d205001f0dd54ca766a6a4ef766a48e6fb\"}}", + "body_len": 725, + "device_secret_key": "5555555555555555555555555555555555555555555555555555555555555555", + "plaintext": "{\"v\":1,\"id\":\"01a0c450-6c00-7000-8000-000000000003\",\"topic\":\"blocktank.incomingHtlc\",\"urgency\":\"time_sensitive\",\"deadline\":1790000060,\"payload\":{\"lspId\":\"blocktank-lsp\",\"paymentHash\":\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\"}}" + }, + { + "name": "apns_fallback", + "provider": "apns", + "inputs": { + "apns_topic": "to.bitkit", + "apns_id": "01a0c450-6c00-7000-8000-0000000000d5", + "expiration": 1790000060, + "wake_id": "01a0c450-6c00-7000-8000-000000000001", + "topic": "blocktank.incomingHtlc", + "collapse_key": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "title": "Incoming Payment", + "body": "Open Bitkit now to receive your payment", + "interruption_level": "time-sensitive" + }, + "headers": [ + [ + "apns-topic", + "to.bitkit" + ], + [ + "apns-id", + "01a0c450-6c00-7000-8000-0000000000d5" + ], + [ + "apns-expiration", + "1790000060" + ], + [ + "apns-push-type", + "alert" + ], + [ + "apns-priority", + "10" + ], + [ + "apns-collapse-id", + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ] + ], + "body": "{\"aps\":{\"alert\":{\"title\":\"Incoming Payment\",\"body\":\"Open Bitkit now to receive your payment\"},\"sound\":\"default\",\"interruption-level\":\"time-sensitive\",\"thread-id\":\"blocktank.incomingHtlc\"},\"wake_fallback\":{\"v\":1,\"id\":\"01a0c450-6c00-7000-8000-000000000001\",\"topic\":\"blocktank.incomingHtlc\"}}", + "body_len": 289 + }, + { + "name": "fcm_v0", + "provider": "fcm", + "inputs": { + "token": "e5Hj9fKqTmW-vector:APA91bG0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + "ttl_secs": 60, + "container": "{\"cipher\":\"mvdDR3nQxKFTs/N1fQ5z38Zw079ynGfhKTULDq9Mzm96yPl4T4HRyvGLxpqEz9Kq/zeJnUbNzNNPFA2Kh5bPISCuuna+L375NLJgKEN65Aqu6q8KLXECOLhlgdXTEt38xl946MINUfY45uWdtO9h\",\"iv\":\"000102030405060708090a0b0c0d0e0f\",\"tag\":\"2bacdc4bd3ba62cdc3376e302dd70a5f\",\"publicKey\":\"034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa\"}" + }, + "body": "{\"message\":{\"token\":\"e5Hj9fKqTmW-vector:APA91bG0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\",\"android\":{\"priority\":\"high\",\"ttl\":\"60s\",\"data\":{\"cipher\":\"mvdDR3nQxKFTs/N1fQ5z38Zw079ynGfhKTULDq9Mzm96yPl4T4HRyvGLxpqEz9Kq/zeJnUbNzNNPFA2Kh5bPISCuuna+L375NLJgKEN65Aqu6q8KLXECOLhlgdXTEt38xl946MINUfY45uWdtO9h\",\"iv\":\"000102030405060708090a0b0c0d0e0f\",\"tag\":\"2bacdc4bd3ba62cdc3376e302dd70a5f\",\"publicKey\":\"034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa\",\"title\":\"Background Operation Request\",\"message\":\"Please open your wallet.\",\"sound\":\"default\"}},\"data\":{\"cipher\":\"mvdDR3nQxKFTs/N1fQ5z38Zw079ynGfhKTULDq9Mzm96yPl4T4HRyvGLxpqEz9Kq/zeJnUbNzNNPFA2Kh5bPISCuuna+L375NLJgKEN65Aqu6q8KLXECOLhlgdXTEt38xl946MINUfY45uWdtO9h\",\"iv\":\"000102030405060708090a0b0c0d0e0f\",\"tag\":\"2bacdc4bd3ba62cdc3376e302dd70a5f\",\"publicKey\":\"034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa\"}}}", + "body_len": 944, + "device_secret_key": "2222222222222222222222222222222222222222222222222222222222222222", + "plaintext": "{\"source\":\"blocktank\",\"type\":\"incomingHtlc\",\"payload\":{\"orderId\":\"abc\"},\"createdAt\":\"2024-01-01T00:00:00.000Z\"}" + }, + { + "name": "fcm_v1", + "provider": "fcm", + "inputs": { + "token": "e5Hj9fKqTmW-vector:APA91bG0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + "ttl_secs": 60, + "priority": "high", + "wake_id": "01a0c450-6c00-7000-8000-000000000001", + "topic": "blocktank.incomingHtlc", + "collapse_key": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "container": "{\"v\":1,\"cipher\":\"bXIadw8XhVApCI59oieKY3P/h33PZz6NSdVFABqcg9CSgNhH5L+odPp6fRlxsbx/A37B0ZTxkuZgONflwXB4AyprdoQGEuDEbM8NSRT1j9uAzrfBR098QxyA5eA4nsKbginEF+oPHY97X0yzHZwxV7qd69X4SKve9PaXn4li383A1ZTqlcXJhtAWA7U5YHiFFrbiHAoQgUr7D01/z0vPKtHvryG+GD0rtFnrIzNwrwt6MH9hGO8gYHp1xPRO0bs432EnkOUO7rRv0JwD6q1VQ+GJrtEp/n8p5tW3QzOd8cexBUGoQM+Q6HlNHFRROjBM1fTIsaZj\",\"iv\":\"000102030405060708090a0b\",\"tag\":\"4521e07642272d9b8aed5157c331ad7a\",\"publicKey\":\"035ab4689e400a4a160cf01cd44730845a54768df8547dcdf073d964f109f18c30\"}" + }, + "body": "{\"message\":{\"token\":\"e5Hj9fKqTmW-vector:APA91bG0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\",\"android\":{\"priority\":\"high\",\"ttl\":\"60s\",\"collapse_key\":\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\"},\"data\":{\"wake\":\"{\\\"v\\\":1,\\\"cipher\\\":\\\"bXIadw8XhVApCI59oieKY3P/h33PZz6NSdVFABqcg9CSgNhH5L+odPp6fRlxsbx/A37B0ZTxkuZgONflwXB4AyprdoQGEuDEbM8NSRT1j9uAzrfBR098QxyA5eA4nsKbginEF+oPHY97X0yzHZwxV7qd69X4SKve9PaXn4li383A1ZTqlcXJhtAWA7U5YHiFFrbiHAoQgUr7D01/z0vPKtHvryG+GD0rtFnrIzNwrwt6MH9hGO8gYHp1xPRO0bs432EnkOUO7rRv0JwD6q1VQ+GJrtEp/n8p5tW3QzOd8cexBUGoQM+Q6HlNHFRROjBM1fTIsaZj\\\",\\\"iv\\\":\\\"000102030405060708090a0b\\\",\\\"tag\\\":\\\"4521e07642272d9b8aed5157c331ad7a\\\",\\\"publicKey\\\":\\\"035ab4689e400a4a160cf01cd44730845a54768df8547dcdf073d964f109f18c30\\\"}\"}}}", + "body_len": 808, + "device_secret_key": "5555555555555555555555555555555555555555555555555555555555555555", + "plaintext": "{\"v\":1,\"id\":\"01a0c450-6c00-7000-8000-000000000001\",\"topic\":\"blocktank.incomingHtlc\",\"urgency\":\"background\",\"deadline\":1790000060,\"payload\":{\"lspId\":\"blocktank-lsp\",\"paymentHash\":\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\"}}" + }, + { + "name": "fcm_v1_topic_collapse", + "provider": "fcm", + "inputs": { + "token": "e5Hj9fKqTmW-vector:APA91bG0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + "ttl_secs": 600, + "priority": "normal", + "wake_id": "01a0c450-6c00-7000-8000-000000000004", + "topic": "paykit.sync", + "collapse_key": null, + "container": "{\"v\":1,\"cipher\":\"nMvhVZYC5nYbEsJ9PMETt49AfmbSzDKo5KDW7OCsyeEZmRP3PZHvTkQcQ3UGYV8d4hsdMaffBIiXd9mrYmxKmr5YeB/NwOuYYUHOT2CdRBUzDwXzwlCBvHQyjBwhSyhxSlwGEWXy4bok+/AEdFLcYgGPfevEhMK+GmGc45s9PR7//n2T1A==\",\"iv\":\"000102030405060708090a0b\",\"tag\":\"eccbf52e56ed92385a2b5ae895a352ea\",\"publicKey\":\"02666bdf2025e32f41088899f2bcb4bf6983187f380e72fc7dee115b1f9957cc72\"}" + }, + "body": "{\"message\":{\"token\":\"e5Hj9fKqTmW-vector:APA91bG0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\",\"android\":{\"priority\":\"normal\",\"ttl\":\"600s\"},\"data\":{\"wake\":\"{\\\"v\\\":1,\\\"cipher\\\":\\\"nMvhVZYC5nYbEsJ9PMETt49AfmbSzDKo5KDW7OCsyeEZmRP3PZHvTkQcQ3UGYV8d4hsdMaffBIiXd9mrYmxKmr5YeB/NwOuYYUHOT2CdRBUzDwXzwlCBvHQyjBwhSyhxSlwGEWXy4bok+/AEdFLcYgGPfevEhMK+GmGc45s9PR7//n2T1A==\\\",\\\"iv\\\":\\\"000102030405060708090a0b\\\",\\\"tag\\\":\\\"eccbf52e56ed92385a2b5ae895a352ea\\\",\\\"publicKey\\\":\\\"02666bdf2025e32f41088899f2bcb4bf6983187f380e72fc7dee115b1f9957cc72\\\"}\"}}}", + "body_len": 581, + "device_secret_key": "5555555555555555555555555555555555555555555555555555555555555555", + "plaintext": "{\"v\":1,\"id\":\"01a0c450-6c00-7000-8000-000000000004\",\"topic\":\"paykit.sync\",\"urgency\":\"background\",\"deadline\":1790000600,\"payload\":null}" + }, + { + "name": "fcm_fallback", + "provider": "fcm", + "inputs": { + "token": "e5Hj9fKqTmW-vector:APA91bG0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + "ttl_secs": 45, + "wake_id": "01a0c450-6c00-7000-8000-000000000001", + "topic": "blocktank.incomingHtlc", + "collapse_key": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "title": "Incoming Payment", + "body": "Open Bitkit now to receive your payment", + "channel_id": "wake" + }, + "body": "{\"message\":{\"token\":\"e5Hj9fKqTmW-vector:APA91bG0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\",\"android\":{\"priority\":\"high\",\"ttl\":\"45s\",\"notification\":{\"title\":\"Incoming Payment\",\"body\":\"Open Bitkit now to receive your payment\",\"channel_id\":\"wake\",\"tag\":\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\"}},\"data\":{\"wake_fallback\":\"{\\\"v\\\":1,\\\"id\\\":\\\"01a0c450-6c00-7000-8000-000000000001\\\",\\\"topic\\\":\\\"blocktank.incomingHtlc\\\"}\"}}}", + "body_len": 496 + } + ] +} diff --git a/src/modules/wake/test_vectors/registration_v1.json b/src/modules/wake/test_vectors/registration_v1.json new file mode 100644 index 0000000..b8ef817 --- /dev/null +++ b/src/modules/wake/test_vectors/registration_v1.json @@ -0,0 +1,45 @@ +{ + "description": "POST /v1/devices: the 12-line preimage every identity signs, an ln: proof (Lightning signed message, zbase32) and a pk: proof (Ed25519, hex) over it, and the full request body.", + "audience": "wake.localhost", + "encryption_secret_key": "5555555555555555555555555555555555555555555555555555555555555555", + "device_secret_bytes": "7777777777777777777777777777777777777777777777777777777777777777", + "device_secret": "wkd_d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3c", + "ln_secret_key": "3333333333333333333333333333333333333333333333333333333333333333", + "ln_identity": "ln:023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b1", + "pk_seed": "4444444444444444444444444444444444444444444444444444444444444444", + "pk_public_key": "d759793bbc13a2819a827c76adb6fba8a49aee007f49f2d0992d99b825ad2c48", + "pk_identity": "pk:47cz1q7hnqtedgwnxt5k5pz5in1ji5oyx7r9fwr3fsc5ojppftry", + "preimage": "wake-register-v1\naud:wake.localhost\napp:bitkit\ninstall:AAAAAAAAAAAAAAAAAAAAAA\nplatform:apns\nenvironment:sandbox\npush_token:00fc13adff785122b4ad28809a3420982341241421348097878e577c991de8f0\nencryption_key:029ac20335eb38768d2052be1dbbc3c8f6178407458e51e6b4ad22f1d91758895b\nsecret_sha256:a4908532a2f65068ce0b3498af2b68e43949be33aa85840288e653295bf52080\nidentities:ln:023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b1,pk:47cz1q7hnqtedgwnxt5k5pz5in1ji5oyx7r9fwr3fsc5ojppftry\ntopics:blocktank.*,paykit.sync\nts:1790000000", + "preimage_sha256": "24a1e86c7c81b6b03c755fa7e18cba5147687fede582e9d0f25a44abd8d00a61", + "ln_message_hash": "d8af6af5731fb4e049049a68dcf4ab7161c1d817499c58d08a467c36301f4b83", + "ln_proof": "rd8ooicd9c6ykxwred545hmdrnnu7nwy6ifj151f9f4cycw5znifgcexjt6bqjwf8c6xfdgzwrs7tfn5o5jcwhdpepif68fwjwu9z4yb", + "pk_proof": "1412afa4780e7db59294f0dca42dda93248b54a4c0d4b29c4d6b35bd9d270826faf66d7b9b3ccf6b660ac32313902c8934731ff2fd288cca6ebf96689abe630d", + "request": { + "app": "bitkit", + "install_id": "AAAAAAAAAAAAAAAAAAAAAA", + "platform": "apns", + "environment": "sandbox", + "push_token": "00fc13adff785122b4ad28809a3420982341241421348097878e577c991de8f0", + "encryption_key": "029ac20335eb38768d2052be1dbbc3c8f6178407458e51e6b4ad22f1d91758895b", + "secret_sha256": "a4908532a2f65068ce0b3498af2b68e43949be33aa85840288e653295bf52080", + "identities": [ + "pk:47cz1q7hnqtedgwnxt5k5pz5in1ji5oyx7r9fwr3fsc5ojppftry", + "ln:023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b1" + ], + "topics": [ + "paykit.sync", + "blocktank.*" + ], + "ts": 1790000000, + "proofs": [ + { + "identity": "ln:023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b1", + "sig": "rd8ooicd9c6ykxwred545hmdrnnu7nwy6ifj151f9f4cycw5znifgcexjt6bqjwf8c6xfdgzwrs7tfn5o5jcwhdpepif68fwjwu9z4yb" + }, + { + "identity": "pk:47cz1q7hnqtedgwnxt5k5pz5in1ji5oyx7r9fwr3fsc5ojppftry", + "sig": "1412afa4780e7db59294f0dca42dda93248b54a4c0d4b29c4d6b35bd9d270826faf66d7b9b3ccf6b660ac32313902c8934731ff2fd288cca6ebf96689abe630d" + } + ] + } +} diff --git a/src/modules/wake/test_vectors/v0_client.json b/src/modules/wake/test_vectors/v0_client.json new file mode 100644 index 0000000..3967c29 --- /dev/null +++ b/src/modules/wake/test_vectors/v0_client.json @@ -0,0 +1,15 @@ +{ + "description": "Client-side v0 decryption vector from bitkit-ios BitkitTests/CryptoTests.swift and bitkit-android CryptoTest.kt. The container is assembled from the vector's fields in v0 key order.", + "scheme": "v0", + "label": "bitkit-notifications", + "device_secret_key": "cc74b1a4fdcd35916c766d3318c5a93b7e33a36ebeff0463128bf284975c2680", + "device_public_key": "02f7c231fe139475b8193194b42043ae546768167e5cf836f5a4063f5d448a127f", + "ephemeral_public_key": "031e9923e689a181a803486b7d8c0d4a5aad360edb70c8bb413a98458d91652213", + "shared_point": "028ce542975d6d7b2307c92e527d507b03ffb3d897eb2e0830d29f40d5efd80ee3", + "key": "3a9d552cb16dfae40feae644254c4ca46cab82e570de5662aacc4018e33b609b", + "iv": "2b8ed77fd2198e3ed88cfaa794a246e8", + "tag": "caddd13746d6a6aed16176734964d3a3", + "cipher": "l2fInfyw64gO12odo8iipISloQJ45Rc4WjFmpe95brdaAMDq+T/L9ZChcmMCXnR0J6BXd8sSIJe/0bmby8uSZZJuVCzwF76XHfY5oq0Y1/hKzyZTn8nG3dqfiLHnAPy1tZFQfm5ALgjwWnViYJLXoGFpXs7kLMA=", + "container": "{\"cipher\":\"l2fInfyw64gO12odo8iipISloQJ45Rc4WjFmpe95brdaAMDq+T/L9ZChcmMCXnR0J6BXd8sSIJe/0bmby8uSZZJuVCzwF76XHfY5oq0Y1/hKzyZTn8nG3dqfiLHnAPy1tZFQfm5ALgjwWnViYJLXoGFpXs7kLMA=\",\"iv\":\"2b8ed77fd2198e3ed88cfaa794a246e8\",\"tag\":\"caddd13746d6a6aed16176734964d3a3\",\"publicKey\":\"031e9923e689a181a803486b7d8c0d4a5aad360edb70c8bb413a98458d91652213\"}", + "plaintext": "{\"source\":\"blocktank\",\"type\":\"incomingHtlc\",\"payload\":{\"secretMessage\":\"hello\"},\"createdAt\":\"2024-09-18T13:33:52.555Z\"}" +} diff --git a/src/modules/wake/test_vectors/v0_server.json b/src/modules/wake/test_vectors/v0_server.json new file mode 100644 index 0000000..b66b287 --- /dev/null +++ b/src/modules/wake/test_vectors/v0_server.json @@ -0,0 +1,21 @@ +{ + "description": "Legacy (v0) envelope as the gateway builds it for deployed Bitkit apps; byte-identical to bitkit-notification-server. The ephemeral key is the per-notification server key.", + "scheme": "v0", + "label": "bitkit-notifications", + "ephemeral_secret_key": "1111111111111111111111111111111111111111111111111111111111111111", + "ephemeral_public_key": "034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa", + "device_secret_key": "2222222222222222222222222222222222222222222222222222222222222222", + "device_public_key": "02466d7fcae563e5cb09a0d1870bb580344804617879a14949cf22285f1bae3f27", + "shared_point": "0277e0510d5042e2f5e9e59c977b81eeed590cf7d20c1c51da451a8eaa9fdc45ff", + "key": "954b48c50f1c6bf3a6cd20a6d7354775cb0d712a3e332333c87dcd52b83f42f9", + "iv": "000102030405060708090a0b0c0d0e0f", + "topic": "blocktank.incomingHtlc", + "payload": "{\"orderId\":\"abc\"}", + "created_at": "2024-01-01T00:00:00.000Z", + "plaintext": "{\"source\":\"blocktank\",\"type\":\"incomingHtlc\",\"payload\":{\"orderId\":\"abc\"},\"createdAt\":\"2024-01-01T00:00:00.000Z\"}", + "plaintext_len": 111, + "cipher": "mvdDR3nQxKFTs/N1fQ5z38Zw079ynGfhKTULDq9Mzm96yPl4T4HRyvGLxpqEz9Kq/zeJnUbNzNNPFA2Kh5bPISCuuna+L375NLJgKEN65Aqu6q8KLXECOLhlgdXTEt38xl946MINUfY45uWdtO9h", + "tag": "2bacdc4bd3ba62cdc3376e302dd70a5f", + "container": "{\"cipher\":\"mvdDR3nQxKFTs/N1fQ5z38Zw079ynGfhKTULDq9Mzm96yPl4T4HRyvGLxpqEz9Kq/zeJnUbNzNNPFA2Kh5bPISCuuna+L375NLJgKEN65Aqu6q8KLXECOLhlgdXTEt38xl946MINUfY45uWdtO9h\",\"iv\":\"000102030405060708090a0b0c0d0e0f\",\"tag\":\"2bacdc4bd3ba62cdc3376e302dd70a5f\",\"publicKey\":\"034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa\"}", + "container_len": 323 +} diff --git a/src/modules/wake/test_vectors/v1_envelope.json b/src/modules/wake/test_vectors/v1_envelope.json new file mode 100644 index 0000000..d064e7b --- /dev/null +++ b/src/modules/wake/test_vectors/v1_envelope.json @@ -0,0 +1,18 @@ +{ + "description": "wake v1 envelope: S33 = compressed(ephemeral * device), key = SHA256(SHA256(S33 || \"wake-v1\")), AES-256-GCM with a 12-byte IV and no AAD.", + "scheme": "v1", + "label": "wake-v1", + "ephemeral_secret_key": "6666666666666666666666666666666666666666666666666666666666666666", + "ephemeral_public_key": "035ab4689e400a4a160cf01cd44730845a54768df8547dcdf073d964f109f18c30", + "device_secret_key": "5555555555555555555555555555555555555555555555555555555555555555", + "device_public_key": "029ac20335eb38768d2052be1dbbc3c8f6178407458e51e6b4ad22f1d91758895b", + "shared_point": "02c88d18b3ab2342f968f14ba920a9f47aeb8437a3ac494f4d263a79dd29b75353", + "key": "71ab2d520b559d67a72bbab49dd521fa10a9a6416a55d9dca42b007eca7c5e2e", + "iv": "000102030405060708090a0b", + "plaintext": "{\"v\":1,\"id\":\"01a0c450-6c00-7000-8000-000000000001\",\"topic\":\"blocktank.incomingHtlc\",\"urgency\":\"background\",\"deadline\":1790000060,\"payload\":{\"lspId\":\"blocktank-lsp\",\"paymentHash\":\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\"}}", + "plaintext_len": 246, + "cipher": "bXIadw8XhVApCI59oieKY3P/h33PZz6NSdVFABqcg9CSgNhH5L+odPp6fRlxsbx/A37B0ZTxkuZgONflwXB4AyprdoQGEuDEbM8NSRT1j9uAzrfBR098QxyA5eA4nsKbginEF+oPHY97X0yzHZwxV7qd69X4SKve9PaXn4li383A1ZTqlcXJhtAWA7U5YHiFFrbiHAoQgUr7D01/z0vPKtHvryG+GD0rtFnrIzNwrwt6MH9hGO8gYHp1xPRO0bs432EnkOUO7rRv0JwD6q1VQ+GJrtEp/n8p5tW3QzOd8cexBUGoQM+Q6HlNHFRROjBM1fTIsaZj", + "tag": "4521e07642272d9b8aed5157c331ad7a", + "container": "{\"v\":1,\"cipher\":\"bXIadw8XhVApCI59oieKY3P/h33PZz6NSdVFABqcg9CSgNhH5L+odPp6fRlxsbx/A37B0ZTxkuZgONflwXB4AyprdoQGEuDEbM8NSRT1j9uAzrfBR098QxyA5eA4nsKbginEF+oPHY97X0yzHZwxV7qd69X4SKve9PaXn4li383A1ZTqlcXJhtAWA7U5YHiFFrbiHAoQgUr7D01/z0vPKtHvryG+GD0rtFnrIzNwrwt6MH9hGO8gYHp1xPRO0bs432EnkOUO7rRv0JwD6q1VQ+GJrtEp/n8p5tW3QzOd8cexBUGoQM+Q6HlNHFRROjBM1fTIsaZj\",\"iv\":\"000102030405060708090a0b\",\"tag\":\"4521e07642272d9b8aed5157c331ad7a\",\"publicKey\":\"035ab4689e400a4a160cf01cd44730845a54768df8547dcdf073d964f109f18c30\"}", + "container_len": 501 +} diff --git a/src/modules/wake/tests.rs b/src/modules/wake/tests.rs new file mode 100644 index 0000000..232e5f3 --- /dev/null +++ b/src/modules/wake/tests.rs @@ -0,0 +1,1779 @@ +use bitcoin::hashes::{sha256, Hash}; +use bitcoin::secp256k1::{Message, PublicKey, Secp256k1, SecretKey}; +use reqwest::Method; +use serde_json::{json, Value}; + +use super::client::*; +use super::crypto::*; +use super::push::*; +use super::registration::*; +use super::*; + +// The golden vectors of wake-proto, copied byte for byte (see README.md). +const V0_SERVER: &str = include_str!("test_vectors/v0_server.json"); +const V0_CLIENT: &str = include_str!("test_vectors/v0_client.json"); +const V1_ENVELOPE: &str = include_str!("test_vectors/v1_envelope.json"); +const REGISTRATION_V1: &str = include_str!("test_vectors/registration_v1.json"); +const LEGACY_REGISTRATION: &str = include_str!("test_vectors/legacy_registration.json"); +const PUSHES: &str = include_str!("test_vectors/pushes.json"); + +const GATEWAY: &str = "https://wake.example"; +const DEVICE_SECRET: &str = "wkd_d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3c"; +const WAKE_ID: &str = "01a0c450-6c00-7000-8000-000000000001"; + +fn load(text: &str) -> Value { + serde_json::from_str(text).unwrap() +} + +fn s<'a>(v: &'a Value, key: &str) -> &'a str { + v[key] + .as_str() + .unwrap_or_else(|| panic!("missing string {key}")) +} + +fn secret(v: &Value, key: &str) -> SecretKey { + parse_secret_key(s(v, key)).unwrap() +} + +fn public_hex(secret: &SecretKey) -> String { + hex::encode(PublicKey::from_secret_key(&Secp256k1::signing_only(), secret).serialize()) +} + +fn bytes33(hex_str: &str) -> [u8; 33] { + hex::decode(hex_str).unwrap().try_into().unwrap() +} + +fn invalid_input(reason: &str) -> WakeError { + WakeError::InvalidInput { + reason: reason.to_string(), + } +} + +fn unsupported(reason: &str) -> WakeError { + WakeError::UnsupportedEnvelope { + reason: reason.to_string(), + } +} + +fn decryption_failed() -> WakeError { + WakeError::DecryptionFailed { + reason: "authentication failed".to_string(), + } +} + +/// `SHA256(SHA256("Lightning Signed Message:" || message))`. +fn lightning_message_hash(message: &[u8]) -> [u8; 32] { + let mut input = b"Lightning Signed Message:".to_vec(); + input.extend_from_slice(message); + sha256::Hash::hash(sha256::Hash::hash(&input).as_byte_array()).to_byte_array() +} + +/// A local stand-in for ldk-node `signMessage`: a recoverable RFC 6979 +/// signature, `[31 + recid] || r || s`, in zbase32. +fn lightning_sign(secret: &SecretKey, message: &[u8]) -> String { + let digest = Message::from_digest(lightning_message_hash(message)); + let (recovery_id, compact) = Secp256k1::signing_only() + .sign_ecdsa_recoverable(&digest, secret) + .serialize_compact(); + let mut bytes = vec![31 + recovery_id.to_i32() as u8]; + bytes.extend_from_slice(&compact); + zbase32(&bytes) +} + +fn zbase32(bytes: &[u8]) -> String { + const ALPHABET: &[u8; 32] = b"ybndrfg8ejkmcpqxot1uwisza345h769"; + let mut out = String::new(); + let (mut buffer, mut bits) = (0u32, 0u32); + for &byte in bytes { + buffer = (buffer << 8) | u32::from(byte); + bits += 8; + while bits >= 5 { + bits -= 5; + out.push(ALPHABET[((buffer >> bits) & 31) as usize] as char); + } + buffer &= (1 << bits) - 1; + } + if bits > 0 { + out.push(ALPHABET[((buffer << (5 - bits)) & 31) as usize] as char); + } + out +} + +/// Checks an envelope vector's intermediates from the file alone: the +/// device's view (S33 from its secret and the ephemeral key), the key, the +/// container round trip and the plaintext. +fn check_envelope_vector(v: &Value, scheme: Scheme) { + let device = secret(v, "device_secret_key"); + assert_eq!(public_hex(&device), s(v, "device_public_key")); + let shared = shared_point(&device, &bytes33(s(v, "ephemeral_public_key"))).unwrap(); + assert_eq!(hex::encode(shared), s(v, "shared_point")); + assert_eq!(s(v, "label"), scheme.label()); + assert_eq!( + hex::encode(derive_key(&shared, scheme.label())), + s(v, "key") + ); + if v.get("ephemeral_secret_key").is_some() { + let ephemeral = secret(v, "ephemeral_secret_key"); + assert_eq!(public_hex(&ephemeral), s(v, "ephemeral_public_key")); + let from_gateway = shared_point(&ephemeral, &bytes33(s(v, "device_public_key"))).unwrap(); + assert_eq!(from_gateway, shared); + } + let container = Container::from_json(s(v, "container")).unwrap(); + assert_eq!(container.scheme(), scheme); + assert_eq!(container.to_json(), s(v, "container")); + let plaintext = String::from_utf8(container.decrypt(&device).unwrap()).unwrap(); + assert_eq!(plaintext, s(v, "plaintext")); +} + +// ----- (a) v0 server envelope ----- + +#[test] +fn v0_server_vector() { + let v = load(V0_SERVER); + check_envelope_vector(&v, Scheme::V0); + assert_eq!( + s(&v, "shared_point"), + "0277e0510d5042e2f5e9e59c977b81eeed590cf7d20c1c51da451a8eaa9fdc45ff" + ); + assert_eq!( + s(&v, "key"), + "954b48c50f1c6bf3a6cd20a6d7354775cb0d712a3e332333c87dcd52b83f42f9" + ); + + // Encrypting the plaintext the way the gateway does gives the same bytes. + let rebuilt = seal_deterministic( + Scheme::V0, + &bytes33(s(&v, "device_public_key")), + s(&v, "plaintext").as_bytes(), + &secret(&v, "ephemeral_secret_key"), + &hex::decode(s(&v, "iv")).unwrap(), + ); + assert_eq!(rebuilt.to_json(), s(&v, "container")); + assert_eq!(rebuilt.to_json().len(), 323); + assert_eq!(v["container_len"], 323); + + let envelope = wake_decrypt_v0( + s(&v, "device_secret_key").to_string(), + s(&v, "cipher").to_string(), + s(&v, "iv").to_string(), + s(&v, "tag").to_string(), + s(&v, "ephemeral_public_key").to_string(), + ) + .unwrap(); + assert_eq!( + envelope, + WakeEnvelope { + version: 0, + id: None, + topic: s(&v, "topic").to_string(), + urgency: None, + deadline: None, + payload_json: Some(s(&v, "payload").to_string()), + created_at: Some(s(&v, "created_at").to_string()), + fallback: false, + } + ); +} + +// ----- (b) v0 client vector from the apps ----- + +#[test] +fn v0_client_vector() { + let v = load(V0_CLIENT); + check_envelope_vector(&v, Scheme::V0); + assert_eq!( + s(&v, "shared_point"), + "028ce542975d6d7b2307c92e527d507b03ffb3d897eb2e0830d29f40d5efd80ee3" + ); + assert_eq!( + s(&v, "key"), + "3a9d552cb16dfae40feae644254c4ca46cab82e570de5662aacc4018e33b609b" + ); + + let expected = WakeEnvelope { + version: 0, + id: None, + topic: "blocktank.incomingHtlc".to_string(), + urgency: None, + deadline: None, + payload_json: Some(r#"{"secretMessage":"hello"}"#.to_string()), + created_at: Some("2024-09-18T13:33:52.555Z".to_string()), + fallback: false, + }; + let envelope = wake_decrypt_v0( + s(&v, "device_secret_key").to_string(), + s(&v, "cipher").to_string(), + s(&v, "iv").to_string(), + s(&v, "tag").to_string(), + s(&v, "ephemeral_public_key").to_string(), + ) + .unwrap(); + assert_eq!(envelope, expected); + + // The same envelope as each platform delivers it. + let container: Value = serde_json::from_str(s(&v, "container")).unwrap(); + let apns = json!({"aps": {"alert": {"title": "t", "body": "b", "payload": container}}}); + let mut fcm = container.clone(); + fcm["title"] = json!("Background Operation Request"); + fcm["message"] = json!("Please open your wallet."); + fcm["sound"] = json!("default"); + for push in [apns, fcm] { + let envelope = + wake_decrypt_push(s(&v, "device_secret_key").to_string(), push.to_string()).unwrap(); + assert_eq!(envelope, expected, "{push}"); + } +} + +// ----- (d) v1 envelope ----- + +#[test] +fn v1_envelope_vector() { + let v = load(V1_ENVELOPE); + check_envelope_vector(&v, Scheme::V1); + + let rebuilt = seal_deterministic( + Scheme::V1, + &bytes33(s(&v, "device_public_key")), + s(&v, "plaintext").as_bytes(), + &secret(&v, "ephemeral_secret_key"), + &hex::decode(s(&v, "iv")).unwrap(), + ); + assert_eq!(rebuilt.to_json(), s(&v, "container")); + assert_eq!(rebuilt.to_json().len(), 501); + + let envelope = wake_decrypt_v1( + s(&v, "device_secret_key").to_string(), + s(&v, "container").to_string(), + ) + .unwrap(); + assert_eq!( + envelope, + WakeEnvelope { + version: 1, + id: Some(WAKE_ID.to_string()), + topic: "blocktank.incomingHtlc".to_string(), + urgency: Some("background".to_string()), + deadline: Some(1790000060), + payload_json: Some( + r#"{"lspId":"blocktank-lsp","paymentHash":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}"# + .to_string() + ), + created_at: None, + fallback: false, + } + ); +} + +// ----- (e) registration preimage and proofs ----- + +fn vector_registration(v: &Value) -> WakeRegistrationRequest { + let request = &v["request"]; + let strings = |key: &str| -> Vec { + request[key] + .as_array() + .unwrap() + .iter() + .map(|i| i.as_str().unwrap().to_string()) + .collect() + }; + wake_prepare_registration( + s(v, "audience").to_string(), + s(request, "app").to_string(), + s(request, "install_id").to_string(), + WakePlatform::Apns, + WakeEnvironment::Sandbox, + s(request, "push_token").to_string(), + s(request, "encryption_key").to_string(), + s(request, "secret_sha256").to_string(), + strings("identities"), + strings("topics"), + request["ts"].as_u64(), + ) + .unwrap() +} + +#[test] +fn registration_v1_vector() { + let v = load(REGISTRATION_V1); + let registration = vector_registration(&v); + assert_eq!(registration.message, s(&v, "preimage")); + assert_eq!(registration.message.lines().count(), 12); + assert_eq!( + hex::encode(sha256::Hash::hash(registration.message.as_bytes()).to_byte_array()), + s(&v, "preimage_sha256") + ); + assert_eq!( + hex::encode(lightning_message_hash(registration.message.as_bytes())), + s(&v, "ln_message_hash") + ); + + // The credentials the request carries. + let device_secret = device_secret_from_bytes( + &hex::decode(s(&v, "device_secret_bytes")) + .unwrap() + .try_into() + .unwrap(), + ); + assert_eq!(device_secret.token, s(&v, "device_secret")); + assert_eq!(device_secret.sha256_hex, registration.secret_sha256); + assert_eq!( + public_hex(&secret(&v, "encryption_secret_key")), + registration.encryption_public_key + ); + let ln_secret = secret(&v, "ln_secret_key"); + assert_eq!( + format!("ln:{}", public_hex(&ln_secret)), + s(&v, "ln_identity") + ); + let pk_seed: [u8; 32] = hex::decode(s(&v, "pk_seed")).unwrap().try_into().unwrap(); + let pk_public = pubky::Keypair::from_secret(&pk_seed).public_key(); + assert_eq!(hex::encode(pk_public.to_bytes()), s(&v, "pk_public_key")); + assert_eq!(format!("pk:{}", pk_public.z32()), s(&v, "pk_identity")); + + // Both proofs, byte for byte. + let pk_proof = + wake_sign_pubky_proof(s(&v, "pk_seed").to_string(), registration.message.clone()).unwrap(); + assert_eq!(pk_proof, s(&v, "pk_proof")); + assert_eq!( + lightning_sign(&ln_secret, registration.message.as_bytes()), + s(&v, "ln_proof") + ); + + // The request body, proofs included, is the vector's request. + let proofs = vec![ + WakeIdentityProof { + identity: s(&v, "ln_identity").to_string(), + signature: s(&v, "ln_proof").to_string(), + }, + WakeIdentityProof { + identity: s(&v, "pk_identity").to_string(), + signature: pk_proof, + }, + ]; + let request = build_register(GATEWAY, ®istration, &proofs).unwrap(); + assert_eq!(request.method, Method::POST); + assert_eq!(request.url.as_str(), "https://wake.example/v1/devices"); + assert!(request.bearer.is_none()); + let body = request.body.unwrap(); + assert!(body.starts_with(r#"{"app":"bitkit","install_id":"AAAAAAAAAAAAAAAAAAAAAA","#)); + assert_eq!(serde_json::from_str::(&body).unwrap(), v["request"]); + + // Fields changed after the message was prepared are refused. + for tweak in 0..3 { + let mut changed = registration.clone(); + match tweak { + 0 => changed.topics.push("paykit.*".to_string()), + 1 => changed.audience = "wake.example".to_string(), + _ => changed.timestamp += 1, + } + assert_eq!( + build_register(GATEWAY, &changed, &proofs).err(), + Some(invalid_input( + "the registration changed after its message was prepared" + )) + ); + } + + // A different audience changes the message. + let other = vector_registration(&json!({"audience": "wake.example", "request": v["request"]})); + assert_ne!(other.message, registration.message); +} + +#[test] +fn legacy_registration_vector_checks_the_test_signer() { + let v = load(LEGACY_REGISTRATION); + let message = format!( + "bitkit-notifications{}{}", + s(&v, "device_token"), + s(&v, "iso_timestamp") + ); + assert_eq!(message, s(&v, "message")); + assert_eq!( + hex::encode(lightning_message_hash(message.as_bytes())), + s(&v, "message_hash") + ); + let node = secret(&v, "node_secret_key"); + assert_eq!(public_hex(&node), s(&v, "node_id")); + assert_eq!( + lightning_sign(&node, message.as_bytes()), + s(&v, "signature_zbase32") + ); +} + +// ----- (f) every push shape ----- + +#[test] +fn pushes_vector() { + let v = load(PUSHES); + let cases = v["cases"].as_array().unwrap(); + assert_eq!(cases.len(), 10); + for case in cases { + let name = s(case, "name"); + let body_text = s(case, "body"); + assert_eq!( + case["body_len"].as_u64().unwrap() as usize, + body_text.len(), + "{name}" + ); + let body: Value = serde_json::from_str(body_text).unwrap(); + let delivered = parse_delivered(body_text).unwrap(); + // An FCM device sees only the data map. + let mut views = vec![body_text.to_string()]; + if s(case, "provider") == "fcm" { + let message = &body["message"]; + let data = message.pointer("/android/data").unwrap_or(&message["data"]); + for map in [data, &message["data"]] { + assert_eq!( + parse_delivered(&map.to_string()).unwrap(), + delivered, + "{name}" + ); + views.push(map.to_string()); + } + } + match &delivered { + Delivered::V0(container) | Delivered::V1(container) => { + let scheme = if name.ends_with("v0") { + Scheme::V0 + } else { + Scheme::V1 + }; + assert_eq!(container.scheme(), scheme, "{name}"); + assert_eq!( + container.to_json(), + s(&case["inputs"], "container"), + "{name}" + ); + let device = secret(case, "device_secret_key"); + let plaintext = String::from_utf8(container.decrypt(&device).unwrap()).unwrap(); + assert_eq!(plaintext, s(case, "plaintext"), "{name}"); + for view in views { + let envelope = + wake_decrypt_push(s(case, "device_secret_key").to_string(), view.clone()) + .unwrap(); + check_envelope_matches_plaintext(&envelope, &plaintext, case, name); + } + } + Delivered::Fallback { id, topic } => { + assert!(case.get("device_secret_key").is_none(), "{name}"); + assert_eq!(id, s(&case["inputs"], "wake_id"), "{name}"); + assert_eq!(topic, s(&case["inputs"], "topic"), "{name}"); + for view in views { + // A fallback needs no key, so an unusable one is fine. + let envelope = wake_decrypt_push(String::new(), view).unwrap(); + assert_eq!( + envelope, + WakeEnvelope { + version: 1, + id: Some(id.clone()), + topic: topic.clone(), + urgency: None, + deadline: None, + payload_json: None, + created_at: None, + fallback: true, + }, + "{name}" + ); + } + } + } + } +} + +fn check_envelope_matches_plaintext( + envelope: &WakeEnvelope, + plaintext: &str, + case: &Value, + name: &str, +) { + let parsed: Value = serde_json::from_str(plaintext).unwrap(); + assert!(!envelope.fallback, "{name}"); + let payload = envelope.payload_json.as_deref().unwrap_or("null"); + assert_eq!( + serde_json::from_str::(payload).unwrap(), + parsed["payload"], + "{name}" + ); + if envelope.version == 0 { + let topic = format!("{}.{}", s(&parsed, "source"), s(&parsed, "type")); + assert_eq!(envelope.topic, topic, "{name}"); + assert_eq!( + envelope.created_at.as_deref(), + parsed["createdAt"].as_str(), + "{name}" + ); + assert!( + plaintext.contains(&format!("\"payload\":{payload},\"createdAt\"")), + "{name}" + ); + assert_eq!(envelope.id, None, "{name}"); + } else { + assert_eq!(envelope.version, 1, "{name}"); + assert_eq!(envelope.id.as_deref(), Some(s(&case["inputs"], "wake_id"))); + assert_eq!(envelope.topic, s(&case["inputs"], "topic"), "{name}"); + assert_eq!(envelope.urgency.as_deref(), parsed["urgency"].as_str()); + assert_eq!(envelope.deadline, parsed["deadline"].as_u64(), "{name}"); + assert!( + plaintext.ends_with(&format!("\"payload\":{payload}}}")), + "{name}" + ); + assert_eq!(envelope.created_at, None, "{name}"); + } +} + +// ----- Negative crypto cases ----- + +fn v1_vector_container() -> (SecretKey, Container) { + let v = load(V1_ENVELOPE); + ( + secret(&v, "device_secret_key"), + Container::from_json(s(&v, "container")).unwrap(), + ) +} + +#[test] +fn tampered_tag_fails() { + let (device, mut container) = v1_vector_container(); + container.tag_mut()[0] ^= 1; + assert_eq!(container.decrypt(&device), Err(decryption_failed())); + assert_eq!( + wake_decrypt_v1(hex::encode(device.secret_bytes()), container.to_json()), + Err(decryption_failed()) + ); +} + +#[test] +fn tampered_cipher_fails() { + let v = load(V0_SERVER); + let cipher = s(&v, "cipher").replacen('m', "n", 1); + let result = wake_decrypt_v0( + s(&v, "device_secret_key").to_string(), + cipher, + s(&v, "iv").to_string(), + s(&v, "tag").to_string(), + s(&v, "ephemeral_public_key").to_string(), + ); + assert_eq!(result, Err(decryption_failed())); +} + +#[test] +fn wrong_key_fails() { + let (_, container) = v1_vector_container(); + assert_eq!( + wake_decrypt_v1("23".repeat(32), container.to_json()), + Err(decryption_failed()) + ); +} + +#[test] +fn wrong_label_fails() { + // A v1 container read as a legacy one derives the key with the v0 label. + let v = load(V1_ENVELOPE); + let result = wake_decrypt_v0( + s(&v, "device_secret_key").to_string(), + s(&v, "cipher").to_string(), + s(&v, "iv").to_string(), + s(&v, "tag").to_string(), + s(&v, "ephemeral_public_key").to_string(), + ); + assert_eq!(result, Err(decryption_failed())); +} + +#[test] +fn eleven_byte_iv_is_rejected() { + let (device, container) = v1_vector_container(); + let json = container + .to_json() + .replace("000102030405060708090a0b", &"00".repeat(11)); + let iv_error = invalid_input("iv must be 12 or 16 bytes"); + assert_eq!(Container::from_json(&json), Err(iv_error.clone())); + assert_eq!( + wake_decrypt_v1(hex::encode(device.secret_bytes()), json), + Err(iv_error.clone()) + ); + let v = load(V0_SERVER); + let result = wake_decrypt_v0( + s(&v, "device_secret_key").to_string(), + s(&v, "cipher").to_string(), + "00".repeat(11), + s(&v, "tag").to_string(), + s(&v, "ephemeral_public_key").to_string(), + ); + assert_eq!(result, Err(iv_error)); +} + +#[test] +fn unknown_version_is_unsupported() { + let (device, container) = v1_vector_container(); + for other in ["2", "\"1\"", "null", "1.0", "0", "{}"] { + let json = container + .to_json() + .replacen("\"v\":1", &format!("\"v\":{other}"), 1); + assert_eq!( + Container::from_json(&json), + Err(unsupported("unsupported envelope version")), + "{other}" + ); + assert_eq!( + wake_decrypt_v1(hex::encode(device.secret_bytes()), json.clone()), + Err(unsupported("unsupported envelope version")), + "{other}" + ); + let push = format!("{{\"wake\":{json}}}"); + assert_eq!( + wake_decrypt_push(hex::encode(device.secret_bytes()), push), + Err(unsupported("unsupported envelope version")), + "{other}" + ); + } + // A container without `v` is a legacy one, which the v1 entry point refuses. + let v0 = load(V0_SERVER); + assert_eq!( + wake_decrypt_v1( + s(&v0, "device_secret_key").to_string(), + s(&v0, "container").to_string() + ), + Err(unsupported("expected a v1 container")) + ); +} + +#[test] +fn unsupported_plaintext_version_and_malformed_plaintexts() { + let v = load(V1_ENVELOPE); + let device = secret(&v, "device_secret_key"); + let seal = |plaintext: &str| { + seal_deterministic( + Scheme::V1, + &bytes33(s(&v, "device_public_key")), + plaintext.as_bytes(), + &secret(&v, "ephemeral_secret_key"), + &[7; 12], + ) + .open_envelope(&device) + }; + let v2 = s(&v, "plaintext").replacen("\"v\":1", "\"v\":2", 1); + assert_eq!(seal(&v2), Err(unsupported("unsupported plaintext version"))); + let secret_payload = "paymentHash=e3b0c44298fc1c14"; + for bad in [ + s(&v, "plaintext").replacen("\"background\"", "\"urgent\"", 1), + s(&v, "plaintext").replacen(WAKE_ID, "not-a-uuid", 1), + format!( + r#"{{"v":1,"id":"{WAKE_ID}","topic":"a.b","urgency":"alert","deadline":"{secret_payload}"}}"# + ), + ] { + let err = seal(&bad).unwrap_err(); + assert!(matches!(err, WakeError::InvalidInput { .. }), "{err:?}"); + assert!(!err.to_string().contains("e3b0c442"), "{err}"); + } + // A null or absent payload is no payload. + let null_payload = format!( + r#"{{"v":1,"id":"{WAKE_ID}","topic":"paykit.sync","urgency":"background","deadline":1,"payload":null}}"# + ); + assert_eq!(seal(&null_payload).unwrap().payload_json, None); + let absent = null_payload.replace(",\"payload\":null", ""); + assert_eq!(seal(&absent).unwrap().payload_json, None); +} + +#[test] +fn unpadded_base64_is_rejected() { + let v = load(V0_SERVER); + let json = s(&v, "container").replacen("O9h\"", "O9h=\"", 1); + assert!(Container::from_json(&json).is_err()); + let unpadded = load(V0_CLIENT); + let json = s(&unpadded, "container").replacen("A=\"", "A\"", 1); + assert_eq!( + Container::from_json(&json), + Err(invalid_input("cipher is not padded standard base64")) + ); +} + +#[test] +fn off_curve_ephemeral_key_fails_decryption() { + let (device, mut container) = v1_vector_container(); + let key = container.public_key_mut(); + key[0] = 0x02; + key[1..].copy_from_slice(&[0xff; 32]); + assert!(matches!( + container.decrypt(&device), + Err(WakeError::InvalidKey { .. }) + )); +} + +#[test] +fn malformed_secret_keys_are_invalid_keys() { + let (_, container) = v1_vector_container(); + for bad in [ + String::new(), + "zz".repeat(32), + "11".repeat(31), + "00".repeat(32), + "ff".repeat(32), + ] { + let result = wake_decrypt_v1(bad.clone(), container.to_json()); + assert!( + matches!(result, Err(WakeError::InvalidKey { .. })), + "{bad}: {result:?}" + ); + } +} + +// ----- Push shape detection ----- + +#[test] +fn parse_delivered_rejects_what_it_cannot_place() { + let v0 = load(V0_SERVER); + let v0 = s(&v0, "container"); + let v1 = load(V1_ENVELOPE); + let v1 = s(&v1, "container"); + let v2 = v1.replacen("\"v\":1", "\"v\":2", 1); + let misplaced = Err(unsupported( + "container version does not match where it was found", + )); + assert_eq!(parse_delivered(&format!("{{\"wake\":{v0}}}")), misplaced); + assert_eq!( + parse_delivered(&format!( + "{{\"wake\":{}}}", + serde_json::to_string(v0).unwrap() + )), + misplaced + ); + assert_eq!( + parse_delivered(&format!("{{\"aps\":{{\"alert\":{{\"payload\":{v1}}}}}}}")), + misplaced + ); + assert_eq!( + parse_delivered(&format!("{{\"wake\":{v2}}}")), + Err(unsupported("unsupported envelope version")) + ); + assert_eq!( + parse_delivered(&format!( + "{{\"wake\":{}}}", + serde_json::to_string(&v2).unwrap() + )), + Err(unsupported("unsupported envelope version")) + ); + assert_eq!( + parse_delivered(&format!( + r#"{{"wake_fallback":{{"v":2,"id":"{WAKE_ID}","topic":"a.b"}}}}"# + )), + Err(unsupported("unsupported envelope version")) + ); + assert!(matches!( + parse_delivered(r#"{"wake_fallback":"{\"v\":1,\"topic\":\"a.b\"}"}"#), + Err(WakeError::InvalidInput { .. }) + )); + assert_eq!( + parse_delivered(r#"{"wake_fallback":{"v":1,"id":"x","topic":"a.b"}}"#), + Err(invalid_input("wake id is not a uuid")) + ); + assert_eq!( + parse_delivered(r#"{"cipher":"AA==","iv":"00"}"#), + Err(invalid_input( + "legacy data needs cipher, iv, tag and publicKey strings" + )) + ); + assert_eq!( + parse_delivered(r#"{"wake":7}"#), + Err(invalid_input("wake is not a container")) + ); + assert_eq!( + parse_delivered(r#"{"aps":{"alert":{"payload":"x"}}}"#), + Err(invalid_input("aps.alert.payload is not a container")) + ); + assert_eq!( + parse_delivered(r#"{"aps":{"content-available":1}}"#), + Err(unsupported("push carries no wake")) + ); + assert_eq!( + parse_delivered(r#"{"message":{"token":"t"}}"#), + Err(unsupported("FCM message carries no data")) + ); + assert_eq!( + parse_delivered("[]"), + Err(invalid_input("push is not a JSON object")) + ); + assert!(matches!( + parse_delivered("{"), + Err(WakeError::InvalidInput { .. }) + )); + // The legacy FCM data map's string `message` is alert text, not a body. + let flat: Value = serde_json::from_str(v0).unwrap(); + let mut flat = flat.as_object().unwrap().clone(); + flat.insert("message".to_string(), json!("Please open your wallet.")); + assert!(matches!( + parse_delivered(&Value::Object(flat).to_string()), + Ok(Delivered::V0(_)) + )); +} + +#[test] +fn fallback_markers_in_both_encodings() { + let marker = json!({"v": 1, "id": WAKE_ID.to_uppercase(), "topic": "blocktank.incomingHtlc"}); + let expected = Delivered::Fallback { + id: WAKE_ID.to_string(), + topic: "blocktank.incomingHtlc".to_string(), + }; + let apns = json!({"aps": {"alert": {"title": "t", "body": "b"}}, "wake_fallback": marker}); + let fcm = json!({"wake_fallback": marker.to_string()}); + assert_eq!(parse_delivered(&apns.to_string()).unwrap(), expected); + assert_eq!(parse_delivered(&fcm.to_string()).unwrap(), expected); +} + +// ----- Registration ----- + +fn fields<'a>(identities: &'a [String], topics: &'a [String]) -> RegistrationFields<'a> { + RegistrationFields { + audience: "wake.localhost", + app: "bitkit", + install_id: "AAAAAAAAAAAAAAAAAAAAAA", + platform: WakePlatform::Apns, + environment: WakeEnvironment::Sandbox, + push_token: "00ff", + encryption_key: "02aa", + secret_sha256: "bb", + identities, + topics, + ts: 1790000000, + } +} + +#[test] +fn preimage_sorts_and_dedupes() { + let ids = vec!["pk:b".to_string(), "ln:a".to_string(), "pk:b".to_string()]; + let topics = vec!["paykit.sync".to_string(), "blocktank.*".to_string()]; + let pre = fields(&ids, &topics).preimage().unwrap(); + assert_eq!( + pre, + "wake-register-v1\naud:wake.localhost\napp:bitkit\ninstall:AAAAAAAAAAAAAAAAAAAAAA\n\ + platform:apns\nenvironment:sandbox\npush_token:00ff\nencryption_key:02aa\n\ + secret_sha256:bb\nidentities:ln:a,pk:b\ntopics:blocktank.*,paykit.sync\nts:1790000000" + ); + assert_eq!(pre.lines().count(), 12); + assert!(!pre.ends_with('\n')); +} + +#[test] +fn empty_topics_give_a_bare_label() { + let ids = vec!["ln:a".to_string()]; + let pre = fields(&ids, &[]).preimage().unwrap(); + assert!(pre.contains("\ntopics:\nts:")); +} + +#[test] +fn ambiguous_preimage_fields_are_rejected() { + let ids = vec!["ln:a".to_string()]; + let comma = vec!["a.b,c.d".to_string()]; + let list_error = Err(invalid_input( + "a list entry is empty or contains a comma or newline", + )); + assert_eq!(fields(&ids, &comma).preimage(), list_error); + let newline_topic = vec!["a.b\nc.d".to_string()]; + assert_eq!(fields(&ids, &newline_topic).preimage(), list_error); + for field in 0..6 { + let mut f = fields(&ids, &[]); + let slot = match field { + 0 => &mut f.audience, + 1 => &mut f.app, + 2 => &mut f.install_id, + 3 => &mut f.push_token, + 4 => &mut f.encryption_key, + _ => &mut f.secret_sha256, + }; + *slot = "ab\ncd"; + assert_eq!( + f.preimage(), + Err(invalid_input("a registration field contains a newline")), + "{field}" + ); + } +} + +#[test] +fn empty_list_entries_are_rejected() { + let list_error = Err(invalid_input( + "a list entry is empty or contains a comma or newline", + )); + let ids = vec!["ln:a".to_string()]; + assert_eq!(fields(&ids, &[String::new()]).preimage(), list_error); + let with_empty = vec!["a.b".to_string(), String::new()]; + assert_eq!(fields(&ids, &with_empty).preimage(), list_error); + assert_eq!(fields(&[String::new()], &[]).preimage(), list_error); + assert!(fields(&[], &[]).preimage().is_ok()); +} + +/// Changes one argument of a registration. +type Tweak = Box; + +/// The vector's registration arguments, for tweaking one at a time. +struct Args { + audience: String, + app: String, + install_id: String, + platform: WakePlatform, + environment: WakeEnvironment, + push_token: String, + encryption_key: String, + secret_sha256: String, + identities: Vec, + topics: Vec, +} + +impl Args { + fn from_vector() -> Self { + let r = vector_registration(&load(REGISTRATION_V1)); + Args { + audience: r.audience, + app: r.app, + install_id: r.install_id, + platform: r.platform, + environment: r.environment, + push_token: r.push_token, + encryption_key: r.encryption_public_key, + secret_sha256: r.secret_sha256, + identities: r.identities, + topics: r.topics, + } + } + + fn prepare(self) -> Result { + wake_prepare_registration( + self.audience, + self.app, + self.install_id, + self.platform, + self.environment, + self.push_token, + self.encryption_key, + self.secret_sha256, + self.identities, + self.topics, + Some(1790000000), + ) + } +} + +#[test] +fn prepare_rejects_what_the_gateway_would() { + let ln = load(REGISTRATION_V1)["ln_identity"] + .as_str() + .unwrap() + .to_string(); + let pk = load(REGISTRATION_V1)["pk_identity"] + .as_str() + .unwrap() + .to_string(); + let other_ln = format!( + "ln:{}", + load(V0_SERVER)["device_public_key"].as_str().unwrap() + ); + let mut non_canonical_pk = pk.clone(); + // The last character carries 1 data bit and 4 padding bits. + assert!(non_canonical_pk.ends_with('y')); + non_canonical_pk.pop(); + non_canonical_pk.push('b'); + let cases: Vec<(&str, Tweak)> = vec![ + ( + "empty audience", + Box::new(|a: &mut Args| a.audience.clear()), + ), + ( + "short install id", + Box::new(|a: &mut Args| a.install_id = "A".repeat(15)), + ), + ( + "padded install id", + Box::new(|a: &mut Args| a.install_id.push('=')), + ), + ( + "fcm sandbox", + Box::new(|a: &mut Args| { + a.platform = WakePlatform::Fcm; + a.push_token = "a:b_c-d".repeat(15); + }), + ), + ( + "uppercase apns token", + Box::new(|a: &mut Args| a.push_token = a.push_token.to_uppercase()), + ), + ( + "odd apns token", + Box::new(|a: &mut Args| a.push_token.push('0')), + ), + ( + "short fcm token", + Box::new(|a: &mut Args| { + a.platform = WakePlatform::Fcm; + a.environment = WakeEnvironment::Production; + a.push_token = "a".repeat(99); + }), + ), + ( + "uppercase key", + Box::new(|a: &mut Args| a.encryption_key = a.encryption_key.to_uppercase()), + ), + ( + "off-curve key", + Box::new(|a: &mut Args| a.encryption_key = format!("02{}", "ff".repeat(32))), + ), + ( + "uppercase secret hash", + Box::new(|a: &mut Args| a.secret_sha256 = a.secret_sha256.to_uppercase()), + ), + ("no identity", Box::new(|a: &mut Args| a.identities.clear())), + ("two ln", { + let (ln, other_ln) = (ln.clone(), other_ln.clone()); + Box::new(move |a: &mut Args| a.identities = vec![ln.clone(), other_ln.clone()]) + }), + ("duplicate", { + let pk = pk.clone(); + Box::new(move |a: &mut Args| a.identities = vec![pk.clone(), pk.clone()]) + }), + ("three", { + let (ln, pk, other_ln) = (ln.clone(), pk.clone(), other_ln.clone()); + Box::new(move |a: &mut Args| { + a.identities = vec![ln.clone(), pk.clone(), other_ln.clone()] + }) + }), + ("pubky prefix", { + let pk = pk.clone(); + Box::new(move |a: &mut Args| a.identities = vec![pk.replacen("pk:", "pk:pubky", 1)]) + }), + ("uppercase pk", { + let pk = pk.clone(); + Box::new(move |a: &mut Args| { + a.identities = vec![format!("pk:{}", pk[3..].to_uppercase())] + }) + }), + ("non-canonical pk", { + let pk = non_canonical_pk.clone(); + Box::new(move |a: &mut Args| a.identities = vec![pk.clone()]) + }), + ( + "weak pk", + Box::new(|a: &mut Args| a.identities = vec![format!("pk:{}", "y".repeat(52))]), + ), + ("uppercase ln", { + let ln = ln.clone(); + Box::new(move |a: &mut Args| { + a.identities = vec![format!("ln:{}", ln[3..].to_uppercase())] + }) + }), + ( + "off-curve ln", + Box::new(|a: &mut Args| a.identities = vec![format!("ln:02{}", "ff".repeat(32))]), + ), + ("bare key", { + let ln = ln.clone(); + Box::new(move |a: &mut Args| a.identities = vec![ln[3..].to_string()]) + }), + ( + "33 topics", + Box::new(|a: &mut Args| a.topics = (0..33).map(|i| format!("t.t{i}")).collect()), + ), + ( + "no dot", + Box::new(|a: &mut Args| a.topics = vec!["blocktank".to_string()]), + ), + ( + "uppercase namespace", + Box::new(|a: &mut Args| a.topics = vec!["Blocktank.x".to_string()]), + ), + ( + "bare star", + Box::new(|a: &mut Args| a.topics = vec!["*".to_string()]), + ), + ( + "long", + Box::new(|a: &mut Args| a.topics = vec![format!("a.{}", "b".repeat(63))]), + ), + ]; + for (name, tweak) in cases { + let mut args = Args::from_vector(); + tweak(&mut args); + let result = args.prepare(); + assert!( + matches!( + result, + Err(WakeError::InvalidInput { .. } | WakeError::InvalidKey { .. }) + ), + "{name}: {result:?}" + ); + } + + let mut fcm = Args::from_vector(); + fcm.platform = WakePlatform::Fcm; + fcm.environment = WakeEnvironment::Production; + fcm.push_token = "a:b_c-d".repeat(15); + fcm.identities = vec![pk.clone()]; + fcm.topics = vec!["blocktank.incomingHtlc".to_string(), "paykit.*".to_string()]; + let prepared = fcm.prepare().unwrap(); + assert!(prepared + .message + .contains("\nplatform:fcm\nenvironment:production\n")); + assert!(prepared.message.contains(&format!("\nidentities:{pk}\n"))); + let mut no_topics = Args::from_vector(); + no_topics.topics.clear(); + assert!(no_topics.prepare().is_ok()); +} + +#[test] +fn prepare_defaults_the_timestamp_to_now() { + let v = load(REGISTRATION_V1); + let mut request = v["request"].clone(); + request["ts"] = Value::Null; + let prepared = vector_registration(&json!({"audience": v["audience"], "request": request})); + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs(); + assert!(prepared.timestamp.abs_diff(now) <= 5); + assert!(prepared + .message + .ends_with(&format!("\nts:{}", prepared.timestamp))); +} + +#[test] +fn pubky_proofs_only_sign_registration_messages() { + let v = load(REGISTRATION_V1); + for message in ["", "wake-register-v1", "wake-register-v1x\n", "hello"] { + assert_eq!( + wake_sign_pubky_proof(s(&v, "pk_seed").to_string(), message.to_string()), + Err(invalid_input( + "only wake registration messages can be signed" + )), + "{message:?}" + ); + } + for key in ["", "44", &"zz".repeat(32)] { + assert!(matches!( + wake_sign_pubky_proof(key.to_string(), s(&v, "preimage").to_string()), + Err(WakeError::InvalidKey { .. }) + )); + } + let signature = wake_sign_pubky_proof( + s(&v, "pk_seed").to_string(), + "wake-register-v1\n".to_string(), + ) + .unwrap(); + assert_eq!(signature.len(), 128); +} + +// ----- Credentials and keys ----- + +#[test] +fn device_secret_format() { + let secret = device_secret_from_bytes(&[0x77; 32]); + assert_eq!(secret.token, DEVICE_SECRET); + assert_eq!( + secret.sha256_hex, + "a4908532a2f65068ce0b3498af2b68e43949be33aa85840288e653295bf52080" + ); + assert!(is_valid_device_secret(&secret.token)); + assert!(!format!("{secret:?}").contains("d3d3")); + + let generated = wake_generate_device_secret().unwrap(); + assert!(is_valid_device_secret(&generated.token)); + assert_ne!(generated.token, secret.token); + assert_eq!( + generated.sha256_hex, + hex::encode(sha256::Hash::hash(generated.token.as_bytes()).to_byte_array()) + ); +} + +#[test] +fn device_secret_rejects_malformed() { + let body = &DEVICE_SECRET[4..]; + for bad in [ + format!("wkp_{body}"), + body.to_string(), + format!("wkd_{}", &body[..42]), + format!("wkd_{body}A"), + format!("wkd_{}d", &body[..42]), + format!("wkd_{}=", &body[..42]), + format!("wkd_{}+", &body[..42]), + ] { + assert!(!is_valid_device_secret(&bad), "{bad}"); + } +} + +#[test] +fn install_ids_are_22_base64url_characters() { + assert_eq!(install_id_from_bytes(&[0xfb; 16]), "-_v7-_v7-_v7-_v7-_v7-w"); + let generated = wake_generate_install_id(); + assert_eq!(generated.len(), 22); + assert!(generated + .bytes() + .all(|b| b.is_ascii_alphanumeric() || b == b'-' || b == b'_')); + assert_ne!(generated, wake_generate_install_id()); +} + +#[test] +fn keypair_generation_retries_out_of_range_draws() { + // Zero and n..2^256 are not scalars; the third draw is. + let mut draws = 0; + let keypair = generate_keypair_with(|buf: &mut [u8]| { + draws += 1; + buf.fill(match draws { + 1 => 0, + 2 => 0xff, + _ => 0x11, + }); + Ok(()) + }) + .unwrap(); + assert_eq!(keypair.secret_key_hex, "11".repeat(32)); + assert_eq!( + keypair.public_key_hex, + s(&load(V0_SERVER), "ephemeral_public_key") + ); + assert!(!format!("{keypair:?}").contains(&"11".repeat(32))); +} + +#[test] +fn constant_invalid_entropy_fails_instead_of_hanging() { + for byte in [0x00, 0xff] { + let mut draws = 0; + let result = generate_keypair_with(|buf: &mut [u8]| { + draws += 1; + buf.fill(byte); + Ok(()) + }); + assert_eq!( + result, + Err(WakeError::InvalidKey { + reason: "entropy source is degenerate".to_string() + }) + ); + assert_eq!(draws, 64); + } +} + +#[test] +fn generated_keypairs_decrypt_what_is_sealed_to_them() { + let keypair = wake_generate_keypair().unwrap(); + assert_eq!(keypair.secret_key_hex.len(), 64); + assert_eq!(keypair.public_key_hex.len(), 66); + assert_ne!(keypair, wake_generate_keypair().unwrap()); + let plaintext = format!( + r#"{{"v":1,"id":"{WAKE_ID}","topic":"a.b","urgency":"alert","deadline":9,"payload":[1]}}"# + ); + let container = seal_deterministic( + Scheme::V1, + &bytes33(&keypair.public_key_hex), + plaintext.as_bytes(), + &parse_secret_key(&"66".repeat(32)).unwrap(), + &[1; 12], + ); + let envelope = wake_decrypt_v1(keypair.secret_key_hex, container.to_json()).unwrap(); + assert_eq!(envelope.payload_json.as_deref(), Some("[1]")); +} + +// ----- Client: builders ----- + +fn body_json(request: &HttpRequest) -> Value { + serde_json::from_str(request.body.as_deref().unwrap()).unwrap() +} + +#[test] +fn builders_for_every_route() { + let topics = vec!["blocktank.*".to_string()]; + let routes = [ + ( + build_server_info(GATEWAY).unwrap(), + Method::GET, + "v1/info", + false, + None, + ), + ( + build_list_topics(GATEWAY).unwrap(), + Method::GET, + "v1/topics", + false, + None, + ), + ( + build_ack(GATEWAY, DEVICE_SECRET, WAKE_ID, WakeAckOutcome::NoHandler).unwrap(), + Method::POST, + "v1/acks", + true, + Some(json!({"id": WAKE_ID, "outcome": "no_handler"})), + ), + ( + build_set_presence(GATEWAY, DEVICE_SECRET, 60).unwrap(), + Method::PUT, + "v1/presence", + true, + Some(json!({"ttl_secs": 60})), + ), + ( + build_clear_presence(GATEWAY, DEVICE_SECRET).unwrap(), + Method::DELETE, + "v1/presence", + true, + None, + ), + ( + build_set_topics(GATEWAY, DEVICE_SECRET, &topics).unwrap(), + Method::PUT, + "v1/devices/self/topics", + true, + Some(json!({"topics": ["blocktank.*"]})), + ), + ( + build_unregister(GATEWAY, DEVICE_SECRET).unwrap(), + Method::DELETE, + "v1/devices/self", + true, + None, + ), + ]; + for (request, method, route, authenticated, body) in routes { + assert_eq!(request.method, method, "{route}"); + assert_eq!(request.url.as_str(), format!("{GATEWAY}/{route}")); + assert_eq!( + request.bearer.as_deref(), + authenticated.then_some(DEVICE_SECRET), + "{route}" + ); + match body { + Some(expected) => assert_eq!(body_json(&request), expected, "{route}"), + None => assert!(request.body.is_none(), "{route}"), + } + } + for (outcome, wire) in [ + (WakeAckOutcome::Handled, "handled"), + (WakeAckOutcome::NoHandler, "no_handler"), + (WakeAckOutcome::Failed, "failed"), + ] { + let request = build_ack(GATEWAY, DEVICE_SECRET, WAKE_ID, outcome).unwrap(); + assert_eq!(body_json(&request)["outcome"], wire); + } +} + +#[test] +fn gateway_urls_keep_their_path_prefix() { + for (base, expected) in [ + ("https://wake.example", "https://wake.example/v1/info"), + ("https://wake.example/", "https://wake.example/v1/info"), + ( + "https://api.example/wake", + "https://api.example/wake/v1/info", + ), + ( + "https://api.example/wake/", + "https://api.example/wake/v1/info", + ), + ( + "http://127.0.0.1:9010?x=1#y", + "http://127.0.0.1:9010/v1/info", + ), + ] { + assert_eq!(build_server_info(base).unwrap().url.as_str(), expected); + } + for bad in ["", "wake.example", "ftp://wake.example", "file:///tmp/x"] { + assert!(matches!( + build_server_info(bad), + Err(WakeError::InvalidInput { .. }) + )); + } +} + +#[test] +fn builders_validate_secrets_and_wake_ids() { + let malformed = "malformed device secret"; + for bad in ["", "wkd_", "Bearer x", &format!("{DEVICE_SECRET}\r\nX: y")] { + assert_eq!( + build_clear_presence(GATEWAY, bad).err(), + Some(invalid_input(malformed)) + ); + } + assert_eq!( + build_ack(GATEWAY, DEVICE_SECRET, "nope", WakeAckOutcome::Handled).err(), + Some(invalid_input("wake id is not a uuid")) + ); + let upper = build_ack( + GATEWAY, + DEVICE_SECRET, + &WAKE_ID.to_uppercase(), + WakeAckOutcome::Handled, + ) + .unwrap(); + assert_eq!(body_json(&upper)["id"], WAKE_ID); +} + +// ----- Client: parsers ----- + +fn response(status: u16, body: &str) -> HttpResponse { + HttpResponse { + status, + body: body.to_string(), + } +} + +fn rejected(status: u16, code: &str, message: &str) -> WakeError { + WakeError::GatewayRejected { + status, + code: code.to_string(), + message: message.to_string(), + } +} + +#[test] +fn parsers_for_every_route() { + assert_eq!( + parse_server_info(&response( + 200, + r#"{"audience":"wake.localhost","server_time":1790000000,"version":"0.1.0"}"# + )) + .unwrap(), + WakeServerInfo { + audience: "wake.localhost".to_string(), + server_time: 1790000000, + version: "0.1.0".to_string(), + } + ); + let registered = r#"{"device_id":"01a0c450-6c00-7000-8000-0000000000aa","identities":["ln:02"],"topics":["blocktank.*"],"unknown_topics":["x.y"],"server_time":5}"#; + for status in [200, 201] { + assert_eq!( + parse_register(&response(status, registered)).unwrap(), + WakeRegistration { + device_id: "01a0c450-6c00-7000-8000-0000000000aa".to_string(), + identities: vec!["ln:02".to_string()], + topics: vec!["blocktank.*".to_string()], + unknown_topics: vec!["x.y".to_string()], + server_time: 5, + } + ); + } + assert_eq!( + parse_set_presence(&response(200, r#"{"expires_at":1790000060}"#)).unwrap(), + 1790000060 + ); + assert_eq!( + parse_set_topics(&response( + 200, + r#"{"topics":["blocktank.*"],"unknown_topics":["x.y"]}"# + )) + .unwrap(), + vec!["blocktank.*".to_string()] + ); + assert_eq!(parse_no_content(&response(204, "")), Ok(())); + + let topics = r#"{"topics":[ + {"name":"blocktank.incomingHtlc","description":"An LSP holds an HTLC","owner":"blocktank", + "apps":["bitkit"],"urgency":"background","deadline_secs":60,"presence":"ignore","peer":false, + "alertable":true,"display":{"title":"Incoming Payment","body":"Open Bitkit"}, + "fallback":{"window_secs":45},"max_payload_bytes":512}, + {"name":"paykit.sync","description":"d","owner":"peer","apps":["bitkit"],"urgency":"background", + "deadline_secs":600,"presence":"only_if_offline","peer":true,"alertable":false,"display":null, + "fallback":null,"max_payload_bytes":0}]}"#; + assert_eq!( + parse_list_topics(&response(200, topics)).unwrap(), + vec![ + WakeTopic { + name: "blocktank.incomingHtlc".to_string(), + description: "An LSP holds an HTLC".to_string(), + urgency: "background".to_string(), + deadline_secs: 60, + alertable: true, + title: Some("Incoming Payment".to_string()), + body: Some("Open Bitkit".to_string()), + peer: false, + }, + WakeTopic { + name: "paykit.sync".to_string(), + description: "d".to_string(), + urgency: "background".to_string(), + deadline_secs: 600, + alertable: false, + title: None, + body: None, + peer: true, + }, + ] + ); +} + +#[test] +fn error_statuses_map_to_gateway_rejected() { + let unauthorized = response(401, r#"{"error":"unauthorized","message":"bad secret"}"#); + let unknown_wake = response(404, r#"{"error":"unknown_wake","message":"no delivery"}"#); + let stale = response( + 409, + r#"{"error":"stale_registration","message":"older than the stored one"}"#, + ); + let stale_ts = response( + 401, + r#"{"error":"stale_timestamp","message":"skew","server_time":1790000000}"#, + ); + assert_eq!( + parse_no_content(&unauthorized), + Err(rejected(401, "unauthorized", "bad secret")) + ); + assert_eq!( + parse_no_content(&unknown_wake), + Err(rejected(404, "unknown_wake", "no delivery")) + ); + assert_eq!( + parse_register(&stale), + Err(rejected( + 409, + "stale_registration", + "older than the stored one" + )) + ); + assert_eq!( + parse_register(&stale_ts), + Err(rejected(401, "stale_timestamp", "skew")) + ); + assert_eq!( + parse_set_presence(&unauthorized), + Err(rejected(401, "unauthorized", "bad secret")) + ); + assert_eq!( + parse_set_topics(&unauthorized), + Err(rejected(401, "unauthorized", "bad secret")) + ); + assert_eq!( + parse_server_info(&response(503, r#"{"error":"unavailable","message":"db"}"#)), + Err(rejected(503, "unavailable", "db")) + ); + let no_body = rejected(502, "unknown", "the response carries no wake error body"); + for body in ["Bad Gateway", "", r#"{"error":7}"#] { + assert_eq!( + parse_list_topics(&response(502, body)), + Err(no_body.clone()) + ); + } + assert_eq!( + parse_no_content(&response(302, "")), + Err(rejected( + 302, + "unknown", + "the response carries no wake error body" + )) + ); +} + +#[test] +fn malformed_success_bodies_are_invalid_responses() { + for body in [ + "", + "", + r#"{"audience":"a"}"#, + r#"{"expires_at":"soon"}"#, + ] { + assert!(matches!( + parse_server_info(&response(200, body)), + Err(WakeError::InvalidResponse { .. }) + )); + let err = parse_set_presence(&response(200, body)).unwrap_err(); + assert!(matches!(err, WakeError::InvalidResponse { .. })); + assert!(!err.to_string().contains("soon"), "{err}"); + } +} + +// ----- Client: executor, against a local server ----- + +/// Serves one canned response on 127.0.0.1 and returns what it received. +async fn serve_once(status: u16, body: &'static str) -> (String, tokio::task::JoinHandle) { + use tokio::io::{AsyncReadExt, AsyncWriteExt}; + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let url = format!("http://{}/prefix", listener.local_addr().unwrap()); + let handle = tokio::spawn(async move { + let (mut socket, _) = listener.accept().await.unwrap(); + let mut received = Vec::new(); + let mut chunk = [0u8; 4096]; + loop { + let n = socket.read(&mut chunk).await.unwrap(); + received.extend_from_slice(&chunk[..n]); + let text = String::from_utf8_lossy(&received).to_string(); + if let Some(end) = text.find("\r\n\r\n") { + let length = text[..end] + .lines() + .find_map(|l| { + l.to_ascii_lowercase() + .strip_prefix("content-length: ") + .map(|v| v.trim().parse::().unwrap()) + }) + .unwrap_or(0); + if received.len() >= end + 4 + length || n == 0 { + break; + } + } + } + let location = if (300..400).contains(&status) { + "Location: /moved\r\n" + } else { + "" + }; + let reply = format!( + "HTTP/1.1 {status} X\r\n{location}Content-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{body}", + body.len() + ); + socket.write_all(reply.as_bytes()).await.unwrap(); + String::from_utf8(received).unwrap() + }); + (url, handle) +} + +#[tokio::test] +async fn executor_sends_the_built_request() { + let (url, server) = serve_once(200, r#"{"expires_at":1790000060}"#).await; + let expires_at = wake_set_presence(url, DEVICE_SECRET.to_string(), 90) + .await + .unwrap(); + assert_eq!(expires_at, 1790000060); + let received = server.await.unwrap(); + let lower = received.to_ascii_lowercase(); + assert!( + received.starts_with("PUT /prefix/v1/presence HTTP/1.1\r\n"), + "{received}" + ); + assert!(lower.contains(&format!( + "authorization: bearer {}", + DEVICE_SECRET.to_ascii_lowercase() + ))); + assert!(lower.contains("content-type: application/json")); + assert!(lower.contains("accept: application/json")); + assert!( + received.ends_with("\r\n\r\n{\"ttl_secs\":90}"), + "{received}" + ); +} + +#[tokio::test] +async fn executor_maps_statuses_and_network_failures() { + let (url, server) = serve_once(404, r#"{"error":"unknown_wake","message":"m"}"#).await; + let result = wake_ack( + url, + DEVICE_SECRET.to_string(), + WAKE_ID.to_string(), + WakeAckOutcome::Handled, + ) + .await; + assert_eq!(result, Err(rejected(404, "unknown_wake", "m"))); + let received = server.await.unwrap(); + assert!(received.starts_with("POST /prefix/v1/acks HTTP/1.1\r\n")); + assert!(received.ends_with(&format!("{{\"id\":\"{WAKE_ID}\",\"outcome\":\"handled\"}}"))); + + let (url, server) = serve_once(204, "").await; + assert_eq!( + wake_unregister(url, DEVICE_SECRET.to_string()).await, + Ok(()) + ); + assert!(server + .await + .unwrap() + .starts_with("DELETE /prefix/v1/devices/self HTTP/1.1\r\n")); + + // Redirects are not followed. + let (url, server) = serve_once(307, "").await; + assert_eq!( + wake_clear_presence(url, DEVICE_SECRET.to_string()).await, + Err(rejected( + 307, + "unknown", + "the response carries no wake error body" + )) + ); + server.await.unwrap(); + + // Nothing listens on a port that was just released. + let port = tokio::net::TcpListener::bind("127.0.0.1:0") + .await + .unwrap() + .local_addr() + .unwrap() + .port(); + let result = wake_server_info(format!("http://127.0.0.1:{port}")).await; + assert!( + matches!(result, Err(WakeError::RequestFailed { .. })), + "{result:?}" + ); +} + +// ----- Live gateway ----- + +/// Registers against a running gateway with both proofs, then exercises +/// presence and topics. Run with +/// `WAKE_GATEWAY_URL=http://127.0.0.1:9010 cargo test wake -- --ignored`. +#[tokio::test] +#[ignore] +async fn live_gateway_round_trip() { + let url = std::env::var("WAKE_GATEWAY_URL").expect("WAKE_GATEWAY_URL is not set"); + let info = wake_server_info(url.clone()).await.unwrap(); + + let encryption = wake_generate_keypair().unwrap(); + let device_secret = wake_generate_device_secret().unwrap(); + let ln_secret = parse_secret_key(&wake_generate_keypair().unwrap().secret_key_hex).unwrap(); + let ln_identity = format!("ln:{}", public_hex(&ln_secret)); + let pk_secret_hex = wake_generate_keypair().unwrap().secret_key_hex; + let pk_seed: [u8; 32] = hex::decode(&pk_secret_hex).unwrap().try_into().unwrap(); + let pk_identity = format!( + "pk:{}", + pubky::Keypair::from_secret(&pk_seed).public_key().z32() + ); + let mut push_token = [0u8; 32]; + os_random(&mut push_token).unwrap(); + + let request = wake_prepare_registration( + info.audience.clone(), + "bitkit".to_string(), + wake_generate_install_id(), + WakePlatform::Apns, + WakeEnvironment::Sandbox, + hex::encode(push_token), + encryption.public_key_hex.clone(), + device_secret.sha256_hex.clone(), + vec![ln_identity.clone(), pk_identity.clone()], + vec!["blocktank.*".to_string()], + None, + ) + .unwrap(); + let proofs = vec![ + WakeIdentityProof { + identity: ln_identity.clone(), + signature: lightning_sign(&ln_secret, request.message.as_bytes()), + }, + WakeIdentityProof { + identity: pk_identity.clone(), + signature: wake_sign_pubky_proof(pk_secret_hex, request.message.clone()).unwrap(), + }, + ]; + let registration = wake_register(url.clone(), request, proofs).await.unwrap(); + let mut identities = registration.identities.clone(); + identities.sort(); + let mut expected = vec![ln_identity, pk_identity]; + expected.sort(); + assert_eq!(identities, expected); + assert_eq!(registration.topics, vec!["blocktank.*".to_string()]); + + let token = device_secret.token.clone(); + let expires_at = wake_set_presence(url.clone(), token.clone(), 60) + .await + .unwrap(); + assert!(expires_at > info.server_time); + wake_clear_presence(url.clone(), token.clone()) + .await + .unwrap(); + + let topics = wake_list_topics(url.clone()).await.unwrap(); + assert!(!topics.is_empty()); + let stored = wake_set_topics( + url.clone(), + token.clone(), + vec!["blocktank.*".to_string(), "unknown.topicName".to_string()], + ) + .await + .unwrap(); + assert_eq!(stored, vec!["blocktank.*".to_string()]); + + wake_unregister(url.clone(), token.clone()).await.unwrap(); + assert!(matches!( + wake_set_presence(url, token, 60).await, + Err(WakeError::GatewayRejected { status: 401, .. }) + )); +} diff --git a/src/modules/wake/types.rs b/src/modules/wake/types.rs new file mode 100644 index 0000000..968a86d --- /dev/null +++ b/src/modules/wake/types.rs @@ -0,0 +1,187 @@ +use std::fmt; + +/// A secp256k1 key pair for push encryption. The public key is registered +/// with the gateway; the secret key decrypts wakes and never leaves the device. +#[derive(uniffi::Record, Clone, PartialEq, Eq)] +pub struct WakeKeyPair { + /// 64 lowercase hex characters. + pub secret_key_hex: String, + /// Compressed public key, 66 lowercase hex characters. + pub public_key_hex: String, +} + +impl fmt::Debug for WakeKeyPair { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("WakeKeyPair") + .field("secret_key_hex", &"") + .field("public_key_hex", &self.public_key_hex) + .finish() + } +} + +/// The bearer credential of the device routes, generated on the device. +#[derive(uniffi::Record, Clone, PartialEq, Eq)] +pub struct WakeDeviceSecret { + /// `wkd_` followed by 43 base64url characters. Keep it in secure storage + /// the notification extension can read. + pub token: String, + /// Lowercase hex SHA-256 of `token`, sent as `secret_sha256` when + /// registering. The gateway stores only this hash. + pub sha256_hex: String, +} + +impl fmt::Debug for WakeDeviceSecret { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("WakeDeviceSecret") + .field("token", &"wkd_") + .field("sha256_hex", &self.sha256_hex) + .finish() + } +} + +/// Push transport of a device. +#[derive(uniffi::Enum, Debug, Clone, Copy, PartialEq, Eq)] +pub enum WakePlatform { + Apns, + Fcm, +} + +impl WakePlatform { + pub(crate) const fn as_str(self) -> &'static str { + match self { + WakePlatform::Apns => "apns", + WakePlatform::Fcm => "fcm", + } + } +} + +/// APNs environment. FCM devices are always `Production`. +#[derive(uniffi::Enum, Debug, Clone, Copy, PartialEq, Eq)] +pub enum WakeEnvironment { + Production, + Sandbox, +} + +impl WakeEnvironment { + pub(crate) const fn as_str(self) -> &'static str { + match self { + WakeEnvironment::Production => "production", + WakeEnvironment::Sandbox => "sandbox", + } + } +} + +/// What the device did with a wake. +#[derive(uniffi::Enum, Debug, Clone, Copy, PartialEq, Eq)] +pub enum WakeAckOutcome { + /// The wake was handled; the gateway cancels its fallback alert. + Handled, + /// The app has no handler for the topic; the fallback alert still fires. + NoHandler, + /// Handling failed; the fallback alert still fires. + Failed, +} + +impl WakeAckOutcome { + pub(crate) const fn as_str(self) -> &'static str { + match self { + WakeAckOutcome::Handled => "handled", + WakeAckOutcome::NoHandler => "no_handler", + WakeAckOutcome::Failed => "failed", + } + } +} + +/// A registration ready to be signed. Every identity signs `message`: `ln:` +/// through the Lightning node's message signing (ldk-node `signMessage`), +/// `pk:` through `wake_sign_pubky_proof`. +#[derive(uniffi::Record, Debug, Clone, PartialEq, Eq)] +pub struct WakeRegistrationRequest { + /// The gateway's audience, from `wake_server_info`. + pub audience: String, + pub app: String, + pub install_id: String, + pub platform: WakePlatform, + pub environment: WakeEnvironment, + pub push_token: String, + /// Compressed secp256k1 public key, 66 lowercase hex characters. + pub encryption_public_key: String, + /// `WakeDeviceSecret.sha256_hex`. + pub secret_sha256: String, + /// One `ln:` and/or one `pk:` identity. + pub identities: Vec, + /// Topic names and `.*` patterns. + pub topics: Vec, + /// Unix seconds. + pub timestamp: u64, + /// The exact UTF-8 text every identity signs. + pub message: String, +} + +/// One identity's signature over `WakeRegistrationRequest.message`. +#[derive(uniffi::Record, Debug, Clone, PartialEq, Eq)] +pub struct WakeIdentityProof { + pub identity: String, + /// `ln:`: 104 zbase32 characters. `pk:`: 128 lowercase hex characters. + pub signature: String, +} + +/// The gateway's view of a registered device. +#[derive(uniffi::Record, Debug, Clone, PartialEq, Eq)] +pub struct WakeRegistration { + pub device_id: String, + pub identities: Vec, + /// Stored topics: the known names and every pattern. + pub topics: Vec, + /// Exact names the gateway does not know. They are not stored. + pub unknown_topics: Vec, + pub server_time: u64, +} + +/// `GET /v1/info`. +#[derive(uniffi::Record, Debug, Clone, PartialEq, Eq)] +pub struct WakeServerInfo { + /// The value registrations must be prepared with. + pub audience: String, + pub server_time: u64, + pub version: String, +} + +/// A wake found in a push. +#[derive(uniffi::Record, Debug, Clone, PartialEq, Eq)] +pub struct WakeEnvelope { + /// 0 for the legacy envelope, 1 for wake v1. + pub version: u8, + /// The wake id to acknowledge (v1 and fallback only). + pub id: Option, + /// The topic; `source.type` for legacy envelopes. + pub topic: String, + /// `background`, `alert` or `time_sensitive` (v1 only). + pub urgency: Option, + /// Unix seconds after which the wake is stale (v1 only). + pub deadline: Option, + /// The producer payload as raw JSON, when there is one. + pub payload_json: Option, + /// `createdAt` of a legacy envelope. + pub created_at: Option, + /// True for a fallback alert, which carries nothing to decrypt. + pub fallback: bool, +} + +/// A topic devices can subscribe to. +#[derive(uniffi::Record, Debug, Clone, PartialEq, Eq)] +pub struct WakeTopic { + pub name: String, + pub description: String, + /// `background`, `alert` or `time_sensitive`. + pub urgency: String, + pub deadline_secs: u32, + /// Whether a visible fallback alert may be shown for this topic. + pub alertable: bool, + /// Alert title, for topics with display text. + pub title: Option, + /// Alert body, for topics with display text. + pub body: Option, + /// Peers (not services) send this topic, through grants. + pub peer: bool, +} From 9a84948d5a289f3cafba89672381f713fd63e1c4 Mon Sep 17 00:00:00 2001 From: coreyphillips Date: Fri, 25 Sep 2026 10:03:05 -0400 Subject: [PATCH 2/4] chore(ios): regenerate the Swift interface for the wake module Generated the way build_ios.sh does: cargo build --release, then uniffi-bindgen generate --library target/release/libbitkitcore.dylib --language swift. Before the wake change the same steps reproduced the committed files exactly, so the diff is only the wake functions and types (additions only). module.modulemap is unchanged. Package.swift and the version are untouched. --- bindings/ios/bitkitcore.swift | 9005 ++++++++++++++++++++------------- bindings/ios/bitkitcoreFFI.h | 179 + 2 files changed, 5539 insertions(+), 3645 deletions(-) diff --git a/bindings/ios/bitkitcore.swift b/bindings/ios/bitkitcore.swift index 27fbb8c..ee8e9f9 100644 --- a/bindings/ios/bitkitcore.swift +++ b/bindings/ios/bitkitcore.swift @@ -403,6 +403,22 @@ private let UNIFFI_CALLBACK_SUCCESS: Int32 = 0 private let UNIFFI_CALLBACK_ERROR: Int32 = 1 private let UNIFFI_CALLBACK_UNEXPECTED_ERROR: Int32 = 2 +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterUInt8: FfiConverterPrimitive { + typealias FfiType = UInt8 + typealias SwiftType = UInt8 + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt8 { + return try lift(readInt(&buf)) + } + + public static func write(_ value: UInt8, into buf: inout [UInt8]) { + writeInt(&buf, lower(value)) + } +} + #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -15738,129 +15754,77 @@ public func FfiConverterTypeValidationResult_lower(_ value: ValidationResult) -> /** - * Balance breakdown from BDK. + * The bearer credential of the device routes, generated on the device. */ -public struct WalletBalance { - /** - * Confirmed and spendable balance (sats) - */ - public var confirmed: UInt64 - /** - * Immature coinbase outputs (sats) - */ - public var immature: UInt64 - /** - * Unconfirmed UTXOs from trusted sources (own change) (sats) - */ - public var trustedPending: UInt64 - /** - * Unconfirmed UTXOs from external sources (sats) - */ - public var untrustedPending: UInt64 +public struct WakeDeviceSecret { /** - * Total spendable: confirmed + trusted_pending (sats) + * `wkd_` followed by 43 base64url characters. Keep it in secure storage + * the notification extension can read. */ - public var spendable: UInt64 + public var token: String /** - * Grand total: all categories (sats) + * Lowercase hex SHA-256 of `token`, sent as `secret_sha256` when + * registering. The gateway stores only this hash. */ - public var total: UInt64 + public var sha256Hex: String // Default memberwise initializers are never public by default, so we // declare one manually. public init( /** - * Confirmed and spendable balance (sats) - */confirmed: UInt64, - /** - * Immature coinbase outputs (sats) - */immature: UInt64, - /** - * Unconfirmed UTXOs from trusted sources (own change) (sats) - */trustedPending: UInt64, - /** - * Unconfirmed UTXOs from external sources (sats) - */untrustedPending: UInt64, - /** - * Total spendable: confirmed + trusted_pending (sats) - */spendable: UInt64, + * `wkd_` followed by 43 base64url characters. Keep it in secure storage + * the notification extension can read. + */token: String, /** - * Grand total: all categories (sats) - */total: UInt64) { - self.confirmed = confirmed - self.immature = immature - self.trustedPending = trustedPending - self.untrustedPending = untrustedPending - self.spendable = spendable - self.total = total + * Lowercase hex SHA-256 of `token`, sent as `secret_sha256` when + * registering. The gateway stores only this hash. + */sha256Hex: String) { + self.token = token + self.sha256Hex = sha256Hex } } #if compiler(>=6) -extension WalletBalance: Sendable {} +extension WakeDeviceSecret: Sendable {} #endif -extension WalletBalance: Equatable, Hashable { - public static func ==(lhs: WalletBalance, rhs: WalletBalance) -> Bool { - if lhs.confirmed != rhs.confirmed { - return false - } - if lhs.immature != rhs.immature { - return false - } - if lhs.trustedPending != rhs.trustedPending { - return false - } - if lhs.untrustedPending != rhs.untrustedPending { +extension WakeDeviceSecret: Equatable, Hashable { + public static func ==(lhs: WakeDeviceSecret, rhs: WakeDeviceSecret) -> Bool { + if lhs.token != rhs.token { return false } - if lhs.spendable != rhs.spendable { - return false - } - if lhs.total != rhs.total { + if lhs.sha256Hex != rhs.sha256Hex { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(confirmed) - hasher.combine(immature) - hasher.combine(trustedPending) - hasher.combine(untrustedPending) - hasher.combine(spendable) - hasher.combine(total) + hasher.combine(token) + hasher.combine(sha256Hex) } } -extension WalletBalance: Codable {} +extension WakeDeviceSecret: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeWalletBalance: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WalletBalance { +public struct FfiConverterTypeWakeDeviceSecret: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WakeDeviceSecret { return - try WalletBalance( - confirmed: FfiConverterUInt64.read(from: &buf), - immature: FfiConverterUInt64.read(from: &buf), - trustedPending: FfiConverterUInt64.read(from: &buf), - untrustedPending: FfiConverterUInt64.read(from: &buf), - spendable: FfiConverterUInt64.read(from: &buf), - total: FfiConverterUInt64.read(from: &buf) + try WakeDeviceSecret( + token: FfiConverterString.read(from: &buf), + sha256Hex: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: WalletBalance, into buf: inout [UInt8]) { - FfiConverterUInt64.write(value.confirmed, into: &buf) - FfiConverterUInt64.write(value.immature, into: &buf) - FfiConverterUInt64.write(value.trustedPending, into: &buf) - FfiConverterUInt64.write(value.untrustedPending, into: &buf) - FfiConverterUInt64.write(value.spendable, into: &buf) - FfiConverterUInt64.write(value.total, into: &buf) + public static func write(_ value: WakeDeviceSecret, into buf: inout [UInt8]) { + FfiConverterString.write(value.token, into: &buf) + FfiConverterString.write(value.sha256Hex, into: &buf) } } @@ -15868,128 +15832,170 @@ public struct FfiConverterTypeWalletBalance: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeWalletBalance_lift(_ buf: RustBuffer) throws -> WalletBalance { - return try FfiConverterTypeWalletBalance.lift(buf) +public func FfiConverterTypeWakeDeviceSecret_lift(_ buf: RustBuffer) throws -> WakeDeviceSecret { + return try FfiConverterTypeWakeDeviceSecret.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeWalletBalance_lower(_ value: WalletBalance) -> RustBuffer { - return FfiConverterTypeWalletBalance.lower(value) +public func FfiConverterTypeWakeDeviceSecret_lower(_ value: WakeDeviceSecret) -> RustBuffer { + return FfiConverterTypeWakeDeviceSecret.lower(value) } /** - * Common parameters for creating and syncing a watch-only BDK wallet. + * A wake found in a push. */ -public struct WalletParams { +public struct WakeEnvelope { /** - * Extended public key (xpub/ypub/zpub/tpub/upub/vpub) + * 0 for the legacy envelope, 1 for wake v1. */ - public var extendedKey: String + public var version: UInt8 /** - * Electrum server URL for wallet sync + * The wake id to acknowledge (v1 and fallback only). */ - public var electrumUrl: String + public var id: String? /** - * Root fingerprint hex (e.g. "73c5da0a"). Required for hardware wallet signing. + * The topic; `source.type` for legacy envelopes. */ - public var fingerprint: String? + public var topic: String /** - * Bitcoin network (auto-detected from key prefix if not specified) + * `background`, `alert` or `time_sensitive` (v1 only). */ - public var network: Network? + public var urgency: String? /** - * Override account type for ambiguous key prefixes (xpub/tpub) + * Unix seconds after which the wake is stale (v1 only). */ - public var accountType: AccountType? + public var deadline: UInt64? + /** + * The producer payload as raw JSON, when there is one. + */ + public var payloadJson: String? + /** + * `createdAt` of a legacy envelope. + */ + public var createdAt: String? + /** + * True for a fallback alert, which carries nothing to decrypt. + */ + public var fallback: Bool // Default memberwise initializers are never public by default, so we // declare one manually. public init( /** - * Extended public key (xpub/ypub/zpub/tpub/upub/vpub) - */extendedKey: String, + * 0 for the legacy envelope, 1 for wake v1. + */version: UInt8, /** - * Electrum server URL for wallet sync - */electrumUrl: String, + * The wake id to acknowledge (v1 and fallback only). + */id: String?, /** - * Root fingerprint hex (e.g. "73c5da0a"). Required for hardware wallet signing. - */fingerprint: String?, + * The topic; `source.type` for legacy envelopes. + */topic: String, /** - * Bitcoin network (auto-detected from key prefix if not specified) - */network: Network?, + * `background`, `alert` or `time_sensitive` (v1 only). + */urgency: String?, /** - * Override account type for ambiguous key prefixes (xpub/tpub) - */accountType: AccountType?) { - self.extendedKey = extendedKey - self.electrumUrl = electrumUrl - self.fingerprint = fingerprint - self.network = network - self.accountType = accountType + * Unix seconds after which the wake is stale (v1 only). + */deadline: UInt64?, + /** + * The producer payload as raw JSON, when there is one. + */payloadJson: String?, + /** + * `createdAt` of a legacy envelope. + */createdAt: String?, + /** + * True for a fallback alert, which carries nothing to decrypt. + */fallback: Bool) { + self.version = version + self.id = id + self.topic = topic + self.urgency = urgency + self.deadline = deadline + self.payloadJson = payloadJson + self.createdAt = createdAt + self.fallback = fallback } } #if compiler(>=6) -extension WalletParams: Sendable {} +extension WakeEnvelope: Sendable {} #endif -extension WalletParams: Equatable, Hashable { - public static func ==(lhs: WalletParams, rhs: WalletParams) -> Bool { - if lhs.extendedKey != rhs.extendedKey { +extension WakeEnvelope: Equatable, Hashable { + public static func ==(lhs: WakeEnvelope, rhs: WakeEnvelope) -> Bool { + if lhs.version != rhs.version { return false } - if lhs.electrumUrl != rhs.electrumUrl { + if lhs.id != rhs.id { return false } - if lhs.fingerprint != rhs.fingerprint { + if lhs.topic != rhs.topic { return false } - if lhs.network != rhs.network { + if lhs.urgency != rhs.urgency { return false } - if lhs.accountType != rhs.accountType { + if lhs.deadline != rhs.deadline { + return false + } + if lhs.payloadJson != rhs.payloadJson { + return false + } + if lhs.createdAt != rhs.createdAt { + return false + } + if lhs.fallback != rhs.fallback { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(extendedKey) - hasher.combine(electrumUrl) - hasher.combine(fingerprint) - hasher.combine(network) - hasher.combine(accountType) + hasher.combine(version) + hasher.combine(id) + hasher.combine(topic) + hasher.combine(urgency) + hasher.combine(deadline) + hasher.combine(payloadJson) + hasher.combine(createdAt) + hasher.combine(fallback) } } -extension WalletParams: Codable {} +extension WakeEnvelope: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeWalletParams: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WalletParams { +public struct FfiConverterTypeWakeEnvelope: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WakeEnvelope { return - try WalletParams( - extendedKey: FfiConverterString.read(from: &buf), - electrumUrl: FfiConverterString.read(from: &buf), - fingerprint: FfiConverterOptionString.read(from: &buf), - network: FfiConverterOptionTypeNetwork.read(from: &buf), - accountType: FfiConverterOptionTypeAccountType.read(from: &buf) + try WakeEnvelope( + version: FfiConverterUInt8.read(from: &buf), + id: FfiConverterOptionString.read(from: &buf), + topic: FfiConverterString.read(from: &buf), + urgency: FfiConverterOptionString.read(from: &buf), + deadline: FfiConverterOptionUInt64.read(from: &buf), + payloadJson: FfiConverterOptionString.read(from: &buf), + createdAt: FfiConverterOptionString.read(from: &buf), + fallback: FfiConverterBool.read(from: &buf) ) } - public static func write(_ value: WalletParams, into buf: inout [UInt8]) { - FfiConverterString.write(value.extendedKey, into: &buf) - FfiConverterString.write(value.electrumUrl, into: &buf) - FfiConverterOptionString.write(value.fingerprint, into: &buf) - FfiConverterOptionTypeNetwork.write(value.network, into: &buf) - FfiConverterOptionTypeAccountType.write(value.accountType, into: &buf) + public static func write(_ value: WakeEnvelope, into buf: inout [UInt8]) { + FfiConverterUInt8.write(value.version, into: &buf) + FfiConverterOptionString.write(value.id, into: &buf) + FfiConverterString.write(value.topic, into: &buf) + FfiConverterOptionString.write(value.urgency, into: &buf) + FfiConverterOptionUInt64.write(value.deadline, into: &buf) + FfiConverterOptionString.write(value.payloadJson, into: &buf) + FfiConverterOptionString.write(value.createdAt, into: &buf) + FfiConverterBool.write(value.fallback, into: &buf) } } @@ -15997,160 +16003,80 @@ public struct FfiConverterTypeWalletParams: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeWalletParams_lift(_ buf: RustBuffer) throws -> WalletParams { - return try FfiConverterTypeWalletParams.lift(buf) +public func FfiConverterTypeWakeEnvelope_lift(_ buf: RustBuffer) throws -> WakeEnvelope { + return try FfiConverterTypeWakeEnvelope.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeWalletParams_lower(_ value: WalletParams) -> RustBuffer { - return FfiConverterTypeWalletParams.lower(value) +public func FfiConverterTypeWakeEnvelope_lower(_ value: WakeEnvelope) -> RustBuffer { + return FfiConverterTypeWakeEnvelope.lower(value) } /** - * Parameters for starting an xpub transaction watcher. + * One identity's signature over `WakeRegistrationRequest.message`. */ -public struct WatcherParams { - /** - * Caller-supplied identifier for this watcher. - */ - public var watcherId: String - /** - * Wallet id that scopes the activities this watcher emits. Apps may use - * one wallet id for several account watchers and merge their snapshots. - */ - public var walletId: String - /** - * Extended public key (xpub/ypub/zpub/tpub/upub/vpub). - */ - public var extendedKey: String - /** - * Electrum server URL (e.g. "ssl://electrum.example.com:50002"). - */ - public var electrumUrl: String - /** - * Bitcoin network override (auto-detected from key prefix if None). - */ - public var network: Network? +public struct WakeIdentityProof { + public var identity: String /** - * Account type override (auto-detected from key prefix if None). - */ - public var accountType: AccountType? - /** - * Number of unused addresses to monitor beyond the last used - * (defaults to `DEFAULT_GAP_LIMIT` when None). + * `ln:`: 104 zbase32 characters. `pk:`: 128 lowercase hex characters. */ - public var gapLimit: UInt32? + public var signature: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init( - /** - * Caller-supplied identifier for this watcher. - */watcherId: String, - /** - * Wallet id that scopes the activities this watcher emits. Apps may use - * one wallet id for several account watchers and merge their snapshots. - */walletId: String, + public init(identity: String, /** - * Extended public key (xpub/ypub/zpub/tpub/upub/vpub). - */extendedKey: String, - /** - * Electrum server URL (e.g. "ssl://electrum.example.com:50002"). - */electrumUrl: String, - /** - * Bitcoin network override (auto-detected from key prefix if None). - */network: Network?, - /** - * Account type override (auto-detected from key prefix if None). - */accountType: AccountType?, - /** - * Number of unused addresses to monitor beyond the last used - * (defaults to `DEFAULT_GAP_LIMIT` when None). - */gapLimit: UInt32?) { - self.watcherId = watcherId - self.walletId = walletId - self.extendedKey = extendedKey - self.electrumUrl = electrumUrl - self.network = network - self.accountType = accountType - self.gapLimit = gapLimit + * `ln:`: 104 zbase32 characters. `pk:`: 128 lowercase hex characters. + */signature: String) { + self.identity = identity + self.signature = signature } } #if compiler(>=6) -extension WatcherParams: Sendable {} +extension WakeIdentityProof: Sendable {} #endif -extension WatcherParams: Equatable, Hashable { - public static func ==(lhs: WatcherParams, rhs: WatcherParams) -> Bool { - if lhs.watcherId != rhs.watcherId { +extension WakeIdentityProof: Equatable, Hashable { + public static func ==(lhs: WakeIdentityProof, rhs: WakeIdentityProof) -> Bool { + if lhs.identity != rhs.identity { return false } - if lhs.walletId != rhs.walletId { - return false - } - if lhs.extendedKey != rhs.extendedKey { - return false - } - if lhs.electrumUrl != rhs.electrumUrl { - return false - } - if lhs.network != rhs.network { - return false - } - if lhs.accountType != rhs.accountType { - return false - } - if lhs.gapLimit != rhs.gapLimit { + if lhs.signature != rhs.signature { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(watcherId) - hasher.combine(walletId) - hasher.combine(extendedKey) - hasher.combine(electrumUrl) - hasher.combine(network) - hasher.combine(accountType) - hasher.combine(gapLimit) + hasher.combine(identity) + hasher.combine(signature) } } -extension WatcherParams: Codable {} +extension WakeIdentityProof: Codable {} #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeWatcherParams: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WatcherParams { +public struct FfiConverterTypeWakeIdentityProof: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WakeIdentityProof { return - try WatcherParams( - watcherId: FfiConverterString.read(from: &buf), - walletId: FfiConverterString.read(from: &buf), - extendedKey: FfiConverterString.read(from: &buf), - electrumUrl: FfiConverterString.read(from: &buf), - network: FfiConverterOptionTypeNetwork.read(from: &buf), - accountType: FfiConverterOptionTypeAccountType.read(from: &buf), - gapLimit: FfiConverterOptionUInt32.read(from: &buf) + try WakeIdentityProof( + identity: FfiConverterString.read(from: &buf), + signature: FfiConverterString.read(from: &buf) ) } - public static func write(_ value: WatcherParams, into buf: inout [UInt8]) { - FfiConverterString.write(value.watcherId, into: &buf) - FfiConverterString.write(value.walletId, into: &buf) - FfiConverterString.write(value.extendedKey, into: &buf) - FfiConverterString.write(value.electrumUrl, into: &buf) - FfiConverterOptionTypeNetwork.write(value.network, into: &buf) - FfiConverterOptionTypeAccountType.write(value.accountType, into: &buf) - FfiConverterOptionUInt32.write(value.gapLimit, into: &buf) + public static func write(_ value: WakeIdentityProof, into buf: inout [UInt8]) { + FfiConverterString.write(value.identity, into: &buf) + FfiConverterString.write(value.signature, into: &buf) } } @@ -16158,371 +16084,397 @@ public struct FfiConverterTypeWatcherParams: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeWatcherParams_lift(_ buf: RustBuffer) throws -> WatcherParams { - return try FfiConverterTypeWatcherParams.lift(buf) +public func FfiConverterTypeWakeIdentityProof_lift(_ buf: RustBuffer) throws -> WakeIdentityProof { + return try FfiConverterTypeWakeIdentityProof.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeWatcherParams_lower(_ value: WatcherParams) -> RustBuffer { - return FfiConverterTypeWatcherParams.lower(value) +public func FfiConverterTypeWakeIdentityProof_lower(_ value: WakeIdentityProof) -> RustBuffer { + return FfiConverterTypeWakeIdentityProof.lower(value) } /** - * Errors specific to account info operations (BDK/Electrum-based). + * A secp256k1 key pair for push encryption. The public key is registered + * with the gateway; the secret key decrypts wakes and never leaves the device. */ -public enum AccountInfoError: Swift.Error { - - - - /** - * The provided extended public key is invalid or cannot be parsed - */ - case InvalidExtendedKey(errorDetails: String - ) - /** - * The provided address is invalid - */ - case InvalidAddress(errorDetails: String - ) - /** - * Electrum connection or query failed - */ - case ElectrumError(errorDetails: String - ) - /** - * BDK wallet creation or operation error - */ - case WalletError(errorDetails: String - ) - /** - * Wallet sync with Electrum failed - */ - case SyncError(errorDetails: String - ) - /** - * The key type/prefix is not recognized - */ - case UnsupportedKeyType(errorDetails: String - ) - /** - * Network mismatch between key prefix and specified network - */ - case NetworkMismatch(errorDetails: String - ) - /** - * Invalid transaction ID provided - */ - case InvalidTxid(errorDetails: String - ) +public struct WakeKeyPair { /** - * A valid transaction ID was not found in the wallet + * 64 lowercase hex characters. */ - case TransactionNotFound(errorDetails: String - ) + public var secretKeyHex: String /** - * Watcher lifecycle or subscription error + * Compressed public key, 66 lowercase hex characters. */ - case WatcherError(errorDetails: String - ) -} + public var publicKeyHex: String + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init( + /** + * 64 lowercase hex characters. + */secretKeyHex: String, + /** + * Compressed public key, 66 lowercase hex characters. + */publicKeyHex: String) { + self.secretKeyHex = secretKeyHex + self.publicKeyHex = publicKeyHex + } +} -#if swift(>=5.8) -@_documentation(visibility: private) +#if compiler(>=6) +extension WakeKeyPair: Sendable {} #endif -public struct FfiConverterTypeAccountInfoError: FfiConverterRustBuffer { - typealias SwiftType = AccountInfoError - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AccountInfoError { - let variant: Int32 = try readInt(&buf) - switch variant { - - - - case 1: return .InvalidExtendedKey( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 2: return .InvalidAddress( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 3: return .ElectrumError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 4: return .WalletError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 5: return .SyncError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 6: return .UnsupportedKeyType( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 7: return .NetworkMismatch( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 8: return .InvalidTxid( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 9: return .TransactionNotFound( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 10: return .WatcherError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - default: throw UniffiInternalError.unexpectedEnumCase +extension WakeKeyPair: Equatable, Hashable { + public static func ==(lhs: WakeKeyPair, rhs: WakeKeyPair) -> Bool { + if lhs.secretKeyHex != rhs.secretKeyHex { + return false + } + if lhs.publicKeyHex != rhs.publicKeyHex { + return false } + return true } - public static func write(_ value: AccountInfoError, into buf: inout [UInt8]) { - switch value { - - - - - - case let .InvalidExtendedKey(errorDetails): - writeInt(&buf, Int32(1)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .InvalidAddress(errorDetails): - writeInt(&buf, Int32(2)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .ElectrumError(errorDetails): - writeInt(&buf, Int32(3)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .WalletError(errorDetails): - writeInt(&buf, Int32(4)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .SyncError(errorDetails): - writeInt(&buf, Int32(5)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .UnsupportedKeyType(errorDetails): - writeInt(&buf, Int32(6)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .NetworkMismatch(errorDetails): - writeInt(&buf, Int32(7)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .InvalidTxid(errorDetails): - writeInt(&buf, Int32(8)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .TransactionNotFound(errorDetails): - writeInt(&buf, Int32(9)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .WatcherError(errorDetails): - writeInt(&buf, Int32(10)) - FfiConverterString.write(errorDetails, into: &buf) - - } + public func hash(into hasher: inout Hasher) { + hasher.combine(secretKeyHex) + hasher.combine(publicKeyHex) } } +extension WakeKeyPair: Codable {} + + #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAccountInfoError_lift(_ buf: RustBuffer) throws -> AccountInfoError { - return try FfiConverterTypeAccountInfoError.lift(buf) +public struct FfiConverterTypeWakeKeyPair: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WakeKeyPair { + return + try WakeKeyPair( + secretKeyHex: FfiConverterString.read(from: &buf), + publicKeyHex: FfiConverterString.read(from: &buf) + ) + } + + public static func write(_ value: WakeKeyPair, into buf: inout [UInt8]) { + FfiConverterString.write(value.secretKeyHex, into: &buf) + FfiConverterString.write(value.publicKeyHex, into: &buf) + } } + #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAccountInfoError_lower(_ value: AccountInfoError) -> RustBuffer { - return FfiConverterTypeAccountInfoError.lower(value) +public func FfiConverterTypeWakeKeyPair_lift(_ buf: RustBuffer) throws -> WakeKeyPair { + return try FfiConverterTypeWakeKeyPair.lift(buf) } - -extension AccountInfoError: Equatable, Hashable {} - -extension AccountInfoError: Codable {} - - - - -extension AccountInfoError: Foundation.LocalizedError { - public var errorDescription: String? { - String(reflecting: self) - } +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeWakeKeyPair_lower(_ value: WakeKeyPair) -> RustBuffer { + return FfiConverterTypeWakeKeyPair.lower(value) } - - -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. /** - * Account type classification for extended public keys. - * - * Determines the BIP standard, derivation path purpose, and script type. + * The gateway's view of a registered device. */ - -public enum AccountType { - - /** - * BIP44 legacy (P2PKH) — xpub/tpub prefix - */ - case legacy +public struct WakeRegistration { + public var deviceId: String + public var identities: [String] /** - * BIP49 wrapped segwit (P2SH-P2WPKH) — ypub/upub prefix + * Stored topics: the known names and every pattern. */ - case wrappedSegwit + public var topics: [String] /** - * BIP84 native segwit (P2WPKH) — zpub/vpub prefix + * Exact names the gateway does not know. They are not stored. */ - case nativeSegwit - /** - * BIP86 taproot (P2TR) - */ - case taproot -} + public var unknownTopics: [String] + public var serverTime: UInt64 + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(deviceId: String, identities: [String], + /** + * Stored topics: the known names and every pattern. + */topics: [String], + /** + * Exact names the gateway does not know. They are not stored. + */unknownTopics: [String], serverTime: UInt64) { + self.deviceId = deviceId + self.identities = identities + self.topics = topics + self.unknownTopics = unknownTopics + self.serverTime = serverTime + } +} #if compiler(>=6) -extension AccountType: Sendable {} +extension WakeRegistration: Sendable {} #endif -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeAccountType: FfiConverterRustBuffer { - typealias SwiftType = AccountType - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AccountType { - let variant: Int32 = try readInt(&buf) - switch variant { - - case 1: return .legacy - - case 2: return .wrappedSegwit - - case 3: return .nativeSegwit - - case 4: return .taproot - - default: throw UniffiInternalError.unexpectedEnumCase +extension WakeRegistration: Equatable, Hashable { + public static func ==(lhs: WakeRegistration, rhs: WakeRegistration) -> Bool { + if lhs.deviceId != rhs.deviceId { + return false } + if lhs.identities != rhs.identities { + return false + } + if lhs.topics != rhs.topics { + return false + } + if lhs.unknownTopics != rhs.unknownTopics { + return false + } + if lhs.serverTime != rhs.serverTime { + return false + } + return true } - public static func write(_ value: AccountType, into buf: inout [UInt8]) { - switch value { - - - case .legacy: - writeInt(&buf, Int32(1)) - - - case .wrappedSegwit: - writeInt(&buf, Int32(2)) - - - case .nativeSegwit: - writeInt(&buf, Int32(3)) - - - case .taproot: - writeInt(&buf, Int32(4)) - - } + public func hash(into hasher: inout Hasher) { + hasher.combine(deviceId) + hasher.combine(identities) + hasher.combine(topics) + hasher.combine(unknownTopics) + hasher.combine(serverTime) } } +extension WakeRegistration: Codable {} + + #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAccountType_lift(_ buf: RustBuffer) throws -> AccountType { - return try FfiConverterTypeAccountType.lift(buf) +public struct FfiConverterTypeWakeRegistration: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WakeRegistration { + return + try WakeRegistration( + deviceId: FfiConverterString.read(from: &buf), + identities: FfiConverterSequenceString.read(from: &buf), + topics: FfiConverterSequenceString.read(from: &buf), + unknownTopics: FfiConverterSequenceString.read(from: &buf), + serverTime: FfiConverterUInt64.read(from: &buf) + ) + } + + public static func write(_ value: WakeRegistration, into buf: inout [UInt8]) { + FfiConverterString.write(value.deviceId, into: &buf) + FfiConverterSequenceString.write(value.identities, into: &buf) + FfiConverterSequenceString.write(value.topics, into: &buf) + FfiConverterSequenceString.write(value.unknownTopics, into: &buf) + FfiConverterUInt64.write(value.serverTime, into: &buf) + } } + #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAccountType_lower(_ value: AccountType) -> RustBuffer { - return FfiConverterTypeAccountType.lower(value) +public func FfiConverterTypeWakeRegistration_lift(_ buf: RustBuffer) throws -> WakeRegistration { + return try FfiConverterTypeWakeRegistration.lift(buf) } - -extension AccountType: Equatable, Hashable {} - -extension AccountType: Codable {} +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeWakeRegistration_lower(_ value: WakeRegistration) -> RustBuffer { + return FfiConverterTypeWakeRegistration.lower(value) +} +/** + * A registration ready to be signed. Every identity signs `message`: `ln:` + * through the Lightning node's message signing (ldk-node `signMessage`), + * `pk:` through `wake_sign_pubky_proof`. + */ +public struct WakeRegistrationRequest { + /** + * The gateway's audience, from `wake_server_info`. + */ + public var audience: String + public var app: String + public var installId: String + public var platform: WakePlatform + public var environment: WakeEnvironment + public var pushToken: String + /** + * Compressed secp256k1 public key, 66 lowercase hex characters. + */ + public var encryptionPublicKey: String + /** + * `WakeDeviceSecret.sha256_hex`. + */ + public var secretSha256: String + /** + * One `ln:` and/or one `pk:` identity. + */ + public var identities: [String] + /** + * Topic names and `.*` patterns. + */ + public var topics: [String] + /** + * Unix seconds. + */ + public var timestamp: UInt64 + /** + * The exact UTF-8 text every identity signs. + */ + public var message: String + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init( + /** + * The gateway's audience, from `wake_server_info`. + */audience: String, app: String, installId: String, platform: WakePlatform, environment: WakeEnvironment, pushToken: String, + /** + * Compressed secp256k1 public key, 66 lowercase hex characters. + */encryptionPublicKey: String, + /** + * `WakeDeviceSecret.sha256_hex`. + */secretSha256: String, + /** + * One `ln:` and/or one `pk:` identity. + */identities: [String], + /** + * Topic names and `.*` patterns. + */topics: [String], + /** + * Unix seconds. + */timestamp: UInt64, + /** + * The exact UTF-8 text every identity signs. + */message: String) { + self.audience = audience + self.app = app + self.installId = installId + self.platform = platform + self.environment = environment + self.pushToken = pushToken + self.encryptionPublicKey = encryptionPublicKey + self.secretSha256 = secretSha256 + self.identities = identities + self.topics = topics + self.timestamp = timestamp + self.message = message + } +} +#if compiler(>=6) +extension WakeRegistrationRequest: Sendable {} +#endif -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +extension WakeRegistrationRequest: Equatable, Hashable { + public static func ==(lhs: WakeRegistrationRequest, rhs: WakeRegistrationRequest) -> Bool { + if lhs.audience != rhs.audience { + return false + } + if lhs.app != rhs.app { + return false + } + if lhs.installId != rhs.installId { + return false + } + if lhs.platform != rhs.platform { + return false + } + if lhs.environment != rhs.environment { + return false + } + if lhs.pushToken != rhs.pushToken { + return false + } + if lhs.encryptionPublicKey != rhs.encryptionPublicKey { + return false + } + if lhs.secretSha256 != rhs.secretSha256 { + return false + } + if lhs.identities != rhs.identities { + return false + } + if lhs.topics != rhs.topics { + return false + } + if lhs.timestamp != rhs.timestamp { + return false + } + if lhs.message != rhs.message { + return false + } + return true + } -public enum Activity { - - case onchain(OnchainActivity - ) - case lightning(LightningActivity - ) + public func hash(into hasher: inout Hasher) { + hasher.combine(audience) + hasher.combine(app) + hasher.combine(installId) + hasher.combine(platform) + hasher.combine(environment) + hasher.combine(pushToken) + hasher.combine(encryptionPublicKey) + hasher.combine(secretSha256) + hasher.combine(identities) + hasher.combine(topics) + hasher.combine(timestamp) + hasher.combine(message) + } } +extension WakeRegistrationRequest: Codable {} + -#if compiler(>=6) -extension Activity: Sendable {} -#endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeActivity: FfiConverterRustBuffer { - typealias SwiftType = Activity - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Activity { - let variant: Int32 = try readInt(&buf) - switch variant { - - case 1: return .onchain(try FfiConverterTypeOnchainActivity.read(from: &buf) - ) - - case 2: return .lightning(try FfiConverterTypeLightningActivity.read(from: &buf) +public struct FfiConverterTypeWakeRegistrationRequest: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WakeRegistrationRequest { + return + try WakeRegistrationRequest( + audience: FfiConverterString.read(from: &buf), + app: FfiConverterString.read(from: &buf), + installId: FfiConverterString.read(from: &buf), + platform: FfiConverterTypeWakePlatform.read(from: &buf), + environment: FfiConverterTypeWakeEnvironment.read(from: &buf), + pushToken: FfiConverterString.read(from: &buf), + encryptionPublicKey: FfiConverterString.read(from: &buf), + secretSha256: FfiConverterString.read(from: &buf), + identities: FfiConverterSequenceString.read(from: &buf), + topics: FfiConverterSequenceString.read(from: &buf), + timestamp: FfiConverterUInt64.read(from: &buf), + message: FfiConverterString.read(from: &buf) ) - - default: throw UniffiInternalError.unexpectedEnumCase - } } - public static func write(_ value: Activity, into buf: inout [UInt8]) { - switch value { - - - case let .onchain(v1): - writeInt(&buf, Int32(1)) - FfiConverterTypeOnchainActivity.write(v1, into: &buf) - - - case let .lightning(v1): - writeInt(&buf, Int32(2)) - FfiConverterTypeLightningActivity.write(v1, into: &buf) - - } + public static func write(_ value: WakeRegistrationRequest, into buf: inout [UInt8]) { + FfiConverterString.write(value.audience, into: &buf) + FfiConverterString.write(value.app, into: &buf) + FfiConverterString.write(value.installId, into: &buf) + FfiConverterTypeWakePlatform.write(value.platform, into: &buf) + FfiConverterTypeWakeEnvironment.write(value.environment, into: &buf) + FfiConverterString.write(value.pushToken, into: &buf) + FfiConverterString.write(value.encryptionPublicKey, into: &buf) + FfiConverterString.write(value.secretSha256, into: &buf) + FfiConverterSequenceString.write(value.identities, into: &buf) + FfiConverterSequenceString.write(value.topics, into: &buf) + FfiConverterUInt64.write(value.timestamp, into: &buf) + FfiConverterString.write(value.message, into: &buf) } } @@ -16530,81 +16482,1920 @@ public struct FfiConverterTypeActivity: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeActivity_lift(_ buf: RustBuffer) throws -> Activity { - return try FfiConverterTypeActivity.lift(buf) +public func FfiConverterTypeWakeRegistrationRequest_lift(_ buf: RustBuffer) throws -> WakeRegistrationRequest { + return try FfiConverterTypeWakeRegistrationRequest.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeActivity_lower(_ value: Activity) -> RustBuffer { - return FfiConverterTypeActivity.lower(value) +public func FfiConverterTypeWakeRegistrationRequest_lower(_ value: WakeRegistrationRequest) -> RustBuffer { + return FfiConverterTypeWakeRegistrationRequest.lower(value) } -extension Activity: Equatable, Hashable {} +/** + * `GET /v1/info`. + */ +public struct WakeServerInfo { + /** + * The value registrations must be prepared with. + */ + public var audience: String + public var serverTime: UInt64 + public var version: String -extension Activity: Codable {} + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init( + /** + * The value registrations must be prepared with. + */audience: String, serverTime: UInt64, version: String) { + self.audience = audience + self.serverTime = serverTime + self.version = version + } +} + +#if compiler(>=6) +extension WakeServerInfo: Sendable {} +#endif +extension WakeServerInfo: Equatable, Hashable { + public static func ==(lhs: WakeServerInfo, rhs: WakeServerInfo) -> Bool { + if lhs.audience != rhs.audience { + return false + } + if lhs.serverTime != rhs.serverTime { + return false + } + if lhs.version != rhs.version { + return false + } + return true + } + public func hash(into hasher: inout Hasher) { + hasher.combine(audience) + hasher.combine(serverTime) + hasher.combine(version) + } +} +extension WakeServerInfo: Codable {} -public enum ActivityError: Swift.Error { +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeWakeServerInfo: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WakeServerInfo { + return + try WakeServerInfo( + audience: FfiConverterString.read(from: &buf), + serverTime: FfiConverterUInt64.read(from: &buf), + version: FfiConverterString.read(from: &buf) + ) + } - - - case InvalidActivity(errorDetails: String - ) - case InitializationError(errorDetails: String - ) - case InsertError(errorDetails: String - ) - case RetrievalError(errorDetails: String - ) - case DataError(errorDetails: String - ) - case ConnectionError(errorDetails: String - ) - case SerializationError(errorDetails: String - ) + public static func write(_ value: WakeServerInfo, into buf: inout [UInt8]) { + FfiConverterString.write(value.audience, into: &buf) + FfiConverterUInt64.write(value.serverTime, into: &buf) + FfiConverterString.write(value.version, into: &buf) + } } #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeActivityError: FfiConverterRustBuffer { - typealias SwiftType = ActivityError +public func FfiConverterTypeWakeServerInfo_lift(_ buf: RustBuffer) throws -> WakeServerInfo { + return try FfiConverterTypeWakeServerInfo.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeWakeServerInfo_lower(_ value: WakeServerInfo) -> RustBuffer { + return FfiConverterTypeWakeServerInfo.lower(value) +} + + +/** + * A topic devices can subscribe to. + */ +public struct WakeTopic { + public var name: String + public var description: String + /** + * `background`, `alert` or `time_sensitive`. + */ + public var urgency: String + public var deadlineSecs: UInt32 + /** + * Whether a visible fallback alert may be shown for this topic. + */ + public var alertable: Bool + /** + * Alert title, for topics with display text. + */ + public var title: String? + /** + * Alert body, for topics with display text. + */ + public var body: String? + /** + * Peers (not services) send this topic, through grants. + */ + public var peer: Bool + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(name: String, description: String, + /** + * `background`, `alert` or `time_sensitive`. + */urgency: String, deadlineSecs: UInt32, + /** + * Whether a visible fallback alert may be shown for this topic. + */alertable: Bool, + /** + * Alert title, for topics with display text. + */title: String?, + /** + * Alert body, for topics with display text. + */body: String?, + /** + * Peers (not services) send this topic, through grants. + */peer: Bool) { + self.name = name + self.description = description + self.urgency = urgency + self.deadlineSecs = deadlineSecs + self.alertable = alertable + self.title = title + self.body = body + self.peer = peer + } +} + +#if compiler(>=6) +extension WakeTopic: Sendable {} +#endif + + +extension WakeTopic: Equatable, Hashable { + public static func ==(lhs: WakeTopic, rhs: WakeTopic) -> Bool { + if lhs.name != rhs.name { + return false + } + if lhs.description != rhs.description { + return false + } + if lhs.urgency != rhs.urgency { + return false + } + if lhs.deadlineSecs != rhs.deadlineSecs { + return false + } + if lhs.alertable != rhs.alertable { + return false + } + if lhs.title != rhs.title { + return false + } + if lhs.body != rhs.body { + return false + } + if lhs.peer != rhs.peer { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(name) + hasher.combine(description) + hasher.combine(urgency) + hasher.combine(deadlineSecs) + hasher.combine(alertable) + hasher.combine(title) + hasher.combine(body) + hasher.combine(peer) + } +} + +extension WakeTopic: Codable {} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeWakeTopic: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WakeTopic { + return + try WakeTopic( + name: FfiConverterString.read(from: &buf), + description: FfiConverterString.read(from: &buf), + urgency: FfiConverterString.read(from: &buf), + deadlineSecs: FfiConverterUInt32.read(from: &buf), + alertable: FfiConverterBool.read(from: &buf), + title: FfiConverterOptionString.read(from: &buf), + body: FfiConverterOptionString.read(from: &buf), + peer: FfiConverterBool.read(from: &buf) + ) + } + + public static func write(_ value: WakeTopic, into buf: inout [UInt8]) { + FfiConverterString.write(value.name, into: &buf) + FfiConverterString.write(value.description, into: &buf) + FfiConverterString.write(value.urgency, into: &buf) + FfiConverterUInt32.write(value.deadlineSecs, into: &buf) + FfiConverterBool.write(value.alertable, into: &buf) + FfiConverterOptionString.write(value.title, into: &buf) + FfiConverterOptionString.write(value.body, into: &buf) + FfiConverterBool.write(value.peer, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeWakeTopic_lift(_ buf: RustBuffer) throws -> WakeTopic { + return try FfiConverterTypeWakeTopic.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeWakeTopic_lower(_ value: WakeTopic) -> RustBuffer { + return FfiConverterTypeWakeTopic.lower(value) +} + + +/** + * Balance breakdown from BDK. + */ +public struct WalletBalance { + /** + * Confirmed and spendable balance (sats) + */ + public var confirmed: UInt64 + /** + * Immature coinbase outputs (sats) + */ + public var immature: UInt64 + /** + * Unconfirmed UTXOs from trusted sources (own change) (sats) + */ + public var trustedPending: UInt64 + /** + * Unconfirmed UTXOs from external sources (sats) + */ + public var untrustedPending: UInt64 + /** + * Total spendable: confirmed + trusted_pending (sats) + */ + public var spendable: UInt64 + /** + * Grand total: all categories (sats) + */ + public var total: UInt64 + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init( + /** + * Confirmed and spendable balance (sats) + */confirmed: UInt64, + /** + * Immature coinbase outputs (sats) + */immature: UInt64, + /** + * Unconfirmed UTXOs from trusted sources (own change) (sats) + */trustedPending: UInt64, + /** + * Unconfirmed UTXOs from external sources (sats) + */untrustedPending: UInt64, + /** + * Total spendable: confirmed + trusted_pending (sats) + */spendable: UInt64, + /** + * Grand total: all categories (sats) + */total: UInt64) { + self.confirmed = confirmed + self.immature = immature + self.trustedPending = trustedPending + self.untrustedPending = untrustedPending + self.spendable = spendable + self.total = total + } +} + +#if compiler(>=6) +extension WalletBalance: Sendable {} +#endif + + +extension WalletBalance: Equatable, Hashable { + public static func ==(lhs: WalletBalance, rhs: WalletBalance) -> Bool { + if lhs.confirmed != rhs.confirmed { + return false + } + if lhs.immature != rhs.immature { + return false + } + if lhs.trustedPending != rhs.trustedPending { + return false + } + if lhs.untrustedPending != rhs.untrustedPending { + return false + } + if lhs.spendable != rhs.spendable { + return false + } + if lhs.total != rhs.total { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(confirmed) + hasher.combine(immature) + hasher.combine(trustedPending) + hasher.combine(untrustedPending) + hasher.combine(spendable) + hasher.combine(total) + } +} + +extension WalletBalance: Codable {} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeWalletBalance: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WalletBalance { + return + try WalletBalance( + confirmed: FfiConverterUInt64.read(from: &buf), + immature: FfiConverterUInt64.read(from: &buf), + trustedPending: FfiConverterUInt64.read(from: &buf), + untrustedPending: FfiConverterUInt64.read(from: &buf), + spendable: FfiConverterUInt64.read(from: &buf), + total: FfiConverterUInt64.read(from: &buf) + ) + } + + public static func write(_ value: WalletBalance, into buf: inout [UInt8]) { + FfiConverterUInt64.write(value.confirmed, into: &buf) + FfiConverterUInt64.write(value.immature, into: &buf) + FfiConverterUInt64.write(value.trustedPending, into: &buf) + FfiConverterUInt64.write(value.untrustedPending, into: &buf) + FfiConverterUInt64.write(value.spendable, into: &buf) + FfiConverterUInt64.write(value.total, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeWalletBalance_lift(_ buf: RustBuffer) throws -> WalletBalance { + return try FfiConverterTypeWalletBalance.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeWalletBalance_lower(_ value: WalletBalance) -> RustBuffer { + return FfiConverterTypeWalletBalance.lower(value) +} + + +/** + * Common parameters for creating and syncing a watch-only BDK wallet. + */ +public struct WalletParams { + /** + * Extended public key (xpub/ypub/zpub/tpub/upub/vpub) + */ + public var extendedKey: String + /** + * Electrum server URL for wallet sync + */ + public var electrumUrl: String + /** + * Root fingerprint hex (e.g. "73c5da0a"). Required for hardware wallet signing. + */ + public var fingerprint: String? + /** + * Bitcoin network (auto-detected from key prefix if not specified) + */ + public var network: Network? + /** + * Override account type for ambiguous key prefixes (xpub/tpub) + */ + public var accountType: AccountType? + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init( + /** + * Extended public key (xpub/ypub/zpub/tpub/upub/vpub) + */extendedKey: String, + /** + * Electrum server URL for wallet sync + */electrumUrl: String, + /** + * Root fingerprint hex (e.g. "73c5da0a"). Required for hardware wallet signing. + */fingerprint: String?, + /** + * Bitcoin network (auto-detected from key prefix if not specified) + */network: Network?, + /** + * Override account type for ambiguous key prefixes (xpub/tpub) + */accountType: AccountType?) { + self.extendedKey = extendedKey + self.electrumUrl = electrumUrl + self.fingerprint = fingerprint + self.network = network + self.accountType = accountType + } +} + +#if compiler(>=6) +extension WalletParams: Sendable {} +#endif + + +extension WalletParams: Equatable, Hashable { + public static func ==(lhs: WalletParams, rhs: WalletParams) -> Bool { + if lhs.extendedKey != rhs.extendedKey { + return false + } + if lhs.electrumUrl != rhs.electrumUrl { + return false + } + if lhs.fingerprint != rhs.fingerprint { + return false + } + if lhs.network != rhs.network { + return false + } + if lhs.accountType != rhs.accountType { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(extendedKey) + hasher.combine(electrumUrl) + hasher.combine(fingerprint) + hasher.combine(network) + hasher.combine(accountType) + } +} + +extension WalletParams: Codable {} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeWalletParams: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WalletParams { + return + try WalletParams( + extendedKey: FfiConverterString.read(from: &buf), + electrumUrl: FfiConverterString.read(from: &buf), + fingerprint: FfiConverterOptionString.read(from: &buf), + network: FfiConverterOptionTypeNetwork.read(from: &buf), + accountType: FfiConverterOptionTypeAccountType.read(from: &buf) + ) + } + + public static func write(_ value: WalletParams, into buf: inout [UInt8]) { + FfiConverterString.write(value.extendedKey, into: &buf) + FfiConverterString.write(value.electrumUrl, into: &buf) + FfiConverterOptionString.write(value.fingerprint, into: &buf) + FfiConverterOptionTypeNetwork.write(value.network, into: &buf) + FfiConverterOptionTypeAccountType.write(value.accountType, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeWalletParams_lift(_ buf: RustBuffer) throws -> WalletParams { + return try FfiConverterTypeWalletParams.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeWalletParams_lower(_ value: WalletParams) -> RustBuffer { + return FfiConverterTypeWalletParams.lower(value) +} + + +/** + * Parameters for starting an xpub transaction watcher. + */ +public struct WatcherParams { + /** + * Caller-supplied identifier for this watcher. + */ + public var watcherId: String + /** + * Wallet id that scopes the activities this watcher emits. Apps may use + * one wallet id for several account watchers and merge their snapshots. + */ + public var walletId: String + /** + * Extended public key (xpub/ypub/zpub/tpub/upub/vpub). + */ + public var extendedKey: String + /** + * Electrum server URL (e.g. "ssl://electrum.example.com:50002"). + */ + public var electrumUrl: String + /** + * Bitcoin network override (auto-detected from key prefix if None). + */ + public var network: Network? + /** + * Account type override (auto-detected from key prefix if None). + */ + public var accountType: AccountType? + /** + * Number of unused addresses to monitor beyond the last used + * (defaults to `DEFAULT_GAP_LIMIT` when None). + */ + public var gapLimit: UInt32? + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init( + /** + * Caller-supplied identifier for this watcher. + */watcherId: String, + /** + * Wallet id that scopes the activities this watcher emits. Apps may use + * one wallet id for several account watchers and merge their snapshots. + */walletId: String, + /** + * Extended public key (xpub/ypub/zpub/tpub/upub/vpub). + */extendedKey: String, + /** + * Electrum server URL (e.g. "ssl://electrum.example.com:50002"). + */electrumUrl: String, + /** + * Bitcoin network override (auto-detected from key prefix if None). + */network: Network?, + /** + * Account type override (auto-detected from key prefix if None). + */accountType: AccountType?, + /** + * Number of unused addresses to monitor beyond the last used + * (defaults to `DEFAULT_GAP_LIMIT` when None). + */gapLimit: UInt32?) { + self.watcherId = watcherId + self.walletId = walletId + self.extendedKey = extendedKey + self.electrumUrl = electrumUrl + self.network = network + self.accountType = accountType + self.gapLimit = gapLimit + } +} + +#if compiler(>=6) +extension WatcherParams: Sendable {} +#endif + + +extension WatcherParams: Equatable, Hashable { + public static func ==(lhs: WatcherParams, rhs: WatcherParams) -> Bool { + if lhs.watcherId != rhs.watcherId { + return false + } + if lhs.walletId != rhs.walletId { + return false + } + if lhs.extendedKey != rhs.extendedKey { + return false + } + if lhs.electrumUrl != rhs.electrumUrl { + return false + } + if lhs.network != rhs.network { + return false + } + if lhs.accountType != rhs.accountType { + return false + } + if lhs.gapLimit != rhs.gapLimit { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(watcherId) + hasher.combine(walletId) + hasher.combine(extendedKey) + hasher.combine(electrumUrl) + hasher.combine(network) + hasher.combine(accountType) + hasher.combine(gapLimit) + } +} + +extension WatcherParams: Codable {} + + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeWatcherParams: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WatcherParams { + return + try WatcherParams( + watcherId: FfiConverterString.read(from: &buf), + walletId: FfiConverterString.read(from: &buf), + extendedKey: FfiConverterString.read(from: &buf), + electrumUrl: FfiConverterString.read(from: &buf), + network: FfiConverterOptionTypeNetwork.read(from: &buf), + accountType: FfiConverterOptionTypeAccountType.read(from: &buf), + gapLimit: FfiConverterOptionUInt32.read(from: &buf) + ) + } + + public static func write(_ value: WatcherParams, into buf: inout [UInt8]) { + FfiConverterString.write(value.watcherId, into: &buf) + FfiConverterString.write(value.walletId, into: &buf) + FfiConverterString.write(value.extendedKey, into: &buf) + FfiConverterString.write(value.electrumUrl, into: &buf) + FfiConverterOptionTypeNetwork.write(value.network, into: &buf) + FfiConverterOptionTypeAccountType.write(value.accountType, into: &buf) + FfiConverterOptionUInt32.write(value.gapLimit, into: &buf) + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeWatcherParams_lift(_ buf: RustBuffer) throws -> WatcherParams { + return try FfiConverterTypeWatcherParams.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeWatcherParams_lower(_ value: WatcherParams) -> RustBuffer { + return FfiConverterTypeWatcherParams.lower(value) +} + + +/** + * Errors specific to account info operations (BDK/Electrum-based). + */ +public enum AccountInfoError: Swift.Error { + + + + /** + * The provided extended public key is invalid or cannot be parsed + */ + case InvalidExtendedKey(errorDetails: String + ) + /** + * The provided address is invalid + */ + case InvalidAddress(errorDetails: String + ) + /** + * Electrum connection or query failed + */ + case ElectrumError(errorDetails: String + ) + /** + * BDK wallet creation or operation error + */ + case WalletError(errorDetails: String + ) + /** + * Wallet sync with Electrum failed + */ + case SyncError(errorDetails: String + ) + /** + * The key type/prefix is not recognized + */ + case UnsupportedKeyType(errorDetails: String + ) + /** + * Network mismatch between key prefix and specified network + */ + case NetworkMismatch(errorDetails: String + ) + /** + * Invalid transaction ID provided + */ + case InvalidTxid(errorDetails: String + ) + /** + * A valid transaction ID was not found in the wallet + */ + case TransactionNotFound(errorDetails: String + ) + /** + * Watcher lifecycle or subscription error + */ + case WatcherError(errorDetails: String + ) +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeAccountInfoError: FfiConverterRustBuffer { + typealias SwiftType = AccountInfoError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AccountInfoError { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .InvalidExtendedKey( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 2: return .InvalidAddress( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 3: return .ElectrumError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 4: return .WalletError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 5: return .SyncError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 6: return .UnsupportedKeyType( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 7: return .NetworkMismatch( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 8: return .InvalidTxid( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 9: return .TransactionNotFound( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 10: return .WatcherError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: AccountInfoError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .InvalidExtendedKey(errorDetails): + writeInt(&buf, Int32(1)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .InvalidAddress(errorDetails): + writeInt(&buf, Int32(2)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .ElectrumError(errorDetails): + writeInt(&buf, Int32(3)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .WalletError(errorDetails): + writeInt(&buf, Int32(4)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .SyncError(errorDetails): + writeInt(&buf, Int32(5)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .UnsupportedKeyType(errorDetails): + writeInt(&buf, Int32(6)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .NetworkMismatch(errorDetails): + writeInt(&buf, Int32(7)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .InvalidTxid(errorDetails): + writeInt(&buf, Int32(8)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .TransactionNotFound(errorDetails): + writeInt(&buf, Int32(9)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .WatcherError(errorDetails): + writeInt(&buf, Int32(10)) + FfiConverterString.write(errorDetails, into: &buf) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeAccountInfoError_lift(_ buf: RustBuffer) throws -> AccountInfoError { + return try FfiConverterTypeAccountInfoError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeAccountInfoError_lower(_ value: AccountInfoError) -> RustBuffer { + return FfiConverterTypeAccountInfoError.lower(value) +} + + +extension AccountInfoError: Equatable, Hashable {} + +extension AccountInfoError: Codable {} + + + + +extension AccountInfoError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * Account type classification for extended public keys. + * + * Determines the BIP standard, derivation path purpose, and script type. + */ + +public enum AccountType { + + /** + * BIP44 legacy (P2PKH) — xpub/tpub prefix + */ + case legacy + /** + * BIP49 wrapped segwit (P2SH-P2WPKH) — ypub/upub prefix + */ + case wrappedSegwit + /** + * BIP84 native segwit (P2WPKH) — zpub/vpub prefix + */ + case nativeSegwit + /** + * BIP86 taproot (P2TR) + */ + case taproot +} + + +#if compiler(>=6) +extension AccountType: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeAccountType: FfiConverterRustBuffer { + typealias SwiftType = AccountType + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AccountType { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .legacy + + case 2: return .wrappedSegwit + + case 3: return .nativeSegwit + + case 4: return .taproot + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: AccountType, into buf: inout [UInt8]) { + switch value { + + + case .legacy: + writeInt(&buf, Int32(1)) + + + case .wrappedSegwit: + writeInt(&buf, Int32(2)) + + + case .nativeSegwit: + writeInt(&buf, Int32(3)) + + + case .taproot: + writeInt(&buf, Int32(4)) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeAccountType_lift(_ buf: RustBuffer) throws -> AccountType { + return try FfiConverterTypeAccountType.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeAccountType_lower(_ value: AccountType) -> RustBuffer { + return FfiConverterTypeAccountType.lower(value) +} + + +extension AccountType: Equatable, Hashable {} + +extension AccountType: Codable {} + + + + + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum Activity { + + case onchain(OnchainActivity + ) + case lightning(LightningActivity + ) +} + + +#if compiler(>=6) +extension Activity: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeActivity: FfiConverterRustBuffer { + typealias SwiftType = Activity + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Activity { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .onchain(try FfiConverterTypeOnchainActivity.read(from: &buf) + ) + + case 2: return .lightning(try FfiConverterTypeLightningActivity.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: Activity, into buf: inout [UInt8]) { + switch value { + + + case let .onchain(v1): + writeInt(&buf, Int32(1)) + FfiConverterTypeOnchainActivity.write(v1, into: &buf) + + + case let .lightning(v1): + writeInt(&buf, Int32(2)) + FfiConverterTypeLightningActivity.write(v1, into: &buf) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeActivity_lift(_ buf: RustBuffer) throws -> Activity { + return try FfiConverterTypeActivity.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeActivity_lower(_ value: Activity) -> RustBuffer { + return FfiConverterTypeActivity.lower(value) +} + + +extension Activity: Equatable, Hashable {} + +extension Activity: Codable {} + + + + + + + +public enum ActivityError: Swift.Error { + + + + case InvalidActivity(errorDetails: String + ) + case InitializationError(errorDetails: String + ) + case InsertError(errorDetails: String + ) + case RetrievalError(errorDetails: String + ) + case DataError(errorDetails: String + ) + case ConnectionError(errorDetails: String + ) + case SerializationError(errorDetails: String + ) +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeActivityError: FfiConverterRustBuffer { + typealias SwiftType = ActivityError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ActivityError { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .InvalidActivity( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 2: return .InitializationError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 3: return .InsertError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 4: return .RetrievalError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 5: return .DataError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 6: return .ConnectionError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 7: return .SerializationError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: ActivityError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .InvalidActivity(errorDetails): + writeInt(&buf, Int32(1)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .InitializationError(errorDetails): + writeInt(&buf, Int32(2)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .InsertError(errorDetails): + writeInt(&buf, Int32(3)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .RetrievalError(errorDetails): + writeInt(&buf, Int32(4)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .DataError(errorDetails): + writeInt(&buf, Int32(5)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .ConnectionError(errorDetails): + writeInt(&buf, Int32(6)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .SerializationError(errorDetails): + writeInt(&buf, Int32(7)) + FfiConverterString.write(errorDetails, into: &buf) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeActivityError_lift(_ buf: RustBuffer) throws -> ActivityError { + return try FfiConverterTypeActivityError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeActivityError_lower(_ value: ActivityError) -> RustBuffer { + return FfiConverterTypeActivityError.lower(value) +} + + +extension ActivityError: Equatable, Hashable {} + +extension ActivityError: Codable {} + + + + +extension ActivityError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum ActivityFilter { + + case all + case lightning + case onchain +} + + +#if compiler(>=6) +extension ActivityFilter: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeActivityFilter: FfiConverterRustBuffer { + typealias SwiftType = ActivityFilter + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ActivityFilter { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .all + + case 2: return .lightning + + case 3: return .onchain + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: ActivityFilter, into buf: inout [UInt8]) { + switch value { + + + case .all: + writeInt(&buf, Int32(1)) + + + case .lightning: + writeInt(&buf, Int32(2)) + + + case .onchain: + writeInt(&buf, Int32(3)) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeActivityFilter_lift(_ buf: RustBuffer) throws -> ActivityFilter { + return try FfiConverterTypeActivityFilter.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeActivityFilter_lower(_ value: ActivityFilter) -> RustBuffer { + return FfiConverterTypeActivityFilter.lower(value) +} + + +extension ActivityFilter: Equatable, Hashable {} + +extension ActivityFilter: Codable {} + + + + + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum ActivityType { + + case onchain + case lightning +} + + +#if compiler(>=6) +extension ActivityType: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeActivityType: FfiConverterRustBuffer { + typealias SwiftType = ActivityType + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ActivityType { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .onchain + + case 2: return .lightning + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: ActivityType, into buf: inout [UInt8]) { + switch value { + + + case .onchain: + writeInt(&buf, Int32(1)) + + + case .lightning: + writeInt(&buf, Int32(2)) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeActivityType_lift(_ buf: RustBuffer) throws -> ActivityType { + return try FfiConverterTypeActivityType.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeActivityType_lower(_ value: ActivityType) -> RustBuffer { + return FfiConverterTypeActivityType.lower(value) +} + + +extension ActivityType: Equatable, Hashable {} + +extension ActivityType: Codable {} + + + + + + + +public enum AddressError: Swift.Error { + + + + case InvalidAddress + case InvalidNetwork + case MnemonicGenerationFailed + case InvalidMnemonic + case InvalidEntropy + case AddressDerivationFailed +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeAddressError: FfiConverterRustBuffer { + typealias SwiftType = AddressError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AddressError { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .InvalidAddress + case 2: return .InvalidNetwork + case 3: return .MnemonicGenerationFailed + case 4: return .InvalidMnemonic + case 5: return .InvalidEntropy + case 6: return .AddressDerivationFailed + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: AddressError, into buf: inout [UInt8]) { + switch value { + + + + + + case .InvalidAddress: + writeInt(&buf, Int32(1)) + + + case .InvalidNetwork: + writeInt(&buf, Int32(2)) + + + case .MnemonicGenerationFailed: + writeInt(&buf, Int32(3)) + + + case .InvalidMnemonic: + writeInt(&buf, Int32(4)) + + + case .InvalidEntropy: + writeInt(&buf, Int32(5)) + + + case .AddressDerivationFailed: + writeInt(&buf, Int32(6)) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeAddressError_lift(_ buf: RustBuffer) throws -> AddressError { + return try FfiConverterTypeAddressError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeAddressError_lower(_ value: AddressError) -> RustBuffer { + return FfiConverterTypeAddressError.lower(value) +} + + +extension AddressError: Equatable, Hashable {} + +extension AddressError: Codable {} + + + + +extension AddressError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum AddressType { + + case p2pkh + case p2sh + case p2wpkh + case p2wsh + case p2tr + case unknown +} + + +#if compiler(>=6) +extension AddressType: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeAddressType: FfiConverterRustBuffer { + typealias SwiftType = AddressType + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AddressType { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .p2pkh + + case 2: return .p2sh + + case 3: return .p2wpkh + + case 4: return .p2wsh + + case 5: return .p2tr + + case 6: return .unknown + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: AddressType, into buf: inout [UInt8]) { + switch value { + + + case .p2pkh: + writeInt(&buf, Int32(1)) + + + case .p2sh: + writeInt(&buf, Int32(2)) + + + case .p2wpkh: + writeInt(&buf, Int32(3)) + + + case .p2wsh: + writeInt(&buf, Int32(4)) + + + case .p2tr: + writeInt(&buf, Int32(5)) + + + case .unknown: + writeInt(&buf, Int32(6)) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeAddressType_lift(_ buf: RustBuffer) throws -> AddressType { + return try FfiConverterTypeAddressType.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeAddressType_lower(_ value: AddressType) -> RustBuffer { + return FfiConverterTypeAddressType.lower(value) +} + + +extension AddressType: Equatable, Hashable {} + +extension AddressType: Codable {} + + + + + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum BitcoinNetworkEnum { + + case mainnet + case testnet + case signet + case regtest +} + + +#if compiler(>=6) +extension BitcoinNetworkEnum: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeBitcoinNetworkEnum: FfiConverterRustBuffer { + typealias SwiftType = BitcoinNetworkEnum + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BitcoinNetworkEnum { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .mainnet + + case 2: return .testnet + + case 3: return .signet + + case 4: return .regtest + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: BitcoinNetworkEnum, into buf: inout [UInt8]) { + switch value { + + + case .mainnet: + writeInt(&buf, Int32(1)) + + + case .testnet: + writeInt(&buf, Int32(2)) + + + case .signet: + writeInt(&buf, Int32(3)) + + + case .regtest: + writeInt(&buf, Int32(4)) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeBitcoinNetworkEnum_lift(_ buf: RustBuffer) throws -> BitcoinNetworkEnum { + return try FfiConverterTypeBitcoinNetworkEnum.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeBitcoinNetworkEnum_lower(_ value: BitcoinNetworkEnum) -> RustBuffer { + return FfiConverterTypeBitcoinNetworkEnum.lower(value) +} + + +extension BitcoinNetworkEnum: Equatable, Hashable {} + +extension BitcoinNetworkEnum: Codable {} + + + + + + + +public enum BlocktankError: Swift.Error { + + + + case HttpClient(errorDetails: String + ) + case BlocktankClient(errorDetails: String + ) + case InvalidBlocktank(errorDetails: String + ) + case InitializationError(errorDetails: String + ) + case InsertError(errorDetails: String + ) + case RetrievalError(errorDetails: String + ) + case DataError(errorDetails: String + ) + case ConnectionError(errorDetails: String + ) + case SerializationError(errorDetails: String + ) + case ChannelOpen(errorType: BtChannelOrderErrorType, errorDetails: String + ) + case OrderState(errorDetails: String + ) + case InvalidParameter(errorDetails: String + ) + case DatabaseError(errorDetails: String + ) +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeBlocktankError: FfiConverterRustBuffer { + typealias SwiftType = BlocktankError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BlocktankError { + let variant: Int32 = try readInt(&buf) + switch variant { + + + + + case 1: return .HttpClient( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 2: return .BlocktankClient( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 3: return .InvalidBlocktank( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 4: return .InitializationError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 5: return .InsertError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 6: return .RetrievalError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 7: return .DataError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 8: return .ConnectionError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 9: return .SerializationError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 10: return .ChannelOpen( + errorType: try FfiConverterTypeBtChannelOrderErrorType.read(from: &buf), + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 11: return .OrderState( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 12: return .InvalidParameter( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 13: return .DatabaseError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: BlocktankError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .HttpClient(errorDetails): + writeInt(&buf, Int32(1)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .BlocktankClient(errorDetails): + writeInt(&buf, Int32(2)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .InvalidBlocktank(errorDetails): + writeInt(&buf, Int32(3)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .InitializationError(errorDetails): + writeInt(&buf, Int32(4)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .InsertError(errorDetails): + writeInt(&buf, Int32(5)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .RetrievalError(errorDetails): + writeInt(&buf, Int32(6)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .DataError(errorDetails): + writeInt(&buf, Int32(7)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .ConnectionError(errorDetails): + writeInt(&buf, Int32(8)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .SerializationError(errorDetails): + writeInt(&buf, Int32(9)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .ChannelOpen(errorType,errorDetails): + writeInt(&buf, Int32(10)) + FfiConverterTypeBtChannelOrderErrorType.write(errorType, into: &buf) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .OrderState(errorDetails): + writeInt(&buf, Int32(11)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .InvalidParameter(errorDetails): + writeInt(&buf, Int32(12)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .DatabaseError(errorDetails): + writeInt(&buf, Int32(13)) + FfiConverterString.write(errorDetails, into: &buf) + + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeBlocktankError_lift(_ buf: RustBuffer) throws -> BlocktankError { + return try FfiConverterTypeBlocktankError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeBlocktankError_lower(_ value: BlocktankError) -> RustBuffer { + return FfiConverterTypeBlocktankError.lower(value) +} + + +extension BlocktankError: Equatable, Hashable {} + +extension BlocktankError: Codable {} + + + + +extension BlocktankError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + + +/** + * Errors surfaced by the Boltz swaps module. + */ +public enum BoltzError: Swift.Error { + + + + case InitializationError(errorDetails: String + ) + case ConnectionError(errorDetails: String + ) + case DatabaseError(errorDetails: String + ) + case ApiError(errorDetails: String + ) + case SwapError(errorDetails: String + ) + case BroadcastError(errorDetails: String + ) + case InvalidInput(errorDetails: String + ) + case SerializationError(errorDetails: String + ) + case NotFound(errorDetails: String + ) +} - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ActivityError { - let variant: Int32 = try readInt(&buf) - switch variant { + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeBoltzError: FfiConverterRustBuffer { + typealias SwiftType = BoltzError + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzError { + let variant: Int32 = try readInt(&buf) + switch variant { - case 1: return .InvalidActivity( + case 1: return .InitializationError( errorDetails: try FfiConverterString.read(from: &buf) ) - case 2: return .InitializationError( + case 2: return .ConnectionError( errorDetails: try FfiConverterString.read(from: &buf) ) - case 3: return .InsertError( + case 3: return .DatabaseError( errorDetails: try FfiConverterString.read(from: &buf) ) - case 4: return .RetrievalError( + case 4: return .ApiError( errorDetails: try FfiConverterString.read(from: &buf) ) - case 5: return .DataError( + case 5: return .SwapError( errorDetails: try FfiConverterString.read(from: &buf) ) - case 6: return .ConnectionError( + case 6: return .BroadcastError( errorDetails: try FfiConverterString.read(from: &buf) ) - case 7: return .SerializationError( + case 7: return .InvalidInput( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 8: return .SerializationError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 9: return .NotFound( errorDetails: try FfiConverterString.read(from: &buf) ) @@ -16612,47 +18403,57 @@ public struct FfiConverterTypeActivityError: FfiConverterRustBuffer { } } - public static func write(_ value: ActivityError, into buf: inout [UInt8]) { + public static func write(_ value: BoltzError, into buf: inout [UInt8]) { switch value { - case let .InvalidActivity(errorDetails): + case let .InitializationError(errorDetails): writeInt(&buf, Int32(1)) FfiConverterString.write(errorDetails, into: &buf) - case let .InitializationError(errorDetails): + case let .ConnectionError(errorDetails): writeInt(&buf, Int32(2)) FfiConverterString.write(errorDetails, into: &buf) - case let .InsertError(errorDetails): + case let .DatabaseError(errorDetails): writeInt(&buf, Int32(3)) FfiConverterString.write(errorDetails, into: &buf) - case let .RetrievalError(errorDetails): + case let .ApiError(errorDetails): writeInt(&buf, Int32(4)) FfiConverterString.write(errorDetails, into: &buf) - case let .DataError(errorDetails): + case let .SwapError(errorDetails): writeInt(&buf, Int32(5)) FfiConverterString.write(errorDetails, into: &buf) - case let .ConnectionError(errorDetails): + case let .BroadcastError(errorDetails): writeInt(&buf, Int32(6)) FfiConverterString.write(errorDetails, into: &buf) - case let .SerializationError(errorDetails): + case let .InvalidInput(errorDetails): writeInt(&buf, Int32(7)) FfiConverterString.write(errorDetails, into: &buf) + + case let .SerializationError(errorDetails): + writeInt(&buf, Int32(8)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .NotFound(errorDetails): + writeInt(&buf, Int32(9)) + FfiConverterString.write(errorDetails, into: &buf) + } } } @@ -16661,26 +18462,26 @@ public struct FfiConverterTypeActivityError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeActivityError_lift(_ buf: RustBuffer) throws -> ActivityError { - return try FfiConverterTypeActivityError.lift(buf) +public func FfiConverterTypeBoltzError_lift(_ buf: RustBuffer) throws -> BoltzError { + return try FfiConverterTypeBoltzError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeActivityError_lower(_ value: ActivityError) -> RustBuffer { - return FfiConverterTypeActivityError.lower(value) +public func FfiConverterTypeBoltzError_lower(_ value: BoltzError) -> RustBuffer { + return FfiConverterTypeBoltzError.lower(value) } -extension ActivityError: Equatable, Hashable {} +extension BoltzError: Equatable, Hashable {} -extension ActivityError: Codable {} +extension BoltzError: Codable {} -extension ActivityError: Foundation.LocalizedError { +extension BoltzError: Foundation.LocalizedError { public var errorDescription: String? { String(reflecting: self) } @@ -16691,52 +18492,56 @@ extension ActivityError: Foundation.LocalizedError { // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * Bitcoin network selection for Boltz swaps. Maps to the networks Boltz + * operates on (mainnet, testnet, regtest). + */ -public enum ActivityFilter { +public enum BoltzNetwork { - case all - case lightning - case onchain + case mainnet + case testnet + case regtest } #if compiler(>=6) -extension ActivityFilter: Sendable {} +extension BoltzNetwork: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeActivityFilter: FfiConverterRustBuffer { - typealias SwiftType = ActivityFilter +public struct FfiConverterTypeBoltzNetwork: FfiConverterRustBuffer { + typealias SwiftType = BoltzNetwork - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ActivityFilter { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzNetwork { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .all + case 1: return .mainnet - case 2: return .lightning + case 2: return .testnet - case 3: return .onchain + case 3: return .regtest default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: ActivityFilter, into buf: inout [UInt8]) { + public static func write(_ value: BoltzNetwork, into buf: inout [UInt8]) { switch value { - case .all: + case .mainnet: writeInt(&buf, Int32(1)) - case .lightning: + case .testnet: writeInt(&buf, Int32(2)) - case .onchain: + case .regtest: writeInt(&buf, Int32(3)) } @@ -16747,21 +18552,21 @@ public struct FfiConverterTypeActivityFilter: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeActivityFilter_lift(_ buf: RustBuffer) throws -> ActivityFilter { - return try FfiConverterTypeActivityFilter.lift(buf) +public func FfiConverterTypeBoltzNetwork_lift(_ buf: RustBuffer) throws -> BoltzNetwork { + return try FfiConverterTypeBoltzNetwork.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeActivityFilter_lower(_ value: ActivityFilter) -> RustBuffer { - return FfiConverterTypeActivityFilter.lower(value) +public func FfiConverterTypeBoltzNetwork_lower(_ value: BoltzNetwork) -> RustBuffer { + return FfiConverterTypeBoltzNetwork.lower(value) } -extension ActivityFilter: Equatable, Hashable {} +extension BoltzNetwork: Equatable, Hashable {} -extension ActivityFilter: Codable {} +extension BoltzNetwork: Codable {} @@ -16770,144 +18575,93 @@ extension ActivityFilter: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * Events emitted to a registered [`crate::modules::boltz::BoltzEventListener`] + * as swaps progress through their lifecycle. + */ -public enum ActivityType { +public enum BoltzSwapEvent { - case onchain - case lightning + /** + * The swap transitioned to a new status. + */ + case statusUpdate(swapId: String, status: BoltzSwapStatus + ) + /** + * A reverse swap was claimed onchain. `txid` is the claim transaction. + */ + case claimed(swapId: String, txid: String + ) + /** + * A submarine swap was refunded onchain. `txid` is the refund transaction. + */ + case refunded(swapId: String, txid: String + ) + /** + * An error occurred while processing the swap (e.g. an auto-claim failed). + */ + case error(swapId: String, message: String + ) } #if compiler(>=6) -extension ActivityType: Sendable {} +extension BoltzSwapEvent: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeActivityType: FfiConverterRustBuffer { - typealias SwiftType = ActivityType +public struct FfiConverterTypeBoltzSwapEvent: FfiConverterRustBuffer { + typealias SwiftType = BoltzSwapEvent - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ActivityType { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzSwapEvent { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .onchain - - case 2: return .lightning - - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - public static func write(_ value: ActivityType, into buf: inout [UInt8]) { - switch value { - - - case .onchain: - writeInt(&buf, Int32(1)) - + case 1: return .statusUpdate(swapId: try FfiConverterString.read(from: &buf), status: try FfiConverterTypeBoltzSwapStatus.read(from: &buf) + ) - case .lightning: - writeInt(&buf, Int32(2)) + case 2: return .claimed(swapId: try FfiConverterString.read(from: &buf), txid: try FfiConverterString.read(from: &buf) + ) - } - } -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeActivityType_lift(_ buf: RustBuffer) throws -> ActivityType { - return try FfiConverterTypeActivityType.lift(buf) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeActivityType_lower(_ value: ActivityType) -> RustBuffer { - return FfiConverterTypeActivityType.lower(value) -} - - -extension ActivityType: Equatable, Hashable {} - -extension ActivityType: Codable {} - - - - - - - -public enum AddressError: Swift.Error { - - - - case InvalidAddress - case InvalidNetwork - case MnemonicGenerationFailed - case InvalidMnemonic - case InvalidEntropy - case AddressDerivationFailed -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeAddressError: FfiConverterRustBuffer { - typealias SwiftType = AddressError - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AddressError { - let variant: Int32 = try readInt(&buf) - switch variant { - + case 3: return .refunded(swapId: try FfiConverterString.read(from: &buf), txid: try FfiConverterString.read(from: &buf) + ) - + case 4: return .error(swapId: try FfiConverterString.read(from: &buf), message: try FfiConverterString.read(from: &buf) + ) - case 1: return .InvalidAddress - case 2: return .InvalidNetwork - case 3: return .MnemonicGenerationFailed - case 4: return .InvalidMnemonic - case 5: return .InvalidEntropy - case 6: return .AddressDerivationFailed - - default: throw UniffiInternalError.unexpectedEnumCase + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: AddressError, into buf: inout [UInt8]) { + public static func write(_ value: BoltzSwapEvent, into buf: inout [UInt8]) { switch value { - - - - case .InvalidAddress: + case let .statusUpdate(swapId,status): writeInt(&buf, Int32(1)) + FfiConverterString.write(swapId, into: &buf) + FfiConverterTypeBoltzSwapStatus.write(status, into: &buf) + - - case .InvalidNetwork: + case let .claimed(swapId,txid): writeInt(&buf, Int32(2)) + FfiConverterString.write(swapId, into: &buf) + FfiConverterString.write(txid, into: &buf) + - - case .MnemonicGenerationFailed: + case let .refunded(swapId,txid): writeInt(&buf, Int32(3)) + FfiConverterString.write(swapId, into: &buf) + FfiConverterString.write(txid, into: &buf) + - - case .InvalidMnemonic: + case let .error(swapId,message): writeInt(&buf, Int32(4)) - - - case .InvalidEntropy: - writeInt(&buf, Int32(5)) - - - case .AddressDerivationFailed: - writeInt(&buf, Int32(6)) - + FfiConverterString.write(swapId, into: &buf) + FfiConverterString.write(message, into: &buf) + } } } @@ -16916,105 +18670,225 @@ public struct FfiConverterTypeAddressError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAddressError_lift(_ buf: RustBuffer) throws -> AddressError { - return try FfiConverterTypeAddressError.lift(buf) +public func FfiConverterTypeBoltzSwapEvent_lift(_ buf: RustBuffer) throws -> BoltzSwapEvent { + return try FfiConverterTypeBoltzSwapEvent.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAddressError_lower(_ value: AddressError) -> RustBuffer { - return FfiConverterTypeAddressError.lower(value) +public func FfiConverterTypeBoltzSwapEvent_lower(_ value: BoltzSwapEvent) -> RustBuffer { + return FfiConverterTypeBoltzSwapEvent.lower(value) } -extension AddressError: Equatable, Hashable {} - -extension AddressError: Codable {} - +extension BoltzSwapEvent: Equatable, Hashable {} +extension BoltzSwapEvent: Codable {} -extension AddressError: Foundation.LocalizedError { - public var errorDescription: String? { - String(reflecting: self) - } -} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * Typed view of the Boltz swap lifecycle. `Unknown` carries the raw status so + * new server-side states don't break the bindings. + * + * See . + */ -public enum AddressType { +public enum BoltzSwapStatus { - case p2pkh - case p2sh - case p2wpkh - case p2wsh - case p2tr - case unknown + /** + * `swap.created` — initial state. + */ + case swapCreated + /** + * `invoice.set` — invoice attached to a submarine swap. + */ + case invoiceSet + /** + * `transaction.mempool` — a lockup transaction is in the mempool. + */ + case transactionMempool + /** + * `transaction.confirmed` — a lockup transaction confirmed. + */ + case transactionConfirmed + /** + * `invoice.pending` — Boltz is paying the submarine swap invoice. + */ + case invoicePending + /** + * `invoice.paid` — submarine swap invoice paid by Boltz. + */ + case invoicePaid + /** + * `invoice.settled` — reverse swap invoice settled (preimage revealed). + */ + case invoiceSettled + /** + * `invoice.failedToPay` — submarine swap invoice could not be paid; refund. + */ + case invoiceFailedToPay + /** + * `invoice.expired` — reverse swap invoice expired before payment. + */ + case invoiceExpired + /** + * `transaction.claim.pending` — Boltz ready for a cooperative claim. + */ + case transactionClaimPending + /** + * `transaction.claimed` — onchain funds claimed. + */ + case transactionClaimed + /** + * `transaction.refunded` — onchain funds refunded. + */ + case transactionRefunded + /** + * `transaction.lockupFailed` — wrong amount locked; can refund. + */ + case transactionLockupFailed + /** + * `transaction.failed` — Boltz failed to lock the agreed funds. + */ + case transactionFailed + /** + * `swap.expired` — swap expired without completing. + */ + case swapExpired + /** + * Any status not yet modelled. `raw` holds the verbatim Boltz status. + */ + case unknown(raw: String + ) } #if compiler(>=6) -extension AddressType: Sendable {} +extension BoltzSwapStatus: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeAddressType: FfiConverterRustBuffer { - typealias SwiftType = AddressType +public struct FfiConverterTypeBoltzSwapStatus: FfiConverterRustBuffer { + typealias SwiftType = BoltzSwapStatus - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> AddressType { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzSwapStatus { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .p2pkh + case 1: return .swapCreated - case 2: return .p2sh + case 2: return .invoiceSet - case 3: return .p2wpkh + case 3: return .transactionMempool - case 4: return .p2wsh + case 4: return .transactionConfirmed - case 5: return .p2tr + case 5: return .invoicePending - case 6: return .unknown + case 6: return .invoicePaid + + case 7: return .invoiceSettled + + case 8: return .invoiceFailedToPay + + case 9: return .invoiceExpired + + case 10: return .transactionClaimPending + + case 11: return .transactionClaimed + + case 12: return .transactionRefunded + + case 13: return .transactionLockupFailed + + case 14: return .transactionFailed + + case 15: return .swapExpired + + case 16: return .unknown(raw: try FfiConverterString.read(from: &buf) + ) default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: AddressType, into buf: inout [UInt8]) { + public static func write(_ value: BoltzSwapStatus, into buf: inout [UInt8]) { switch value { - case .p2pkh: + case .swapCreated: writeInt(&buf, Int32(1)) - case .p2sh: + case .invoiceSet: writeInt(&buf, Int32(2)) - case .p2wpkh: + case .transactionMempool: writeInt(&buf, Int32(3)) - case .p2wsh: + case .transactionConfirmed: writeInt(&buf, Int32(4)) - case .p2tr: + case .invoicePending: writeInt(&buf, Int32(5)) - case .unknown: + case .invoicePaid: writeInt(&buf, Int32(6)) + + case .invoiceSettled: + writeInt(&buf, Int32(7)) + + + case .invoiceFailedToPay: + writeInt(&buf, Int32(8)) + + + case .invoiceExpired: + writeInt(&buf, Int32(9)) + + + case .transactionClaimPending: + writeInt(&buf, Int32(10)) + + + case .transactionClaimed: + writeInt(&buf, Int32(11)) + + + case .transactionRefunded: + writeInt(&buf, Int32(12)) + + + case .transactionLockupFailed: + writeInt(&buf, Int32(13)) + + + case .transactionFailed: + writeInt(&buf, Int32(14)) + + + case .swapExpired: + writeInt(&buf, Int32(15)) + + + case let .unknown(raw): + writeInt(&buf, Int32(16)) + FfiConverterString.write(raw, into: &buf) + } } } @@ -17023,21 +18897,21 @@ public struct FfiConverterTypeAddressType: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAddressType_lift(_ buf: RustBuffer) throws -> AddressType { - return try FfiConverterTypeAddressType.lift(buf) +public func FfiConverterTypeBoltzSwapStatus_lift(_ buf: RustBuffer) throws -> BoltzSwapStatus { + return try FfiConverterTypeBoltzSwapStatus.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeAddressType_lower(_ value: AddressType) -> RustBuffer { - return FfiConverterTypeAddressType.lower(value) +public func FfiConverterTypeBoltzSwapStatus_lower(_ value: BoltzSwapStatus) -> RustBuffer { + return FfiConverterTypeBoltzSwapStatus.lower(value) } -extension AddressType: Equatable, Hashable {} +extension BoltzSwapStatus: Equatable, Hashable {} -extension AddressType: Codable {} +extension BoltzSwapStatus: Codable {} @@ -17046,61 +18920,55 @@ extension AddressType: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * The direction of a Boltz swap. + * + * - `Submarine`: onchain Bitcoin -> Lightning (the user locks onchain funds, + * Boltz pays a Lightning invoice). + * - `Reverse`: Lightning -> onchain Bitcoin (the user pays a Boltz hold + * invoice, Boltz locks onchain funds the user then claims). + */ -public enum BitcoinNetworkEnum { +public enum BoltzSwapType { - case mainnet - case testnet - case signet - case regtest + case submarine + case reverse } #if compiler(>=6) -extension BitcoinNetworkEnum: Sendable {} +extension BoltzSwapType: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBitcoinNetworkEnum: FfiConverterRustBuffer { - typealias SwiftType = BitcoinNetworkEnum +public struct FfiConverterTypeBoltzSwapType: FfiConverterRustBuffer { + typealias SwiftType = BoltzSwapType - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BitcoinNetworkEnum { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzSwapType { let variant: Int32 = try readInt(&buf) - switch variant { - - case 1: return .mainnet - - case 2: return .testnet + switch variant { - case 3: return .signet + case 1: return .submarine - case 4: return .regtest + case 2: return .reverse default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BitcoinNetworkEnum, into buf: inout [UInt8]) { + public static func write(_ value: BoltzSwapType, into buf: inout [UInt8]) { switch value { - case .mainnet: + case .submarine: writeInt(&buf, Int32(1)) - case .testnet: + case .reverse: writeInt(&buf, Int32(2)) - - case .signet: - writeInt(&buf, Int32(3)) - - - case .regtest: - writeInt(&buf, Int32(4)) - } } } @@ -17109,21 +18977,21 @@ public struct FfiConverterTypeBitcoinNetworkEnum: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBitcoinNetworkEnum_lift(_ buf: RustBuffer) throws -> BitcoinNetworkEnum { - return try FfiConverterTypeBitcoinNetworkEnum.lift(buf) +public func FfiConverterTypeBoltzSwapType_lift(_ buf: RustBuffer) throws -> BoltzSwapType { + return try FfiConverterTypeBoltzSwapType.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBitcoinNetworkEnum_lower(_ value: BitcoinNetworkEnum) -> RustBuffer { - return FfiConverterTypeBitcoinNetworkEnum.lower(value) +public func FfiConverterTypeBoltzSwapType_lower(_ value: BoltzSwapType) -> RustBuffer { + return FfiConverterTypeBoltzSwapType.lower(value) } -extension BitcoinNetworkEnum: Equatable, Hashable {} +extension BoltzSwapType: Equatable, Hashable {} -extension BitcoinNetworkEnum: Codable {} +extension BoltzSwapType: Codable {} @@ -17131,35 +18999,17 @@ extension BitcoinNetworkEnum: Codable {} -public enum BlocktankError: Swift.Error { +public enum BroadcastError: Swift.Error { - case HttpClient(errorDetails: String - ) - case BlocktankClient(errorDetails: String - ) - case InvalidBlocktank(errorDetails: String - ) - case InitializationError(errorDetails: String - ) - case InsertError(errorDetails: String - ) - case RetrievalError(errorDetails: String - ) - case DataError(errorDetails: String - ) - case ConnectionError(errorDetails: String - ) - case SerializationError(errorDetails: String - ) - case ChannelOpen(errorType: BtChannelOrderErrorType, errorDetails: String + case InvalidHex(errorDetails: String ) - case OrderState(errorDetails: String + case InvalidTransaction(errorDetails: String ) - case InvalidParameter(errorDetails: String + case ElectrumError(errorDetails: String ) - case DatabaseError(errorDetails: String + case TaskError(errorDetails: String ) } @@ -17167,54 +19017,26 @@ public enum BlocktankError: Swift.Error { #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBlocktankError: FfiConverterRustBuffer { - typealias SwiftType = BlocktankError +public struct FfiConverterTypeBroadcastError: FfiConverterRustBuffer { + typealias SwiftType = BroadcastError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BlocktankError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BroadcastError { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .HttpClient( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 2: return .BlocktankClient( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 3: return .InvalidBlocktank( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 4: return .InitializationError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 5: return .InsertError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 6: return .RetrievalError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 7: return .DataError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 8: return .ConnectionError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 9: return .SerializationError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 10: return .ChannelOpen( - errorType: try FfiConverterTypeBtChannelOrderErrorType.read(from: &buf), + case 1: return .InvalidHex( errorDetails: try FfiConverterString.read(from: &buf) ) - case 11: return .OrderState( + case 2: return .InvalidTransaction( errorDetails: try FfiConverterString.read(from: &buf) ) - case 12: return .InvalidParameter( + case 3: return .ElectrumError( errorDetails: try FfiConverterString.read(from: &buf) ) - case 13: return .DatabaseError( + case 4: return .TaskError( errorDetails: try FfiConverterString.read(from: &buf) ) @@ -17222,78 +19044,125 @@ public struct FfiConverterTypeBlocktankError: FfiConverterRustBuffer { } } - public static func write(_ value: BlocktankError, into buf: inout [UInt8]) { + public static func write(_ value: BroadcastError, into buf: inout [UInt8]) { switch value { - case let .HttpClient(errorDetails): + case let .InvalidHex(errorDetails): writeInt(&buf, Int32(1)) FfiConverterString.write(errorDetails, into: &buf) - case let .BlocktankClient(errorDetails): + case let .InvalidTransaction(errorDetails): writeInt(&buf, Int32(2)) FfiConverterString.write(errorDetails, into: &buf) - case let .InvalidBlocktank(errorDetails): + case let .ElectrumError(errorDetails): writeInt(&buf, Int32(3)) FfiConverterString.write(errorDetails, into: &buf) - case let .InitializationError(errorDetails): + case let .TaskError(errorDetails): writeInt(&buf, Int32(4)) FfiConverterString.write(errorDetails, into: &buf) + } + } +} + + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeBroadcastError_lift(_ buf: RustBuffer) throws -> BroadcastError { + return try FfiConverterTypeBroadcastError.lift(buf) +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public func FfiConverterTypeBroadcastError_lower(_ value: BroadcastError) -> RustBuffer { + return FfiConverterTypeBroadcastError.lower(value) +} + + +extension BroadcastError: Equatable, Hashable {} + +extension BroadcastError: Codable {} + + + + +extension BroadcastError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum BtBolt11InvoiceState { + + case pending + case holding + case paid + case canceled +} + + +#if compiler(>=6) +extension BtBolt11InvoiceState: Sendable {} +#endif + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeBtBolt11InvoiceState: FfiConverterRustBuffer { + typealias SwiftType = BtBolt11InvoiceState + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtBolt11InvoiceState { + let variant: Int32 = try readInt(&buf) + switch variant { - case let .InsertError(errorDetails): - writeInt(&buf, Int32(5)) - FfiConverterString.write(errorDetails, into: &buf) - + case 1: return .pending - case let .RetrievalError(errorDetails): - writeInt(&buf, Int32(6)) - FfiConverterString.write(errorDetails, into: &buf) - + case 2: return .holding - case let .DataError(errorDetails): - writeInt(&buf, Int32(7)) - FfiConverterString.write(errorDetails, into: &buf) - + case 3: return .paid - case let .ConnectionError(errorDetails): - writeInt(&buf, Int32(8)) - FfiConverterString.write(errorDetails, into: &buf) - + case 4: return .canceled - case let .SerializationError(errorDetails): - writeInt(&buf, Int32(9)) - FfiConverterString.write(errorDetails, into: &buf) - + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: BtBolt11InvoiceState, into buf: inout [UInt8]) { + switch value { - case let .ChannelOpen(errorType,errorDetails): - writeInt(&buf, Int32(10)) - FfiConverterTypeBtChannelOrderErrorType.write(errorType, into: &buf) - FfiConverterString.write(errorDetails, into: &buf) - - case let .OrderState(errorDetails): - writeInt(&buf, Int32(11)) - FfiConverterString.write(errorDetails, into: &buf) - + case .pending: + writeInt(&buf, Int32(1)) - case let .InvalidParameter(errorDetails): - writeInt(&buf, Int32(12)) - FfiConverterString.write(errorDetails, into: &buf) - - case let .DatabaseError(errorDetails): - writeInt(&buf, Int32(13)) - FfiConverterString.write(errorDetails, into: &buf) - + case .holding: + writeInt(&buf, Int32(2)) + + + case .paid: + writeInt(&buf, Int32(3)) + + + case .canceled: + writeInt(&buf, Int32(4)) + } } } @@ -17302,159 +19171,91 @@ public struct FfiConverterTypeBlocktankError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBlocktankError_lift(_ buf: RustBuffer) throws -> BlocktankError { - return try FfiConverterTypeBlocktankError.lift(buf) +public func FfiConverterTypeBtBolt11InvoiceState_lift(_ buf: RustBuffer) throws -> BtBolt11InvoiceState { + return try FfiConverterTypeBtBolt11InvoiceState.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBlocktankError_lower(_ value: BlocktankError) -> RustBuffer { - return FfiConverterTypeBlocktankError.lower(value) +public func FfiConverterTypeBtBolt11InvoiceState_lower(_ value: BtBolt11InvoiceState) -> RustBuffer { + return FfiConverterTypeBtBolt11InvoiceState.lower(value) } -extension BlocktankError: Equatable, Hashable {} - -extension BlocktankError: Codable {} - - +extension BtBolt11InvoiceState: Equatable, Hashable {} +extension BtBolt11InvoiceState: Codable {} -extension BlocktankError: Foundation.LocalizedError { - public var errorDescription: String? { - String(reflecting: self) - } -} -/** - * Errors surfaced by the Boltz swaps module. - */ -public enum BoltzError: Swift.Error { +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +public enum BtChannelOrderErrorType { - - case InitializationError(errorDetails: String - ) - case ConnectionError(errorDetails: String - ) - case DatabaseError(errorDetails: String - ) - case ApiError(errorDetails: String - ) - case SwapError(errorDetails: String - ) - case BroadcastError(errorDetails: String - ) - case InvalidInput(errorDetails: String - ) - case SerializationError(errorDetails: String - ) - case NotFound(errorDetails: String - ) + case wrongOrderState + case peerNotReachable + case channelRejectedByDestination + case channelRejectedByLsp + case blocktankNotReady } +#if compiler(>=6) +extension BtChannelOrderErrorType: Sendable {} +#endif + #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBoltzError: FfiConverterRustBuffer { - typealias SwiftType = BoltzError +public struct FfiConverterTypeBtChannelOrderErrorType: FfiConverterRustBuffer { + typealias SwiftType = BtChannelOrderErrorType - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtChannelOrderErrorType { let variant: Int32 = try readInt(&buf) switch variant { - - + case 1: return .wrongOrderState - case 1: return .InitializationError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 2: return .ConnectionError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 3: return .DatabaseError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 4: return .ApiError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 5: return .SwapError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 6: return .BroadcastError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 7: return .InvalidInput( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 8: return .SerializationError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 9: return .NotFound( - errorDetails: try FfiConverterString.read(from: &buf) - ) - - default: throw UniffiInternalError.unexpectedEnumCase + case 2: return .peerNotReachable + + case 3: return .channelRejectedByDestination + + case 4: return .channelRejectedByLsp + + case 5: return .blocktankNotReady + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BoltzError, into buf: inout [UInt8]) { + public static func write(_ value: BtChannelOrderErrorType, into buf: inout [UInt8]) { switch value { - - - - case let .InitializationError(errorDetails): + case .wrongOrderState: writeInt(&buf, Int32(1)) - FfiConverterString.write(errorDetails, into: &buf) - - case let .ConnectionError(errorDetails): + + case .peerNotReachable: writeInt(&buf, Int32(2)) - FfiConverterString.write(errorDetails, into: &buf) - - case let .DatabaseError(errorDetails): - writeInt(&buf, Int32(3)) - FfiConverterString.write(errorDetails, into: &buf) - - case let .ApiError(errorDetails): - writeInt(&buf, Int32(4)) - FfiConverterString.write(errorDetails, into: &buf) - + case .channelRejectedByDestination: + writeInt(&buf, Int32(3)) - case let .SwapError(errorDetails): - writeInt(&buf, Int32(5)) - FfiConverterString.write(errorDetails, into: &buf) - - case let .BroadcastError(errorDetails): - writeInt(&buf, Int32(6)) - FfiConverterString.write(errorDetails, into: &buf) - + case .channelRejectedByLsp: + writeInt(&buf, Int32(4)) - case let .InvalidInput(errorDetails): - writeInt(&buf, Int32(7)) - FfiConverterString.write(errorDetails, into: &buf) - - case let .SerializationError(errorDetails): - writeInt(&buf, Int32(8)) - FfiConverterString.write(errorDetails, into: &buf) - + case .blocktankNotReady: + writeInt(&buf, Int32(5)) - case let .NotFound(errorDetails): - writeInt(&buf, Int32(9)) - FfiConverterString.write(errorDetails, into: &buf) - } } } @@ -17463,86 +19264,75 @@ public struct FfiConverterTypeBoltzError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBoltzError_lift(_ buf: RustBuffer) throws -> BoltzError { - return try FfiConverterTypeBoltzError.lift(buf) +public func FfiConverterTypeBtChannelOrderErrorType_lift(_ buf: RustBuffer) throws -> BtChannelOrderErrorType { + return try FfiConverterTypeBtChannelOrderErrorType.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBoltzError_lower(_ value: BoltzError) -> RustBuffer { - return FfiConverterTypeBoltzError.lower(value) +public func FfiConverterTypeBtChannelOrderErrorType_lower(_ value: BtChannelOrderErrorType) -> RustBuffer { + return FfiConverterTypeBtChannelOrderErrorType.lower(value) } -extension BoltzError: Equatable, Hashable {} - -extension BoltzError: Codable {} - +extension BtChannelOrderErrorType: Equatable, Hashable {} +extension BtChannelOrderErrorType: Codable {} -extension BoltzError: Foundation.LocalizedError { - public var errorDescription: String? { - String(reflecting: self) - } -} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -/** - * Bitcoin network selection for Boltz swaps. Maps to the networks Boltz - * operates on (mainnet, testnet, regtest). - */ -public enum BoltzNetwork { +public enum BtOpenChannelState { - case mainnet - case testnet - case regtest + case opening + case `open` + case closed } #if compiler(>=6) -extension BoltzNetwork: Sendable {} +extension BtOpenChannelState: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBoltzNetwork: FfiConverterRustBuffer { - typealias SwiftType = BoltzNetwork +public struct FfiConverterTypeBtOpenChannelState: FfiConverterRustBuffer { + typealias SwiftType = BtOpenChannelState - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzNetwork { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtOpenChannelState { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .mainnet + case 1: return .opening - case 2: return .testnet + case 2: return .`open` - case 3: return .regtest + case 3: return .closed default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BoltzNetwork, into buf: inout [UInt8]) { + public static func write(_ value: BtOpenChannelState, into buf: inout [UInt8]) { switch value { - case .mainnet: + case .opening: writeInt(&buf, Int32(1)) - case .testnet: + case .`open`: writeInt(&buf, Int32(2)) - case .regtest: + case .closed: writeInt(&buf, Int32(3)) } @@ -17553,21 +19343,21 @@ public struct FfiConverterTypeBoltzNetwork: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBoltzNetwork_lift(_ buf: RustBuffer) throws -> BoltzNetwork { - return try FfiConverterTypeBoltzNetwork.lift(buf) +public func FfiConverterTypeBtOpenChannelState_lift(_ buf: RustBuffer) throws -> BtOpenChannelState { + return try FfiConverterTypeBtOpenChannelState.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBoltzNetwork_lower(_ value: BoltzNetwork) -> RustBuffer { - return FfiConverterTypeBoltzNetwork.lower(value) +public func FfiConverterTypeBtOpenChannelState_lower(_ value: BtOpenChannelState) -> RustBuffer { + return FfiConverterTypeBtOpenChannelState.lower(value) } -extension BoltzNetwork: Equatable, Hashable {} +extension BtOpenChannelState: Equatable, Hashable {} -extension BoltzNetwork: Codable {} +extension BtOpenChannelState: Codable {} @@ -17576,93 +19366,61 @@ extension BoltzNetwork: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -/** - * Events emitted to a registered [`crate::modules::boltz::BoltzEventListener`] - * as swaps progress through their lifecycle. - */ -public enum BoltzSwapEvent { +public enum BtOrderState { - /** - * The swap transitioned to a new status. - */ - case statusUpdate(swapId: String, status: BoltzSwapStatus - ) - /** - * A reverse swap was claimed onchain. `txid` is the claim transaction. - */ - case claimed(swapId: String, txid: String - ) - /** - * A submarine swap was refunded onchain. `txid` is the refund transaction. - */ - case refunded(swapId: String, txid: String - ) - /** - * An error occurred while processing the swap (e.g. an auto-claim failed). - */ - case error(swapId: String, message: String - ) + case created + case expired + case `open` + case closed } #if compiler(>=6) -extension BoltzSwapEvent: Sendable {} +extension BtOrderState: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBoltzSwapEvent: FfiConverterRustBuffer { - typealias SwiftType = BoltzSwapEvent +public struct FfiConverterTypeBtOrderState: FfiConverterRustBuffer { + typealias SwiftType = BtOrderState - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzSwapEvent { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtOrderState { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .statusUpdate(swapId: try FfiConverterString.read(from: &buf), status: try FfiConverterTypeBoltzSwapStatus.read(from: &buf) - ) + case 1: return .created - case 2: return .claimed(swapId: try FfiConverterString.read(from: &buf), txid: try FfiConverterString.read(from: &buf) - ) + case 2: return .expired - case 3: return .refunded(swapId: try FfiConverterString.read(from: &buf), txid: try FfiConverterString.read(from: &buf) - ) + case 3: return .`open` - case 4: return .error(swapId: try FfiConverterString.read(from: &buf), message: try FfiConverterString.read(from: &buf) - ) + case 4: return .closed default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BoltzSwapEvent, into buf: inout [UInt8]) { + public static func write(_ value: BtOrderState, into buf: inout [UInt8]) { switch value { - case let .statusUpdate(swapId,status): + case .created: writeInt(&buf, Int32(1)) - FfiConverterString.write(swapId, into: &buf) - FfiConverterTypeBoltzSwapStatus.write(status, into: &buf) - - case let .claimed(swapId,txid): + + case .expired: writeInt(&buf, Int32(2)) - FfiConverterString.write(swapId, into: &buf) - FfiConverterString.write(txid, into: &buf) - - case let .refunded(swapId,txid): + + case .`open`: writeInt(&buf, Int32(3)) - FfiConverterString.write(swapId, into: &buf) - FfiConverterString.write(txid, into: &buf) - - case let .error(swapId,message): + + case .closed: writeInt(&buf, Int32(4)) - FfiConverterString.write(swapId, into: &buf) - FfiConverterString.write(message, into: &buf) - + } } } @@ -17671,21 +19429,21 @@ public struct FfiConverterTypeBoltzSwapEvent: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBoltzSwapEvent_lift(_ buf: RustBuffer) throws -> BoltzSwapEvent { - return try FfiConverterTypeBoltzSwapEvent.lift(buf) +public func FfiConverterTypeBtOrderState_lift(_ buf: RustBuffer) throws -> BtOrderState { + return try FfiConverterTypeBtOrderState.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBoltzSwapEvent_lower(_ value: BoltzSwapEvent) -> RustBuffer { - return FfiConverterTypeBoltzSwapEvent.lower(value) +public func FfiConverterTypeBtOrderState_lower(_ value: BtOrderState) -> RustBuffer { + return FfiConverterTypeBtOrderState.lower(value) } -extension BoltzSwapEvent: Equatable, Hashable {} +extension BtOrderState: Equatable, Hashable {} -extension BoltzSwapEvent: Codable {} +extension BtOrderState: Codable {} @@ -17694,202 +19452,61 @@ extension BoltzSwapEvent: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -/** - * Typed view of the Boltz swap lifecycle. `Unknown` carries the raw status so - * new server-side states don't break the bindings. - * - * See . - */ -public enum BoltzSwapStatus { +public enum BtOrderState2 { - /** - * `swap.created` — initial state. - */ - case swapCreated - /** - * `invoice.set` — invoice attached to a submarine swap. - */ - case invoiceSet - /** - * `transaction.mempool` — a lockup transaction is in the mempool. - */ - case transactionMempool - /** - * `transaction.confirmed` — a lockup transaction confirmed. - */ - case transactionConfirmed - /** - * `invoice.pending` — Boltz is paying the submarine swap invoice. - */ - case invoicePending - /** - * `invoice.paid` — submarine swap invoice paid by Boltz. - */ - case invoicePaid - /** - * `invoice.settled` — reverse swap invoice settled (preimage revealed). - */ - case invoiceSettled - /** - * `invoice.failedToPay` — submarine swap invoice could not be paid; refund. - */ - case invoiceFailedToPay - /** - * `invoice.expired` — reverse swap invoice expired before payment. - */ - case invoiceExpired - /** - * `transaction.claim.pending` — Boltz ready for a cooperative claim. - */ - case transactionClaimPending - /** - * `transaction.claimed` — onchain funds claimed. - */ - case transactionClaimed - /** - * `transaction.refunded` — onchain funds refunded. - */ - case transactionRefunded - /** - * `transaction.lockupFailed` — wrong amount locked; can refund. - */ - case transactionLockupFailed - /** - * `transaction.failed` — Boltz failed to lock the agreed funds. - */ - case transactionFailed - /** - * `swap.expired` — swap expired without completing. - */ - case swapExpired - /** - * Any status not yet modelled. `raw` holds the verbatim Boltz status. - */ - case unknown(raw: String - ) + case created + case expired + case executed + case paid } #if compiler(>=6) -extension BoltzSwapStatus: Sendable {} +extension BtOrderState2: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBoltzSwapStatus: FfiConverterRustBuffer { - typealias SwiftType = BoltzSwapStatus +public struct FfiConverterTypeBtOrderState2: FfiConverterRustBuffer { + typealias SwiftType = BtOrderState2 - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzSwapStatus { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtOrderState2 { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .swapCreated - - case 2: return .invoiceSet - - case 3: return .transactionMempool - - case 4: return .transactionConfirmed - - case 5: return .invoicePending - - case 6: return .invoicePaid - - case 7: return .invoiceSettled - - case 8: return .invoiceFailedToPay - - case 9: return .invoiceExpired - - case 10: return .transactionClaimPending - - case 11: return .transactionClaimed - - case 12: return .transactionRefunded - - case 13: return .transactionLockupFailed + case 1: return .created - case 14: return .transactionFailed + case 2: return .expired - case 15: return .swapExpired + case 3: return .executed - case 16: return .unknown(raw: try FfiConverterString.read(from: &buf) - ) + case 4: return .paid default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BoltzSwapStatus, into buf: inout [UInt8]) { + public static func write(_ value: BtOrderState2, into buf: inout [UInt8]) { switch value { - case .swapCreated: + case .created: writeInt(&buf, Int32(1)) - case .invoiceSet: + case .expired: writeInt(&buf, Int32(2)) - case .transactionMempool: + case .executed: writeInt(&buf, Int32(3)) - case .transactionConfirmed: + case .paid: writeInt(&buf, Int32(4)) - - case .invoicePending: - writeInt(&buf, Int32(5)) - - - case .invoicePaid: - writeInt(&buf, Int32(6)) - - - case .invoiceSettled: - writeInt(&buf, Int32(7)) - - - case .invoiceFailedToPay: - writeInt(&buf, Int32(8)) - - - case .invoiceExpired: - writeInt(&buf, Int32(9)) - - - case .transactionClaimPending: - writeInt(&buf, Int32(10)) - - - case .transactionClaimed: - writeInt(&buf, Int32(11)) - - - case .transactionRefunded: - writeInt(&buf, Int32(12)) - - - case .transactionLockupFailed: - writeInt(&buf, Int32(13)) - - - case .transactionFailed: - writeInt(&buf, Int32(14)) - - - case .swapExpired: - writeInt(&buf, Int32(15)) - - - case let .unknown(raw): - writeInt(&buf, Int32(16)) - FfiConverterString.write(raw, into: &buf) - } } } @@ -17898,21 +19515,21 @@ public struct FfiConverterTypeBoltzSwapStatus: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBoltzSwapStatus_lift(_ buf: RustBuffer) throws -> BoltzSwapStatus { - return try FfiConverterTypeBoltzSwapStatus.lift(buf) +public func FfiConverterTypeBtOrderState2_lift(_ buf: RustBuffer) throws -> BtOrderState2 { + return try FfiConverterTypeBtOrderState2.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBoltzSwapStatus_lower(_ value: BoltzSwapStatus) -> RustBuffer { - return FfiConverterTypeBoltzSwapStatus.lower(value) +public func FfiConverterTypeBtOrderState2_lower(_ value: BtOrderState2) -> RustBuffer { + return FfiConverterTypeBtOrderState2.lower(value) } -extension BoltzSwapStatus: Equatable, Hashable {} +extension BtOrderState2: Equatable, Hashable {} -extension BoltzSwapStatus: Codable {} +extension BtOrderState2: Codable {} @@ -17921,54 +19538,67 @@ extension BoltzSwapStatus: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -/** - * The direction of a Boltz swap. - * - * - `Submarine`: onchain Bitcoin -> Lightning (the user locks onchain funds, - * Boltz pays a Lightning invoice). - * - `Reverse`: Lightning -> onchain Bitcoin (the user pays a Boltz hold - * invoice, Boltz locks onchain funds the user then claims). - */ -public enum BoltzSwapType { +public enum BtPaymentState { - case submarine - case reverse + case created + case partiallyPaid + case paid + case refunded + case refundAvailable } #if compiler(>=6) -extension BoltzSwapType: Sendable {} +extension BtPaymentState: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBoltzSwapType: FfiConverterRustBuffer { - typealias SwiftType = BoltzSwapType +public struct FfiConverterTypeBtPaymentState: FfiConverterRustBuffer { + typealias SwiftType = BtPaymentState - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BoltzSwapType { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtPaymentState { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .submarine + case 1: return .created - case 2: return .reverse + case 2: return .partiallyPaid + + case 3: return .paid + + case 4: return .refunded + + case 5: return .refundAvailable default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BoltzSwapType, into buf: inout [UInt8]) { + public static func write(_ value: BtPaymentState, into buf: inout [UInt8]) { switch value { - case .submarine: - writeInt(&buf, Int32(1)) + case .created: + writeInt(&buf, Int32(1)) + + + case .partiallyPaid: + writeInt(&buf, Int32(2)) + + + case .paid: + writeInt(&buf, Int32(3)) - case .reverse: - writeInt(&buf, Int32(2)) + case .refunded: + writeInt(&buf, Int32(4)) + + + case .refundAvailable: + writeInt(&buf, Int32(5)) } } @@ -17978,99 +19608,91 @@ public struct FfiConverterTypeBoltzSwapType: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBoltzSwapType_lift(_ buf: RustBuffer) throws -> BoltzSwapType { - return try FfiConverterTypeBoltzSwapType.lift(buf) +public func FfiConverterTypeBtPaymentState_lift(_ buf: RustBuffer) throws -> BtPaymentState { + return try FfiConverterTypeBtPaymentState.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBoltzSwapType_lower(_ value: BoltzSwapType) -> RustBuffer { - return FfiConverterTypeBoltzSwapType.lower(value) +public func FfiConverterTypeBtPaymentState_lower(_ value: BtPaymentState) -> RustBuffer { + return FfiConverterTypeBtPaymentState.lower(value) } -extension BoltzSwapType: Equatable, Hashable {} - -extension BoltzSwapType: Codable {} +extension BtPaymentState: Equatable, Hashable {} +extension BtPaymentState: Codable {} -public enum BroadcastError: Swift.Error { +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +public enum BtPaymentState2 { - - case InvalidHex(errorDetails: String - ) - case InvalidTransaction(errorDetails: String - ) - case ElectrumError(errorDetails: String - ) - case TaskError(errorDetails: String - ) + case created + case paid + case refunded + case refundAvailable + case canceled } +#if compiler(>=6) +extension BtPaymentState2: Sendable {} +#endif + #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBroadcastError: FfiConverterRustBuffer { - typealias SwiftType = BroadcastError +public struct FfiConverterTypeBtPaymentState2: FfiConverterRustBuffer { + typealias SwiftType = BtPaymentState2 - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BroadcastError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtPaymentState2 { let variant: Int32 = try readInt(&buf) switch variant { - - + case 1: return .created - case 1: return .InvalidHex( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 2: return .InvalidTransaction( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 3: return .ElectrumError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 4: return .TaskError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - - default: throw UniffiInternalError.unexpectedEnumCase + case 2: return .paid + + case 3: return .refunded + + case 4: return .refundAvailable + + case 5: return .canceled + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BroadcastError, into buf: inout [UInt8]) { + public static func write(_ value: BtPaymentState2, into buf: inout [UInt8]) { switch value { - - - - case let .InvalidHex(errorDetails): + case .created: writeInt(&buf, Int32(1)) - FfiConverterString.write(errorDetails, into: &buf) - - case let .InvalidTransaction(errorDetails): + + case .paid: writeInt(&buf, Int32(2)) - FfiConverterString.write(errorDetails, into: &buf) - - case let .ElectrumError(errorDetails): + + case .refunded: writeInt(&buf, Int32(3)) - FfiConverterString.write(errorDetails, into: &buf) - - case let .TaskError(errorDetails): + + case .refundAvailable: writeInt(&buf, Int32(4)) - FfiConverterString.write(errorDetails, into: &buf) - + + + case .canceled: + writeInt(&buf, Int32(5)) + } } } @@ -18079,30 +19701,23 @@ public struct FfiConverterTypeBroadcastError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBroadcastError_lift(_ buf: RustBuffer) throws -> BroadcastError { - return try FfiConverterTypeBroadcastError.lift(buf) +public func FfiConverterTypeBtPaymentState2_lift(_ buf: RustBuffer) throws -> BtPaymentState2 { + return try FfiConverterTypeBtPaymentState2.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBroadcastError_lower(_ value: BroadcastError) -> RustBuffer { - return FfiConverterTypeBroadcastError.lower(value) +public func FfiConverterTypeBtPaymentState2_lower(_ value: BtPaymentState2) -> RustBuffer { + return FfiConverterTypeBtPaymentState2.lower(value) } -extension BroadcastError: Equatable, Hashable {} - -extension BroadcastError: Codable {} - +extension BtPaymentState2: Equatable, Hashable {} +extension BtPaymentState2: Codable {} -extension BroadcastError: Foundation.LocalizedError { - public var errorDescription: String? { - String(reflecting: self) - } -} @@ -18110,58 +19725,58 @@ extension BroadcastError: Foundation.LocalizedError { // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum BtBolt11InvoiceState { +public enum CJitStateEnum { - case pending - case holding - case paid - case canceled + case created + case completed + case expired + case failed } #if compiler(>=6) -extension BtBolt11InvoiceState: Sendable {} +extension CJitStateEnum: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBtBolt11InvoiceState: FfiConverterRustBuffer { - typealias SwiftType = BtBolt11InvoiceState +public struct FfiConverterTypeCJitStateEnum: FfiConverterRustBuffer { + typealias SwiftType = CJitStateEnum - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtBolt11InvoiceState { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CJitStateEnum { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .pending + case 1: return .created - case 2: return .holding + case 2: return .completed - case 3: return .paid + case 3: return .expired - case 4: return .canceled + case 4: return .failed default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BtBolt11InvoiceState, into buf: inout [UInt8]) { + public static func write(_ value: CJitStateEnum, into buf: inout [UInt8]) { switch value { - case .pending: + case .created: writeInt(&buf, Int32(1)) - case .holding: + case .completed: writeInt(&buf, Int32(2)) - case .paid: + case .expired: writeInt(&buf, Int32(3)) - case .canceled: + case .failed: writeInt(&buf, Int32(4)) } @@ -18172,21 +19787,21 @@ public struct FfiConverterTypeBtBolt11InvoiceState: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtBolt11InvoiceState_lift(_ buf: RustBuffer) throws -> BtBolt11InvoiceState { - return try FfiConverterTypeBtBolt11InvoiceState.lift(buf) +public func FfiConverterTypeCJitStateEnum_lift(_ buf: RustBuffer) throws -> CJitStateEnum { + return try FfiConverterTypeCJitStateEnum.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtBolt11InvoiceState_lower(_ value: BtBolt11InvoiceState) -> RustBuffer { - return FfiConverterTypeBtBolt11InvoiceState.lower(value) +public func FfiConverterTypeCJitStateEnum_lower(_ value: CJitStateEnum) -> RustBuffer { + return FfiConverterTypeCJitStateEnum.lower(value) } -extension BtBolt11InvoiceState: Equatable, Hashable {} +extension CJitStateEnum: Equatable, Hashable {} -extension BtBolt11InvoiceState: Codable {} +extension CJitStateEnum: Codable {} @@ -18195,68 +19810,66 @@ extension BtBolt11InvoiceState: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * Coin selection strategy for transaction composition. + */ -public enum BtChannelOrderErrorType { +public enum CoinSelection { - case wrongOrderState - case peerNotReachable - case channelRejectedByDestination - case channelRejectedByLsp - case blocktankNotReady + /** + * Branch-and-bound (default). Minimizes change by searching for exact matches. + */ + case branchAndBound + /** + * Selects largest UTXOs first. Useful for UTXO consolidation. + */ + case largestFirst + /** + * Selects oldest UTXOs first. Maximizes coin-age spending. + */ + case oldestFirst } #if compiler(>=6) -extension BtChannelOrderErrorType: Sendable {} +extension CoinSelection: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBtChannelOrderErrorType: FfiConverterRustBuffer { - typealias SwiftType = BtChannelOrderErrorType +public struct FfiConverterTypeCoinSelection: FfiConverterRustBuffer { + typealias SwiftType = CoinSelection - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtChannelOrderErrorType { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CoinSelection { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .wrongOrderState - - case 2: return .peerNotReachable - - case 3: return .channelRejectedByDestination + case 1: return .branchAndBound - case 4: return .channelRejectedByLsp + case 2: return .largestFirst - case 5: return .blocktankNotReady + case 3: return .oldestFirst default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BtChannelOrderErrorType, into buf: inout [UInt8]) { + public static func write(_ value: CoinSelection, into buf: inout [UInt8]) { switch value { - case .wrongOrderState: + case .branchAndBound: writeInt(&buf, Int32(1)) - case .peerNotReachable: + case .largestFirst: writeInt(&buf, Int32(2)) - case .channelRejectedByDestination: + case .oldestFirst: writeInt(&buf, Int32(3)) - - case .channelRejectedByLsp: - writeInt(&buf, Int32(4)) - - - case .blocktankNotReady: - writeInt(&buf, Int32(5)) - } } } @@ -18265,21 +19878,21 @@ public struct FfiConverterTypeBtChannelOrderErrorType: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtChannelOrderErrorType_lift(_ buf: RustBuffer) throws -> BtChannelOrderErrorType { - return try FfiConverterTypeBtChannelOrderErrorType.lift(buf) +public func FfiConverterTypeCoinSelection_lift(_ buf: RustBuffer) throws -> CoinSelection { + return try FfiConverterTypeCoinSelection.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtChannelOrderErrorType_lower(_ value: BtChannelOrderErrorType) -> RustBuffer { - return FfiConverterTypeBtChannelOrderErrorType.lower(value) +public func FfiConverterTypeCoinSelection_lower(_ value: CoinSelection) -> RustBuffer { + return FfiConverterTypeCoinSelection.lower(value) } -extension BtChannelOrderErrorType: Equatable, Hashable {} +extension CoinSelection: Equatable, Hashable {} -extension BtChannelOrderErrorType: Codable {} +extension CoinSelection: Codable {} @@ -18288,54 +19901,76 @@ extension BtChannelOrderErrorType: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * Output specification for transaction composition. + */ -public enum BtOpenChannelState { +public enum ComposeOutput { - case opening - case `open` - case closed + /** + * Payment to a specific address with a fixed amount (satoshis) + */ + case payment(address: String, amountSats: UInt64 + ) + /** + * Send all remaining funds (after fees) to an address + */ + case sendMax(address: String + ) + /** + * OP_RETURN data output (hex-encoded payload) + */ + case opReturn(dataHex: String + ) } #if compiler(>=6) -extension BtOpenChannelState: Sendable {} +extension ComposeOutput: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBtOpenChannelState: FfiConverterRustBuffer { - typealias SwiftType = BtOpenChannelState +public struct FfiConverterTypeComposeOutput: FfiConverterRustBuffer { + typealias SwiftType = ComposeOutput - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtOpenChannelState { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ComposeOutput { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .opening + case 1: return .payment(address: try FfiConverterString.read(from: &buf), amountSats: try FfiConverterUInt64.read(from: &buf) + ) - case 2: return .`open` + case 2: return .sendMax(address: try FfiConverterString.read(from: &buf) + ) - case 3: return .closed + case 3: return .opReturn(dataHex: try FfiConverterString.read(from: &buf) + ) default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BtOpenChannelState, into buf: inout [UInt8]) { + public static func write(_ value: ComposeOutput, into buf: inout [UInt8]) { switch value { - case .opening: + case let .payment(address,amountSats): writeInt(&buf, Int32(1)) + FfiConverterString.write(address, into: &buf) + FfiConverterUInt64.write(amountSats, into: &buf) + - - case .`open`: + case let .sendMax(address): writeInt(&buf, Int32(2)) + FfiConverterString.write(address, into: &buf) + - - case .closed: + case let .opReturn(dataHex): writeInt(&buf, Int32(3)) - + FfiConverterString.write(dataHex, into: &buf) + } } } @@ -18344,21 +19979,21 @@ public struct FfiConverterTypeBtOpenChannelState: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtOpenChannelState_lift(_ buf: RustBuffer) throws -> BtOpenChannelState { - return try FfiConverterTypeBtOpenChannelState.lift(buf) +public func FfiConverterTypeComposeOutput_lift(_ buf: RustBuffer) throws -> ComposeOutput { + return try FfiConverterTypeComposeOutput.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtOpenChannelState_lower(_ value: BtOpenChannelState) -> RustBuffer { - return FfiConverterTypeBtOpenChannelState.lower(value) +public func FfiConverterTypeComposeOutput_lower(_ value: ComposeOutput) -> RustBuffer { + return FfiConverterTypeComposeOutput.lower(value) } -extension BtOpenChannelState: Equatable, Hashable {} +extension ComposeOutput: Equatable, Hashable {} -extension BtOpenChannelState: Codable {} +extension ComposeOutput: Codable {} @@ -18367,61 +20002,79 @@ extension BtOpenChannelState: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * Result of composing a transaction at a single fee rate. + */ -public enum BtOrderState { +public enum ComposeResult { - case created - case expired - case `open` - case closed + /** + * Successfully built a signable PSBT + */ + case success( + /** + * Base64-encoded PSBT ready for signing + */psbt: String, + /** + * Total fee in satoshis + */fee: UInt64, + /** + * Target fee rate in sat/vB (actual may differ slightly due to rounding) + */feeRate: Float, + /** + * Total value spent (payments + fee, excluding change). + * Uses BDK's `sent - received` semantics, which may undercount for + * self-transfers where the destination is also owned by the wallet. + */totalSpent: UInt64 + ) + /** + * Composition failed (e.g. insufficient funds) + */ + case error(error: String + ) } #if compiler(>=6) -extension BtOrderState: Sendable {} +extension ComposeResult: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBtOrderState: FfiConverterRustBuffer { - typealias SwiftType = BtOrderState +public struct FfiConverterTypeComposeResult: FfiConverterRustBuffer { + typealias SwiftType = ComposeResult - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtOrderState { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ComposeResult { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .created - - case 2: return .expired - - case 3: return .`open` + case 1: return .success(psbt: try FfiConverterString.read(from: &buf), fee: try FfiConverterUInt64.read(from: &buf), feeRate: try FfiConverterFloat.read(from: &buf), totalSpent: try FfiConverterUInt64.read(from: &buf) + ) - case 4: return .closed + case 2: return .error(error: try FfiConverterString.read(from: &buf) + ) default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BtOrderState, into buf: inout [UInt8]) { + public static func write(_ value: ComposeResult, into buf: inout [UInt8]) { switch value { - case .created: + case let .success(psbt,fee,feeRate,totalSpent): writeInt(&buf, Int32(1)) + FfiConverterString.write(psbt, into: &buf) + FfiConverterUInt64.write(fee, into: &buf) + FfiConverterFloat.write(feeRate, into: &buf) + FfiConverterUInt64.write(totalSpent, into: &buf) + - - case .expired: + case let .error(error): writeInt(&buf, Int32(2)) - - - case .`open`: - writeInt(&buf, Int32(3)) - - - case .closed: - writeInt(&buf, Int32(4)) - + FfiConverterString.write(error, into: &buf) + } } } @@ -18430,84 +20083,99 @@ public struct FfiConverterTypeBtOrderState: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtOrderState_lift(_ buf: RustBuffer) throws -> BtOrderState { - return try FfiConverterTypeBtOrderState.lift(buf) +public func FfiConverterTypeComposeResult_lift(_ buf: RustBuffer) throws -> ComposeResult { + return try FfiConverterTypeComposeResult.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtOrderState_lower(_ value: BtOrderState) -> RustBuffer { - return FfiConverterTypeBtOrderState.lower(value) +public func FfiConverterTypeComposeResult_lower(_ value: ComposeResult) -> RustBuffer { + return FfiConverterTypeComposeResult.lower(value) } -extension BtOrderState: Equatable, Hashable {} +extension ComposeResult: Equatable, Hashable {} -extension BtOrderState: Codable {} +extension ComposeResult: Codable {} -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum BtOrderState2 { +public enum DbError: Swift.Error { + - case created - case expired - case executed - case paid + + case DbActivityError(errorDetails: ActivityError + ) + case DbBlocktankError(errorDetails: BlocktankError + ) + case DbBoltzError(errorDetails: BoltzError + ) + case InitializationError(errorDetails: String + ) } -#if compiler(>=6) -extension BtOrderState2: Sendable {} -#endif - #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBtOrderState2: FfiConverterRustBuffer { - typealias SwiftType = BtOrderState2 +public struct FfiConverterTypeDbError: FfiConverterRustBuffer { + typealias SwiftType = DbError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtOrderState2 { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> DbError { let variant: Int32 = try readInt(&buf) switch variant { + - case 1: return .created - - case 2: return .expired - - case 3: return .executed - - case 4: return .paid + - default: throw UniffiInternalError.unexpectedEnumCase + case 1: return .DbActivityError( + errorDetails: try FfiConverterTypeActivityError.read(from: &buf) + ) + case 2: return .DbBlocktankError( + errorDetails: try FfiConverterTypeBlocktankError.read(from: &buf) + ) + case 3: return .DbBoltzError( + errorDetails: try FfiConverterTypeBoltzError.read(from: &buf) + ) + case 4: return .InitializationError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BtOrderState2, into buf: inout [UInt8]) { + public static func write(_ value: DbError, into buf: inout [UInt8]) { switch value { + + + - case .created: + case let .DbActivityError(errorDetails): writeInt(&buf, Int32(1)) + FfiConverterTypeActivityError.write(errorDetails, into: &buf) + - - case .expired: + case let .DbBlocktankError(errorDetails): writeInt(&buf, Int32(2)) + FfiConverterTypeBlocktankError.write(errorDetails, into: &buf) + - - case .executed: + case let .DbBoltzError(errorDetails): writeInt(&buf, Int32(3)) + FfiConverterTypeBoltzError.write(errorDetails, into: &buf) + - - case .paid: + case let .InitializationError(errorDetails): writeInt(&buf, Int32(4)) - + FfiConverterString.write(errorDetails, into: &buf) + } } } @@ -18516,91 +20184,150 @@ public struct FfiConverterTypeBtOrderState2: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtOrderState2_lift(_ buf: RustBuffer) throws -> BtOrderState2 { - return try FfiConverterTypeBtOrderState2.lift(buf) +public func FfiConverterTypeDbError_lift(_ buf: RustBuffer) throws -> DbError { + return try FfiConverterTypeDbError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtOrderState2_lower(_ value: BtOrderState2) -> RustBuffer { - return FfiConverterTypeBtOrderState2.lower(value) +public func FfiConverterTypeDbError_lower(_ value: DbError) -> RustBuffer { + return FfiConverterTypeDbError.lower(value) } -extension BtOrderState2: Equatable, Hashable {} +extension DbError: Equatable, Hashable {} -extension BtOrderState2: Codable {} +extension DbError: Codable {} +extension DbError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum BtPaymentState { + + +public enum DecodingError: Swift.Error { + - case created - case partiallyPaid - case paid - case refunded - case refundAvailable + + case InvalidFormat + case InvalidNetwork + case InvalidAmount + case InvalidLnurlPayAmount(amountSatoshis: UInt64, min: UInt64, max: UInt64 + ) + case InvalidTimestamp + case InvalidChecksum + case InvalidResponse + case UnsupportedType + case InvalidAddress + case RequestFailed + case ClientCreationFailed + case InvoiceCreationFailed(errorMessage: String + ) } -#if compiler(>=6) -extension BtPaymentState: Sendable {} -#endif - #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBtPaymentState: FfiConverterRustBuffer { - typealias SwiftType = BtPaymentState +public struct FfiConverterTypeDecodingError: FfiConverterRustBuffer { + typealias SwiftType = DecodingError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtPaymentState { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> DecodingError { let variant: Int32 = try readInt(&buf) switch variant { + - case 1: return .created - - case 2: return .partiallyPaid - - case 3: return .paid - - case 4: return .refunded - - case 5: return .refundAvailable + - default: throw UniffiInternalError.unexpectedEnumCase + case 1: return .InvalidFormat + case 2: return .InvalidNetwork + case 3: return .InvalidAmount + case 4: return .InvalidLnurlPayAmount( + amountSatoshis: try FfiConverterUInt64.read(from: &buf), + min: try FfiConverterUInt64.read(from: &buf), + max: try FfiConverterUInt64.read(from: &buf) + ) + case 5: return .InvalidTimestamp + case 6: return .InvalidChecksum + case 7: return .InvalidResponse + case 8: return .UnsupportedType + case 9: return .InvalidAddress + case 10: return .RequestFailed + case 11: return .ClientCreationFailed + case 12: return .InvoiceCreationFailed( + errorMessage: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BtPaymentState, into buf: inout [UInt8]) { + public static func write(_ value: DecodingError, into buf: inout [UInt8]) { switch value { + + - case .created: + + case .InvalidFormat: writeInt(&buf, Int32(1)) - case .partiallyPaid: + case .InvalidNetwork: writeInt(&buf, Int32(2)) - case .paid: + case .InvalidAmount: writeInt(&buf, Int32(3)) - case .refunded: + case let .InvalidLnurlPayAmount(amountSatoshis,min,max): writeInt(&buf, Int32(4)) + FfiConverterUInt64.write(amountSatoshis, into: &buf) + FfiConverterUInt64.write(min, into: &buf) + FfiConverterUInt64.write(max, into: &buf) + - - case .refundAvailable: + case .InvalidTimestamp: writeInt(&buf, Int32(5)) + + case .InvalidChecksum: + writeInt(&buf, Int32(6)) + + + case .InvalidResponse: + writeInt(&buf, Int32(7)) + + + case .UnsupportedType: + writeInt(&buf, Int32(8)) + + + case .InvalidAddress: + writeInt(&buf, Int32(9)) + + + case .RequestFailed: + writeInt(&buf, Int32(10)) + + + case .ClientCreationFailed: + writeInt(&buf, Int32(11)) + + + case let .InvoiceCreationFailed(errorMessage): + writeInt(&buf, Int32(12)) + FfiConverterString.write(errorMessage, into: &buf) + } } } @@ -18609,91 +20336,87 @@ public struct FfiConverterTypeBtPaymentState: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtPaymentState_lift(_ buf: RustBuffer) throws -> BtPaymentState { - return try FfiConverterTypeBtPaymentState.lift(buf) +public func FfiConverterTypeDecodingError_lift(_ buf: RustBuffer) throws -> DecodingError { + return try FfiConverterTypeDecodingError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtPaymentState_lower(_ value: BtPaymentState) -> RustBuffer { - return FfiConverterTypeBtPaymentState.lower(value) +public func FfiConverterTypeDecodingError_lower(_ value: DecodingError) -> RustBuffer { + return FfiConverterTypeDecodingError.lower(value) } -extension BtPaymentState: Equatable, Hashable {} +extension DecodingError: Equatable, Hashable {} -extension BtPaymentState: Codable {} +extension DecodingError: Codable {} +extension DecodingError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + + // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * How an application exchanges data with a hardware wallet. + */ -public enum BtPaymentState2 { +public enum HardwareWalletTransport { - case created - case paid - case refunded - case refundAvailable - case canceled + case usb + case bluetooth + case qr } #if compiler(>=6) -extension BtPaymentState2: Sendable {} +extension HardwareWalletTransport: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeBtPaymentState2: FfiConverterRustBuffer { - typealias SwiftType = BtPaymentState2 +public struct FfiConverterTypeHardwareWalletTransport: FfiConverterRustBuffer { + typealias SwiftType = HardwareWalletTransport - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BtPaymentState2 { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> HardwareWalletTransport { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .created - - case 2: return .paid - - case 3: return .refunded + case 1: return .usb - case 4: return .refundAvailable + case 2: return .bluetooth - case 5: return .canceled + case 3: return .qr default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: BtPaymentState2, into buf: inout [UInt8]) { + public static func write(_ value: HardwareWalletTransport, into buf: inout [UInt8]) { switch value { - case .created: + case .usb: writeInt(&buf, Int32(1)) - case .paid: + case .bluetooth: writeInt(&buf, Int32(2)) - case .refunded: + case .qr: writeInt(&buf, Int32(3)) - - case .refundAvailable: - writeInt(&buf, Int32(4)) - - - case .canceled: - writeInt(&buf, Int32(5)) - } } } @@ -18702,21 +20425,21 @@ public struct FfiConverterTypeBtPaymentState2: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtPaymentState2_lift(_ buf: RustBuffer) throws -> BtPaymentState2 { - return try FfiConverterTypeBtPaymentState2.lift(buf) +public func FfiConverterTypeHardwareWalletTransport_lift(_ buf: RustBuffer) throws -> HardwareWalletTransport { + return try FfiConverterTypeHardwareWalletTransport.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeBtPaymentState2_lower(_ value: BtPaymentState2) -> RustBuffer { - return FfiConverterTypeBtPaymentState2.lower(value) +public func FfiConverterTypeHardwareWalletTransport_lower(_ value: HardwareWalletTransport) -> RustBuffer { + return FfiConverterTypeHardwareWalletTransport.lower(value) } -extension BtPaymentState2: Equatable, Hashable {} +extension HardwareWalletTransport: Equatable, Hashable {} -extension BtPaymentState2: Codable {} +extension HardwareWalletTransport: Codable {} @@ -18725,61 +20448,57 @@ extension BtPaymentState2: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * A hardware-wallet vendor recognized by Bitkit. + */ -public enum CJitStateEnum { +public enum HardwareWalletVendor { - case created - case completed - case expired - case failed + case trezor + case foundation + case blockstream } #if compiler(>=6) -extension CJitStateEnum: Sendable {} +extension HardwareWalletVendor: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeCJitStateEnum: FfiConverterRustBuffer { - typealias SwiftType = CJitStateEnum +public struct FfiConverterTypeHardwareWalletVendor: FfiConverterRustBuffer { + typealias SwiftType = HardwareWalletVendor - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CJitStateEnum { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> HardwareWalletVendor { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .created - - case 2: return .completed + case 1: return .trezor - case 3: return .expired + case 2: return .foundation - case 4: return .failed + case 3: return .blockstream default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: CJitStateEnum, into buf: inout [UInt8]) { + public static func write(_ value: HardwareWalletVendor, into buf: inout [UInt8]) { switch value { - case .created: + case .trezor: writeInt(&buf, Int32(1)) - case .completed: + case .foundation: writeInt(&buf, Int32(2)) - case .expired: + case .blockstream: writeInt(&buf, Int32(3)) - - case .failed: - writeInt(&buf, Int32(4)) - } } } @@ -18788,21 +20507,21 @@ public struct FfiConverterTypeCJitStateEnum: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeCJitStateEnum_lift(_ buf: RustBuffer) throws -> CJitStateEnum { - return try FfiConverterTypeCJitStateEnum.lift(buf) +public func FfiConverterTypeHardwareWalletVendor_lift(_ buf: RustBuffer) throws -> HardwareWalletVendor { + return try FfiConverterTypeHardwareWalletVendor.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeCJitStateEnum_lower(_ value: CJitStateEnum) -> RustBuffer { - return FfiConverterTypeCJitStateEnum.lower(value) +public func FfiConverterTypeHardwareWalletVendor_lower(_ value: HardwareWalletVendor) -> RustBuffer { + return FfiConverterTypeHardwareWalletVendor.lower(value) } -extension CJitStateEnum: Equatable, Hashable {} +extension HardwareWalletVendor: Equatable, Hashable {} -extension CJitStateEnum: Codable {} +extension HardwareWalletVendor: Codable {} @@ -18811,66 +20530,61 @@ extension CJitStateEnum: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -/** - * Coin selection strategy for transaction composition. - */ -public enum CoinSelection { +public enum JadeAddressVariant { - /** - * Branch-and-bound (default). Minimizes change by searching for exact matches. - */ - case branchAndBound - /** - * Selects largest UTXOs first. Useful for UTXO consolidation. - */ - case largestFirst - /** - * Selects oldest UTXOs first. Maximizes coin-age spending. - */ - case oldestFirst + case pkh + case wpkh + case shWpkh + case tr } #if compiler(>=6) -extension CoinSelection: Sendable {} +extension JadeAddressVariant: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeCoinSelection: FfiConverterRustBuffer { - typealias SwiftType = CoinSelection +public struct FfiConverterTypeJadeAddressVariant: FfiConverterRustBuffer { + typealias SwiftType = JadeAddressVariant - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> CoinSelection { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeAddressVariant { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .branchAndBound + case 1: return .pkh - case 2: return .largestFirst + case 2: return .wpkh - case 3: return .oldestFirst + case 3: return .shWpkh + + case 4: return .tr default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: CoinSelection, into buf: inout [UInt8]) { + public static func write(_ value: JadeAddressVariant, into buf: inout [UInt8]) { switch value { - case .branchAndBound: + case .pkh: writeInt(&buf, Int32(1)) - case .largestFirst: + case .wpkh: writeInt(&buf, Int32(2)) - case .oldestFirst: + case .shWpkh: writeInt(&buf, Int32(3)) + + case .tr: + writeInt(&buf, Int32(4)) + } } } @@ -18879,98 +20593,262 @@ public struct FfiConverterTypeCoinSelection: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeCoinSelection_lift(_ buf: RustBuffer) throws -> CoinSelection { - return try FfiConverterTypeCoinSelection.lift(buf) +public func FfiConverterTypeJadeAddressVariant_lift(_ buf: RustBuffer) throws -> JadeAddressVariant { + return try FfiConverterTypeJadeAddressVariant.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeCoinSelection_lower(_ value: CoinSelection) -> RustBuffer { - return FfiConverterTypeCoinSelection.lower(value) +public func FfiConverterTypeJadeAddressVariant_lower(_ value: JadeAddressVariant) -> RustBuffer { + return FfiConverterTypeJadeAddressVariant.lower(value) } -extension CoinSelection: Equatable, Hashable {} +extension JadeAddressVariant: Equatable, Hashable {} -extension CoinSelection: Codable {} +extension JadeAddressVariant: Codable {} -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -/** - * Output specification for transaction composition. - */ -public enum ComposeOutput { +public enum JadeError: Swift.Error { + - /** - * Payment to a specific address with a fixed amount (satoshis) - */ - case payment(address: String, amountSats: UInt64 + + case TransportError(errorDetails: String ) - /** - * Send all remaining funds (after fees) to an address - */ - case sendMax(address: String + case DeviceNotFound + case DeviceDisconnected + case DeviceBusy + case NotConnected + case NotInitialized + case ConnectionError(errorDetails: String ) - /** - * OP_RETURN data output (hex-encoded payload) - */ - case opReturn(dataHex: String + case ProtocolError(errorDetails: String + ) + case Timeout + case UserCancelled + case DeviceLocked + case DeviceUninitialized + case InvalidPin + case NetworkMismatch(errorDetails: String + ) + case UnsupportedFirmware(installed: String, required: String + ) + case InvalidPath(errorDetails: String + ) + case InvalidPsbt(errorDetails: String + ) + case PsbtTooLarge(size: UInt64, max: UInt64 + ) + case FingerprintMismatch(device: String, psbt: String + ) + case NothingSigned + case AddressMismatch(expected: String, returned: String + ) + case PinServerError(errorDetails: String + ) + case DeviceError(errorDetails: String + ) + case IoError(errorDetails: String ) } -#if compiler(>=6) -extension ComposeOutput: Sendable {} -#endif - #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeComposeOutput: FfiConverterRustBuffer { - typealias SwiftType = ComposeOutput +public struct FfiConverterTypeJadeError: FfiConverterRustBuffer { + typealias SwiftType = JadeError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ComposeOutput { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeError { let variant: Int32 = try readInt(&buf) switch variant { + - case 1: return .payment(address: try FfiConverterString.read(from: &buf), amountSats: try FfiConverterUInt64.read(from: &buf) - ) + - case 2: return .sendMax(address: try FfiConverterString.read(from: &buf) - ) + case 1: return .TransportError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 2: return .DeviceNotFound + case 3: return .DeviceDisconnected + case 4: return .DeviceBusy + case 5: return .NotConnected + case 6: return .NotInitialized + case 7: return .ConnectionError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 8: return .ProtocolError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 9: return .Timeout + case 10: return .UserCancelled + case 11: return .DeviceLocked + case 12: return .DeviceUninitialized + case 13: return .InvalidPin + case 14: return .NetworkMismatch( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 15: return .UnsupportedFirmware( + installed: try FfiConverterString.read(from: &buf), + required: try FfiConverterString.read(from: &buf) + ) + case 16: return .InvalidPath( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 17: return .InvalidPsbt( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 18: return .PsbtTooLarge( + size: try FfiConverterUInt64.read(from: &buf), + max: try FfiConverterUInt64.read(from: &buf) + ) + case 19: return .FingerprintMismatch( + device: try FfiConverterString.read(from: &buf), + psbt: try FfiConverterString.read(from: &buf) + ) + case 20: return .NothingSigned + case 21: return .AddressMismatch( + expected: try FfiConverterString.read(from: &buf), + returned: try FfiConverterString.read(from: &buf) + ) + case 22: return .PinServerError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 23: return .DeviceError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 24: return .IoError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: JadeError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .TransportError(errorDetails): + writeInt(&buf, Int32(1)) + FfiConverterString.write(errorDetails, into: &buf) + + + case .DeviceNotFound: + writeInt(&buf, Int32(2)) + + + case .DeviceDisconnected: + writeInt(&buf, Int32(3)) + + + case .DeviceBusy: + writeInt(&buf, Int32(4)) + + + case .NotConnected: + writeInt(&buf, Int32(5)) + + + case .NotInitialized: + writeInt(&buf, Int32(6)) + + + case let .ConnectionError(errorDetails): + writeInt(&buf, Int32(7)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .ProtocolError(errorDetails): + writeInt(&buf, Int32(8)) + FfiConverterString.write(errorDetails, into: &buf) + + + case .Timeout: + writeInt(&buf, Int32(9)) + + + case .UserCancelled: + writeInt(&buf, Int32(10)) + + + case .DeviceLocked: + writeInt(&buf, Int32(11)) + + + case .DeviceUninitialized: + writeInt(&buf, Int32(12)) + + + case .InvalidPin: + writeInt(&buf, Int32(13)) + + + case let .NetworkMismatch(errorDetails): + writeInt(&buf, Int32(14)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .UnsupportedFirmware(installed,required): + writeInt(&buf, Int32(15)) + FfiConverterString.write(installed, into: &buf) + FfiConverterString.write(required, into: &buf) + + + case let .InvalidPath(errorDetails): + writeInt(&buf, Int32(16)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .InvalidPsbt(errorDetails): + writeInt(&buf, Int32(17)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .PsbtTooLarge(size,max): + writeInt(&buf, Int32(18)) + FfiConverterUInt64.write(size, into: &buf) + FfiConverterUInt64.write(max, into: &buf) + + + case let .FingerprintMismatch(device,psbt): + writeInt(&buf, Int32(19)) + FfiConverterString.write(device, into: &buf) + FfiConverterString.write(psbt, into: &buf) + - case 3: return .opReturn(dataHex: try FfiConverterString.read(from: &buf) - ) + case .NothingSigned: + writeInt(&buf, Int32(20)) - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - public static func write(_ value: ComposeOutput, into buf: inout [UInt8]) { - switch value { + case let .AddressMismatch(expected,returned): + writeInt(&buf, Int32(21)) + FfiConverterString.write(expected, into: &buf) + FfiConverterString.write(returned, into: &buf) + - case let .payment(address,amountSats): - writeInt(&buf, Int32(1)) - FfiConverterString.write(address, into: &buf) - FfiConverterUInt64.write(amountSats, into: &buf) + case let .PinServerError(errorDetails): + writeInt(&buf, Int32(22)) + FfiConverterString.write(errorDetails, into: &buf) - case let .sendMax(address): - writeInt(&buf, Int32(2)) - FfiConverterString.write(address, into: &buf) + case let .DeviceError(errorDetails): + writeInt(&buf, Int32(23)) + FfiConverterString.write(errorDetails, into: &buf) - case let .opReturn(dataHex): - writeInt(&buf, Int32(3)) - FfiConverterString.write(dataHex, into: &buf) + case let .IoError(errorDetails): + writeInt(&buf, Int32(24)) + FfiConverterString.write(errorDetails, into: &buf) } } @@ -18980,203 +20858,84 @@ public struct FfiConverterTypeComposeOutput: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeComposeOutput_lift(_ buf: RustBuffer) throws -> ComposeOutput { - return try FfiConverterTypeComposeOutput.lift(buf) +public func FfiConverterTypeJadeError_lift(_ buf: RustBuffer) throws -> JadeError { + return try FfiConverterTypeJadeError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeComposeOutput_lower(_ value: ComposeOutput) -> RustBuffer { - return FfiConverterTypeComposeOutput.lower(value) +public func FfiConverterTypeJadeError_lower(_ value: JadeError) -> RustBuffer { + return FfiConverterTypeJadeError.lower(value) } -extension ComposeOutput: Equatable, Hashable {} +extension JadeError: Equatable, Hashable {} + +extension JadeError: Codable {} -extension ComposeOutput: Codable {} +extension JadeError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -/** - * Result of composing a transaction at a single fee rate. - */ -public enum ComposeResult { +public enum JadeNetwork { - /** - * Successfully built a signable PSBT - */ - case success( - /** - * Base64-encoded PSBT ready for signing - */psbt: String, - /** - * Total fee in satoshis - */fee: UInt64, - /** - * Target fee rate in sat/vB (actual may differ slightly due to rounding) - */feeRate: Float, - /** - * Total value spent (payments + fee, excluding change). - * Uses BDK's `sent - received` semantics, which may undercount for - * self-transfers where the destination is also owned by the wallet. - */totalSpent: UInt64 - ) - /** - * Composition failed (e.g. insufficient funds) - */ - case error(error: String - ) + case mainnet + case testnet + case regtest } #if compiler(>=6) -extension ComposeResult: Sendable {} +extension JadeNetwork: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeComposeResult: FfiConverterRustBuffer { - typealias SwiftType = ComposeResult +public struct FfiConverterTypeJadeNetwork: FfiConverterRustBuffer { + typealias SwiftType = JadeNetwork - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ComposeResult { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeNetwork { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .success(psbt: try FfiConverterString.read(from: &buf), fee: try FfiConverterUInt64.read(from: &buf), feeRate: try FfiConverterFloat.read(from: &buf), totalSpent: try FfiConverterUInt64.read(from: &buf) - ) + case 1: return .mainnet - case 2: return .error(error: try FfiConverterString.read(from: &buf) - ) + case 2: return .testnet + + case 3: return .regtest default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: ComposeResult, into buf: inout [UInt8]) { + public static func write(_ value: JadeNetwork, into buf: inout [UInt8]) { switch value { - case let .success(psbt,fee,feeRate,totalSpent): + case .mainnet: writeInt(&buf, Int32(1)) - FfiConverterString.write(psbt, into: &buf) - FfiConverterUInt64.write(fee, into: &buf) - FfiConverterFloat.write(feeRate, into: &buf) - FfiConverterUInt64.write(totalSpent, into: &buf) - - - case let .error(error): - writeInt(&buf, Int32(2)) - FfiConverterString.write(error, into: &buf) - - } - } -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeComposeResult_lift(_ buf: RustBuffer) throws -> ComposeResult { - return try FfiConverterTypeComposeResult.lift(buf) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeComposeResult_lower(_ value: ComposeResult) -> RustBuffer { - return FfiConverterTypeComposeResult.lower(value) -} - - -extension ComposeResult: Equatable, Hashable {} - -extension ComposeResult: Codable {} - - - - - - - -public enum DbError: Swift.Error { - - - - case DbActivityError(errorDetails: ActivityError - ) - case DbBlocktankError(errorDetails: BlocktankError - ) - case DbBoltzError(errorDetails: BoltzError - ) - case InitializationError(errorDetails: String - ) -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeDbError: FfiConverterRustBuffer { - typealias SwiftType = DbError - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> DbError { - let variant: Int32 = try readInt(&buf) - switch variant { - - - - - case 1: return .DbActivityError( - errorDetails: try FfiConverterTypeActivityError.read(from: &buf) - ) - case 2: return .DbBlocktankError( - errorDetails: try FfiConverterTypeBlocktankError.read(from: &buf) - ) - case 3: return .DbBoltzError( - errorDetails: try FfiConverterTypeBoltzError.read(from: &buf) - ) - case 4: return .InitializationError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - public static func write(_ value: DbError, into buf: inout [UInt8]) { - switch value { - - - - - case let .DbActivityError(errorDetails): - writeInt(&buf, Int32(1)) - FfiConverterTypeActivityError.write(errorDetails, into: &buf) - - case let .DbBlocktankError(errorDetails): + case .testnet: writeInt(&buf, Int32(2)) - FfiConverterTypeBlocktankError.write(errorDetails, into: &buf) - - case let .DbBoltzError(errorDetails): + + case .regtest: writeInt(&buf, Int32(3)) - FfiConverterTypeBoltzError.write(errorDetails, into: &buf) - - case let .InitializationError(errorDetails): - writeInt(&buf, Int32(4)) - FfiConverterString.write(errorDetails, into: &buf) - } } } @@ -19185,150 +20944,77 @@ public struct FfiConverterTypeDbError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeDbError_lift(_ buf: RustBuffer) throws -> DbError { - return try FfiConverterTypeDbError.lift(buf) +public func FfiConverterTypeJadeNetwork_lift(_ buf: RustBuffer) throws -> JadeNetwork { + return try FfiConverterTypeJadeNetwork.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeDbError_lower(_ value: DbError) -> RustBuffer { - return FfiConverterTypeDbError.lower(value) +public func FfiConverterTypeJadeNetwork_lower(_ value: JadeNetwork) -> RustBuffer { + return FfiConverterTypeJadeNetwork.lower(value) } -extension DbError: Equatable, Hashable {} - -extension DbError: Codable {} - - - - -extension DbError: Foundation.LocalizedError { - public var errorDescription: String? { - String(reflecting: self) - } -} - +extension JadeNetwork: Equatable, Hashable {} +extension JadeNetwork: Codable {} -public enum DecodingError: Swift.Error { - - - case InvalidFormat - case InvalidNetwork - case InvalidAmount - case InvalidLnurlPayAmount(amountSatoshis: UInt64, min: UInt64, max: UInt64 - ) - case InvalidTimestamp - case InvalidChecksum - case InvalidResponse - case UnsupportedType - case InvalidAddress - case RequestFailed - case ClientCreationFailed - case InvoiceCreationFailed(errorMessage: String - ) + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum JadePingStatus { + + case idle + case busy + case awaitingUserInput } +#if compiler(>=6) +extension JadePingStatus: Sendable {} +#endif + #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeDecodingError: FfiConverterRustBuffer { - typealias SwiftType = DecodingError +public struct FfiConverterTypeJadePingStatus: FfiConverterRustBuffer { + typealias SwiftType = JadePingStatus - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> DecodingError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadePingStatus { let variant: Int32 = try readInt(&buf) switch variant { - - + case 1: return .idle - case 1: return .InvalidFormat - case 2: return .InvalidNetwork - case 3: return .InvalidAmount - case 4: return .InvalidLnurlPayAmount( - amountSatoshis: try FfiConverterUInt64.read(from: &buf), - min: try FfiConverterUInt64.read(from: &buf), - max: try FfiConverterUInt64.read(from: &buf) - ) - case 5: return .InvalidTimestamp - case 6: return .InvalidChecksum - case 7: return .InvalidResponse - case 8: return .UnsupportedType - case 9: return .InvalidAddress - case 10: return .RequestFailed - case 11: return .ClientCreationFailed - case 12: return .InvoiceCreationFailed( - errorMessage: try FfiConverterString.read(from: &buf) - ) - - default: throw UniffiInternalError.unexpectedEnumCase + case 2: return .busy + + case 3: return .awaitingUserInput + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: DecodingError, into buf: inout [UInt8]) { + public static func write(_ value: JadePingStatus, into buf: inout [UInt8]) { switch value { - - - - case .InvalidFormat: + case .idle: writeInt(&buf, Int32(1)) - case .InvalidNetwork: + case .busy: writeInt(&buf, Int32(2)) - case .InvalidAmount: + case .awaitingUserInput: writeInt(&buf, Int32(3)) - - case let .InvalidLnurlPayAmount(amountSatoshis,min,max): - writeInt(&buf, Int32(4)) - FfiConverterUInt64.write(amountSatoshis, into: &buf) - FfiConverterUInt64.write(min, into: &buf) - FfiConverterUInt64.write(max, into: &buf) - - - case .InvalidTimestamp: - writeInt(&buf, Int32(5)) - - - case .InvalidChecksum: - writeInt(&buf, Int32(6)) - - - case .InvalidResponse: - writeInt(&buf, Int32(7)) - - - case .UnsupportedType: - writeInt(&buf, Int32(8)) - - - case .InvalidAddress: - writeInt(&buf, Int32(9)) - - - case .RequestFailed: - writeInt(&buf, Int32(10)) - - - case .ClientCreationFailed: - writeInt(&buf, Int32(11)) - - - case let .InvoiceCreationFailed(errorMessage): - writeInt(&buf, Int32(12)) - FfiConverterString.write(errorMessage, into: &buf) - } } } @@ -19337,87 +21023,98 @@ public struct FfiConverterTypeDecodingError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeDecodingError_lift(_ buf: RustBuffer) throws -> DecodingError { - return try FfiConverterTypeDecodingError.lift(buf) +public func FfiConverterTypeJadePingStatus_lift(_ buf: RustBuffer) throws -> JadePingStatus { + return try FfiConverterTypeJadePingStatus.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeDecodingError_lower(_ value: DecodingError) -> RustBuffer { - return FfiConverterTypeDecodingError.lower(value) +public func FfiConverterTypeJadePingStatus_lower(_ value: JadePingStatus) -> RustBuffer { + return FfiConverterTypeJadePingStatus.lower(value) } -extension DecodingError: Equatable, Hashable {} - -extension DecodingError: Codable {} - +extension JadePingStatus: Equatable, Hashable {} +extension JadePingStatus: Codable {} -extension DecodingError: Foundation.LocalizedError { - public var errorDescription: String? { - String(reflecting: self) - } -} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -/** - * How an application exchanges data with a hardware wallet. - */ -public enum HardwareWalletTransport { +public enum JadeState { - case usb - case bluetooth - case qr + case uninit + case unsaved + case locked + case ready + case temp + case unknown } #if compiler(>=6) -extension HardwareWalletTransport: Sendable {} +extension JadeState: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeHardwareWalletTransport: FfiConverterRustBuffer { - typealias SwiftType = HardwareWalletTransport +public struct FfiConverterTypeJadeState: FfiConverterRustBuffer { + typealias SwiftType = JadeState - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> HardwareWalletTransport { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeState { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .usb + case 1: return .uninit - case 2: return .bluetooth + case 2: return .unsaved - case 3: return .qr + case 3: return .locked + + case 4: return .ready + + case 5: return .temp + + case 6: return .unknown default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: HardwareWalletTransport, into buf: inout [UInt8]) { + public static func write(_ value: JadeState, into buf: inout [UInt8]) { switch value { - case .usb: + case .uninit: writeInt(&buf, Int32(1)) - case .bluetooth: + case .unsaved: writeInt(&buf, Int32(2)) - case .qr: + case .locked: writeInt(&buf, Int32(3)) + + case .ready: + writeInt(&buf, Int32(4)) + + + case .temp: + writeInt(&buf, Int32(5)) + + + case .unknown: + writeInt(&buf, Int32(6)) + } } } @@ -19426,21 +21123,21 @@ public struct FfiConverterTypeHardwareWalletTransport: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeHardwareWalletTransport_lift(_ buf: RustBuffer) throws -> HardwareWalletTransport { - return try FfiConverterTypeHardwareWalletTransport.lift(buf) +public func FfiConverterTypeJadeState_lift(_ buf: RustBuffer) throws -> JadeState { + return try FfiConverterTypeJadeState.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeHardwareWalletTransport_lower(_ value: HardwareWalletTransport) -> RustBuffer { - return FfiConverterTypeHardwareWalletTransport.lower(value) +public func FfiConverterTypeJadeState_lower(_ value: JadeState) -> RustBuffer { + return FfiConverterTypeJadeState.lower(value) } -extension HardwareWalletTransport: Equatable, Hashable {} +extension JadeState: Equatable, Hashable {} -extension HardwareWalletTransport: Codable {} +extension JadeState: Codable {} @@ -19449,57 +21146,68 @@ extension HardwareWalletTransport: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -/** - * A hardware-wallet vendor recognized by Bitkit. - */ -public enum HardwareWalletVendor { +public enum JadeTransportErrorCode { - case trezor - case foundation - case blockstream + case deviceBusy + case notConnected + case disconnected + case timeout + case permissionDenied } #if compiler(>=6) -extension HardwareWalletVendor: Sendable {} +extension JadeTransportErrorCode: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeHardwareWalletVendor: FfiConverterRustBuffer { - typealias SwiftType = HardwareWalletVendor +public struct FfiConverterTypeJadeTransportErrorCode: FfiConverterRustBuffer { + typealias SwiftType = JadeTransportErrorCode - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> HardwareWalletVendor { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeTransportErrorCode { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .trezor + case 1: return .deviceBusy - case 2: return .foundation + case 2: return .notConnected - case 3: return .blockstream + case 3: return .disconnected + + case 4: return .timeout + + case 5: return .permissionDenied default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: HardwareWalletVendor, into buf: inout [UInt8]) { + public static func write(_ value: JadeTransportErrorCode, into buf: inout [UInt8]) { switch value { - case .trezor: + case .deviceBusy: writeInt(&buf, Int32(1)) - case .foundation: + case .notConnected: writeInt(&buf, Int32(2)) - case .blockstream: + case .disconnected: writeInt(&buf, Int32(3)) + + case .timeout: + writeInt(&buf, Int32(4)) + + + case .permissionDenied: + writeInt(&buf, Int32(5)) + } } } @@ -19508,21 +21216,21 @@ public struct FfiConverterTypeHardwareWalletVendor: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeHardwareWalletVendor_lift(_ buf: RustBuffer) throws -> HardwareWalletVendor { - return try FfiConverterTypeHardwareWalletVendor.lift(buf) +public func FfiConverterTypeJadeTransportErrorCode_lift(_ buf: RustBuffer) throws -> JadeTransportErrorCode { + return try FfiConverterTypeJadeTransportErrorCode.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeHardwareWalletVendor_lower(_ value: HardwareWalletVendor) -> RustBuffer { - return FfiConverterTypeHardwareWalletVendor.lower(value) +public func FfiConverterTypeJadeTransportErrorCode_lower(_ value: JadeTransportErrorCode) -> RustBuffer { + return FfiConverterTypeJadeTransportErrorCode.lower(value) } -extension HardwareWalletVendor: Equatable, Hashable {} +extension JadeTransportErrorCode: Equatable, Hashable {} -extension HardwareWalletVendor: Codable {} +extension JadeTransportErrorCode: Codable {} @@ -19532,60 +21240,46 @@ extension HardwareWalletVendor: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum JadeAddressVariant { +public enum JadeTransportKind { - case pkh - case wpkh - case shWpkh - case tr + case bluetooth + case serial } #if compiler(>=6) -extension JadeAddressVariant: Sendable {} +extension JadeTransportKind: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeJadeAddressVariant: FfiConverterRustBuffer { - typealias SwiftType = JadeAddressVariant +public struct FfiConverterTypeJadeTransportKind: FfiConverterRustBuffer { + typealias SwiftType = JadeTransportKind - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeAddressVariant { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeTransportKind { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .pkh - - case 2: return .wpkh - - case 3: return .shWpkh + case 1: return .bluetooth - case 4: return .tr + case 2: return .serial default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: JadeAddressVariant, into buf: inout [UInt8]) { + public static func write(_ value: JadeTransportKind, into buf: inout [UInt8]) { switch value { - case .pkh: + case .bluetooth: writeInt(&buf, Int32(1)) - case .wpkh: + case .serial: writeInt(&buf, Int32(2)) - - case .shWpkh: - writeInt(&buf, Int32(3)) - - - case .tr: - writeInt(&buf, Int32(4)) - } } } @@ -19594,21 +21288,21 @@ public struct FfiConverterTypeJadeAddressVariant: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeAddressVariant_lift(_ buf: RustBuffer) throws -> JadeAddressVariant { - return try FfiConverterTypeJadeAddressVariant.lift(buf) +public func FfiConverterTypeJadeTransportKind_lift(_ buf: RustBuffer) throws -> JadeTransportKind { + return try FfiConverterTypeJadeTransportKind.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeAddressVariant_lower(_ value: JadeAddressVariant) -> RustBuffer { - return FfiConverterTypeJadeAddressVariant.lower(value) +public func FfiConverterTypeJadeTransportKind_lower(_ value: JadeTransportKind) -> RustBuffer { + return FfiConverterTypeJadeTransportKind.lower(value) } -extension JadeAddressVariant: Equatable, Hashable {} +extension JadeTransportKind: Equatable, Hashable {} -extension JadeAddressVariant: Codable {} +extension JadeTransportKind: Codable {} @@ -19616,241 +21310,103 @@ extension JadeAddressVariant: Codable {} -public enum JadeError: Swift.Error { +public enum LnurlError: Swift.Error { - case TransportError(errorDetails: String - ) - case DeviceNotFound - case DeviceDisconnected - case DeviceBusy - case NotConnected - case NotInitialized - case ConnectionError(errorDetails: String - ) - case ProtocolError(errorDetails: String - ) - case Timeout - case UserCancelled - case DeviceLocked - case DeviceUninitialized - case InvalidPin - case NetworkMismatch(errorDetails: String - ) - case UnsupportedFirmware(installed: String, required: String - ) - case InvalidPath(errorDetails: String - ) - case InvalidPsbt(errorDetails: String - ) - case PsbtTooLarge(size: UInt64, max: UInt64 - ) - case FingerprintMismatch(device: String, psbt: String - ) - case NothingSigned - case AddressMismatch(expected: String, returned: String - ) - case PinServerError(errorDetails: String + case InvalidAddress + case ClientCreationFailed + case RequestFailed + case InvalidResponse + case InvalidAmount(amountSatoshis: UInt64, min: UInt64, max: UInt64 ) - case DeviceError(errorDetails: String + case InvoiceCreationFailed(errorDetails: String ) - case IoError(errorDetails: String + case AmountMismatch(requestedMsats: UInt64, invoiceMsats: UInt64 ) + case AuthenticationFailed } #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeJadeError: FfiConverterRustBuffer { - typealias SwiftType = JadeError +public struct FfiConverterTypeLnurlError: FfiConverterRustBuffer { + typealias SwiftType = LnurlError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LnurlError { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .TransportError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 2: return .DeviceNotFound - case 3: return .DeviceDisconnected - case 4: return .DeviceBusy - case 5: return .NotConnected - case 6: return .NotInitialized - case 7: return .ConnectionError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 8: return .ProtocolError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 9: return .Timeout - case 10: return .UserCancelled - case 11: return .DeviceLocked - case 12: return .DeviceUninitialized - case 13: return .InvalidPin - case 14: return .NetworkMismatch( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 15: return .UnsupportedFirmware( - installed: try FfiConverterString.read(from: &buf), - required: try FfiConverterString.read(from: &buf) - ) - case 16: return .InvalidPath( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 17: return .InvalidPsbt( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 18: return .PsbtTooLarge( - size: try FfiConverterUInt64.read(from: &buf), + case 1: return .InvalidAddress + case 2: return .ClientCreationFailed + case 3: return .RequestFailed + case 4: return .InvalidResponse + case 5: return .InvalidAmount( + amountSatoshis: try FfiConverterUInt64.read(from: &buf), + min: try FfiConverterUInt64.read(from: &buf), max: try FfiConverterUInt64.read(from: &buf) ) - case 19: return .FingerprintMismatch( - device: try FfiConverterString.read(from: &buf), - psbt: try FfiConverterString.read(from: &buf) - ) - case 20: return .NothingSigned - case 21: return .AddressMismatch( - expected: try FfiConverterString.read(from: &buf), - returned: try FfiConverterString.read(from: &buf) - ) - case 22: return .PinServerError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 23: return .DeviceError( + case 6: return .InvoiceCreationFailed( errorDetails: try FfiConverterString.read(from: &buf) ) - case 24: return .IoError( - errorDetails: try FfiConverterString.read(from: &buf) + case 7: return .AmountMismatch( + requestedMsats: try FfiConverterUInt64.read(from: &buf), + invoiceMsats: try FfiConverterUInt64.read(from: &buf) ) + case 8: return .AuthenticationFailed default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: JadeError, into buf: inout [UInt8]) { + public static func write(_ value: LnurlError, into buf: inout [UInt8]) { switch value { - case let .TransportError(errorDetails): + case .InvalidAddress: writeInt(&buf, Int32(1)) - FfiConverterString.write(errorDetails, into: &buf) - - case .DeviceNotFound: + + case .ClientCreationFailed: writeInt(&buf, Int32(2)) - case .DeviceDisconnected: + case .RequestFailed: writeInt(&buf, Int32(3)) - case .DeviceBusy: + case .InvalidResponse: writeInt(&buf, Int32(4)) - case .NotConnected: + case let .InvalidAmount(amountSatoshis,min,max): writeInt(&buf, Int32(5)) - - - case .NotInitialized: - writeInt(&buf, Int32(6)) - - - case let .ConnectionError(errorDetails): - writeInt(&buf, Int32(7)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .ProtocolError(errorDetails): - writeInt(&buf, Int32(8)) - FfiConverterString.write(errorDetails, into: &buf) - - - case .Timeout: - writeInt(&buf, Int32(9)) - - - case .UserCancelled: - writeInt(&buf, Int32(10)) - - - case .DeviceLocked: - writeInt(&buf, Int32(11)) - - - case .DeviceUninitialized: - writeInt(&buf, Int32(12)) - - - case .InvalidPin: - writeInt(&buf, Int32(13)) - - - case let .NetworkMismatch(errorDetails): - writeInt(&buf, Int32(14)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .UnsupportedFirmware(installed,required): - writeInt(&buf, Int32(15)) - FfiConverterString.write(installed, into: &buf) - FfiConverterString.write(required, into: &buf) - - - case let .InvalidPath(errorDetails): - writeInt(&buf, Int32(16)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .InvalidPsbt(errorDetails): - writeInt(&buf, Int32(17)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .PsbtTooLarge(size,max): - writeInt(&buf, Int32(18)) - FfiConverterUInt64.write(size, into: &buf) + FfiConverterUInt64.write(amountSatoshis, into: &buf) + FfiConverterUInt64.write(min, into: &buf) FfiConverterUInt64.write(max, into: &buf) - case let .FingerprintMismatch(device,psbt): - writeInt(&buf, Int32(19)) - FfiConverterString.write(device, into: &buf) - FfiConverterString.write(psbt, into: &buf) - - - case .NothingSigned: - writeInt(&buf, Int32(20)) - - - case let .AddressMismatch(expected,returned): - writeInt(&buf, Int32(21)) - FfiConverterString.write(expected, into: &buf) - FfiConverterString.write(returned, into: &buf) - - - case let .PinServerError(errorDetails): - writeInt(&buf, Int32(22)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .DeviceError(errorDetails): - writeInt(&buf, Int32(23)) + case let .InvoiceCreationFailed(errorDetails): + writeInt(&buf, Int32(6)) FfiConverterString.write(errorDetails, into: &buf) - case let .IoError(errorDetails): - writeInt(&buf, Int32(24)) - FfiConverterString.write(errorDetails, into: &buf) + case let .AmountMismatch(requestedMsats,invoiceMsats): + writeInt(&buf, Int32(7)) + FfiConverterUInt64.write(requestedMsats, into: &buf) + FfiConverterUInt64.write(invoiceMsats, into: &buf) + + case .AuthenticationFailed: + writeInt(&buf, Int32(8)) + } } } @@ -19859,26 +21415,26 @@ public struct FfiConverterTypeJadeError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeError_lift(_ buf: RustBuffer) throws -> JadeError { - return try FfiConverterTypeJadeError.lift(buf) +public func FfiConverterTypeLnurlError_lift(_ buf: RustBuffer) throws -> LnurlError { + return try FfiConverterTypeLnurlError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeError_lower(_ value: JadeError) -> RustBuffer { - return FfiConverterTypeJadeError.lower(value) +public func FfiConverterTypeLnurlError_lower(_ value: LnurlError) -> RustBuffer { + return FfiConverterTypeLnurlError.lower(value) } -extension JadeError: Equatable, Hashable {} +extension LnurlError: Equatable, Hashable {} -extension JadeError: Codable {} +extension LnurlError: Codable {} -extension JadeError: Foundation.LocalizedError { +extension LnurlError: Foundation.LocalizedError { public var errorDescription: String? { String(reflecting: self) } @@ -19890,53 +21446,60 @@ extension JadeError: Foundation.LocalizedError { // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum JadeNetwork { +public enum ManualRefundStateEnum { - case mainnet - case testnet - case regtest + case created + case approved + case rejected + case sent } #if compiler(>=6) -extension JadeNetwork: Sendable {} +extension ManualRefundStateEnum: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeJadeNetwork: FfiConverterRustBuffer { - typealias SwiftType = JadeNetwork +public struct FfiConverterTypeManualRefundStateEnum: FfiConverterRustBuffer { + typealias SwiftType = ManualRefundStateEnum - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeNetwork { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ManualRefundStateEnum { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .mainnet + case 1: return .created - case 2: return .testnet + case 2: return .approved - case 3: return .regtest + case 3: return .rejected + + case 4: return .sent default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: JadeNetwork, into buf: inout [UInt8]) { + public static func write(_ value: ManualRefundStateEnum, into buf: inout [UInt8]) { switch value { - case .mainnet: + case .created: writeInt(&buf, Int32(1)) - case .testnet: + case .approved: writeInt(&buf, Int32(2)) - case .regtest: + case .rejected: writeInt(&buf, Int32(3)) + + case .sent: + writeInt(&buf, Int32(4)) + } } } @@ -19945,21 +21508,21 @@ public struct FfiConverterTypeJadeNetwork: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeNetwork_lift(_ buf: RustBuffer) throws -> JadeNetwork { - return try FfiConverterTypeJadeNetwork.lift(buf) +public func FfiConverterTypeManualRefundStateEnum_lift(_ buf: RustBuffer) throws -> ManualRefundStateEnum { + return try FfiConverterTypeManualRefundStateEnum.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeNetwork_lower(_ value: JadeNetwork) -> RustBuffer { - return FfiConverterTypeJadeNetwork.lower(value) +public func FfiConverterTypeManualRefundStateEnum_lower(_ value: ManualRefundStateEnum) -> RustBuffer { + return FfiConverterTypeManualRefundStateEnum.lower(value) } -extension JadeNetwork: Equatable, Hashable {} +extension ManualRefundStateEnum: Equatable, Hashable {} -extension JadeNetwork: Codable {} +extension ManualRefundStateEnum: Codable {} @@ -19969,53 +21532,82 @@ extension JadeNetwork: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum JadePingStatus { +public enum Network { - case idle - case busy - case awaitingUserInput + /** + * Mainnet Bitcoin. + */ + case bitcoin + /** + * Bitcoin's testnet network. + */ + case testnet + /** + * Bitcoin's testnet4 network. + */ + case testnet4 + /** + * Bitcoin's signet network. + */ + case signet + /** + * Bitcoin's regtest network. + */ + case regtest } #if compiler(>=6) -extension JadePingStatus: Sendable {} +extension Network: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeJadePingStatus: FfiConverterRustBuffer { - typealias SwiftType = JadePingStatus +public struct FfiConverterTypeNetwork: FfiConverterRustBuffer { + typealias SwiftType = Network - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadePingStatus { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Network { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .idle + case 1: return .bitcoin - case 2: return .busy + case 2: return .testnet - case 3: return .awaitingUserInput + case 3: return .testnet4 + + case 4: return .signet + + case 5: return .regtest default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: JadePingStatus, into buf: inout [UInt8]) { + public static func write(_ value: Network, into buf: inout [UInt8]) { switch value { - case .idle: + case .bitcoin: writeInt(&buf, Int32(1)) - case .busy: + case .testnet: writeInt(&buf, Int32(2)) - case .awaitingUserInput: + case .testnet4: writeInt(&buf, Int32(3)) + + case .signet: + writeInt(&buf, Int32(4)) + + + case .regtest: + writeInt(&buf, Int32(5)) + } } } @@ -20024,21 +21616,21 @@ public struct FfiConverterTypeJadePingStatus: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadePingStatus_lift(_ buf: RustBuffer) throws -> JadePingStatus { - return try FfiConverterTypeJadePingStatus.lift(buf) +public func FfiConverterTypeNetwork_lift(_ buf: RustBuffer) throws -> Network { + return try FfiConverterTypeNetwork.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadePingStatus_lower(_ value: JadePingStatus) -> RustBuffer { - return FfiConverterTypeJadePingStatus.lower(value) +public func FfiConverterTypeNetwork_lower(_ value: Network) -> RustBuffer { + return FfiConverterTypeNetwork.lower(value) } -extension JadePingStatus: Equatable, Hashable {} +extension Network: Equatable, Hashable {} -extension JadePingStatus: Codable {} +extension Network: Codable {} @@ -20048,74 +21640,60 @@ extension JadePingStatus: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum JadeState { +public enum NetworkType { - case uninit - case unsaved - case locked - case ready - case temp - case unknown + case bitcoin + case testnet + case regtest + case signet } #if compiler(>=6) -extension JadeState: Sendable {} +extension NetworkType: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeJadeState: FfiConverterRustBuffer { - typealias SwiftType = JadeState +public struct FfiConverterTypeNetworkType: FfiConverterRustBuffer { + typealias SwiftType = NetworkType - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeState { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NetworkType { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .uninit - - case 2: return .unsaved - - case 3: return .locked + case 1: return .bitcoin - case 4: return .ready + case 2: return .testnet - case 5: return .temp + case 3: return .regtest - case 6: return .unknown + case 4: return .signet default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: JadeState, into buf: inout [UInt8]) { + public static func write(_ value: NetworkType, into buf: inout [UInt8]) { switch value { - case .uninit: + case .bitcoin: writeInt(&buf, Int32(1)) - case .unsaved: + case .testnet: writeInt(&buf, Int32(2)) - case .locked: + case .regtest: writeInt(&buf, Int32(3)) - case .ready: + case .signet: writeInt(&buf, Int32(4)) - - case .temp: - writeInt(&buf, Int32(5)) - - - case .unknown: - writeInt(&buf, Int32(6)) - } } } @@ -20124,91 +21702,69 @@ public struct FfiConverterTypeJadeState: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeState_lift(_ buf: RustBuffer) throws -> JadeState { - return try FfiConverterTypeJadeState.lift(buf) +public func FfiConverterTypeNetworkType_lift(_ buf: RustBuffer) throws -> NetworkType { + return try FfiConverterTypeNetworkType.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeState_lower(_ value: JadeState) -> RustBuffer { - return FfiConverterTypeJadeState.lower(value) +public func FfiConverterTypeNetworkType_lower(_ value: NetworkType) -> RustBuffer { + return FfiConverterTypeNetworkType.lower(value) } -extension JadeState: Equatable, Hashable {} +extension NetworkType: Equatable, Hashable {} -extension JadeState: Codable {} +extension NetworkType: Codable {} -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum JadeTransportErrorCode { +public enum OnchainError: Swift.Error { + - case deviceBusy - case notConnected - case disconnected - case timeout - case permissionDenied + + case InvalidExtendedPublicKey(errorDetails: String + ) } -#if compiler(>=6) -extension JadeTransportErrorCode: Sendable {} -#endif - #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeJadeTransportErrorCode: FfiConverterRustBuffer { - typealias SwiftType = JadeTransportErrorCode +public struct FfiConverterTypeOnchainError: FfiConverterRustBuffer { + typealias SwiftType = OnchainError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeTransportErrorCode { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> OnchainError { let variant: Int32 = try readInt(&buf) switch variant { - - case 1: return .deviceBusy - - case 2: return .notConnected - - case 3: return .disconnected - - case 4: return .timeout - - case 5: return .permissionDenied - - default: throw UniffiInternalError.unexpectedEnumCase - } - } - public static func write(_ value: JadeTransportErrorCode, into buf: inout [UInt8]) { - switch value { - - - case .deviceBusy: - writeInt(&buf, Int32(1)) - - - case .notConnected: - writeInt(&buf, Int32(2)) - - - case .disconnected: - writeInt(&buf, Int32(3)) + - case .timeout: - writeInt(&buf, Int32(4)) + case 1: return .InvalidExtendedPublicKey( + errorDetails: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: OnchainError, into buf: inout [UInt8]) { + switch value { + + - case .permissionDenied: - writeInt(&buf, Int32(5)) + case let .InvalidExtendedPublicKey(errorDetails): + writeInt(&buf, Int32(1)) + FfiConverterString.write(errorDetails, into: &buf) + } } } @@ -20217,70 +21773,106 @@ public struct FfiConverterTypeJadeTransportErrorCode: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeTransportErrorCode_lift(_ buf: RustBuffer) throws -> JadeTransportErrorCode { - return try FfiConverterTypeJadeTransportErrorCode.lift(buf) +public func FfiConverterTypeOnchainError_lift(_ buf: RustBuffer) throws -> OnchainError { + return try FfiConverterTypeOnchainError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeTransportErrorCode_lower(_ value: JadeTransportErrorCode) -> RustBuffer { - return FfiConverterTypeJadeTransportErrorCode.lower(value) +public func FfiConverterTypeOnchainError_lower(_ value: OnchainError) -> RustBuffer { + return FfiConverterTypeOnchainError.lower(value) } -extension JadeTransportErrorCode: Equatable, Hashable {} +extension OnchainError: Equatable, Hashable {} + +extension OnchainError: Codable {} -extension JadeTransportErrorCode: Codable {} +extension OnchainError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum JadeTransportKind { +public enum PassphraseResponse { - case bluetooth - case serial + /** + * User cancelled — aborts the pending operation. + */ + case cancel + /** + * Standard wallet — no passphrase, equivalent to `Some("")` on the device. + */ + case standard + /** + * Hidden wallet — derived from the passphrase entered on the host. + */ + case hidden(value: String + ) + /** + * Enter the passphrase on the Trezor device itself instead of on the host. + */ + case onDevice } #if compiler(>=6) -extension JadeTransportKind: Sendable {} +extension PassphraseResponse: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeJadeTransportKind: FfiConverterRustBuffer { - typealias SwiftType = JadeTransportKind +public struct FfiConverterTypePassphraseResponse: FfiConverterRustBuffer { + typealias SwiftType = PassphraseResponse - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> JadeTransportKind { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PassphraseResponse { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .bluetooth + case 1: return .cancel - case 2: return .serial + case 2: return .standard + + case 3: return .hidden(value: try FfiConverterString.read(from: &buf) + ) + + case 4: return .onDevice default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: JadeTransportKind, into buf: inout [UInt8]) { + public static func write(_ value: PassphraseResponse, into buf: inout [UInt8]) { switch value { - case .bluetooth: + case .cancel: writeInt(&buf, Int32(1)) - case .serial: + case .standard: writeInt(&buf, Int32(2)) + + case let .hidden(value): + writeInt(&buf, Int32(3)) + FfiConverterString.write(value, into: &buf) + + + case .onDevice: + writeInt(&buf, Int32(4)) + } } } @@ -20289,125 +21881,77 @@ public struct FfiConverterTypeJadeTransportKind: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeTransportKind_lift(_ buf: RustBuffer) throws -> JadeTransportKind { - return try FfiConverterTypeJadeTransportKind.lift(buf) +public func FfiConverterTypePassphraseResponse_lift(_ buf: RustBuffer) throws -> PassphraseResponse { + return try FfiConverterTypePassphraseResponse.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeJadeTransportKind_lower(_ value: JadeTransportKind) -> RustBuffer { - return FfiConverterTypeJadeTransportKind.lower(value) +public func FfiConverterTypePassphraseResponse_lower(_ value: PassphraseResponse) -> RustBuffer { + return FfiConverterTypePassphraseResponse.lower(value) } -extension JadeTransportKind: Equatable, Hashable {} - -extension JadeTransportKind: Codable {} +extension PassphraseResponse: Equatable, Hashable {} +extension PassphraseResponse: Codable {} -public enum LnurlError: Swift.Error { +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +public enum PaymentState { - - case InvalidAddress - case ClientCreationFailed - case RequestFailed - case InvalidResponse - case InvalidAmount(amountSatoshis: UInt64, min: UInt64, max: UInt64 - ) - case InvoiceCreationFailed(errorDetails: String - ) - case AmountMismatch(requestedMsats: UInt64, invoiceMsats: UInt64 - ) - case AuthenticationFailed + case pending + case succeeded + case failed } +#if compiler(>=6) +extension PaymentState: Sendable {} +#endif + #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeLnurlError: FfiConverterRustBuffer { - typealias SwiftType = LnurlError +public struct FfiConverterTypePaymentState: FfiConverterRustBuffer { + typealias SwiftType = PaymentState - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> LnurlError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PaymentState { let variant: Int32 = try readInt(&buf) switch variant { - - + case 1: return .pending - case 1: return .InvalidAddress - case 2: return .ClientCreationFailed - case 3: return .RequestFailed - case 4: return .InvalidResponse - case 5: return .InvalidAmount( - amountSatoshis: try FfiConverterUInt64.read(from: &buf), - min: try FfiConverterUInt64.read(from: &buf), - max: try FfiConverterUInt64.read(from: &buf) - ) - case 6: return .InvoiceCreationFailed( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 7: return .AmountMismatch( - requestedMsats: try FfiConverterUInt64.read(from: &buf), - invoiceMsats: try FfiConverterUInt64.read(from: &buf) - ) - case 8: return .AuthenticationFailed - - default: throw UniffiInternalError.unexpectedEnumCase + case 2: return .succeeded + + case 3: return .failed + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: LnurlError, into buf: inout [UInt8]) { + public static func write(_ value: PaymentState, into buf: inout [UInt8]) { switch value { - - - - case .InvalidAddress: + case .pending: writeInt(&buf, Int32(1)) - case .ClientCreationFailed: + case .succeeded: writeInt(&buf, Int32(2)) - case .RequestFailed: + case .failed: writeInt(&buf, Int32(3)) - - case .InvalidResponse: - writeInt(&buf, Int32(4)) - - - case let .InvalidAmount(amountSatoshis,min,max): - writeInt(&buf, Int32(5)) - FfiConverterUInt64.write(amountSatoshis, into: &buf) - FfiConverterUInt64.write(min, into: &buf) - FfiConverterUInt64.write(max, into: &buf) - - - case let .InvoiceCreationFailed(errorDetails): - writeInt(&buf, Int32(6)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .AmountMismatch(requestedMsats,invoiceMsats): - writeInt(&buf, Int32(7)) - FfiConverterUInt64.write(requestedMsats, into: &buf) - FfiConverterUInt64.write(invoiceMsats, into: &buf) - - - case .AuthenticationFailed: - writeInt(&buf, Int32(8)) - } } } @@ -20416,30 +21960,23 @@ public struct FfiConverterTypeLnurlError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeLnurlError_lift(_ buf: RustBuffer) throws -> LnurlError { - return try FfiConverterTypeLnurlError.lift(buf) +public func FfiConverterTypePaymentState_lift(_ buf: RustBuffer) throws -> PaymentState { + return try FfiConverterTypePaymentState.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeLnurlError_lower(_ value: LnurlError) -> RustBuffer { - return FfiConverterTypeLnurlError.lower(value) +public func FfiConverterTypePaymentState_lower(_ value: PaymentState) -> RustBuffer { + return FfiConverterTypePaymentState.lower(value) } -extension LnurlError: Equatable, Hashable {} - -extension LnurlError: Codable {} - +extension PaymentState: Equatable, Hashable {} +extension PaymentState: Codable {} -extension LnurlError: Foundation.LocalizedError { - public var errorDescription: String? { - String(reflecting: self) - } -} @@ -20447,60 +21984,46 @@ extension LnurlError: Foundation.LocalizedError { // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum ManualRefundStateEnum { +public enum PaymentType { - case created - case approved - case rejected case sent + case received } #if compiler(>=6) -extension ManualRefundStateEnum: Sendable {} +extension PaymentType: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeManualRefundStateEnum: FfiConverterRustBuffer { - typealias SwiftType = ManualRefundStateEnum +public struct FfiConverterTypePaymentType: FfiConverterRustBuffer { + typealias SwiftType = PaymentType - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ManualRefundStateEnum { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PaymentType { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .created - - case 2: return .approved - - case 3: return .rejected + case 1: return .sent - case 4: return .sent + case 2: return .received default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: ManualRefundStateEnum, into buf: inout [UInt8]) { + public static func write(_ value: PaymentType, into buf: inout [UInt8]) { switch value { - case .created: + case .sent: writeInt(&buf, Int32(1)) - case .approved: + case .received: writeInt(&buf, Int32(2)) - - case .rejected: - writeInt(&buf, Int32(3)) - - - case .sent: - writeInt(&buf, Int32(4)) - } } } @@ -20509,106 +22032,109 @@ public struct FfiConverterTypeManualRefundStateEnum: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeManualRefundStateEnum_lift(_ buf: RustBuffer) throws -> ManualRefundStateEnum { - return try FfiConverterTypeManualRefundStateEnum.lift(buf) +public func FfiConverterTypePaymentType_lift(_ buf: RustBuffer) throws -> PaymentType { + return try FfiConverterTypePaymentType.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeManualRefundStateEnum_lower(_ value: ManualRefundStateEnum) -> RustBuffer { - return FfiConverterTypeManualRefundStateEnum.lower(value) +public func FfiConverterTypePaymentType_lower(_ value: PaymentType) -> RustBuffer { + return FfiConverterTypePaymentType.lower(value) } -extension ManualRefundStateEnum: Equatable, Hashable {} +extension PaymentType: Equatable, Hashable {} -extension ManualRefundStateEnum: Codable {} +extension PaymentType: Codable {} -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum Network { +public enum PsbtCompletionError: Swift.Error { + - /** - * Mainnet Bitcoin. - */ - case bitcoin - /** - * Bitcoin's testnet network. - */ - case testnet - /** - * Bitcoin's testnet4 network. - */ - case testnet4 - /** - * Bitcoin's signet network. - */ - case signet - /** - * Bitcoin's regtest network. - */ - case regtest + + case InvalidPsbt(reason: String + ) + case CombineFailed(reason: String + ) + case FinalizationFailed(reason: String + ) + case VerificationFailed(reason: String + ) + case ExtractionFailed(reason: String + ) } -#if compiler(>=6) -extension Network: Sendable {} -#endif - #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeNetwork: FfiConverterRustBuffer { - typealias SwiftType = Network +public struct FfiConverterTypePsbtCompletionError: FfiConverterRustBuffer { + typealias SwiftType = PsbtCompletionError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Network { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PsbtCompletionError { let variant: Int32 = try readInt(&buf) switch variant { + - case 1: return .bitcoin - - case 2: return .testnet - - case 3: return .testnet4 - - case 4: return .signet - - case 5: return .regtest + - default: throw UniffiInternalError.unexpectedEnumCase + case 1: return .InvalidPsbt( + reason: try FfiConverterString.read(from: &buf) + ) + case 2: return .CombineFailed( + reason: try FfiConverterString.read(from: &buf) + ) + case 3: return .FinalizationFailed( + reason: try FfiConverterString.read(from: &buf) + ) + case 4: return .VerificationFailed( + reason: try FfiConverterString.read(from: &buf) + ) + case 5: return .ExtractionFailed( + reason: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: Network, into buf: inout [UInt8]) { + public static func write(_ value: PsbtCompletionError, into buf: inout [UInt8]) { switch value { + + - case .bitcoin: - writeInt(&buf, Int32(1)) + case let .InvalidPsbt(reason): + writeInt(&buf, Int32(1)) + FfiConverterString.write(reason, into: &buf) + - case .testnet: + case let .CombineFailed(reason): writeInt(&buf, Int32(2)) + FfiConverterString.write(reason, into: &buf) + - - case .testnet4: + case let .FinalizationFailed(reason): writeInt(&buf, Int32(3)) + FfiConverterString.write(reason, into: &buf) + - - case .signet: + case let .VerificationFailed(reason): writeInt(&buf, Int32(4)) + FfiConverterString.write(reason, into: &buf) + - - case .regtest: + case let .ExtractionFailed(reason): writeInt(&buf, Int32(5)) - + FfiConverterString.write(reason, into: &buf) + } } } @@ -20617,84 +22143,80 @@ public struct FfiConverterTypeNetwork: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeNetwork_lift(_ buf: RustBuffer) throws -> Network { - return try FfiConverterTypeNetwork.lift(buf) +public func FfiConverterTypePsbtCompletionError_lift(_ buf: RustBuffer) throws -> PsbtCompletionError { + return try FfiConverterTypePsbtCompletionError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeNetwork_lower(_ value: Network) -> RustBuffer { - return FfiConverterTypeNetwork.lower(value) +public func FfiConverterTypePsbtCompletionError_lower(_ value: PsbtCompletionError) -> RustBuffer { + return FfiConverterTypePsbtCompletionError.lower(value) } -extension Network: Equatable, Hashable {} +extension PsbtCompletionError: Equatable, Hashable {} + +extension PsbtCompletionError: Codable {} -extension Network: Codable {} +extension PsbtCompletionError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * The type of a `pubkyauth://` deep-link flow. + */ -public enum NetworkType { +public enum PubkyAuthKind { - case bitcoin - case testnet - case regtest - case signet + case signin + case signup } #if compiler(>=6) -extension NetworkType: Sendable {} +extension PubkyAuthKind: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeNetworkType: FfiConverterRustBuffer { - typealias SwiftType = NetworkType +public struct FfiConverterTypePubkyAuthKind: FfiConverterRustBuffer { + typealias SwiftType = PubkyAuthKind - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NetworkType { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PubkyAuthKind { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .bitcoin - - case 2: return .testnet - - case 3: return .regtest + case 1: return .signin - case 4: return .signet + case 2: return .signup default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: NetworkType, into buf: inout [UInt8]) { + public static func write(_ value: PubkyAuthKind, into buf: inout [UInt8]) { switch value { - case .bitcoin: + case .signin: writeInt(&buf, Int32(1)) - case .testnet: + case .signup: writeInt(&buf, Int32(2)) - - case .regtest: - writeInt(&buf, Int32(3)) - - - case .signet: - writeInt(&buf, Int32(4)) - } } } @@ -20703,21 +22225,21 @@ public struct FfiConverterTypeNetworkType: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeNetworkType_lift(_ buf: RustBuffer) throws -> NetworkType { - return try FfiConverterTypeNetworkType.lift(buf) +public func FfiConverterTypePubkyAuthKind_lift(_ buf: RustBuffer) throws -> PubkyAuthKind { + return try FfiConverterTypePubkyAuthKind.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeNetworkType_lower(_ value: NetworkType) -> RustBuffer { - return FfiConverterTypeNetworkType.lower(value) +public func FfiConverterTypePubkyAuthKind_lower(_ value: PubkyAuthKind) -> RustBuffer { + return FfiConverterTypePubkyAuthKind.lower(value) } -extension NetworkType: Equatable, Hashable {} +extension PubkyAuthKind: Equatable, Hashable {} -extension NetworkType: Codable {} +extension PubkyAuthKind: Codable {} @@ -20725,11 +22247,25 @@ extension NetworkType: Codable {} -public enum OnchainError: Swift.Error { +public enum PubkyError: Swift.Error { - case InvalidExtendedPublicKey(errorDetails: String + case InvalidCapabilities(reason: String + ) + case AuthFailed(reason: String + ) + case NoActiveFlow + case ResolutionFailed(reason: String + ) + case FetchFailed(reason: String + ) + case ProfileNotFound + case ProfileParseFailed(reason: String + ) + case KeyError(reason: String + ) + case WriteFailed(reason: String ) } @@ -20737,34 +22273,92 @@ public enum OnchainError: Swift.Error { #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeOnchainError: FfiConverterRustBuffer { - typealias SwiftType = OnchainError +public struct FfiConverterTypePubkyError: FfiConverterRustBuffer { + typealias SwiftType = PubkyError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> OnchainError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PubkyError { let variant: Int32 = try readInt(&buf) switch variant { - + + + case 1: return .InvalidCapabilities( + reason: try FfiConverterString.read(from: &buf) + ) + case 2: return .AuthFailed( + reason: try FfiConverterString.read(from: &buf) + ) + case 3: return .NoActiveFlow + case 4: return .ResolutionFailed( + reason: try FfiConverterString.read(from: &buf) + ) + case 5: return .FetchFailed( + reason: try FfiConverterString.read(from: &buf) + ) + case 6: return .ProfileNotFound + case 7: return .ProfileParseFailed( + reason: try FfiConverterString.read(from: &buf) + ) + case 8: return .KeyError( + reason: try FfiConverterString.read(from: &buf) + ) + case 9: return .WriteFailed( + reason: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: PubkyError, into buf: inout [UInt8]) { + switch value { + + + + + + case let .InvalidCapabilities(reason): + writeInt(&buf, Int32(1)) + FfiConverterString.write(reason, into: &buf) + + + case let .AuthFailed(reason): + writeInt(&buf, Int32(2)) + FfiConverterString.write(reason, into: &buf) + + + case .NoActiveFlow: + writeInt(&buf, Int32(3)) + - case 1: return .InvalidExtendedPublicKey( - errorDetails: try FfiConverterString.read(from: &buf) - ) - - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - public static func write(_ value: OnchainError, into buf: inout [UInt8]) { - switch value { - + case let .ResolutionFailed(reason): + writeInt(&buf, Int32(4)) + FfiConverterString.write(reason, into: &buf) + - + case let .FetchFailed(reason): + writeInt(&buf, Int32(5)) + FfiConverterString.write(reason, into: &buf) + + + case .ProfileNotFound: + writeInt(&buf, Int32(6)) - case let .InvalidExtendedPublicKey(errorDetails): - writeInt(&buf, Int32(1)) - FfiConverterString.write(errorDetails, into: &buf) + case let .ProfileParseFailed(reason): + writeInt(&buf, Int32(7)) + FfiConverterString.write(reason, into: &buf) + + + case let .KeyError(reason): + writeInt(&buf, Int32(8)) + FfiConverterString.write(reason, into: &buf) + + + case let .WriteFailed(reason): + writeInt(&buf, Int32(9)) + FfiConverterString.write(reason, into: &buf) } } @@ -20774,26 +22368,26 @@ public struct FfiConverterTypeOnchainError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeOnchainError_lift(_ buf: RustBuffer) throws -> OnchainError { - return try FfiConverterTypeOnchainError.lift(buf) +public func FfiConverterTypePubkyError_lift(_ buf: RustBuffer) throws -> PubkyError { + return try FfiConverterTypePubkyError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeOnchainError_lower(_ value: OnchainError) -> RustBuffer { - return FfiConverterTypeOnchainError.lower(value) +public func FfiConverterTypePubkyError_lower(_ value: PubkyError) -> RustBuffer { + return FfiConverterTypePubkyError.lower(value) } -extension OnchainError: Equatable, Hashable {} +extension PubkyError: Equatable, Hashable {} -extension OnchainError: Codable {} +extension PubkyError: Codable {} -extension OnchainError: Foundation.LocalizedError { +extension PubkyError: Foundation.LocalizedError { public var errorDescription: String? { String(reflecting: self) } @@ -20805,75 +22399,134 @@ extension OnchainError: Foundation.LocalizedError { // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum PassphraseResponse { +public enum Scanner { - /** - * User cancelled — aborts the pending operation. - */ - case cancel - /** - * Standard wallet — no passphrase, equivalent to `Some("")` on the device. - */ - case standard - /** - * Hidden wallet — derived from the passphrase entered on the host. - */ - case hidden(value: String + case onChain(invoice: OnChainInvoice + ) + case lightning(invoice: LightningInvoice + ) + case pubkyAuth(data: String + ) + case lnurlChannel(data: LnurlChannelData + ) + case lnurlAuth(data: LnurlAuthData + ) + case lnurlWithdraw(data: LnurlWithdrawData + ) + case lnurlAddress(data: LnurlAddressData + ) + case lnurlPay(data: LnurlPayData + ) + case nodeId(url: String, network: NetworkType + ) + case gift(code: String, amount: UInt64 ) - /** - * Enter the passphrase on the Trezor device itself instead of on the host. - */ - case onDevice } #if compiler(>=6) -extension PassphraseResponse: Sendable {} +extension Scanner: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypePassphraseResponse: FfiConverterRustBuffer { - typealias SwiftType = PassphraseResponse +public struct FfiConverterTypeScanner: FfiConverterRustBuffer { + typealias SwiftType = Scanner - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PassphraseResponse { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Scanner { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .cancel + case 1: return .onChain(invoice: try FfiConverterTypeOnChainInvoice.read(from: &buf) + ) - case 2: return .standard + case 2: return .lightning(invoice: try FfiConverterTypeLightningInvoice.read(from: &buf) + ) - case 3: return .hidden(value: try FfiConverterString.read(from: &buf) + case 3: return .pubkyAuth(data: try FfiConverterString.read(from: &buf) ) - case 4: return .onDevice + case 4: return .lnurlChannel(data: try FfiConverterTypeLnurlChannelData.read(from: &buf) + ) + + case 5: return .lnurlAuth(data: try FfiConverterTypeLnurlAuthData.read(from: &buf) + ) + + case 6: return .lnurlWithdraw(data: try FfiConverterTypeLnurlWithdrawData.read(from: &buf) + ) + + case 7: return .lnurlAddress(data: try FfiConverterTypeLnurlAddressData.read(from: &buf) + ) + + case 8: return .lnurlPay(data: try FfiConverterTypeLnurlPayData.read(from: &buf) + ) + + case 9: return .nodeId(url: try FfiConverterString.read(from: &buf), network: try FfiConverterTypeNetworkType.read(from: &buf) + ) + + case 10: return .gift(code: try FfiConverterString.read(from: &buf), amount: try FfiConverterUInt64.read(from: &buf) + ) default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: PassphraseResponse, into buf: inout [UInt8]) { + public static func write(_ value: Scanner, into buf: inout [UInt8]) { switch value { - case .cancel: + case let .onChain(invoice): writeInt(&buf, Int32(1)) + FfiConverterTypeOnChainInvoice.write(invoice, into: &buf) + - - case .standard: + case let .lightning(invoice): writeInt(&buf, Int32(2)) + FfiConverterTypeLightningInvoice.write(invoice, into: &buf) + - - case let .hidden(value): + case let .pubkyAuth(data): writeInt(&buf, Int32(3)) - FfiConverterString.write(value, into: &buf) + FfiConverterString.write(data, into: &buf) - case .onDevice: + case let .lnurlChannel(data): writeInt(&buf, Int32(4)) + FfiConverterTypeLnurlChannelData.write(data, into: &buf) + + + case let .lnurlAuth(data): + writeInt(&buf, Int32(5)) + FfiConverterTypeLnurlAuthData.write(data, into: &buf) + + + case let .lnurlWithdraw(data): + writeInt(&buf, Int32(6)) + FfiConverterTypeLnurlWithdrawData.write(data, into: &buf) + + + case let .lnurlAddress(data): + writeInt(&buf, Int32(7)) + FfiConverterTypeLnurlAddressData.write(data, into: &buf) + + + case let .lnurlPay(data): + writeInt(&buf, Int32(8)) + FfiConverterTypeLnurlPayData.write(data, into: &buf) + + + case let .nodeId(url,network): + writeInt(&buf, Int32(9)) + FfiConverterString.write(url, into: &buf) + FfiConverterTypeNetworkType.write(network, into: &buf) + + case let .gift(code,amount): + writeInt(&buf, Int32(10)) + FfiConverterString.write(code, into: &buf) + FfiConverterUInt64.write(amount, into: &buf) + } } } @@ -20882,75 +22535,75 @@ public struct FfiConverterTypePassphraseResponse: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePassphraseResponse_lift(_ buf: RustBuffer) throws -> PassphraseResponse { - return try FfiConverterTypePassphraseResponse.lift(buf) +public func FfiConverterTypeScanner_lift(_ buf: RustBuffer) throws -> Scanner { + return try FfiConverterTypeScanner.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePassphraseResponse_lower(_ value: PassphraseResponse) -> RustBuffer { - return FfiConverterTypePassphraseResponse.lower(value) +public func FfiConverterTypeScanner_lower(_ value: Scanner) -> RustBuffer { + return FfiConverterTypeScanner.lower(value) } -extension PassphraseResponse: Equatable, Hashable {} +extension Scanner: Equatable, Hashable {} -extension PassphraseResponse: Codable {} +extension Scanner: Codable {} -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum PaymentState { +public enum SeedQrError: Swift.Error { + - case pending - case succeeded - case failed + + case InvalidStandardPayload + case InvalidCompactPayload + case InvalidMnemonic } -#if compiler(>=6) -extension PaymentState: Sendable {} -#endif - #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypePaymentState: FfiConverterRustBuffer { - typealias SwiftType = PaymentState +public struct FfiConverterTypeSeedQrError: FfiConverterRustBuffer { + typealias SwiftType = SeedQrError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PaymentState { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SeedQrError { let variant: Int32 = try readInt(&buf) switch variant { + - case 1: return .pending - - case 2: return .succeeded - - case 3: return .failed + - default: throw UniffiInternalError.unexpectedEnumCase + case 1: return .InvalidStandardPayload + case 2: return .InvalidCompactPayload + case 3: return .InvalidMnemonic + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: PaymentState, into buf: inout [UInt8]) { + public static func write(_ value: SeedQrError, into buf: inout [UInt8]) { switch value { + + - case .pending: + + case .InvalidStandardPayload: writeInt(&buf, Int32(1)) - case .succeeded: + case .InvalidCompactPayload: writeInt(&buf, Int32(2)) - case .failed: + case .InvalidMnemonic: writeInt(&buf, Int32(3)) } @@ -20961,23 +22614,30 @@ public struct FfiConverterTypePaymentState: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePaymentState_lift(_ buf: RustBuffer) throws -> PaymentState { - return try FfiConverterTypePaymentState.lift(buf) +public func FfiConverterTypeSeedQrError_lift(_ buf: RustBuffer) throws -> SeedQrError { + return try FfiConverterTypeSeedQrError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePaymentState_lower(_ value: PaymentState) -> RustBuffer { - return FfiConverterTypePaymentState.lower(value) +public func FfiConverterTypeSeedQrError_lower(_ value: SeedQrError) -> RustBuffer { + return FfiConverterTypeSeedQrError.lower(value) } -extension PaymentState: Equatable, Hashable {} +extension SeedQrError: Equatable, Hashable {} + +extension SeedQrError: Codable {} + -extension PaymentState: Codable {} +extension SeedQrError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} @@ -20985,44 +22645,44 @@ extension PaymentState: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum PaymentType { +public enum SortDirection { - case sent - case received + case asc + case desc } #if compiler(>=6) -extension PaymentType: Sendable {} +extension SortDirection: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypePaymentType: FfiConverterRustBuffer { - typealias SwiftType = PaymentType +public struct FfiConverterTypeSortDirection: FfiConverterRustBuffer { + typealias SwiftType = SortDirection - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PaymentType { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SortDirection { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .sent + case 1: return .asc - case 2: return .received + case 2: return .desc default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: PaymentType, into buf: inout [UInt8]) { + public static func write(_ value: SortDirection, into buf: inout [UInt8]) { switch value { - case .sent: + case .asc: writeInt(&buf, Int32(1)) - case .received: + case .desc: writeInt(&buf, Int32(2)) } @@ -21033,21 +22693,21 @@ public struct FfiConverterTypePaymentType: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePaymentType_lift(_ buf: RustBuffer) throws -> PaymentType { - return try FfiConverterTypePaymentType.lift(buf) +public func FfiConverterTypeSortDirection_lift(_ buf: RustBuffer) throws -> SortDirection { + return try FfiConverterTypeSortDirection.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePaymentType_lower(_ value: PaymentType) -> RustBuffer { - return FfiConverterTypePaymentType.lower(value) +public func FfiConverterTypeSortDirection_lower(_ value: SortDirection) -> RustBuffer { + return FfiConverterTypeSortDirection.lower(value) } -extension PaymentType: Equatable, Hashable {} +extension SortDirection: Equatable, Hashable {} -extension PaymentType: Codable {} +extension SortDirection: Codable {} @@ -21055,87 +22715,59 @@ extension PaymentType: Codable {} -public enum PsbtCompletionError: Swift.Error { +public enum SweepError: Swift.Error { - case InvalidPsbt(reason: String - ) - case CombineFailed(reason: String - ) - case FinalizationFailed(reason: String - ) - case VerificationFailed(reason: String - ) - case ExtractionFailed(reason: String + case SweepFailed(String ) + case NoUtxosFound + case InvalidMnemonic } #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypePsbtCompletionError: FfiConverterRustBuffer { - typealias SwiftType = PsbtCompletionError +public struct FfiConverterTypeSweepError: FfiConverterRustBuffer { + typealias SwiftType = SweepError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PsbtCompletionError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SweepError { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .InvalidPsbt( - reason: try FfiConverterString.read(from: &buf) - ) - case 2: return .CombineFailed( - reason: try FfiConverterString.read(from: &buf) - ) - case 3: return .FinalizationFailed( - reason: try FfiConverterString.read(from: &buf) - ) - case 4: return .VerificationFailed( - reason: try FfiConverterString.read(from: &buf) - ) - case 5: return .ExtractionFailed( - reason: try FfiConverterString.read(from: &buf) + case 1: return .SweepFailed( + try FfiConverterString.read(from: &buf) ) + case 2: return .NoUtxosFound + case 3: return .InvalidMnemonic default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: PsbtCompletionError, into buf: inout [UInt8]) { + public static func write(_ value: SweepError, into buf: inout [UInt8]) { switch value { - case let .InvalidPsbt(reason): + case let .SweepFailed(v1): writeInt(&buf, Int32(1)) - FfiConverterString.write(reason, into: &buf) + FfiConverterString.write(v1, into: &buf) - case let .CombineFailed(reason): + case .NoUtxosFound: writeInt(&buf, Int32(2)) - FfiConverterString.write(reason, into: &buf) - - case let .FinalizationFailed(reason): - writeInt(&buf, Int32(3)) - FfiConverterString.write(reason, into: &buf) - - case let .VerificationFailed(reason): - writeInt(&buf, Int32(4)) - FfiConverterString.write(reason, into: &buf) - + case .InvalidMnemonic: + writeInt(&buf, Int32(3)) - case let .ExtractionFailed(reason): - writeInt(&buf, Int32(5)) - FfiConverterString.write(reason, into: &buf) - } } } @@ -21144,26 +22776,26 @@ public struct FfiConverterTypePsbtCompletionError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePsbtCompletionError_lift(_ buf: RustBuffer) throws -> PsbtCompletionError { - return try FfiConverterTypePsbtCompletionError.lift(buf) +public func FfiConverterTypeSweepError_lift(_ buf: RustBuffer) throws -> SweepError { + return try FfiConverterTypeSweepError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePsbtCompletionError_lower(_ value: PsbtCompletionError) -> RustBuffer { - return FfiConverterTypePsbtCompletionError.lower(value) +public func FfiConverterTypeSweepError_lower(_ value: SweepError) -> RustBuffer { + return FfiConverterTypeSweepError.lower(value) } -extension PsbtCompletionError: Equatable, Hashable {} +extension SweepError: Equatable, Hashable {} -extension PsbtCompletionError: Codable {} +extension SweepError: Codable {} -extension PsbtCompletionError: Foundation.LocalizedError { +extension SweepError: Foundation.LocalizedError { public var errorDescription: String? { String(reflecting: self) } @@ -21175,192 +22807,75 @@ extension PsbtCompletionError: Foundation.LocalizedError { // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. /** - * The type of a `pubkyauth://` deep-link flow. + * Bitcoin network / coin type for Trezor operations. */ -public enum PubkyAuthKind { +public enum TrezorCoinType { - case signin - case signup + /** + * Bitcoin mainnet + */ + case bitcoin + /** + * Bitcoin testnet + */ + case testnet + /** + * Bitcoin signet (treated as testnet by the device) + */ + case signet + /** + * Bitcoin regtest + */ + case regtest } #if compiler(>=6) -extension PubkyAuthKind: Sendable {} -#endif - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypePubkyAuthKind: FfiConverterRustBuffer { - typealias SwiftType = PubkyAuthKind - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PubkyAuthKind { - let variant: Int32 = try readInt(&buf) - switch variant { - - case 1: return .signin - - case 2: return .signup - - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - public static func write(_ value: PubkyAuthKind, into buf: inout [UInt8]) { - switch value { - - - case .signin: - writeInt(&buf, Int32(1)) - - - case .signup: - writeInt(&buf, Int32(2)) - - } - } -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypePubkyAuthKind_lift(_ buf: RustBuffer) throws -> PubkyAuthKind { - return try FfiConverterTypePubkyAuthKind.lift(buf) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypePubkyAuthKind_lower(_ value: PubkyAuthKind) -> RustBuffer { - return FfiConverterTypePubkyAuthKind.lower(value) -} - - -extension PubkyAuthKind: Equatable, Hashable {} - -extension PubkyAuthKind: Codable {} - - - - - - - -public enum PubkyError: Swift.Error { - - - - case InvalidCapabilities(reason: String - ) - case AuthFailed(reason: String - ) - case NoActiveFlow - case ResolutionFailed(reason: String - ) - case FetchFailed(reason: String - ) - case ProfileNotFound - case ProfileParseFailed(reason: String - ) - case KeyError(reason: String - ) - case WriteFailed(reason: String - ) -} - - -#if swift(>=5.8) -@_documentation(visibility: private) +extension TrezorCoinType: Sendable {} #endif -public struct FfiConverterTypePubkyError: FfiConverterRustBuffer { - typealias SwiftType = PubkyError - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PubkyError { - let variant: Int32 = try readInt(&buf) - switch variant { - - - - - case 1: return .InvalidCapabilities( - reason: try FfiConverterString.read(from: &buf) - ) - case 2: return .AuthFailed( - reason: try FfiConverterString.read(from: &buf) - ) - case 3: return .NoActiveFlow - case 4: return .ResolutionFailed( - reason: try FfiConverterString.read(from: &buf) - ) - case 5: return .FetchFailed( - reason: try FfiConverterString.read(from: &buf) - ) - case 6: return .ProfileNotFound - case 7: return .ProfileParseFailed( - reason: try FfiConverterString.read(from: &buf) - ) - case 8: return .KeyError( - reason: try FfiConverterString.read(from: &buf) - ) - case 9: return .WriteFailed( - reason: try FfiConverterString.read(from: &buf) - ) - default: throw UniffiInternalError.unexpectedEnumCase +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +public struct FfiConverterTypeTrezorCoinType: FfiConverterRustBuffer { + typealias SwiftType = TrezorCoinType + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorCoinType { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .bitcoin + + case 2: return .testnet + + case 3: return .signet + + case 4: return .regtest + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: PubkyError, into buf: inout [UInt8]) { + public static func write(_ value: TrezorCoinType, into buf: inout [UInt8]) { switch value { - - - - case let .InvalidCapabilities(reason): + case .bitcoin: writeInt(&buf, Int32(1)) - FfiConverterString.write(reason, into: &buf) - - case let .AuthFailed(reason): + + case .testnet: writeInt(&buf, Int32(2)) - FfiConverterString.write(reason, into: &buf) - - case .NoActiveFlow: + + case .signet: writeInt(&buf, Int32(3)) - case let .ResolutionFailed(reason): + case .regtest: writeInt(&buf, Int32(4)) - FfiConverterString.write(reason, into: &buf) - - - case let .FetchFailed(reason): - writeInt(&buf, Int32(5)) - FfiConverterString.write(reason, into: &buf) - - - case .ProfileNotFound: - writeInt(&buf, Int32(6)) - - - case let .ProfileParseFailed(reason): - writeInt(&buf, Int32(7)) - FfiConverterString.write(reason, into: &buf) - - - case let .KeyError(reason): - writeInt(&buf, Int32(8)) - FfiConverterString.write(reason, into: &buf) - - case let .WriteFailed(reason): - writeInt(&buf, Int32(9)) - FfiConverterString.write(reason, into: &buf) - } } } @@ -21369,244 +22884,283 @@ public struct FfiConverterTypePubkyError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePubkyError_lift(_ buf: RustBuffer) throws -> PubkyError { - return try FfiConverterTypePubkyError.lift(buf) +public func FfiConverterTypeTrezorCoinType_lift(_ buf: RustBuffer) throws -> TrezorCoinType { + return try FfiConverterTypeTrezorCoinType.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypePubkyError_lower(_ value: PubkyError) -> RustBuffer { - return FfiConverterTypePubkyError.lower(value) +public func FfiConverterTypeTrezorCoinType_lower(_ value: TrezorCoinType) -> RustBuffer { + return FfiConverterTypeTrezorCoinType.lower(value) } -extension PubkyError: Equatable, Hashable {} - -extension PubkyError: Codable {} +extension TrezorCoinType: Equatable, Hashable {} +extension TrezorCoinType: Codable {} -extension PubkyError: Foundation.LocalizedError { - public var errorDescription: String? { - String(reflecting: self) - } -} -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * Trezor-related errors exposed via FFI. + */ +public enum TrezorError: Swift.Error { -public enum Scanner { - case onChain(invoice: OnChainInvoice - ) - case lightning(invoice: LightningInvoice - ) - case pubkyAuth(data: String + + /** + * Transport layer error (USB/Bluetooth communication) + */ + case TransportError(errorDetails: String ) - case lnurlChannel(data: LnurlChannelData + /** + * No Trezor device found + */ + case DeviceNotFound + /** + * Device disconnected during operation + */ + case DeviceDisconnected + /** + * Device is busy and the caller should back off before retrying + */ + case DeviceBusy + /** + * Connection error + */ + case ConnectionError(errorDetails: String ) - case lnurlAuth(data: LnurlAuthData + /** + * Protocol error (encoding/decoding) + */ + case ProtocolError(errorDetails: String ) - case lnurlWithdraw(data: LnurlWithdrawData + /** + * Pairing required for Bluetooth connection + */ + case PairingRequired + /** + * Pairing failed + */ + case PairingFailed(errorDetails: String ) - case lnurlAddress(data: LnurlAddressData + /** + * PIN is required + */ + case PinRequired + /** + * PIN entry cancelled + */ + case PinCancelled + /** + * Invalid PIN entered + */ + case InvalidPin + /** + * Passphrase is required + */ + case PassphraseRequired + /** + * Passphrase entry cancelled + */ + case PassphraseCancelled + /** + * Action cancelled by user on device + */ + case UserCancelled + /** + * Operation timed out + */ + case Timeout + /** + * Invalid derivation path + */ + case InvalidPath(errorDetails: String ) - case lnurlPay(data: LnurlPayData + /** + * Device returned an error + */ + case DeviceError(errorDetails: String ) - case nodeId(url: String, network: NetworkType + /** + * Trezor manager not initialized + */ + case NotInitialized + /** + * No device connected + */ + case NotConnected + /** + * Session error + */ + case SessionError(errorDetails: String ) - case gift(code: String, amount: UInt64 + /** + * IO error + */ + case IoError(errorDetails: String ) } -#if compiler(>=6) -extension Scanner: Sendable {} -#endif - #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeScanner: FfiConverterRustBuffer { - typealias SwiftType = Scanner +public struct FfiConverterTypeTrezorError: FfiConverterRustBuffer { + typealias SwiftType = TrezorError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Scanner { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorError { let variant: Int32 = try readInt(&buf) switch variant { + - case 1: return .onChain(invoice: try FfiConverterTypeOnChainInvoice.read(from: &buf) - ) - - case 2: return .lightning(invoice: try FfiConverterTypeLightningInvoice.read(from: &buf) - ) - - case 3: return .pubkyAuth(data: try FfiConverterString.read(from: &buf) - ) - - case 4: return .lnurlChannel(data: try FfiConverterTypeLnurlChannelData.read(from: &buf) - ) - - case 5: return .lnurlAuth(data: try FfiConverterTypeLnurlAuthData.read(from: &buf) - ) - - case 6: return .lnurlWithdraw(data: try FfiConverterTypeLnurlWithdrawData.read(from: &buf) - ) - - case 7: return .lnurlAddress(data: try FfiConverterTypeLnurlAddressData.read(from: &buf) - ) - - case 8: return .lnurlPay(data: try FfiConverterTypeLnurlPayData.read(from: &buf) - ) - - case 9: return .nodeId(url: try FfiConverterString.read(from: &buf), network: try FfiConverterTypeNetworkType.read(from: &buf) - ) - - case 10: return .gift(code: try FfiConverterString.read(from: &buf), amount: try FfiConverterUInt64.read(from: &buf) - ) + - default: throw UniffiInternalError.unexpectedEnumCase + case 1: return .TransportError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 2: return .DeviceNotFound + case 3: return .DeviceDisconnected + case 4: return .DeviceBusy + case 5: return .ConnectionError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 6: return .ProtocolError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 7: return .PairingRequired + case 8: return .PairingFailed( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 9: return .PinRequired + case 10: return .PinCancelled + case 11: return .InvalidPin + case 12: return .PassphraseRequired + case 13: return .PassphraseCancelled + case 14: return .UserCancelled + case 15: return .Timeout + case 16: return .InvalidPath( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 17: return .DeviceError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 18: return .NotInitialized + case 19: return .NotConnected + case 20: return .SessionError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + case 21: return .IoError( + errorDetails: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: Scanner, into buf: inout [UInt8]) { + public static func write(_ value: TrezorError, into buf: inout [UInt8]) { switch value { + + - case let .onChain(invoice): + + case let .TransportError(errorDetails): writeInt(&buf, Int32(1)) - FfiConverterTypeOnChainInvoice.write(invoice, into: &buf) + FfiConverterString.write(errorDetails, into: &buf) - case let .lightning(invoice): + case .DeviceNotFound: writeInt(&buf, Int32(2)) - FfiConverterTypeLightningInvoice.write(invoice, into: &buf) - - case let .pubkyAuth(data): + + case .DeviceDisconnected: writeInt(&buf, Int32(3)) - FfiConverterString.write(data, into: &buf) - - case let .lnurlChannel(data): + + case .DeviceBusy: writeInt(&buf, Int32(4)) - FfiConverterTypeLnurlChannelData.write(data, into: &buf) - - case let .lnurlAuth(data): + + case let .ConnectionError(errorDetails): writeInt(&buf, Int32(5)) - FfiConverterTypeLnurlAuthData.write(data, into: &buf) + FfiConverterString.write(errorDetails, into: &buf) - case let .lnurlWithdraw(data): + case let .ProtocolError(errorDetails): writeInt(&buf, Int32(6)) - FfiConverterTypeLnurlWithdrawData.write(data, into: &buf) + FfiConverterString.write(errorDetails, into: &buf) - case let .lnurlAddress(data): + case .PairingRequired: writeInt(&buf, Int32(7)) - FfiConverterTypeLnurlAddressData.write(data, into: &buf) - - case let .lnurlPay(data): + + case let .PairingFailed(errorDetails): writeInt(&buf, Int32(8)) - FfiConverterTypeLnurlPayData.write(data, into: &buf) + FfiConverterString.write(errorDetails, into: &buf) - case let .nodeId(url,network): + case .PinRequired: writeInt(&buf, Int32(9)) - FfiConverterString.write(url, into: &buf) - FfiConverterTypeNetworkType.write(network, into: &buf) - - case let .gift(code,amount): + + case .PinCancelled: writeInt(&buf, Int32(10)) - FfiConverterString.write(code, into: &buf) - FfiConverterUInt64.write(amount, into: &buf) - - } - } -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeScanner_lift(_ buf: RustBuffer) throws -> Scanner { - return try FfiConverterTypeScanner.lift(buf) -} - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public func FfiConverterTypeScanner_lower(_ value: Scanner) -> RustBuffer { - return FfiConverterTypeScanner.lower(value) -} - - -extension Scanner: Equatable, Hashable {} - -extension Scanner: Codable {} - - - - - - - -public enum SeedQrError: Swift.Error { - - - - case InvalidStandardPayload - case InvalidCompactPayload - case InvalidMnemonic -} - - -#if swift(>=5.8) -@_documentation(visibility: private) -#endif -public struct FfiConverterTypeSeedQrError: FfiConverterRustBuffer { - typealias SwiftType = SeedQrError - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SeedQrError { - let variant: Int32 = try readInt(&buf) - switch variant { - - - case 1: return .InvalidStandardPayload - case 2: return .InvalidCompactPayload - case 3: return .InvalidMnemonic - - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - public static func write(_ value: SeedQrError, into buf: inout [UInt8]) { - switch value { - + case .InvalidPin: + writeInt(&buf, Int32(11)) - + case .PassphraseRequired: + writeInt(&buf, Int32(12)) - case .InvalidStandardPayload: - writeInt(&buf, Int32(1)) + case .PassphraseCancelled: + writeInt(&buf, Int32(13)) - case .InvalidCompactPayload: - writeInt(&buf, Int32(2)) + case .UserCancelled: + writeInt(&buf, Int32(14)) - case .InvalidMnemonic: - writeInt(&buf, Int32(3)) + case .Timeout: + writeInt(&buf, Int32(15)) + + + case let .InvalidPath(errorDetails): + writeInt(&buf, Int32(16)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .DeviceError(errorDetails): + writeInt(&buf, Int32(17)) + FfiConverterString.write(errorDetails, into: &buf) + + + case .NotInitialized: + writeInt(&buf, Int32(18)) + + + case .NotConnected: + writeInt(&buf, Int32(19)) + + + case let .SessionError(errorDetails): + writeInt(&buf, Int32(20)) + FfiConverterString.write(errorDetails, into: &buf) + + + case let .IoError(errorDetails): + writeInt(&buf, Int32(21)) + FfiConverterString.write(errorDetails, into: &buf) + } } } @@ -21615,26 +23169,26 @@ public struct FfiConverterTypeSeedQrError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSeedQrError_lift(_ buf: RustBuffer) throws -> SeedQrError { - return try FfiConverterTypeSeedQrError.lift(buf) +public func FfiConverterTypeTrezorError_lift(_ buf: RustBuffer) throws -> TrezorError { + return try FfiConverterTypeTrezorError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSeedQrError_lower(_ value: SeedQrError) -> RustBuffer { - return FfiConverterTypeSeedQrError.lower(value) +public func FfiConverterTypeTrezorError_lower(_ value: TrezorError) -> RustBuffer { + return FfiConverterTypeTrezorError.lower(value) } -extension SeedQrError: Equatable, Hashable {} +extension TrezorError: Equatable, Hashable {} -extension SeedQrError: Codable {} +extension TrezorError: Codable {} -extension SeedQrError: Foundation.LocalizedError { +extension TrezorError: Foundation.LocalizedError { public var errorDescription: String? { String(reflecting: self) } @@ -21645,46 +23199,95 @@ extension SeedQrError: Foundation.LocalizedError { // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * Script types for address derivation. + */ -public enum SortDirection { +public enum TrezorScriptType { - case asc - case desc + /** + * P2PKH (legacy) + */ + case spendAddress + /** + * P2SH-P2WPKH (nested SegWit) + */ + case spendP2shWitness + /** + * P2WPKH (native SegWit) + */ + case spendWitness + /** + * P2TR (Taproot) + */ + case spendTaproot + /** + * P2SH multisig + */ + case spendMultisig + /** + * External/watch-only input (not signed by device) + */ + case external } #if compiler(>=6) -extension SortDirection: Sendable {} +extension TrezorScriptType: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeSortDirection: FfiConverterRustBuffer { - typealias SwiftType = SortDirection +public struct FfiConverterTypeTrezorScriptType: FfiConverterRustBuffer { + typealias SwiftType = TrezorScriptType - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SortDirection { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorScriptType { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .asc + case 1: return .spendAddress + + case 2: return .spendP2shWitness + + case 3: return .spendWitness + + case 4: return .spendTaproot + + case 5: return .spendMultisig + + case 6: return .external + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: TrezorScriptType, into buf: inout [UInt8]) { + switch value { + + + case .spendAddress: + writeInt(&buf, Int32(1)) + + + case .spendP2shWitness: + writeInt(&buf, Int32(2)) + + + case .spendWitness: + writeInt(&buf, Int32(3)) - case 2: return .desc - default: throw UniffiInternalError.unexpectedEnumCase - } - } - - public static func write(_ value: SortDirection, into buf: inout [UInt8]) { - switch value { + case .spendTaproot: + writeInt(&buf, Int32(4)) - case .asc: - writeInt(&buf, Int32(1)) + case .spendMultisig: + writeInt(&buf, Int32(5)) - case .desc: - writeInt(&buf, Int32(2)) + case .external: + writeInt(&buf, Int32(6)) } } @@ -21694,80 +23297,68 @@ public struct FfiConverterTypeSortDirection: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSortDirection_lift(_ buf: RustBuffer) throws -> SortDirection { - return try FfiConverterTypeSortDirection.lift(buf) +public func FfiConverterTypeTrezorScriptType_lift(_ buf: RustBuffer) throws -> TrezorScriptType { + return try FfiConverterTypeTrezorScriptType.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSortDirection_lower(_ value: SortDirection) -> RustBuffer { - return FfiConverterTypeSortDirection.lower(value) +public func FfiConverterTypeTrezorScriptType_lower(_ value: TrezorScriptType) -> RustBuffer { + return FfiConverterTypeTrezorScriptType.lower(value) } -extension SortDirection: Equatable, Hashable {} - -extension SortDirection: Codable {} +extension TrezorScriptType: Equatable, Hashable {} +extension TrezorScriptType: Codable {} -public enum SweepError: Swift.Error { +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * Structured transport error code returned by native callback operations. + */ +public enum TrezorTransportErrorCode { - - case SweepFailed(String - ) - case NoUtxosFound - case InvalidMnemonic + /** + * Device is busy and the caller should back off before retrying. + */ + case deviceBusy } +#if compiler(>=6) +extension TrezorTransportErrorCode: Sendable {} +#endif + #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeSweepError: FfiConverterRustBuffer { - typealias SwiftType = SweepError +public struct FfiConverterTypeTrezorTransportErrorCode: FfiConverterRustBuffer { + typealias SwiftType = TrezorTransportErrorCode - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SweepError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorTransportErrorCode { let variant: Int32 = try readInt(&buf) switch variant { - - + case 1: return .deviceBusy - case 1: return .SweepFailed( - try FfiConverterString.read(from: &buf) - ) - case 2: return .NoUtxosFound - case 3: return .InvalidMnemonic - - default: throw UniffiInternalError.unexpectedEnumCase + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: SweepError, into buf: inout [UInt8]) { + public static func write(_ value: TrezorTransportErrorCode, into buf: inout [UInt8]) { switch value { - - - - case let .SweepFailed(v1): + case .deviceBusy: writeInt(&buf, Int32(1)) - FfiConverterString.write(v1, into: &buf) - - - case .NoUtxosFound: - writeInt(&buf, Int32(2)) - - - case .InvalidMnemonic: - writeInt(&buf, Int32(3)) } } @@ -21777,30 +23368,23 @@ public struct FfiConverterTypeSweepError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSweepError_lift(_ buf: RustBuffer) throws -> SweepError { - return try FfiConverterTypeSweepError.lift(buf) +public func FfiConverterTypeTrezorTransportErrorCode_lift(_ buf: RustBuffer) throws -> TrezorTransportErrorCode { + return try FfiConverterTypeTrezorTransportErrorCode.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeSweepError_lower(_ value: SweepError) -> RustBuffer { - return FfiConverterTypeSweepError.lower(value) +public func FfiConverterTypeTrezorTransportErrorCode_lower(_ value: TrezorTransportErrorCode) -> RustBuffer { + return FfiConverterTypeTrezorTransportErrorCode.lower(value) } -extension SweepError: Equatable, Hashable {} - -extension SweepError: Codable {} - +extension TrezorTransportErrorCode: Equatable, Hashable {} +extension TrezorTransportErrorCode: Codable {} -extension SweepError: Foundation.LocalizedError { - public var errorDescription: String? { - String(reflecting: self) - } -} @@ -21808,75 +23392,55 @@ extension SweepError: Foundation.LocalizedError { // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. /** - * Bitcoin network / coin type for Trezor operations. + * Transport type for Trezor devices. */ -public enum TrezorCoinType { +public enum TrezorTransportType { /** - * Bitcoin mainnet - */ - case bitcoin - /** - * Bitcoin testnet - */ - case testnet - /** - * Bitcoin signet (treated as testnet by the device) + * USB connection */ - case signet + case usb /** - * Bitcoin regtest + * Bluetooth connection */ - case regtest + case bluetooth } #if compiler(>=6) -extension TrezorCoinType: Sendable {} +extension TrezorTransportType: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorCoinType: FfiConverterRustBuffer { - typealias SwiftType = TrezorCoinType +public struct FfiConverterTypeTrezorTransportType: FfiConverterRustBuffer { + typealias SwiftType = TrezorTransportType - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorCoinType { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorTransportType { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .bitcoin - - case 2: return .testnet - - case 3: return .signet + case 1: return .usb - case 4: return .regtest + case 2: return .bluetooth default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: TrezorCoinType, into buf: inout [UInt8]) { + public static func write(_ value: TrezorTransportType, into buf: inout [UInt8]) { switch value { - case .bitcoin: + case .usb: writeInt(&buf, Int32(1)) - case .testnet: + case .bluetooth: writeInt(&buf, Int32(2)) - - case .signet: - writeInt(&buf, Int32(3)) - - - case .regtest: - writeInt(&buf, Int32(4)) - } } } @@ -21885,283 +23449,89 @@ public struct FfiConverterTypeTrezorCoinType: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorCoinType_lift(_ buf: RustBuffer) throws -> TrezorCoinType { - return try FfiConverterTypeTrezorCoinType.lift(buf) +public func FfiConverterTypeTrezorTransportType_lift(_ buf: RustBuffer) throws -> TrezorTransportType { + return try FfiConverterTypeTrezorTransportType.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) -#endif -public func FfiConverterTypeTrezorCoinType_lower(_ value: TrezorCoinType) -> RustBuffer { - return FfiConverterTypeTrezorCoinType.lower(value) -} - - -extension TrezorCoinType: Equatable, Hashable {} - -extension TrezorCoinType: Codable {} - - - - - - - -/** - * Trezor-related errors exposed via FFI. - */ -public enum TrezorError: Swift.Error { - - - - /** - * Transport layer error (USB/Bluetooth communication) - */ - case TransportError(errorDetails: String - ) - /** - * No Trezor device found - */ - case DeviceNotFound - /** - * Device disconnected during operation - */ - case DeviceDisconnected - /** - * Device is busy and the caller should back off before retrying - */ - case DeviceBusy - /** - * Connection error - */ - case ConnectionError(errorDetails: String - ) - /** - * Protocol error (encoding/decoding) - */ - case ProtocolError(errorDetails: String - ) - /** - * Pairing required for Bluetooth connection - */ - case PairingRequired - /** - * Pairing failed - */ - case PairingFailed(errorDetails: String - ) - /** - * PIN is required - */ - case PinRequired - /** - * PIN entry cancelled - */ - case PinCancelled - /** - * Invalid PIN entered - */ - case InvalidPin - /** - * Passphrase is required - */ - case PassphraseRequired - /** - * Passphrase entry cancelled - */ - case PassphraseCancelled - /** - * Action cancelled by user on device - */ - case UserCancelled - /** - * Operation timed out - */ - case Timeout - /** - * Invalid derivation path - */ - case InvalidPath(errorDetails: String - ) - /** - * Device returned an error - */ - case DeviceError(errorDetails: String - ) - /** - * Trezor manager not initialized - */ - case NotInitialized +#endif +public func FfiConverterTypeTrezorTransportType_lower(_ value: TrezorTransportType) -> RustBuffer { + return FfiConverterTypeTrezorTransportType.lower(value) +} + + +extension TrezorTransportType: Equatable, Hashable {} + +extension TrezorTransportType: Codable {} + + + + + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. +/** + * Transaction direction from the wallet's perspective. + */ + +public enum TxDirection { + /** - * No device connected + * Wallet sent funds to an external address */ - case NotConnected + case sent /** - * Session error + * Wallet received funds from an external source */ - case SessionError(errorDetails: String - ) + case received /** - * IO error + * Wallet sent funds to itself (e.g. consolidation, change-only) */ - case IoError(errorDetails: String - ) + case selfTransfer } +#if compiler(>=6) +extension TxDirection: Sendable {} +#endif + #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorError: FfiConverterRustBuffer { - typealias SwiftType = TrezorError +public struct FfiConverterTypeTxDirection: FfiConverterRustBuffer { + typealias SwiftType = TxDirection - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TxDirection { let variant: Int32 = try readInt(&buf) switch variant { - - + case 1: return .sent - case 1: return .TransportError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 2: return .DeviceNotFound - case 3: return .DeviceDisconnected - case 4: return .DeviceBusy - case 5: return .ConnectionError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 6: return .ProtocolError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 7: return .PairingRequired - case 8: return .PairingFailed( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 9: return .PinRequired - case 10: return .PinCancelled - case 11: return .InvalidPin - case 12: return .PassphraseRequired - case 13: return .PassphraseCancelled - case 14: return .UserCancelled - case 15: return .Timeout - case 16: return .InvalidPath( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 17: return .DeviceError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 18: return .NotInitialized - case 19: return .NotConnected - case 20: return .SessionError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - case 21: return .IoError( - errorDetails: try FfiConverterString.read(from: &buf) - ) - - default: throw UniffiInternalError.unexpectedEnumCase + case 2: return .received + + case 3: return .selfTransfer + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: TrezorError, into buf: inout [UInt8]) { + public static func write(_ value: TxDirection, into buf: inout [UInt8]) { switch value { - - - - case let .TransportError(errorDetails): + case .sent: writeInt(&buf, Int32(1)) - FfiConverterString.write(errorDetails, into: &buf) - - case .DeviceNotFound: + + case .received: writeInt(&buf, Int32(2)) - case .DeviceDisconnected: + case .selfTransfer: writeInt(&buf, Int32(3)) - - case .DeviceBusy: - writeInt(&buf, Int32(4)) - - - case let .ConnectionError(errorDetails): - writeInt(&buf, Int32(5)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .ProtocolError(errorDetails): - writeInt(&buf, Int32(6)) - FfiConverterString.write(errorDetails, into: &buf) - - - case .PairingRequired: - writeInt(&buf, Int32(7)) - - - case let .PairingFailed(errorDetails): - writeInt(&buf, Int32(8)) - FfiConverterString.write(errorDetails, into: &buf) - - - case .PinRequired: - writeInt(&buf, Int32(9)) - - - case .PinCancelled: - writeInt(&buf, Int32(10)) - - - case .InvalidPin: - writeInt(&buf, Int32(11)) - - - case .PassphraseRequired: - writeInt(&buf, Int32(12)) - - - case .PassphraseCancelled: - writeInt(&buf, Int32(13)) - - - case .UserCancelled: - writeInt(&buf, Int32(14)) - - - case .Timeout: - writeInt(&buf, Int32(15)) - - - case let .InvalidPath(errorDetails): - writeInt(&buf, Int32(16)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .DeviceError(errorDetails): - writeInt(&buf, Int32(17)) - FfiConverterString.write(errorDetails, into: &buf) - - - case .NotInitialized: - writeInt(&buf, Int32(18)) - - - case .NotConnected: - writeInt(&buf, Int32(19)) - - - case let .SessionError(errorDetails): - writeInt(&buf, Int32(20)) - FfiConverterString.write(errorDetails, into: &buf) - - - case let .IoError(errorDetails): - writeInt(&buf, Int32(21)) - FfiConverterString.write(errorDetails, into: &buf) - } } } @@ -22170,126 +23540,109 @@ public struct FfiConverterTypeTrezorError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorError_lift(_ buf: RustBuffer) throws -> TrezorError { - return try FfiConverterTypeTrezorError.lift(buf) +public func FfiConverterTypeTxDirection_lift(_ buf: RustBuffer) throws -> TxDirection { + return try FfiConverterTypeTxDirection.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorError_lower(_ value: TrezorError) -> RustBuffer { - return FfiConverterTypeTrezorError.lower(value) +public func FfiConverterTypeTxDirection_lower(_ value: TxDirection) -> RustBuffer { + return FfiConverterTypeTxDirection.lower(value) } -extension TrezorError: Equatable, Hashable {} - -extension TrezorError: Codable {} +extension TxDirection: Equatable, Hashable {} +extension TxDirection: Codable {} -extension TrezorError: Foundation.LocalizedError { - public var errorDescription: String? { - String(reflecting: self) - } -} -// Note that we don't yet support `indirect` for enums. -// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -/** - * Script types for address derivation. - */ +public enum UrError: Swift.Error { -public enum TrezorScriptType { - /** - * P2PKH (legacy) - */ - case spendAddress - /** - * P2SH-P2WPKH (nested SegWit) - */ - case spendP2shWitness - /** - * P2WPKH (native SegWit) - */ - case spendWitness - /** - * P2TR (Taproot) - */ - case spendTaproot - /** - * P2SH multisig - */ - case spendMultisig - /** - * External/watch-only input (not signed by device) - */ - case external + + case InvalidUr(reason: String + ) + case TooLarge(reason: String + ) + case InvalidPayload(reason: String + ) + case InvalidPsbt(reason: String + ) + case InvalidPassportExport(reason: String + ) } -#if compiler(>=6) -extension TrezorScriptType: Sendable {} -#endif - #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorScriptType: FfiConverterRustBuffer { - typealias SwiftType = TrezorScriptType +public struct FfiConverterTypeUrError: FfiConverterRustBuffer { + typealias SwiftType = UrError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorScriptType { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UrError { let variant: Int32 = try readInt(&buf) switch variant { + - case 1: return .spendAddress - - case 2: return .spendP2shWitness - - case 3: return .spendWitness - - case 4: return .spendTaproot - - case 5: return .spendMultisig - - case 6: return .external + - default: throw UniffiInternalError.unexpectedEnumCase + case 1: return .InvalidUr( + reason: try FfiConverterString.read(from: &buf) + ) + case 2: return .TooLarge( + reason: try FfiConverterString.read(from: &buf) + ) + case 3: return .InvalidPayload( + reason: try FfiConverterString.read(from: &buf) + ) + case 4: return .InvalidPsbt( + reason: try FfiConverterString.read(from: &buf) + ) + case 5: return .InvalidPassportExport( + reason: try FfiConverterString.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: TrezorScriptType, into buf: inout [UInt8]) { + public static func write(_ value: UrError, into buf: inout [UInt8]) { switch value { + + + - case .spendAddress: + case let .InvalidUr(reason): writeInt(&buf, Int32(1)) + FfiConverterString.write(reason, into: &buf) + - - case .spendP2shWitness: + case let .TooLarge(reason): writeInt(&buf, Int32(2)) + FfiConverterString.write(reason, into: &buf) + - - case .spendWitness: + case let .InvalidPayload(reason): writeInt(&buf, Int32(3)) + FfiConverterString.write(reason, into: &buf) + - - case .spendTaproot: + case let .InvalidPsbt(reason): writeInt(&buf, Int32(4)) + FfiConverterString.write(reason, into: &buf) + - - case .spendMultisig: + case let .InvalidPassportExport(reason): writeInt(&buf, Int32(5)) - - - case .external: - writeInt(&buf, Int32(6)) - + FfiConverterString.write(reason, into: &buf) + } } } @@ -22298,23 +23651,30 @@ public struct FfiConverterTypeTrezorScriptType: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorScriptType_lift(_ buf: RustBuffer) throws -> TrezorScriptType { - return try FfiConverterTypeTrezorScriptType.lift(buf) +public func FfiConverterTypeUrError_lift(_ buf: RustBuffer) throws -> UrError { + return try FfiConverterTypeUrError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorScriptType_lower(_ value: TrezorScriptType) -> RustBuffer { - return FfiConverterTypeTrezorScriptType.lower(value) +public func FfiConverterTypeUrError_lower(_ value: UrError) -> RustBuffer { + return FfiConverterTypeUrError.lower(value) } -extension TrezorScriptType: Equatable, Hashable {} +extension UrError: Equatable, Hashable {} + +extension UrError: Codable {} + -extension TrezorScriptType: Codable {} +extension UrError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} @@ -22322,45 +23682,75 @@ extension TrezorScriptType: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. /** - * Structured transport error code returned by native callback operations. + * A completely decoded UR payload. */ -public enum TrezorTransportErrorCode { +public enum UrPayload { /** - * Device is busy and the caller should back off before retrying. + * The byte string wrapped by a `bytes` registry item. */ - case deviceBusy + case bytes(data: Data + ) + /** + * A `crypto-psbt` registry item, returned in Bitkit's usual base64 form. + */ + case cryptoPsbt(psbt: String + ) + /** + * An uninterpreted UR registry item. + */ + case cbor(urType: String, cbor: Data + ) } #if compiler(>=6) -extension TrezorTransportErrorCode: Sendable {} +extension UrPayload: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorTransportErrorCode: FfiConverterRustBuffer { - typealias SwiftType = TrezorTransportErrorCode +public struct FfiConverterTypeUrPayload: FfiConverterRustBuffer { + typealias SwiftType = UrPayload - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorTransportErrorCode { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UrPayload { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .deviceBusy + case 1: return .bytes(data: try FfiConverterData.read(from: &buf) + ) + + case 2: return .cryptoPsbt(psbt: try FfiConverterString.read(from: &buf) + ) + + case 3: return .cbor(urType: try FfiConverterString.read(from: &buf), cbor: try FfiConverterData.read(from: &buf) + ) default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: TrezorTransportErrorCode, into buf: inout [UInt8]) { + public static func write(_ value: UrPayload, into buf: inout [UInt8]) { switch value { - case .deviceBusy: + case let .bytes(data): writeInt(&buf, Int32(1)) + FfiConverterData.write(data, into: &buf) + + + case let .cryptoPsbt(psbt): + writeInt(&buf, Int32(2)) + FfiConverterString.write(psbt, into: &buf) + + case let .cbor(urType,cbor): + writeInt(&buf, Int32(3)) + FfiConverterString.write(urType, into: &buf) + FfiConverterData.write(cbor, into: &buf) + } } } @@ -22369,21 +23759,21 @@ public struct FfiConverterTypeTrezorTransportErrorCode: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorTransportErrorCode_lift(_ buf: RustBuffer) throws -> TrezorTransportErrorCode { - return try FfiConverterTypeTrezorTransportErrorCode.lift(buf) +public func FfiConverterTypeUrPayload_lift(_ buf: RustBuffer) throws -> UrPayload { + return try FfiConverterTypeUrPayload.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorTransportErrorCode_lower(_ value: TrezorTransportErrorCode) -> RustBuffer { - return FfiConverterTypeTrezorTransportErrorCode.lower(value) +public func FfiConverterTypeUrPayload_lower(_ value: UrPayload) -> RustBuffer { + return FfiConverterTypeUrPayload.lower(value) } -extension TrezorTransportErrorCode: Equatable, Hashable {} +extension UrPayload: Equatable, Hashable {} -extension TrezorTransportErrorCode: Codable {} +extension UrPayload: Codable {} @@ -22393,55 +23783,65 @@ extension TrezorTransportErrorCode: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. /** - * Transport type for Trezor devices. + * What the device did with a wake. */ -public enum TrezorTransportType { +public enum WakeAckOutcome { /** - * USB connection + * The wake was handled; the gateway cancels its fallback alert. */ - case usb + case handled /** - * Bluetooth connection + * The app has no handler for the topic; the fallback alert still fires. */ - case bluetooth + case noHandler + /** + * Handling failed; the fallback alert still fires. + */ + case failed } #if compiler(>=6) -extension TrezorTransportType: Sendable {} +extension WakeAckOutcome: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTrezorTransportType: FfiConverterRustBuffer { - typealias SwiftType = TrezorTransportType +public struct FfiConverterTypeWakeAckOutcome: FfiConverterRustBuffer { + typealias SwiftType = WakeAckOutcome - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TrezorTransportType { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WakeAckOutcome { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .usb + case 1: return .handled - case 2: return .bluetooth + case 2: return .noHandler + + case 3: return .failed default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: TrezorTransportType, into buf: inout [UInt8]) { + public static func write(_ value: WakeAckOutcome, into buf: inout [UInt8]) { switch value { - case .usb: + case .handled: writeInt(&buf, Int32(1)) - case .bluetooth: + case .noHandler: writeInt(&buf, Int32(2)) + + case .failed: + writeInt(&buf, Int32(3)) + } } } @@ -22450,21 +23850,21 @@ public struct FfiConverterTypeTrezorTransportType: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorTransportType_lift(_ buf: RustBuffer) throws -> TrezorTransportType { - return try FfiConverterTypeTrezorTransportType.lift(buf) +public func FfiConverterTypeWakeAckOutcome_lift(_ buf: RustBuffer) throws -> WakeAckOutcome { + return try FfiConverterTypeWakeAckOutcome.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTrezorTransportType_lower(_ value: TrezorTransportType) -> RustBuffer { - return FfiConverterTypeTrezorTransportType.lower(value) +public func FfiConverterTypeWakeAckOutcome_lower(_ value: WakeAckOutcome) -> RustBuffer { + return FfiConverterTypeWakeAckOutcome.lower(value) } -extension TrezorTransportType: Equatable, Hashable {} +extension WakeAckOutcome: Equatable, Hashable {} -extension TrezorTransportType: Codable {} +extension WakeAckOutcome: Codable {} @@ -22474,65 +23874,49 @@ extension TrezorTransportType: Codable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. /** - * Transaction direction from the wallet's perspective. + * APNs environment. FCM devices are always `Production`. */ -public enum TxDirection { +public enum WakeEnvironment { - /** - * Wallet sent funds to an external address - */ - case sent - /** - * Wallet received funds from an external source - */ - case received - /** - * Wallet sent funds to itself (e.g. consolidation, change-only) - */ - case selfTransfer + case production + case sandbox } #if compiler(>=6) -extension TxDirection: Sendable {} +extension WakeEnvironment: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeTxDirection: FfiConverterRustBuffer { - typealias SwiftType = TxDirection +public struct FfiConverterTypeWakeEnvironment: FfiConverterRustBuffer { + typealias SwiftType = WakeEnvironment - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TxDirection { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WakeEnvironment { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .sent - - case 2: return .received + case 1: return .production - case 3: return .selfTransfer + case 2: return .sandbox default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: TxDirection, into buf: inout [UInt8]) { + public static func write(_ value: WakeEnvironment, into buf: inout [UInt8]) { switch value { - case .sent: + case .production: writeInt(&buf, Int32(1)) - case .received: + case .sandbox: writeInt(&buf, Int32(2)) - - case .selfTransfer: - writeInt(&buf, Int32(3)) - } } } @@ -22541,21 +23925,21 @@ public struct FfiConverterTypeTxDirection: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTxDirection_lift(_ buf: RustBuffer) throws -> TxDirection { - return try FfiConverterTypeTxDirection.lift(buf) +public func FfiConverterTypeWakeEnvironment_lift(_ buf: RustBuffer) throws -> WakeEnvironment { + return try FfiConverterTypeWakeEnvironment.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeTxDirection_lower(_ value: TxDirection) -> RustBuffer { - return FfiConverterTypeTxDirection.lower(value) +public func FfiConverterTypeWakeEnvironment_lower(_ value: WakeEnvironment) -> RustBuffer { + return FfiConverterTypeWakeEnvironment.lower(value) } -extension TxDirection: Equatable, Hashable {} +extension WakeEnvironment: Equatable, Hashable {} -extension TxDirection: Codable {} +extension WakeEnvironment: Codable {} @@ -22563,19 +23947,27 @@ extension TxDirection: Codable {} -public enum UrError: Swift.Error { +/** + * Errors of the wake module. Messages never contain secrets, push tokens or + * decrypted plaintext. + */ +public enum WakeError: Swift.Error { - case InvalidUr(reason: String + case InvalidInput(reason: String ) - case TooLarge(reason: String + case InvalidKey(reason: String ) - case InvalidPayload(reason: String + case DecryptionFailed(reason: String ) - case InvalidPsbt(reason: String + case UnsupportedEnvelope(reason: String ) - case InvalidPassportExport(reason: String + case RequestFailed(reason: String + ) + case GatewayRejected(status: UInt16, code: String, message: String + ) + case InvalidResponse(reason: String ) } @@ -22583,29 +23975,37 @@ public enum UrError: Swift.Error { #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeUrError: FfiConverterRustBuffer { - typealias SwiftType = UrError +public struct FfiConverterTypeWakeError: FfiConverterRustBuffer { + typealias SwiftType = WakeError - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UrError { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WakeError { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .InvalidUr( + case 1: return .InvalidInput( reason: try FfiConverterString.read(from: &buf) ) - case 2: return .TooLarge( + case 2: return .InvalidKey( reason: try FfiConverterString.read(from: &buf) ) - case 3: return .InvalidPayload( + case 3: return .DecryptionFailed( reason: try FfiConverterString.read(from: &buf) ) - case 4: return .InvalidPsbt( + case 4: return .UnsupportedEnvelope( reason: try FfiConverterString.read(from: &buf) ) - case 5: return .InvalidPassportExport( + case 5: return .RequestFailed( + reason: try FfiConverterString.read(from: &buf) + ) + case 6: return .GatewayRejected( + status: try FfiConverterUInt16.read(from: &buf), + code: try FfiConverterString.read(from: &buf), + message: try FfiConverterString.read(from: &buf) + ) + case 7: return .InvalidResponse( reason: try FfiConverterString.read(from: &buf) ) @@ -22613,37 +24013,49 @@ public struct FfiConverterTypeUrError: FfiConverterRustBuffer { } } - public static func write(_ value: UrError, into buf: inout [UInt8]) { + public static func write(_ value: WakeError, into buf: inout [UInt8]) { switch value { - case let .InvalidUr(reason): + case let .InvalidInput(reason): writeInt(&buf, Int32(1)) FfiConverterString.write(reason, into: &buf) - case let .TooLarge(reason): + case let .InvalidKey(reason): writeInt(&buf, Int32(2)) FfiConverterString.write(reason, into: &buf) - case let .InvalidPayload(reason): + case let .DecryptionFailed(reason): writeInt(&buf, Int32(3)) FfiConverterString.write(reason, into: &buf) - case let .InvalidPsbt(reason): + case let .UnsupportedEnvelope(reason): writeInt(&buf, Int32(4)) FfiConverterString.write(reason, into: &buf) - case let .InvalidPassportExport(reason): + case let .RequestFailed(reason): writeInt(&buf, Int32(5)) FfiConverterString.write(reason, into: &buf) + + case let .GatewayRejected(status,code,message): + writeInt(&buf, Int32(6)) + FfiConverterUInt16.write(status, into: &buf) + FfiConverterString.write(code, into: &buf) + FfiConverterString.write(message, into: &buf) + + + case let .InvalidResponse(reason): + writeInt(&buf, Int32(7)) + FfiConverterString.write(reason, into: &buf) + } } } @@ -22652,26 +24064,26 @@ public struct FfiConverterTypeUrError: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeUrError_lift(_ buf: RustBuffer) throws -> UrError { - return try FfiConverterTypeUrError.lift(buf) +public func FfiConverterTypeWakeError_lift(_ buf: RustBuffer) throws -> WakeError { + return try FfiConverterTypeWakeError.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeUrError_lower(_ value: UrError) -> RustBuffer { - return FfiConverterTypeUrError.lower(value) +public func FfiConverterTypeWakeError_lower(_ value: WakeError) -> RustBuffer { + return FfiConverterTypeWakeError.lower(value) } -extension UrError: Equatable, Hashable {} +extension WakeError: Equatable, Hashable {} -extension UrError: Codable {} +extension WakeError: Codable {} -extension UrError: Foundation.LocalizedError { +extension WakeError: Foundation.LocalizedError { public var errorDescription: String? { String(reflecting: self) } @@ -22683,75 +24095,49 @@ extension UrError: Foundation.LocalizedError { // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. /** - * A completely decoded UR payload. + * Push transport of a device. */ -public enum UrPayload { +public enum WakePlatform { - /** - * The byte string wrapped by a `bytes` registry item. - */ - case bytes(data: Data - ) - /** - * A `crypto-psbt` registry item, returned in Bitkit's usual base64 form. - */ - case cryptoPsbt(psbt: String - ) - /** - * An uninterpreted UR registry item. - */ - case cbor(urType: String, cbor: Data - ) + case apns + case fcm } #if compiler(>=6) -extension UrPayload: Sendable {} +extension WakePlatform: Sendable {} #endif #if swift(>=5.8) @_documentation(visibility: private) #endif -public struct FfiConverterTypeUrPayload: FfiConverterRustBuffer { - typealias SwiftType = UrPayload +public struct FfiConverterTypeWakePlatform: FfiConverterRustBuffer { + typealias SwiftType = WakePlatform - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UrPayload { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WakePlatform { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .bytes(data: try FfiConverterData.read(from: &buf) - ) - - case 2: return .cryptoPsbt(psbt: try FfiConverterString.read(from: &buf) - ) + case 1: return .apns - case 3: return .cbor(urType: try FfiConverterString.read(from: &buf), cbor: try FfiConverterData.read(from: &buf) - ) + case 2: return .fcm default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: UrPayload, into buf: inout [UInt8]) { + public static func write(_ value: WakePlatform, into buf: inout [UInt8]) { switch value { - case let .bytes(data): + case .apns: writeInt(&buf, Int32(1)) - FfiConverterData.write(data, into: &buf) - - case let .cryptoPsbt(psbt): + + case .fcm: writeInt(&buf, Int32(2)) - FfiConverterString.write(psbt, into: &buf) - - case let .cbor(urType,cbor): - writeInt(&buf, Int32(3)) - FfiConverterString.write(urType, into: &buf) - FfiConverterData.write(cbor, into: &buf) - } } } @@ -22760,21 +24146,21 @@ public struct FfiConverterTypeUrPayload: FfiConverterRustBuffer { #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeUrPayload_lift(_ buf: RustBuffer) throws -> UrPayload { - return try FfiConverterTypeUrPayload.lift(buf) +public func FfiConverterTypeWakePlatform_lift(_ buf: RustBuffer) throws -> WakePlatform { + return try FfiConverterTypeWakePlatform.lift(buf) } #if swift(>=5.8) @_documentation(visibility: private) #endif -public func FfiConverterTypeUrPayload_lower(_ value: UrPayload) -> RustBuffer { - return FfiConverterTypeUrPayload.lower(value) +public func FfiConverterTypeWakePlatform_lower(_ value: WakePlatform) -> RustBuffer { + return FfiConverterTypeWakePlatform.lower(value) } -extension UrPayload: Equatable, Hashable {} +extension WakePlatform: Equatable, Hashable {} -extension UrPayload: Codable {} +extension WakePlatform: Codable {} @@ -25260,6 +26646,56 @@ fileprivate struct FfiConverterSequenceTypeTxOutput: FfiConverterRustBuffer { } } +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterSequenceTypeWakeIdentityProof: FfiConverterRustBuffer { + typealias SwiftType = [WakeIdentityProof] + + public static func write(_ value: [WakeIdentityProof], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterTypeWakeIdentityProof.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [WakeIdentityProof] { + let len: Int32 = try readInt(&buf) + var seq = [WakeIdentityProof]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterTypeWakeIdentityProof.read(from: &buf)) + } + return seq + } +} + +#if swift(>=5.8) +@_documentation(visibility: private) +#endif +fileprivate struct FfiConverterSequenceTypeWakeTopic: FfiConverterRustBuffer { + typealias SwiftType = [WakeTopic] + + public static func write(_ value: [WakeTopic], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterTypeWakeTopic.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [WakeTopic] { + let len: Int32 = try readInt(&buf) + var seq = [WakeTopic]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterTypeWakeTopic.read(from: &buf)) + } + return seq + } +} + #if swift(>=5.8) @_documentation(visibility: private) #endif @@ -28054,6 +29490,237 @@ public func validateMnemonic(mnemonicPhrase: String)throws {try rustCallWithEr ) } } +/** + * Acknowledge a wake by the id from its envelope. + */ +public func wakeAck(gatewayUrl: String, deviceSecret: String, wakeId: String, outcome: WakeAckOutcome)async throws { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_bitkitcore_fn_func_wake_ack(FfiConverterString.lower(gatewayUrl),FfiConverterString.lower(deviceSecret),FfiConverterString.lower(wakeId),FfiConverterTypeWakeAckOutcome_lower(outcome) + ) + }, + pollFunc: ffi_bitkitcore_rust_future_poll_void, + completeFunc: ffi_bitkitcore_rust_future_complete_void, + freeFunc: ffi_bitkitcore_rust_future_free_void, + liftFunc: { $0 }, + errorHandler: FfiConverterTypeWakeError_lift + ) +} +/** + * End presence early. + */ +public func wakeClearPresence(gatewayUrl: String, deviceSecret: String)async throws { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_bitkitcore_fn_func_wake_clear_presence(FfiConverterString.lower(gatewayUrl),FfiConverterString.lower(deviceSecret) + ) + }, + pollFunc: ffi_bitkitcore_rust_future_poll_void, + completeFunc: ffi_bitkitcore_rust_future_complete_void, + freeFunc: ffi_bitkitcore_rust_future_free_void, + liftFunc: { $0 }, + errorHandler: FfiConverterTypeWakeError_lift + ) +} +/** + * Find and decrypt the wake in a delivered push (the APNs payload or the FCM data map as JSON). + */ +public func wakeDecryptPush(secretKeyHex: String, pushJson: String)throws -> WakeEnvelope { + return try FfiConverterTypeWakeEnvelope_lift(try rustCallWithError(FfiConverterTypeWakeError_lift) { + uniffi_bitkitcore_fn_func_wake_decrypt_push( + FfiConverterString.lower(secretKeyHex), + FfiConverterString.lower(pushJson),$0 + ) +}) +} +/** + * Decrypt a legacy (v0) envelope from its four fields. + */ +public func wakeDecryptV0(secretKeyHex: String, cipher: String, iv: String, tag: String, publicKey: String)throws -> WakeEnvelope { + return try FfiConverterTypeWakeEnvelope_lift(try rustCallWithError(FfiConverterTypeWakeError_lift) { + uniffi_bitkitcore_fn_func_wake_decrypt_v0( + FfiConverterString.lower(secretKeyHex), + FfiConverterString.lower(cipher), + FfiConverterString.lower(iv), + FfiConverterString.lower(tag), + FfiConverterString.lower(publicKey),$0 + ) +}) +} +/** + * Decrypt a v1 container given as JSON. + */ +public func wakeDecryptV1(secretKeyHex: String, containerJson: String)throws -> WakeEnvelope { + return try FfiConverterTypeWakeEnvelope_lift(try rustCallWithError(FfiConverterTypeWakeError_lift) { + uniffi_bitkitcore_fn_func_wake_decrypt_v1( + FfiConverterString.lower(secretKeyHex), + FfiConverterString.lower(containerJson),$0 + ) +}) +} +/** + * Generate a device secret (`wkd_...`), the bearer credential of the device routes. + */ +public func wakeGenerateDeviceSecret()throws -> WakeDeviceSecret { + return try FfiConverterTypeWakeDeviceSecret_lift(try rustCallWithError(FfiConverterTypeWakeError_lift) { + uniffi_bitkitcore_fn_func_wake_generate_device_secret($0 + ) +}) +} +/** + * Generate an install id (22 base64url characters), stable for the life of the install. + */ +public func wakeGenerateInstallId() -> String { + return try! FfiConverterString.lift(try! rustCall() { + uniffi_bitkitcore_fn_func_wake_generate_install_id($0 + ) +}) +} +/** + * Generate the secp256k1 key pair wakes are encrypted to. + */ +public func wakeGenerateKeypair()throws -> WakeKeyPair { + return try FfiConverterTypeWakeKeyPair_lift(try rustCallWithError(FfiConverterTypeWakeError_lift) { + uniffi_bitkitcore_fn_func_wake_generate_keypair($0 + ) +}) +} +/** + * List the topics devices can subscribe to. + */ +public func wakeListTopics(gatewayUrl: String)async throws -> [WakeTopic] { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_bitkitcore_fn_func_wake_list_topics(FfiConverterString.lower(gatewayUrl) + ) + }, + pollFunc: ffi_bitkitcore_rust_future_poll_rust_buffer, + completeFunc: ffi_bitkitcore_rust_future_complete_rust_buffer, + freeFunc: ffi_bitkitcore_rust_future_free_rust_buffer, + liftFunc: FfiConverterSequenceTypeWakeTopic.lift, + errorHandler: FfiConverterTypeWakeError_lift + ) +} +/** + * Validate a registration and build the message every identity signs. + * `timestamp` defaults to now. + */ +public func wakePrepareRegistration(audience: String, app: String, installId: String, platform: WakePlatform, environment: WakeEnvironment, pushToken: String, encryptionPublicKey: String, secretSha256: String, identities: [String], topics: [String], timestamp: UInt64?)throws -> WakeRegistrationRequest { + return try FfiConverterTypeWakeRegistrationRequest_lift(try rustCallWithError(FfiConverterTypeWakeError_lift) { + uniffi_bitkitcore_fn_func_wake_prepare_registration( + FfiConverterString.lower(audience), + FfiConverterString.lower(app), + FfiConverterString.lower(installId), + FfiConverterTypeWakePlatform_lower(platform), + FfiConverterTypeWakeEnvironment_lower(environment), + FfiConverterString.lower(pushToken), + FfiConverterString.lower(encryptionPublicKey), + FfiConverterString.lower(secretSha256), + FfiConverterSequenceString.lower(identities), + FfiConverterSequenceString.lower(topics), + FfiConverterOptionUInt64.lower(timestamp),$0 + ) +}) +} +/** + * Register the device, or update its registration, with one proof per identity. + */ +public func wakeRegister(gatewayUrl: String, request: WakeRegistrationRequest, proofs: [WakeIdentityProof])async throws -> WakeRegistration { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_bitkitcore_fn_func_wake_register(FfiConverterString.lower(gatewayUrl),FfiConverterTypeWakeRegistrationRequest_lower(request),FfiConverterSequenceTypeWakeIdentityProof.lower(proofs) + ) + }, + pollFunc: ffi_bitkitcore_rust_future_poll_rust_buffer, + completeFunc: ffi_bitkitcore_rust_future_complete_rust_buffer, + freeFunc: ffi_bitkitcore_rust_future_free_rust_buffer, + liftFunc: FfiConverterTypeWakeRegistration_lift, + errorHandler: FfiConverterTypeWakeError_lift + ) +} +/** + * Read the gateway's audience, clock and version. + */ +public func wakeServerInfo(gatewayUrl: String)async throws -> WakeServerInfo { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_bitkitcore_fn_func_wake_server_info(FfiConverterString.lower(gatewayUrl) + ) + }, + pollFunc: ffi_bitkitcore_rust_future_poll_rust_buffer, + completeFunc: ffi_bitkitcore_rust_future_complete_rust_buffer, + freeFunc: ffi_bitkitcore_rust_future_free_rust_buffer, + liftFunc: FfiConverterTypeWakeServerInfo_lift, + errorHandler: FfiConverterTypeWakeError_lift + ) +} +/** + * Mark the app as in use for `ttl_secs` (clamped to 15..=300). Returns the expiry, unix seconds. + */ +public func wakeSetPresence(gatewayUrl: String, deviceSecret: String, ttlSecs: UInt32)async throws -> UInt64 { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_bitkitcore_fn_func_wake_set_presence(FfiConverterString.lower(gatewayUrl),FfiConverterString.lower(deviceSecret),FfiConverterUInt32.lower(ttlSecs) + ) + }, + pollFunc: ffi_bitkitcore_rust_future_poll_u64, + completeFunc: ffi_bitkitcore_rust_future_complete_u64, + freeFunc: ffi_bitkitcore_rust_future_free_u64, + liftFunc: FfiConverterUInt64.lift, + errorHandler: FfiConverterTypeWakeError_lift + ) +} +/** + * Replace the device's topics. Returns the stored topics; unknown names are dropped. + */ +public func wakeSetTopics(gatewayUrl: String, deviceSecret: String, topics: [String])async throws -> [String] { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_bitkitcore_fn_func_wake_set_topics(FfiConverterString.lower(gatewayUrl),FfiConverterString.lower(deviceSecret),FfiConverterSequenceString.lower(topics) + ) + }, + pollFunc: ffi_bitkitcore_rust_future_poll_rust_buffer, + completeFunc: ffi_bitkitcore_rust_future_complete_rust_buffer, + freeFunc: ffi_bitkitcore_rust_future_free_rust_buffer, + liftFunc: FfiConverterSequenceString.lift, + errorHandler: FfiConverterTypeWakeError_lift + ) +} +/** + * Sign a registration message with a pubky secret key (hex). Returns 128 hex characters. + */ +public func wakeSignPubkyProof(secretKeyHex: String, message: String)throws -> String { + return try FfiConverterString.lift(try rustCallWithError(FfiConverterTypeWakeError_lift) { + uniffi_bitkitcore_fn_func_wake_sign_pubky_proof( + FfiConverterString.lower(secretKeyHex), + FfiConverterString.lower(message),$0 + ) +}) +} +/** + * Unregister the device and revoke its secret. + */ +public func wakeUnregister(gatewayUrl: String, deviceSecret: String)async throws { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_bitkitcore_fn_func_wake_unregister(FfiConverterString.lower(gatewayUrl),FfiConverterString.lower(deviceSecret) + ) + }, + pollFunc: ffi_bitkitcore_rust_future_poll_void, + completeFunc: ffi_bitkitcore_rust_future_complete_void, + freeFunc: ffi_bitkitcore_rust_future_free_void, + liftFunc: { $0 }, + errorHandler: FfiConverterTypeWakeError_lift + ) +} public func wipeAllClosedChannels()throws {try rustCallWithError(FfiConverterTypeActivityError_lift) { uniffi_bitkitcore_fn_func_wipe_all_closed_channels($0 ) @@ -28697,6 +30364,54 @@ private let initializationResult: InitializationResult = { if (uniffi_bitkitcore_checksum_func_validate_mnemonic() != 31005) { return InitializationResult.apiChecksumMismatch } + if (uniffi_bitkitcore_checksum_func_wake_ack() != 21924) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_func_wake_clear_presence() != 38792) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_func_wake_decrypt_push() != 65285) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_func_wake_decrypt_v0() != 47601) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_func_wake_decrypt_v1() != 33747) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_func_wake_generate_device_secret() != 48167) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_func_wake_generate_install_id() != 8335) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_func_wake_generate_keypair() != 41918) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_func_wake_list_topics() != 5582) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_func_wake_prepare_registration() != 32376) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_func_wake_register() != 32128) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_func_wake_server_info() != 21220) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_func_wake_set_presence() != 25525) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_func_wake_set_topics() != 27232) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_func_wake_sign_pubky_proof() != 15089) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_bitkitcore_checksum_func_wake_unregister() != 56421) { + return InitializationResult.apiChecksumMismatch + } if (uniffi_bitkitcore_checksum_func_wipe_all_closed_channels() != 41511) { return InitializationResult.apiChecksumMismatch } diff --git a/bindings/ios/bitkitcoreFFI.h b/bindings/ios/bitkitcoreFFI.h index f6e06a7..bbd5f5c 100644 --- a/bindings/ios/bitkitcoreFFI.h +++ b/bindings/ios/bitkitcoreFFI.h @@ -1704,6 +1704,89 @@ RustBuffer uniffi_bitkitcore_fn_func_validate_bitcoin_address(RustBuffer address void uniffi_bitkitcore_fn_func_validate_mnemonic(RustBuffer mnemonic_phrase, RustCallStatus *_Nonnull out_status ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_ACK +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_ACK +uint64_t uniffi_bitkitcore_fn_func_wake_ack(RustBuffer gateway_url, RustBuffer device_secret, RustBuffer wake_id, RustBuffer outcome +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_CLEAR_PRESENCE +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_CLEAR_PRESENCE +uint64_t uniffi_bitkitcore_fn_func_wake_clear_presence(RustBuffer gateway_url, RustBuffer device_secret +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_DECRYPT_PUSH +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_DECRYPT_PUSH +RustBuffer uniffi_bitkitcore_fn_func_wake_decrypt_push(RustBuffer secret_key_hex, RustBuffer push_json, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_DECRYPT_V0 +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_DECRYPT_V0 +RustBuffer uniffi_bitkitcore_fn_func_wake_decrypt_v0(RustBuffer secret_key_hex, RustBuffer cipher, RustBuffer iv, RustBuffer tag, RustBuffer public_key, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_DECRYPT_V1 +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_DECRYPT_V1 +RustBuffer uniffi_bitkitcore_fn_func_wake_decrypt_v1(RustBuffer secret_key_hex, RustBuffer container_json, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_GENERATE_DEVICE_SECRET +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_GENERATE_DEVICE_SECRET +RustBuffer uniffi_bitkitcore_fn_func_wake_generate_device_secret(RustCallStatus *_Nonnull out_status + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_GENERATE_INSTALL_ID +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_GENERATE_INSTALL_ID +RustBuffer uniffi_bitkitcore_fn_func_wake_generate_install_id(RustCallStatus *_Nonnull out_status + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_GENERATE_KEYPAIR +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_GENERATE_KEYPAIR +RustBuffer uniffi_bitkitcore_fn_func_wake_generate_keypair(RustCallStatus *_Nonnull out_status + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_LIST_TOPICS +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_LIST_TOPICS +uint64_t uniffi_bitkitcore_fn_func_wake_list_topics(RustBuffer gateway_url +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_PREPARE_REGISTRATION +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_PREPARE_REGISTRATION +RustBuffer uniffi_bitkitcore_fn_func_wake_prepare_registration(RustBuffer audience, RustBuffer app, RustBuffer install_id, RustBuffer platform, RustBuffer environment, RustBuffer push_token, RustBuffer encryption_public_key, RustBuffer secret_sha256, RustBuffer identities, RustBuffer topics, RustBuffer timestamp, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_REGISTER +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_REGISTER +uint64_t uniffi_bitkitcore_fn_func_wake_register(RustBuffer gateway_url, RustBuffer request, RustBuffer proofs +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_SERVER_INFO +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_SERVER_INFO +uint64_t uniffi_bitkitcore_fn_func_wake_server_info(RustBuffer gateway_url +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_SET_PRESENCE +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_SET_PRESENCE +uint64_t uniffi_bitkitcore_fn_func_wake_set_presence(RustBuffer gateway_url, RustBuffer device_secret, uint32_t ttl_secs +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_SET_TOPICS +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_SET_TOPICS +uint64_t uniffi_bitkitcore_fn_func_wake_set_topics(RustBuffer gateway_url, RustBuffer device_secret, RustBuffer topics +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_SIGN_PUBKY_PROOF +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_SIGN_PUBKY_PROOF +RustBuffer uniffi_bitkitcore_fn_func_wake_sign_pubky_proof(RustBuffer secret_key_hex, RustBuffer message, RustCallStatus *_Nonnull out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_UNREGISTER +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WAKE_UNREGISTER +uint64_t uniffi_bitkitcore_fn_func_wake_unregister(RustBuffer gateway_url, RustBuffer device_secret +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WIPE_ALL_CLOSED_CHANNELS #define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_FN_FUNC_WIPE_ALL_CLOSED_CHANNELS void uniffi_bitkitcore_fn_func_wipe_all_closed_channels(RustCallStatus *_Nonnull out_status @@ -3206,6 +3289,102 @@ uint16_t uniffi_bitkitcore_checksum_func_validate_bitcoin_address(void #define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_VALIDATE_MNEMONIC uint16_t uniffi_bitkitcore_checksum_func_validate_mnemonic(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_ACK +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_ACK +uint16_t uniffi_bitkitcore_checksum_func_wake_ack(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_CLEAR_PRESENCE +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_CLEAR_PRESENCE +uint16_t uniffi_bitkitcore_checksum_func_wake_clear_presence(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_DECRYPT_PUSH +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_DECRYPT_PUSH +uint16_t uniffi_bitkitcore_checksum_func_wake_decrypt_push(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_DECRYPT_V0 +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_DECRYPT_V0 +uint16_t uniffi_bitkitcore_checksum_func_wake_decrypt_v0(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_DECRYPT_V1 +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_DECRYPT_V1 +uint16_t uniffi_bitkitcore_checksum_func_wake_decrypt_v1(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_GENERATE_DEVICE_SECRET +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_GENERATE_DEVICE_SECRET +uint16_t uniffi_bitkitcore_checksum_func_wake_generate_device_secret(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_GENERATE_INSTALL_ID +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_GENERATE_INSTALL_ID +uint16_t uniffi_bitkitcore_checksum_func_wake_generate_install_id(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_GENERATE_KEYPAIR +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_GENERATE_KEYPAIR +uint16_t uniffi_bitkitcore_checksum_func_wake_generate_keypair(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_LIST_TOPICS +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_LIST_TOPICS +uint16_t uniffi_bitkitcore_checksum_func_wake_list_topics(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_PREPARE_REGISTRATION +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_PREPARE_REGISTRATION +uint16_t uniffi_bitkitcore_checksum_func_wake_prepare_registration(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_REGISTER +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_REGISTER +uint16_t uniffi_bitkitcore_checksum_func_wake_register(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_SERVER_INFO +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_SERVER_INFO +uint16_t uniffi_bitkitcore_checksum_func_wake_server_info(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_SET_PRESENCE +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_SET_PRESENCE +uint16_t uniffi_bitkitcore_checksum_func_wake_set_presence(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_SET_TOPICS +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_SET_TOPICS +uint16_t uniffi_bitkitcore_checksum_func_wake_set_topics(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_SIGN_PUBKY_PROOF +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_SIGN_PUBKY_PROOF +uint16_t uniffi_bitkitcore_checksum_func_wake_sign_pubky_proof(void + +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_UNREGISTER +#define UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WAKE_UNREGISTER +uint16_t uniffi_bitkitcore_checksum_func_wake_unregister(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_BITKITCORE_CHECKSUM_FUNC_WIPE_ALL_CLOSED_CHANNELS From 6f92e8d43568fce3cba0e5cea944fb6f4d32efb2 Mon Sep 17 00:00:00 2001 From: coreyphillips Date: Tue, 29 Sep 2026 15:51:17 -0400 Subject: [PATCH 3/4] test(wake): sync the push vectors with the gateway's derived collapse ids --- src/modules/wake/README.md | 2 +- src/modules/wake/test_vectors/pushes.json | 25 ++++++++++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/modules/wake/README.md b/src/modules/wake/README.md index dea6937..1a45092 100644 --- a/src/modules/wake/README.md +++ b/src/modules/wake/README.md @@ -126,7 +126,7 @@ Error messages never contain keys, secrets or decrypted plaintext. `test_vectors/*.json` are copied byte for byte from `crates/wake-proto/vectors` of the wake repository at commit -`6037343ad39603ce436e2a551cac9db877ede9c6`. The tests assert them exactly: +`782f66510c505b0331e6299e96c1233d3c263484`. The tests assert them exactly: the v0 server and client envelopes with their shared points and keys, the v1 envelope, the registration preimage with both proofs, the legacy signature (used to check the test signer), and every push shape in `pushes.json`. diff --git a/src/modules/wake/test_vectors/pushes.json b/src/modules/wake/test_vectors/pushes.json index b2799e0..3db3744 100644 --- a/src/modules/wake/test_vectors/pushes.json +++ b/src/modules/wake/test_vectors/pushes.json @@ -1,6 +1,7 @@ { - "description": "Every provider request shape, byte-exact. APNs headers are listed in order; FCM bodies are POSTed to {fcm.base_url}/v1/projects/{project}/messages:send. Cases with device_secret_key decrypt to plaintext. inputs.collapse_key is the producer's collapse_key; topics with collapse = \"topic\" (every peer topic) pass none, so their background and FCM v1 pushes carry no collapse id.", - "collapse_rule": "collapse(x) = x when it is 1 to 64 visible ASCII characters (0x21 to 0x7e), else the 64 hex characters of sha256(x)", + "description": "Every provider request shape, byte-exact. APNs headers are listed in order; FCM bodies are POSTed to {fcm.base_url}/v1/projects/{project}/messages:send. Cases with device_secret_key decrypt to plaintext. inputs.collapse_key is the producer's collapse_key, which the gateway keeps to itself: pushes carry inputs.collapse_id, derived from it with collapse_secret. Topics with collapse = \"topic\" (every peer topic) pass none, so their background and FCM v1 pushes carry no collapse id.", + "collapse_rule": "collapse_id = the 64 lowercase hex characters of HMAC-SHA256(collapse_secret, identity + \"|\" + topic + \"|\" + collapse_key); the apns-collapse-id, FCM collapse_key and fallback tag are collapse(collapse_id), or collapse(wake id) where one is always sent; collapse(x) = x when it is 1 to 64 visible ASCII characters (0x21 to 0x7e), else the 64 hex characters of sha256(x)", + "collapse_secret": "7777777777777777777777777777777777777777777777777777777777777777", "cases": [ { "name": "apns_v0", @@ -47,7 +48,9 @@ "expiration": 1790000060, "wake_id": "01a0c450-6c00-7000-8000-000000000001", "topic": "blocktank.incomingHtlc", + "identity": "ln:023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b1", "collapse_key": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "collapse_id": "01708c8803f4e752df60858c2b4fae1c7a5a77df1be560dd5658e10c0b194085", "container": "{\"v\":1,\"cipher\":\"bXIadw8XhVApCI59oieKY3P/h33PZz6NSdVFABqcg9CSgNhH5L+odPp6fRlxsbx/A37B0ZTxkuZgONflwXB4AyprdoQGEuDEbM8NSRT1j9uAzrfBR098QxyA5eA4nsKbginEF+oPHY97X0yzHZwxV7qd69X4SKve9PaXn4li383A1ZTqlcXJhtAWA7U5YHiFFrbiHAoQgUr7D01/z0vPKtHvryG+GD0rtFnrIzNwrwt6MH9hGO8gYHp1xPRO0bs432EnkOUO7rRv0JwD6q1VQ+GJrtEp/n8p5tW3QzOd8cexBUGoQM+Q6HlNHFRROjBM1fTIsaZj\",\"iv\":\"000102030405060708090a0b\",\"tag\":\"4521e07642272d9b8aed5157c331ad7a\",\"publicKey\":\"035ab4689e400a4a160cf01cd44730845a54768df8547dcdf073d964f109f18c30\"}" }, "headers": [ @@ -73,7 +76,7 @@ ], [ "apns-collapse-id", - "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + "01708c8803f4e752df60858c2b4fae1c7a5a77df1be560dd5658e10c0b194085" ] ], "body": "{\"aps\":{\"content-available\":1},\"wake\":{\"v\":1,\"cipher\":\"bXIadw8XhVApCI59oieKY3P/h33PZz6NSdVFABqcg9CSgNhH5L+odPp6fRlxsbx/A37B0ZTxkuZgONflwXB4AyprdoQGEuDEbM8NSRT1j9uAzrfBR098QxyA5eA4nsKbginEF+oPHY97X0yzHZwxV7qd69X4SKve9PaXn4li383A1ZTqlcXJhtAWA7U5YHiFFrbiHAoQgUr7D01/z0vPKtHvryG+GD0rtFnrIzNwrwt6MH9hGO8gYHp1xPRO0bs432EnkOUO7rRv0JwD6q1VQ+GJrtEp/n8p5tW3QzOd8cexBUGoQM+Q6HlNHFRROjBM1fTIsaZj\",\"iv\":\"000102030405060708090a0b\",\"tag\":\"4521e07642272d9b8aed5157c331ad7a\",\"publicKey\":\"035ab4689e400a4a160cf01cd44730845a54768df8547dcdf073d964f109f18c30\"}}", @@ -175,7 +178,9 @@ "expiration": 1790000060, "wake_id": "01a0c450-6c00-7000-8000-000000000003", "topic": "blocktank.incomingHtlc", + "identity": "ln:023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b1", "collapse_key": "incomingHtlc:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "collapse_id": "572ecbf3b26e4181d933ff3c1593690288b85dd0f872af6897d29742d3b20dec", "title": "Incoming Payment", "body": "Open Bitkit now to receive your payment", "interruption_level": "time-sensitive", @@ -204,7 +209,7 @@ ], [ "apns-collapse-id", - "abda80501930fdfd4ae15e1b29c5df0a047946cf41cfd257312306e4a9d8d8a5" + "572ecbf3b26e4181d933ff3c1593690288b85dd0f872af6897d29742d3b20dec" ] ], "body": "{\"aps\":{\"alert\":{\"title\":\"Incoming Payment\",\"body\":\"Open Bitkit now to receive your payment\"},\"sound\":\"default\",\"mutable-content\":1,\"interruption-level\":\"time-sensitive\",\"thread-id\":\"blocktank.incomingHtlc\"},\"wake\":{\"v\":1,\"cipher\":\"N8vLNLPyiSvIytusmyeLOes0HH5PiRqiWP1PaZMnU7tzjAXeWkjOfPTHmd81o1zXjtqfj08qV6+nhebRqoFKATvSzGoUeKnhp8AGmAgNBjNklfgJx8GkKjFyaj00975BlBI4s3kfElILeqjis+6KhBHx/gcn+YHIpwyuA91bHEY5C07Nl0hy6jTGesU3h5pxBrSq+JvWz8EBKsVCAWNFAxW/FfRZWUWyFgtjwdl6b1gTM2MLQZ4lqTbftYTJQ7R++g4d/J9NdoDXHvF9gf95nI73NYlEqMFKUUmvMVy8l6eHOP3WFx9e4z/MAlY9O6id2MZrXugm8HoqPQ==\",\"iv\":\"000102030405060708090a0b\",\"tag\":\"08e2e6ca1df355ca8907d3ba21245464\",\"publicKey\":\"03652d1b5efa70cb3b83a53cdf536172d205001f0dd54ca766a6a4ef766a48e6fb\"}}", @@ -221,7 +226,9 @@ "expiration": 1790000060, "wake_id": "01a0c450-6c00-7000-8000-000000000001", "topic": "blocktank.incomingHtlc", + "identity": "ln:023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b1", "collapse_key": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "collapse_id": "01708c8803f4e752df60858c2b4fae1c7a5a77df1be560dd5658e10c0b194085", "title": "Incoming Payment", "body": "Open Bitkit now to receive your payment", "interruption_level": "time-sensitive" @@ -249,7 +256,7 @@ ], [ "apns-collapse-id", - "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + "01708c8803f4e752df60858c2b4fae1c7a5a77df1be560dd5658e10c0b194085" ] ], "body": "{\"aps\":{\"alert\":{\"title\":\"Incoming Payment\",\"body\":\"Open Bitkit now to receive your payment\"},\"sound\":\"default\",\"interruption-level\":\"time-sensitive\",\"thread-id\":\"blocktank.incomingHtlc\"},\"wake_fallback\":{\"v\":1,\"id\":\"01a0c450-6c00-7000-8000-000000000001\",\"topic\":\"blocktank.incomingHtlc\"}}", @@ -277,10 +284,12 @@ "priority": "high", "wake_id": "01a0c450-6c00-7000-8000-000000000001", "topic": "blocktank.incomingHtlc", + "identity": "ln:023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b1", "collapse_key": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "collapse_id": "01708c8803f4e752df60858c2b4fae1c7a5a77df1be560dd5658e10c0b194085", "container": "{\"v\":1,\"cipher\":\"bXIadw8XhVApCI59oieKY3P/h33PZz6NSdVFABqcg9CSgNhH5L+odPp6fRlxsbx/A37B0ZTxkuZgONflwXB4AyprdoQGEuDEbM8NSRT1j9uAzrfBR098QxyA5eA4nsKbginEF+oPHY97X0yzHZwxV7qd69X4SKve9PaXn4li383A1ZTqlcXJhtAWA7U5YHiFFrbiHAoQgUr7D01/z0vPKtHvryG+GD0rtFnrIzNwrwt6MH9hGO8gYHp1xPRO0bs432EnkOUO7rRv0JwD6q1VQ+GJrtEp/n8p5tW3QzOd8cexBUGoQM+Q6HlNHFRROjBM1fTIsaZj\",\"iv\":\"000102030405060708090a0b\",\"tag\":\"4521e07642272d9b8aed5157c331ad7a\",\"publicKey\":\"035ab4689e400a4a160cf01cd44730845a54768df8547dcdf073d964f109f18c30\"}" }, - "body": "{\"message\":{\"token\":\"e5Hj9fKqTmW-vector:APA91bG0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\",\"android\":{\"priority\":\"high\",\"ttl\":\"60s\",\"collapse_key\":\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\"},\"data\":{\"wake\":\"{\\\"v\\\":1,\\\"cipher\\\":\\\"bXIadw8XhVApCI59oieKY3P/h33PZz6NSdVFABqcg9CSgNhH5L+odPp6fRlxsbx/A37B0ZTxkuZgONflwXB4AyprdoQGEuDEbM8NSRT1j9uAzrfBR098QxyA5eA4nsKbginEF+oPHY97X0yzHZwxV7qd69X4SKve9PaXn4li383A1ZTqlcXJhtAWA7U5YHiFFrbiHAoQgUr7D01/z0vPKtHvryG+GD0rtFnrIzNwrwt6MH9hGO8gYHp1xPRO0bs432EnkOUO7rRv0JwD6q1VQ+GJrtEp/n8p5tW3QzOd8cexBUGoQM+Q6HlNHFRROjBM1fTIsaZj\\\",\\\"iv\\\":\\\"000102030405060708090a0b\\\",\\\"tag\\\":\\\"4521e07642272d9b8aed5157c331ad7a\\\",\\\"publicKey\\\":\\\"035ab4689e400a4a160cf01cd44730845a54768df8547dcdf073d964f109f18c30\\\"}\"}}}", + "body": "{\"message\":{\"token\":\"e5Hj9fKqTmW-vector:APA91bG0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\",\"android\":{\"priority\":\"high\",\"ttl\":\"60s\",\"collapse_key\":\"01708c8803f4e752df60858c2b4fae1c7a5a77df1be560dd5658e10c0b194085\"},\"data\":{\"wake\":\"{\\\"v\\\":1,\\\"cipher\\\":\\\"bXIadw8XhVApCI59oieKY3P/h33PZz6NSdVFABqcg9CSgNhH5L+odPp6fRlxsbx/A37B0ZTxkuZgONflwXB4AyprdoQGEuDEbM8NSRT1j9uAzrfBR098QxyA5eA4nsKbginEF+oPHY97X0yzHZwxV7qd69X4SKve9PaXn4li383A1ZTqlcXJhtAWA7U5YHiFFrbiHAoQgUr7D01/z0vPKtHvryG+GD0rtFnrIzNwrwt6MH9hGO8gYHp1xPRO0bs432EnkOUO7rRv0JwD6q1VQ+GJrtEp/n8p5tW3QzOd8cexBUGoQM+Q6HlNHFRROjBM1fTIsaZj\\\",\\\"iv\\\":\\\"000102030405060708090a0b\\\",\\\"tag\\\":\\\"4521e07642272d9b8aed5157c331ad7a\\\",\\\"publicKey\\\":\\\"035ab4689e400a4a160cf01cd44730845a54768df8547dcdf073d964f109f18c30\\\"}\"}}}", "body_len": 808, "device_secret_key": "5555555555555555555555555555555555555555555555555555555555555555", "plaintext": "{\"v\":1,\"id\":\"01a0c450-6c00-7000-8000-000000000001\",\"topic\":\"blocktank.incomingHtlc\",\"urgency\":\"background\",\"deadline\":1790000060,\"payload\":{\"lspId\":\"blocktank-lsp\",\"paymentHash\":\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\"}}" @@ -310,12 +319,14 @@ "ttl_secs": 45, "wake_id": "01a0c450-6c00-7000-8000-000000000001", "topic": "blocktank.incomingHtlc", + "identity": "ln:023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b1", "collapse_key": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "collapse_id": "01708c8803f4e752df60858c2b4fae1c7a5a77df1be560dd5658e10c0b194085", "title": "Incoming Payment", "body": "Open Bitkit now to receive your payment", "channel_id": "wake" }, - "body": "{\"message\":{\"token\":\"e5Hj9fKqTmW-vector:APA91bG0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\",\"android\":{\"priority\":\"high\",\"ttl\":\"45s\",\"notification\":{\"title\":\"Incoming Payment\",\"body\":\"Open Bitkit now to receive your payment\",\"channel_id\":\"wake\",\"tag\":\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\"}},\"data\":{\"wake_fallback\":\"{\\\"v\\\":1,\\\"id\\\":\\\"01a0c450-6c00-7000-8000-000000000001\\\",\\\"topic\\\":\\\"blocktank.incomingHtlc\\\"}\"}}}", + "body": "{\"message\":{\"token\":\"e5Hj9fKqTmW-vector:APA91bG0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\",\"android\":{\"priority\":\"high\",\"ttl\":\"45s\",\"notification\":{\"title\":\"Incoming Payment\",\"body\":\"Open Bitkit now to receive your payment\",\"channel_id\":\"wake\",\"tag\":\"01708c8803f4e752df60858c2b4fae1c7a5a77df1be560dd5658e10c0b194085\"}},\"data\":{\"wake_fallback\":\"{\\\"v\\\":1,\\\"id\\\":\\\"01a0c450-6c00-7000-8000-000000000001\\\",\\\"topic\\\":\\\"blocktank.incomingHtlc\\\"}\"}}}", "body_len": 496 } ] From 9a07a29b2142cf05994cf6b6010bf39e1a5437f9 Mon Sep 17 00:00:00 2001 From: coreyphillips Date: Wed, 30 Sep 2026 14:23:26 -0400 Subject: [PATCH 4/4] fix(wake): rename the GatewayRejected message field to detail Kotlin exceptions already have a message property, so a field of that name in an error variant breaks the Android bindings build. --- bindings/ios/bitkitcore.swift | 8 ++++---- src/modules/wake/client.rs | 4 ++-- src/modules/wake/errors.rs | 4 ++-- src/modules/wake/tests.rs | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bindings/ios/bitkitcore.swift b/bindings/ios/bitkitcore.swift index ee8e9f9..b73529e 100644 --- a/bindings/ios/bitkitcore.swift +++ b/bindings/ios/bitkitcore.swift @@ -23965,7 +23965,7 @@ public enum WakeError: Swift.Error { ) case RequestFailed(reason: String ) - case GatewayRejected(status: UInt16, code: String, message: String + case GatewayRejected(status: UInt16, code: String, detail: String ) case InvalidResponse(reason: String ) @@ -24003,7 +24003,7 @@ public struct FfiConverterTypeWakeError: FfiConverterRustBuffer { case 6: return .GatewayRejected( status: try FfiConverterUInt16.read(from: &buf), code: try FfiConverterString.read(from: &buf), - message: try FfiConverterString.read(from: &buf) + detail: try FfiConverterString.read(from: &buf) ) case 7: return .InvalidResponse( reason: try FfiConverterString.read(from: &buf) @@ -24045,11 +24045,11 @@ public struct FfiConverterTypeWakeError: FfiConverterRustBuffer { FfiConverterString.write(reason, into: &buf) - case let .GatewayRejected(status,code,message): + case let .GatewayRejected(status,code,detail): writeInt(&buf, Int32(6)) FfiConverterUInt16.write(status, into: &buf) FfiConverterString.write(code, into: &buf) - FfiConverterString.write(message, into: &buf) + FfiConverterString.write(detail, into: &buf) case let .InvalidResponse(reason): diff --git a/src/modules/wake/client.rs b/src/modules/wake/client.rs index 14116bb..a512278 100644 --- a/src/modules/wake/client.rs +++ b/src/modules/wake/client.rs @@ -342,12 +342,12 @@ fn rejection(response: &HttpResponse) -> WakeError { Ok(body) => WakeError::GatewayRejected { status: response.status, code: body.error, - message: body.message, + detail: body.message, }, Err(_) => WakeError::GatewayRejected { status: response.status, code: "unknown".to_string(), - message: "the response carries no wake error body".to_string(), + detail: "the response carries no wake error body".to_string(), }, } } diff --git a/src/modules/wake/errors.rs b/src/modules/wake/errors.rs index 90d74de..4bdde0e 100644 --- a/src/modules/wake/errors.rs +++ b/src/modules/wake/errors.rs @@ -18,11 +18,11 @@ pub enum WakeError { #[error("Request failed: {reason}")] RequestFailed { reason: String }, - #[error("Gateway rejected the request with HTTP {status} ({code}): {message}")] + #[error("Gateway rejected the request with HTTP {status} ({code}): {detail}")] GatewayRejected { status: u16, code: String, - message: String, + detail: String, }, #[error("Invalid response: {reason}")] diff --git a/src/modules/wake/tests.rs b/src/modules/wake/tests.rs index 232e5f3..1fd9827 100644 --- a/src/modules/wake/tests.rs +++ b/src/modules/wake/tests.rs @@ -1412,11 +1412,11 @@ fn response(status: u16, body: &str) -> HttpResponse { } } -fn rejected(status: u16, code: &str, message: &str) -> WakeError { +fn rejected(status: u16, code: &str, detail: &str) -> WakeError { WakeError::GatewayRejected { status, code: code.to_string(), - message: message.to_string(), + detail: detail.to_string(), } }