From 8296f44a4e0e34bddbc01e6955bffeccc865583c Mon Sep 17 00:00:00 2001 From: PacoX Date: Wed, 13 May 2026 09:09:11 +0200 Subject: [PATCH] update doc --- documentation/routing-paths.md | 91 +++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 41 deletions(-) diff --git a/documentation/routing-paths.md b/documentation/routing-paths.md index 2a25683e..1de549bd 100644 --- a/documentation/routing-paths.md +++ b/documentation/routing-paths.md @@ -6,61 +6,70 @@ This page covers how MeshCore Open represents, selects, validates, and stores ro MeshCore supports variable-length multi-byte routing paths so the app can scale from small meshes to very large node sets. -### Path Encoding Format +### Hash Width and Multi-Byte Paths -Each path byte packs both the hop count and the hash width: +The device capability determines the hash width (number of bytes per hop): -``` -Bit 7-6: Hash mode (2 bits) → hash_bytes = 1 << mode -Bit 5-0: Hop count (6 bits) → max 63 hops -``` - -| Mode | Hash Bytes | Max Hops | Max Unique Nodes | Firmware | -|------|-----------|----------|------------------|----------| -| 0 | 1 | 63 | 256 | v1.13+ | -| 1 | 2 | 63 | 65,536 | v1.14+ | -| 2 | 3 | 63 | 16.7M | v1.14+ | -| 3 | 4 | 63 | 4.3B | v1.14+ | - -### Path Byte Calculation - -```dart -final pathLenRaw = frameData[index]; -final hopCount = pathLenRaw & 0x3F; -final hashMode = (pathLenRaw >> 6) & 0x03; -final hashBytes = 1 << hashMode; - -final pathByteLength = hopCount * hashBytes; -final pathData = frameData.sublist(index + 1, index + 1 + pathByteLength); -``` +| Width | Max Unique Nodes | Typical Use | +|-------|-----------------|-------------| +| 1 byte | 256 | Single-byte node IDs | +| 2 bytes | 65,536 | Medium meshes | +| 3 bytes | 16.7M | Large networks | +| 4 bytes | 4.3B | Gigantic deployments | ### Device Capability Detection -The app reads the device's reported capability byte to determine whether multi-byte paths are available: +On device connection, the app reads the firmware capability to set `pathHashByteWidth`: ```dart -if (firmwareBytes.length >= 82) { - final mode = (firmwareBytes[81] & 0xFF).clamp(0, 2); - pathHashByteWidth = mode + 1; -} else { - pathHashByteWidth = 1; -} +// Read from device info response (offset 81) +final modeRaw = firmwareBytes.length >= 82 + ? (firmwareBytes[81] & 0x3F) + : 0; +final mode = modeRaw.clamp(0, 3); +_pathHashByteWidth = mode + 1; // 1, 2, 3, or 4 bytes per hop ``` +### Path Data Structure + +Paths in messages and storage consist of: + +- **`pathLength`**: Hop count reported in the radio header (may differ from decoded path) +- **`pathBytes`**: Raw bytes of the path, grouped by `pathHashByteWidth` +- **Example**: With `pathHashByteWidth=2`, a 3-hop path needs 6 bytes: + - `pathBytes = [0xA1, 0xA2, 0xB1, 0xB2, 0xC1, 0xC2]` + - Hops: `[0xA1A2]`, `[0xB1B2]`, `[0xC1C2]` + +### Hop Count Calculation + +Convert path byte length to hop count: + +```dart +int hopCount = (byteCount + hashByteWidth - 1) ~/ hashByteWidth; +``` + +Use this consistently when displaying hop counts in UI to avoid mismatch between metadata (`pathLength`) and observed paths (`pathBytes`). + ### Path Usage in Different Message Types -- Direct messages extract the path from the decrypted payload to trace the sender route. -- Channel messages decrypt a hop-by-hop routing chain from the payload. -- Contact storage keeps the path length encoding in byte 32 and the raw path bytes in bytes 33-96. +- **Direct messages**: Extract path from decrypted payload to trace sender route. +- **Channel messages**: Decrypt hop-by-hop routing chain from payload; header includes `pathLength` metadata. +- **Contact storage**: Path length in byte 32, raw path bytes in bytes 33-96, grouped by detected `pathHashByteWidth`. -### Example Contact Path Parsing +### UI Hop Count Display + +⚠️ **Important**: In screens like "Channel Message Path", prefer the actual decoded `pathBytes` hop count over `pathLength` metadata: ```dart -// Mode 0 (1-byte): 3 hops = 3 bytes needed -// [0x83, 0xA1, 0xB2, 0xC3] → hopCount=3, mode=0, path=[A1,B2,C3] +// Preferred: use actual observed path length +final effectiveHopCount = (pathBytes.length + width - 1) ~/ width; -// Mode 1 (2-byte): 3 hops = 6 bytes needed -// [0xC3, 0xA1, 0xA2, 0xB1, 0xB2, 0xC1, 0xC2] -// → hopCount=3, mode=1, path=[A1A2, B1B2, C1C2] +// Avoid: using radio metadata directly +// pathLength can diverge from decoded bytes and cause UI mismatches ``` +Example scenario: +- Radio header reports `pathLength: 32` +- Decoded path bytes: `[0xAB, 0xCD]` (2 bytes = 1 hop with width=2) +- **Display**: "1 hop" (from pathBytes), NOT "32 hops" (from pathLength) +