From b33e5221f2a5d7e8144e1c0d00d6a2cd1fe22557 Mon Sep 17 00:00:00 2001 From: Strycher Date: Tue, 30 Jun 2026 22:56:54 -0400 Subject: [PATCH] fix(#150): exclude our own origin prefix from the trace route (firmware contract) Per meshcore-firmware/docs/trace-path-format.md: a TRACE route must exclude the origin (implicit at both ends). If route[0] is our own node's prefix it matches no forwarding node (we are the sender) -> the packet never advances -> RESP_CODE_SENT then a silent timeout (the doc's #1, most-common cause). buildPath now drops a leading self hop before assembling the round trip. Bumps build to +51 for the b51 test binary. Co-Authored-By: Claude Opus 4.8 --- lib/screens/path_trace_map.dart | 21 +++++++++++++++++++-- pubspec.yaml | 2 +- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/lib/screens/path_trace_map.dart b/lib/screens/path_trace_map.dart index 493b7d2..fe87a98 100644 --- a/lib/screens/path_trace_map.dart +++ b/lib/screens/path_trace_map.dart @@ -199,10 +199,27 @@ class _PathTraceMapScreenState extends State { final hopBytes = PathHelper.traceHopBytes(widget.pathHashByteWidth); final pk = widget.targetContact?.publicKey; + // Per the firmware trace contract, the route must EXCLUDE our own origin + // prefix (it is implicit at both ends). Drop a leading self hop if the + // stored path includes it — otherwise route[0] matches no forwarding node + // (we are the sender) and the packet never advances → silent timeout. (#150) + var route = pathBytes; + final self = context.read().selfPublicKey; + if (self != null && self.length >= hopBytes && route.length >= hopBytes) { + var isSelf = true; + for (var i = 0; i < hopBytes; i++) { + if (route[i] != self[i]) { + isSelf = false; + break; + } + } + if (isSelf) route = Uint8List.fromList(route.sublist(hopBytes)); + } + // Empty path, or an all-zero "flood" marker, means no real route — trace // the target's own width prefix directly. Never return an empty payload: // the firmware rejects an empty trace with error 1. (#150) - if (pathBytes.isEmpty || pathBytes.every((b) => b == 0)) { + if (route.isEmpty || route.every((b) => b == 0)) { if (pk != null && pk.length >= hopBytes) { return Uint8List.fromList(pk.sublist(0, hopBytes)); } @@ -219,7 +236,7 @@ class _PathTraceMapScreenState extends State { : const []; return Uint8List.fromList( PathHelper.buildTraceRoundTrip( - routingPath: pathBytes, + routingPath: route, routingWidth: widget.pathHashByteWidth < 1 ? 1 : widget.pathHashByteWidth, diff --git a/pubspec.yaml b/pubspec.yaml index a1964f1..a888108 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.1.2-rc.1+50 +version: 1.1.2-rc.1+51 environment: sdk: ^3.9.2