fix(#115): decode multi-byte path hashes at the device width
Packet Path mis-identified repeaters: a 2-byte path hash like 6A3D was
sliced into two 1-byte hops (6A + 3D), so the first byte collided with an
unrelated repeater (Freemasons 6A..) and the second matched nothing
("Unknown Repeater").
- meshcore_protocol: realHopCount() converts the firmware path BYTE length
to true hops via the device hash width.
- channel_message_path_screen: slice/count/match at the device
pathHashByteWidth (not the unreliable per-message high-bits); bucket and
match on the full w-byte hash (mirrors _pathMatchesContact), not hash[0].
- channel_chat_screen: same width fix on the per-message via-line + hop badge.
- settings: show the build number alongside the version name.
- bump 1.1.2-beta.4+36.
- test: realHopCount regression coverage.
Gemini review (llm-consult): fix-then-ship; 2 findings clarification-only
(_formatHash is canonical hex used identically both sides; pathHashByteWidth
is static per-connection).
Part of epic #112.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pull/130/head
parent
59da762c76
commit
31f850530b
@ -0,0 +1,32 @@
|
||||
// Path-hash width / hop-count decode (#112). The firmware path-length byte is a
|
||||
// BYTE count of the hop-hash array, not a hop count, so at a 2-byte hash width a
|
||||
// single 2-byte hop reports 2. realHopCount divides by the device width to get
|
||||
// true hops; the Packet Path screen slices and matches at the device width so a
|
||||
// 2-byte hash like 6A3D resolves to one repeater instead of two 1-byte hops.
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:meshcore_open/connector/meshcore_protocol.dart';
|
||||
|
||||
void main() {
|
||||
group('realHopCount', () {
|
||||
test('divides path byte-length by hash width', () {
|
||||
// The bug: one 2-byte hop (6A3D) reported byteLen 2 -> shown as "2 hops".
|
||||
expect(realHopCount(2, 2), 1);
|
||||
expect(realHopCount(4, 2), 2);
|
||||
expect(realHopCount(6, 3), 2);
|
||||
});
|
||||
|
||||
test('is a no-op at 1-byte width (no regression for legacy nets)', () {
|
||||
expect(realHopCount(3, 1), 3);
|
||||
});
|
||||
|
||||
test('treats width < 1 as 1', () {
|
||||
expect(realHopCount(3, 0), 3);
|
||||
});
|
||||
|
||||
test('passes null (unknown) and negative (flood) sentinels through', () {
|
||||
expect(realHopCount(null, 2), isNull);
|
||||
expect(realHopCount(-1, 2), -1);
|
||||
});
|
||||
});
|
||||
}
|
||||
Loading…
Reference in new issue