mirror of
https://github.com/agessaman/meshcore-bot.git
synced 2026-11-04 03:52:34 +00:00
Builds on #303. Channel text is capped at 155 bytes of "<name>: <text>" instead of 124. The firmware encrypts a 4-byte timestamp and a 1-byte text type ahead of the text in 16-byte AES blocks, so 155 fills the 160-byte block exactly (163-byte payload), and the last 5 bytes up to the firmware's 160 would spill into another block (179-byte payload). 124 was set on the premise that repeaters drop 165-byte frames. They don't: allowPacketForward has no size check and MAX_PACKET_PAYLOAD is 184, and 163-byte GRP_TXT payloads are routinely seen at 3+ hops. The frame-budget tests now pin the limit within the firmware's 160 and to a whole cipher block instead of to a 131-byte relay ceiling. Also corrects the payload model's plaintext header from 4 bytes to 5, and moves a test import back above the helper that had split the import block.
65 lines
2.2 KiB
Python
65 lines
2.2 KiB
Python
"""The channel text budget must fit the firmware and fill whole cipher blocks."""
|
|
|
|
import pytest
|
|
|
|
from modules.models import CHANNEL_BODY_FLOOR, CHANNEL_FRAME_TEXT_LIMIT, channel_body_limit
|
|
|
|
# Firmware MAX_TEXT_LEN (BaseChatMesh.h): "<name>: <text>" past this is truncated.
|
|
FIRMWARE_MAX_TEXT_LEN = 160
|
|
# Firmware encrypts a 4-byte timestamp and a 1-byte text type ahead of the text.
|
|
PLAINTEXT_HEADER = 5
|
|
CIPHER_BLOCK = 16
|
|
|
|
|
|
def _payload_bytes(framed_len: int) -> int:
|
|
"""GRP_TXT payload size: channel hash + 2-byte MAC + padded ciphertext."""
|
|
cipher = -(-(PLAINTEXT_HEADER + framed_len) // CIPHER_BLOCK) * CIPHER_BLOCK
|
|
return 3 + cipher
|
|
|
|
|
|
def test_limit_is_within_the_firmware_text_limit():
|
|
assert CHANNEL_FRAME_TEXT_LIMIT <= FIRMWARE_MAX_TEXT_LEN
|
|
|
|
|
|
def test_limit_fills_its_cipher_block_exactly():
|
|
"""One more byte would cost a whole extra block of airtime."""
|
|
assert (PLAINTEXT_HEADER + CHANNEL_FRAME_TEXT_LIMIT) % CIPHER_BLOCK == 0
|
|
assert _payload_bytes(CHANNEL_FRAME_TEXT_LIMIT) == 163
|
|
assert _payload_bytes(CHANNEL_FRAME_TEXT_LIMIT + 1) == 179
|
|
|
|
|
|
@pytest.mark.parametrize("name", ["Bot", "TestBot", "KY-ComchanBot \U0001f916", "\U0001f916" * 4, "A" * 121])
|
|
def test_full_body_plus_prefix_fits_the_limit(name):
|
|
framed = len(name.encode("utf-8")) + 2 + channel_body_limit(name)
|
|
assert framed == CHANNEL_FRAME_TEXT_LIMIT
|
|
|
|
|
|
def test_long_name_gets_the_floor():
|
|
assert channel_body_limit("A" * 200) == CHANNEL_BODY_FLOOR
|
|
|
|
|
|
class TestSharedByEveryPath:
|
|
"""Three call sites used to carry their own copy of this arithmetic."""
|
|
|
|
def test_scheduler_defers_to_the_shared_limit(self):
|
|
import inspect
|
|
|
|
from modules import scheduler
|
|
|
|
src = inspect.getsource(scheduler.MessageScheduler._channel_body_budget)
|
|
assert "channel_body_limit(" in src
|
|
assert "160 -" not in src
|
|
|
|
def test_command_manager_defers_to_the_shared_limit(self):
|
|
import inspect
|
|
|
|
from modules import command_manager
|
|
|
|
for fn in (
|
|
command_manager.CommandManager.get_max_message_length,
|
|
command_manager.CommandManager.channel_body_budget,
|
|
):
|
|
src = inspect.getsource(fn)
|
|
assert "channel_body_limit(" in src, fn.__name__
|
|
assert "160 -" not in src, fn.__name__
|