mirror of
https://github.com/l5yth/potato-mesh.git
synced 2026-08-11 19:33:09 +02:00
meshcore/fix: short name should be 1st 4 hex digits of public key (#679)
This commit is contained in:
@@ -34,7 +34,9 @@ Connection type is detected automatically from the target string:
|
||||
|
||||
Node identities are derived from the first four bytes (eight hex characters)
|
||||
of each contact's 32-byte public key, formatted as ``!xxxxxxxx`` to match
|
||||
the canonical node-ID schema used across the system.
|
||||
the canonical node-ID schema used across the system. Ingested
|
||||
``user.shortName`` is the first four hex digits of that key (two bytes),
|
||||
not the advertised name.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -118,6 +120,25 @@ def _meshcore_node_id(public_key_hex: str | None) -> str | None:
|
||||
return "!" + public_key_hex[:8].lower()
|
||||
|
||||
|
||||
def _meshcore_short_name(public_key_hex: str | None) -> str:
|
||||
"""Return the first four hex digits of a MeshCore public key as short name.
|
||||
|
||||
Meshtastic-style ``shortName`` fields are four characters wide; MeshCore
|
||||
ingest uses the leading two bytes of the 32-byte public key in lowercase
|
||||
hex so the label is stable and unique per key prefix.
|
||||
|
||||
Parameters:
|
||||
public_key_hex: Full public key as a hex string from the MeshCore API.
|
||||
|
||||
Returns:
|
||||
Four lowercase hex characters, or an empty string when the key is
|
||||
missing or shorter than four hex digits.
|
||||
"""
|
||||
if not public_key_hex or len(public_key_hex) < 4:
|
||||
return ""
|
||||
return public_key_hex[:4].lower()
|
||||
|
||||
|
||||
def _pubkey_prefix_to_node_id(contacts: dict, pubkey_prefix: str) -> str | None:
|
||||
"""Look up a canonical node ID by six-byte public-key prefix.
|
||||
|
||||
@@ -153,7 +174,7 @@ def _contact_to_node_dict(contact: dict) -> dict:
|
||||
"lastHeard": contact.get("last_advert"),
|
||||
"user": {
|
||||
"longName": name,
|
||||
"shortName": name[:4] if name else "",
|
||||
"shortName": _meshcore_short_name(pub_key),
|
||||
"publicKey": pub_key,
|
||||
},
|
||||
}
|
||||
@@ -180,7 +201,7 @@ def _self_info_to_node_dict(self_info: dict) -> dict:
|
||||
"lastHeard": int(time.time()),
|
||||
"user": {
|
||||
"longName": name,
|
||||
"shortName": name[:4] if name else "",
|
||||
"shortName": _meshcore_short_name(pub_key),
|
||||
"publicKey": pub_key,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ from data.mesh_ingestor.providers.meshcore import ( # noqa: E402 - path setup
|
||||
_make_connection,
|
||||
_make_event_handlers,
|
||||
_meshcore_node_id,
|
||||
_meshcore_short_name,
|
||||
_process_contact_update,
|
||||
_process_contacts,
|
||||
_process_self_info,
|
||||
@@ -434,6 +435,7 @@ def test_meshcore_node_snapshot_items_with_contacts(monkeypatch):
|
||||
node_id, node_dict = items[0]
|
||||
assert node_id == "!aabbccdd"
|
||||
assert node_dict["user"]["longName"] == "Alice"
|
||||
assert node_dict["user"]["shortName"] == "aabb"
|
||||
iface.close()
|
||||
|
||||
|
||||
@@ -537,6 +539,18 @@ def test_meshcore_node_id_none_on_empty():
|
||||
assert _meshcore_node_id(None) is None # type: ignore[arg-type]
|
||||
|
||||
|
||||
def test_meshcore_short_name_first_four_hex_digits():
|
||||
"""_meshcore_short_name returns the first four hex chars, lowercased."""
|
||||
assert _meshcore_short_name("AABBccdd" + "00" * 28) == "aabb"
|
||||
|
||||
|
||||
def test_meshcore_short_name_empty_when_too_short():
|
||||
"""_meshcore_short_name returns '' when the key has fewer than four hex digits."""
|
||||
assert _meshcore_short_name("") == ""
|
||||
assert _meshcore_short_name("abc") == ""
|
||||
assert _meshcore_short_name(None) == "" # type: ignore[arg-type]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# _pubkey_prefix_to_node_id
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -576,7 +590,7 @@ def test_contact_to_node_dict_basic_fields():
|
||||
node = _contact_to_node_dict(contact)
|
||||
assert node["lastHeard"] == 1700000000
|
||||
assert node["user"]["longName"] == "Alice"
|
||||
assert node["user"]["shortName"] == "Alic"
|
||||
assert node["user"]["shortName"] == "aabb"
|
||||
assert node["user"]["publicKey"] == contact["public_key"]
|
||||
|
||||
|
||||
@@ -616,7 +630,7 @@ def test_self_info_to_node_dict_basic_fields():
|
||||
self_info = {"name": "MyNode", "public_key": "bb" * 32}
|
||||
node = _self_info_to_node_dict(self_info)
|
||||
assert node["user"]["longName"] == "MyNode"
|
||||
assert node["user"]["shortName"] == "MyNo"
|
||||
assert node["user"]["shortName"] == "bbbb"
|
||||
assert node["user"]["publicKey"] == "bb" * 32
|
||||
assert isinstance(node["lastHeard"], int)
|
||||
|
||||
@@ -649,6 +663,7 @@ def test_interface_update_and_snapshot_contacts():
|
||||
node_id, node_dict = snapshot[0]
|
||||
assert node_id == "!aabbccdd"
|
||||
assert node_dict["user"]["longName"] == "Bob"
|
||||
assert node_dict["user"]["shortName"] == "aabb"
|
||||
|
||||
|
||||
def test_interface_lookup_node_id_by_prefix():
|
||||
|
||||
Reference in New Issue
Block a user