fix: tolerate openhop_core without QueuedMessage.sender_prefix

Installed openhop_core releases predating the sender_prefix change
(paired with fd43d86) reject the kwarg, so restoring queued messages
raised TypeError and aborted companion init. Detect support via the
QueuedMessage signature and drop persisted prefixes with a warning on
older cores instead of failing the boot.
This commit is contained in:
agessaman
2026-07-07 07:53:49 -07:00
parent e6d4b68d01
commit 3a29061e24
2 changed files with 58 additions and 11 deletions
+23 -11
View File
@@ -1,5 +1,6 @@
import asyncio
import functools
import inspect
import logging
import os
import signal
@@ -780,6 +781,17 @@ class RepeaterDaemon:
limit=retention or 100,
)
loaded_messages = len(message_rows)
# openhop_core < the sender_prefix change (paired with fd43d86) has no
# QueuedMessage.sender_prefix; drop the prefix there instead of failing init.
supports_sender_prefix = "sender_prefix" in inspect.signature(QueuedMessage).parameters
if message_rows and not supports_sender_prefix:
logger.warning(
"Companion %s ('%s'): installed openhop_core QueuedMessage has no "
"sender_prefix field; persisted sender prefixes will be dropped "
"(update openhop_core to restore signed room-post authors)",
companion_hash_str,
name,
)
for msg_dict in message_rows:
sk = msg_dict.get("sender_key", b"")
if isinstance(sk, str):
@@ -787,18 +799,18 @@ class RepeaterDaemon:
sp = msg_dict.get("sender_prefix", b"")
if isinstance(sp, str):
sp = bytes.fromhex(sp) if sp else b""
bridge.message_queue.push(
QueuedMessage(
sender_key=sk,
txt_type=msg_dict.get("txt_type", 0),
timestamp=msg_dict.get("timestamp", 0),
text=msg_dict.get("text", ""),
is_channel=bool(msg_dict.get("is_channel", False)),
channel_idx=msg_dict.get("channel_idx", 0),
path_len=msg_dict.get("path_len", 0),
sender_prefix=sp,
)
msg_kwargs = dict(
sender_key=sk,
txt_type=msg_dict.get("txt_type", 0),
timestamp=msg_dict.get("timestamp", 0),
text=msg_dict.get("text", ""),
is_channel=bool(msg_dict.get("is_channel", False)),
channel_idx=msg_dict.get("channel_idx", 0),
path_len=msg_dict.get("path_len", 0),
)
if supports_sender_prefix:
msg_kwargs["sender_prefix"] = sp
bridge.message_queue.push(QueuedMessage(**msg_kwargs))
logger.info(
"Companion %s ('%s'): restored %d/%d contact(s), %d/%d channel(s), "
+35
View File
@@ -128,6 +128,41 @@ class TestRestoreCompanionState:
await daemon._restore_companion_state(sqlite, bridge, _HASH, _NAME)
assert any("rejected persisted channel" in r.message for r in caplog.records)
@pytest.mark.asyncio
async def test_old_core_without_sender_prefix_drops_prefix(self, caplog):
# openhop_core releases before the sender_prefix change reject the kwarg;
# message restore must degrade (drop the prefix) instead of failing init.
from dataclasses import dataclass, field
@dataclass
class LegacyQueuedMessage:
sender_key: bytes = b""
txt_type: int = 0
timestamp: int = 0
text: str = ""
is_channel: bool = False
channel_idx: int = 0
path_len: int = 0
snr: float = 0.0
rssi: int = 0
channel_data_payload: bytes = field(default=b"")
daemon = self._daemon()
bridge = self._bridge()
sqlite = self._sqlite(
messages=[{"sender_key": b"", "text": "hi", "sender_prefix": b"\xab\xcd"}]
)
with (
patch("openhop_core.companion.models.QueuedMessage", LegacyQueuedMessage),
caplog.at_level(logging.WARNING),
):
await daemon._restore_companion_state(sqlite, bridge, _HASH, _NAME)
bridge.message_queue.push.assert_called_once()
pushed = bridge.message_queue.push.call_args[0][0]
assert isinstance(pushed, LegacyQueuedMessage)
assert pushed.text == "hi"
assert any("sender prefixes will be dropped" in r.message for r in caplog.records)
@pytest.mark.asyncio
async def test_zero_retention_skips_message_load(self):
daemon = self._daemon()