From e88281b7b6f7f7d9e67a698a44ebd17863330319 Mon Sep 17 00:00:00 2001 From: PacoX Date: Tue, 26 May 2026 09:04:38 +0200 Subject: [PATCH] fix case-sensitive prefix --- documentation/additional-features.md | 2 +- documentation/chat-and-messaging.md | 2 +- documentation/routing-paths.md | 1 + lib/connector/meshcore_connector.dart | 8 +++++--- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/documentation/additional-features.md b/documentation/additional-features.md index 5dfd2686..8dd9fb63 100644 --- a/documentation/additional-features.md +++ b/documentation/additional-features.md @@ -183,7 +183,7 @@ An ML-based service that predicts expected delivery timeouts: - Applies a **1.5x safety margin** to raw predictions (the actual timeout issued is 1.5× the model's predicted delivery time) - Features with zero variance are automatically excluded from training - Blends per-contact statistics with ML predictions -- Falls back to `3000 + 3000 × pathLength` ms when insufficient data +- Falls back to `3000 + 3000 × pathLength` ms when insufficient data. Note: `pathLength` here refers to the stored hop count in the app's model/storage (number of hops), not the on-air encoded byte length. - Observations are persisted to storage via a 2-second debounced timer (observations within 2s of app termination may be lost) --- diff --git a/documentation/chat-and-messaging.md b/documentation/chat-and-messaging.md index 19dc4f05..290be3e9 100644 --- a/documentation/chat-and-messaging.md +++ b/documentation/chat-and-messaging.md @@ -88,7 +88,7 @@ When a direct message is sent: 1. The app computes an expected ACK hash: `SHA256([timestamp][attempt][text][selfPubKey])[0:4]` — matching the firmware's hash calculation. If SMAZ compression is enabled, the compressed text (not the original) is hashed 2. On device acknowledgment (`RESP_CODE_SENT`), the message transitions to "sent" and a timeout timer starts -3. **Timeout duration**: Preferably from the ML timeout prediction service; otherwise calculated from LoRa airtime physics: `500 + (airtime × 6 + 250) × (pathLength + 1)` ms for direct paths, `500 + 16 × airtime` ms for flood (airtime is estimated from the radio's current spreading factor, bandwidth, and coding rate) +3. **Timeout duration**: Preferably from the ML timeout prediction service; otherwise calculated from LoRa airtime physics: `500 + (airtime × 6 + 250) × (pathLength + 1)` ms for direct paths, `500 + 16 × airtime` ms for flood (airtime is estimated from the radio's current spreading factor, bandwidth, and coding rate). Note: in app code and storage `pathLength` refers to the stored hop count (number of hops) in the message/contact model — not the on-air encoded byte length — so the formula above uses the model hop count. 4. On timeout, the message is retried with **exponential backoff**: `1000 × 2^retryCount` ms (1s, 2s, 4s, 8s, 16s...) 5. **Max retries**: Configurable (default 5, range 2–10) 6. After max retries, the message is marked "failed" — but a **30-second grace window** remains during which a late ACK can still resolve the message to "delivered" diff --git a/documentation/routing-paths.md b/documentation/routing-paths.md index 9d5d93df..88cac9f2 100644 --- a/documentation/routing-paths.md +++ b/documentation/routing-paths.md @@ -37,6 +37,7 @@ The connector reads a single-mode byte and clamps to `0..3`, so the supported ho Paths in messages and storage consist of: - **`pathLength`**: Encoded path length in bytes as carried by the frame/header. This is not the hop count when `pathHashByteWidth > 1`. + - Note: on-air headers carry `pathLength` as a byte count. However, in the app's data model and storage the `pathLength` field is stored as the decoded hop count (number of hops). When reading or writing model objects (contacts, messages, storage), treat `pathLength` as the hop count; when constructing frames use the encoded byte length. - **`pathBytes`**: Raw bytes of the path, grouped by `pathHashByteWidth` - **`hopCount`**: Derived display value computed from bytes and width: `(byteCount + hashByteWidth - 1) ~/ hashByteWidth` - **Example**: With `pathHashByteWidth=2`, a 3-hop path needs 6 bytes, so `pathLength = 6` and `hopCount = 3`: diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index b83a6ba0..43456424 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -6385,10 +6385,12 @@ class MeshCoreConnector extends ChangeNotifier { } bool _contactKeyMatchesPrefix(String contactKeyHex, List pubkeyPrefix) { - final prefixHex = pubkeyPrefix - .map((b) => b.toRadixString(16).padLeft(2, '0')) + // Normalize both sides to upper-case hex to avoid case-sensitive mismatches. + final normalizedPrefixHex = pubkeyPrefix + .map((b) => b.toRadixString(16).padLeft(2, '0').toUpperCase()) .join(); - return contactKeyHex.startsWith(prefixHex); + final normalizedContactKeyHex = contactKeyHex.toUpperCase(); + return normalizedContactKeyHex.startsWith(normalizedPrefixHex); } void _handleAutoAddConfig(Uint8List frame) {