mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
Enable route hash settings
This commit is contained in:
@@ -4,38 +4,49 @@ import '../models/contact.dart';
|
||||
|
||||
class DecodedLogRxRoute {
|
||||
final int payloadType;
|
||||
final List<int> pathHashes;
|
||||
final List<int> pathBytes;
|
||||
final int hashSize;
|
||||
|
||||
const DecodedLogRxRoute({
|
||||
required this.payloadType,
|
||||
required this.pathHashes,
|
||||
required this.pathBytes,
|
||||
required this.hashSize,
|
||||
});
|
||||
|
||||
int? get originalSenderHash => pathHashes.isEmpty ? null : pathHashes.first;
|
||||
List<String> get hopHashes =>
|
||||
LogRxRouteDecoder.splitHopHashes(pathBytes, hashSize: hashSize);
|
||||
|
||||
int get hopCount => hopHashes.length;
|
||||
|
||||
String? get originalSenderHashHex =>
|
||||
hopHashes.isEmpty ? null : hopHashes.first;
|
||||
}
|
||||
|
||||
class ResolvedNodeHash {
|
||||
final int hash;
|
||||
final String hashHex;
|
||||
final String label;
|
||||
final bool isOwnNode;
|
||||
final bool isUniqueMatch;
|
||||
final int matchCount;
|
||||
|
||||
const ResolvedNodeHash({
|
||||
required this.hash,
|
||||
required this.hashHex,
|
||||
required this.label,
|
||||
required this.isOwnNode,
|
||||
required this.isUniqueMatch,
|
||||
required this.matchCount,
|
||||
});
|
||||
|
||||
String get hexLabel => '0x${hash.toRadixString(16).padLeft(2, '0')}';
|
||||
String get hexLabel => '0x${hashHex.toUpperCase()}';
|
||||
}
|
||||
|
||||
class LogRxRouteDecoder {
|
||||
const LogRxRouteDecoder._();
|
||||
|
||||
static DecodedLogRxRoute? decode(Uint8List rawData) {
|
||||
static DecodedLogRxRoute? decode(
|
||||
Uint8List rawData, {
|
||||
int? preferredHashSize,
|
||||
}) {
|
||||
if (rawData.length < 5 || rawData[0] != 0x88) return null;
|
||||
|
||||
final rawPacketData = rawData.sublist(3);
|
||||
@@ -54,28 +65,79 @@ class LogRxRouteDecoder {
|
||||
if (rawPacketData.length <= index) return null;
|
||||
final pathLen = rawPacketData[index++];
|
||||
if (rawPacketData.length < index + pathLen) return null;
|
||||
final pathBytes = rawPacketData.sublist(index, index + pathLen);
|
||||
final hashSize = inferHashSize(
|
||||
pathBytes,
|
||||
preferredHashSize: preferredHashSize,
|
||||
);
|
||||
|
||||
return DecodedLogRxRoute(
|
||||
payloadType: payloadType,
|
||||
pathHashes: rawPacketData.sublist(index, index + pathLen),
|
||||
pathBytes: pathBytes,
|
||||
hashSize: hashSize,
|
||||
);
|
||||
}
|
||||
|
||||
static int inferHashSize(List<int> pathBytes, {int? preferredHashSize}) {
|
||||
if (pathBytes.isEmpty) return 1;
|
||||
|
||||
final normalizedPreferred =
|
||||
preferredHashSize != null &&
|
||||
preferredHashSize >= 1 &&
|
||||
preferredHashSize <= 3
|
||||
? preferredHashSize
|
||||
: null;
|
||||
if (normalizedPreferred != null &&
|
||||
pathBytes.length % normalizedPreferred == 0) {
|
||||
return normalizedPreferred;
|
||||
}
|
||||
|
||||
for (final candidate in const [3, 2, 1]) {
|
||||
if (pathBytes.length % candidate == 0) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static List<String> splitHopHashes(
|
||||
List<int> pathBytes, {
|
||||
required int hashSize,
|
||||
}) {
|
||||
if (pathBytes.isEmpty) return const [];
|
||||
if (hashSize < 1 || hashSize > 3 || pathBytes.length % hashSize != 0) {
|
||||
return pathBytes
|
||||
.map((byte) => byte.toRadixString(16).padLeft(2, '0'))
|
||||
.toList();
|
||||
}
|
||||
|
||||
final hops = <String>[];
|
||||
for (var index = 0; index < pathBytes.length; index += hashSize) {
|
||||
hops.add(
|
||||
pathBytes
|
||||
.sublist(index, index + hashSize)
|
||||
.map((byte) => byte.toRadixString(16).padLeft(2, '0'))
|
||||
.join(),
|
||||
);
|
||||
}
|
||||
return hops;
|
||||
}
|
||||
|
||||
static ResolvedNodeHash resolveHash(
|
||||
int hash, {
|
||||
String hashHex, {
|
||||
required Iterable<Contact> contacts,
|
||||
Uint8List? ownPublicKey,
|
||||
String? ownName,
|
||||
}) {
|
||||
final ownHash = ownPublicKey != null && ownPublicKey.isNotEmpty
|
||||
? ownPublicKey.first
|
||||
: null;
|
||||
if (ownHash == hash) {
|
||||
final normalizedHashHex = hashHex.toLowerCase();
|
||||
final ownKeyHex = _bytesToHex(ownPublicKey);
|
||||
if (ownKeyHex != null && ownKeyHex.startsWith(normalizedHashHex)) {
|
||||
final ownLabel = (ownName != null && ownName.trim().isNotEmpty)
|
||||
? '$ownName (you)'
|
||||
: 'You';
|
||||
return ResolvedNodeHash(
|
||||
hash: hash,
|
||||
hashHex: normalizedHashHex,
|
||||
label: ownLabel,
|
||||
isOwnNode: true,
|
||||
isUniqueMatch: true,
|
||||
@@ -84,12 +146,12 @@ class LogRxRouteDecoder {
|
||||
}
|
||||
|
||||
final matches = contacts.where((contact) {
|
||||
return contact.publicKey.isNotEmpty && contact.publicKey.first == hash;
|
||||
return contact.publicKeyHex.toLowerCase().startsWith(normalizedHashHex);
|
||||
}).toList();
|
||||
|
||||
if (matches.isEmpty) {
|
||||
return ResolvedNodeHash(
|
||||
hash: hash,
|
||||
hashHex: normalizedHashHex,
|
||||
label: 'Unknown',
|
||||
isOwnNode: false,
|
||||
isUniqueMatch: false,
|
||||
@@ -99,7 +161,7 @@ class LogRxRouteDecoder {
|
||||
|
||||
if (matches.length == 1) {
|
||||
return ResolvedNodeHash(
|
||||
hash: hash,
|
||||
hashHex: normalizedHashHex,
|
||||
label: matches.first.displayName,
|
||||
isOwnNode: false,
|
||||
isUniqueMatch: true,
|
||||
@@ -119,11 +181,19 @@ class LogRxRouteDecoder {
|
||||
? '$candidateNames +$extraCount'
|
||||
: candidateNames;
|
||||
return ResolvedNodeHash(
|
||||
hash: hash,
|
||||
hashHex: normalizedHashHex,
|
||||
label: label,
|
||||
isOwnNode: false,
|
||||
isUniqueMatch: false,
|
||||
matchCount: matches.length,
|
||||
);
|
||||
}
|
||||
|
||||
static String? _bytesToHex(Uint8List? bytes) {
|
||||
if (bytes == null || bytes.isEmpty) return null;
|
||||
return bytes
|
||||
.map((byte) => byte.toRadixString(16).padLeft(2, '0'))
|
||||
.join()
|
||||
.toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user