From de97f515a5a611e0a8f364bcfbc1f099243e471a Mon Sep 17 00:00:00 2001 From: Strycher Date: Mon, 29 Jun 2026 18:59:07 -0400 Subject: [PATCH] fix(#150): trace flood/empty paths as a direct ping (was sending empty -> firmware error 1) App Debug Log showed every failing trace sending an empty path -> "Firmware responded with error code: 1". Cause: repeaters with a flood route (path stored as all-zeros) were treated as routed; buildPath collapsed the all-zero path to empty; the firmware rejects an empty trace. - buildPath: an empty OR all-zero path now traces the target's own width prefix (never returns an empty payload). - _doPathTrace: guard - never send an empty path; surface "not available". - Repeater menu: constant "Path Trace" label (no more Ping/Path-Trace flip-flop); a flood path (all-zero) counts as no route -> direct ping. Bumps build to +50 for the b50 test binary. Co-Authored-By: Claude Opus 4.8 --- lib/screens/contacts_screen.dart | 19 ++++++++++--------- lib/screens/path_trace_map.dart | 27 +++++++++++++++++++++------ pubspec.yaml | 2 +- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/lib/screens/contacts_screen.dart b/lib/screens/contacts_screen.dart index 97e47b1..13f116a 100644 --- a/lib/screens/contacts_screen.dart +++ b/lib/screens/contacts_screen.dart @@ -1266,31 +1266,32 @@ class _ContactsScreenState extends State if (isRepeater) ...[ ListTile( leading: const Icon(Icons.radar, color: Colors.green), - title: Text( - contact.pathBytesForDisplay.isNotEmpty - ? context.l10n.contacts_pathTrace - : context.l10n.contacts_ping, - ), + title: Text(context.l10n.contacts_pathTrace), onTap: () { final hw = context .read() .pathHashByteWidth; + // A flood route is stored as all-zero bytes = "no real route", + // so trace the target directly instead of routing through (and + // sending) an all-zero/empty path. (#150) + final route = contact.pathBytesForDisplay; + final hasRoute = route.isNotEmpty && route.any((b) => b != 0); Navigator.push( context, MaterialPageRoute( builder: (context) => PathTraceMapScreen( - title: contact.pathBytesForDisplay.isNotEmpty + title: hasRoute ? context.l10n.contacts_repeaterPathTrace : context.l10n.contacts_repeaterPing, - path: contact.pathBytesForDisplay.isNotEmpty - ? contact.pathBytesForDisplay + path: hasRoute + ? route : Uint8List.fromList( contact.publicKey.sublist( 0, PathHelper.traceHopBytes(hw), ), ), - flipPathAround: contact.pathBytesForDisplay.isNotEmpty, + flipPathAround: hasRoute, targetContact: contact, pathHashByteWidth: hw, ), diff --git a/lib/screens/path_trace_map.dart b/lib/screens/path_trace_map.dart index 3e15297..493b7d2 100644 --- a/lib/screens/path_trace_map.dart +++ b/lib/screens/path_trace_map.dart @@ -199,17 +199,15 @@ class _PathTraceMapScreenState extends State { final hopBytes = PathHelper.traceHopBytes(widget.pathHashByteWidth); final pk = widget.targetContact?.publicKey; - if (pathBytes.isEmpty) { - // Direct target: send its own width prefix. + // 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 (pk != null && pk.length >= hopBytes) { return Uint8List.fromList(pk.sublist(0, hopBytes)); } return Uint8List.fromList([pk != null && pk.isNotEmpty ? pk[0] : 0]); } - // An all-zero path is the "no known path" marker → no trace payload. - if (pathBytes.every((b) => b == 0)) { - return Uint8List(0); - } final throughTarget = widget.targetContact?.type == advTypeRepeater || @@ -246,6 +244,23 @@ class _PathTraceMapScreenState extends State { final path = widget.flipPathAround ? buildPath(pathTmp) : pathTmp; + if (path.isEmpty) { + // No route to trace — don't send an empty payload (firmware error 1); + // surface "not available" instead. (#150) + appLogger.info( + 'Path trace skipped: no route to trace', + tag: 'PathTraceMapScreen', + noNotify: !mounted, + ); + if (mounted) { + setState(() { + _isLoading = false; + _failed2Loaded = true; + }); + } + return; + } + appLogger.info( 'Initiating path trace with path: ${_formatPathPrefixes(path)}', tag: 'PathTraceMapScreen', diff --git a/pubspec.yaml b/pubspec.yaml index bae4e47..a1964f1 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+49 +version: 1.1.2-rc.1+50 environment: sdk: ^3.9.2