mirror of
https://github.com/torlando-tech/pyxis.git
synced 2026-09-25 22:23:37 +00:00
fix(propagation): gate endpoint hot-path path-table reads (microReticulum bump)
Bumps microReticulum pin to e2c9d4d (diag/path-get-caller on cd0338e): Transport::inbound() and path_request() no longer perform a full microStore get() (flash write + read) for every inbound packet / path request on endpoint-only nodes. The read is gated on the exact conditions where destination_entry is consumed, and the local-destination path-request answer uses the in-memory _destinations table, so the device stays discoverable. The build still carries the temporary [PG] counters for the live before/after capture; counters are stripped in a follow-up before anything merges.
This commit is contained in:
+1
-1
@@ -105,7 +105,7 @@ lib_deps =
|
||||
; tables so the explicit 1 MiB RNS container pool cannot grow without limit.
|
||||
; Immutable published pin for bounded transport tables and sensitive
|
||||
; packet-request envelope wiping.
|
||||
https://github.com/torlando-tech/microReticulum.git#ef071871421b244e4209f1c2b5b2a838b22bd756
|
||||
https://github.com/torlando-tech/microReticulum.git#e2c9d4d17a510e40821a0b0651de07469f78d2cc
|
||||
; microLXMF: chore/microreticulum-0.4.1-layout — includes namespaced to
|
||||
; <microReticulum/...> for the 0.4.x src/microReticulum/ layout.
|
||||
; 3cdde79: faster load_message_metadata — single LittleFS open (read_file
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
"""Source-level contract for the endpoint-node hot-path read gate.
|
||||
|
||||
In the CBA microStore fork, Transport::inbound() and Transport::path_request()
|
||||
each perform a full _new_path_table.get() (flush_buffer flash write + index
|
||||
scan + segment open) for every non-announce inbound packet / path request,
|
||||
BEFORE consulting whether the node can act on the result. On an endpoint-only
|
||||
node (transport disabled, no attached local clients) that result is never
|
||||
used, so each air packet costs a multi-second flash operation on the 2MB
|
||||
partition that also holds message storage. Measured on a T-Deck idle: 792
|
||||
such reads in 600s, all from these two sites, driving a 840s display stall.
|
||||
|
||||
The fix gates each read on the exact condition where its destination_entry is
|
||||
actually consumed:
|
||||
- inbound(): for_local_client is only actionable when relaying, so gate on
|
||||
(transport_enabled || any local client attached).
|
||||
- path_request(): destination_entry is only read in the
|
||||
(transport_enabled || is_from_local_client) answer branch; the
|
||||
local-destination answer for THIS node uses the in-memory
|
||||
_destinations table and must remain reachable.
|
||||
|
||||
These checks lock the gates in place and prove the local-destination answer
|
||||
path is preserved (an endpoint must still be discoverable).
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
TRANSPORT_CPP = ROOT / ".pio/libdeps/tdeck/microReticulum/src/microReticulum/Transport.cpp"
|
||||
|
||||
|
||||
def require_source() -> str:
|
||||
if not TRANSPORT_CPP.is_file():
|
||||
pytest.skip("pinned microReticulum libdeps not populated (run: pio run -e tdeck)")
|
||||
return TRANSPORT_CPP.read_text()
|
||||
|
||||
|
||||
def function_body(source: str, signature: str, next_signature: str) -> str:
|
||||
start = source.index(signature)
|
||||
nxt = source.index(next_signature, start + len(signature))
|
||||
return source[start:nxt]
|
||||
|
||||
|
||||
def test_inbound_for_local_client_read_gated():
|
||||
source = require_source()
|
||||
body = function_body(
|
||||
source,
|
||||
"void Transport::inbound(const Bytes& raw",
|
||||
"void Transport::synthesize_tunnel(",
|
||||
)
|
||||
# The for_local_client determination is the hot read we gate.
|
||||
marker = "If packet is anything besides ANNOUNCE then determine"
|
||||
assert marker in body, "inbound() for_local_client block not found"
|
||||
block = body[body.index(marker):]
|
||||
|
||||
# The get() must now be guarded by the endpoint condition.
|
||||
gate = block.index("Reticulum::transport_enabled() || _local_client_interfaces.size() > 0")
|
||||
get = block.index("_new_path_table.get(packet.destination_hash()", gate)
|
||||
assert gate < get, (
|
||||
"inbound() _new_path_table.get() for for_local_client must be gated "
|
||||
"behind (transport_enabled || local clients attached); an endpoint-only "
|
||||
"node must not pay a flash read for a value it never uses"
|
||||
)
|
||||
# The hops==0 for_local_client assignment must still follow the (skippable) read.
|
||||
assert "for_local_client = true" in block[get:], (
|
||||
"inbound() must still set for_local_client on a hops==0 hit"
|
||||
)
|
||||
|
||||
|
||||
def test_path_request_read_gated_but_local_answer_preserved():
|
||||
source = require_source()
|
||||
body = function_body(
|
||||
source,
|
||||
"void Transport::path_request(const Bytes& destination_hash",
|
||||
"bool Transport::from_local_client(",
|
||||
)
|
||||
# destination_entry is only consumed in the answer branch; the read must be
|
||||
# gated on the same condition.
|
||||
gate = body.index("Reticulum::transport_enabled() || is_from_local_client")
|
||||
get = body.index("_new_path_table.get(destination_hash", gate)
|
||||
assert gate < get, (
|
||||
"path_request() _new_path_table.get() must be gated behind "
|
||||
"(transport_enabled || is_from_local_client); an endpoint-only node "
|
||||
"must not pay a flash read to answer a path request"
|
||||
)
|
||||
# The local-destination answer (in-memory _destinations) must remain and be
|
||||
# reachable AFTER the (skippable) read so an endpoint stays discoverable.
|
||||
local_lookup = body.index("_destinations.find(destination_hash)")
|
||||
assert local_lookup > get, (
|
||||
"path_request() must still consult the in-memory _destinations table "
|
||||
"to answer path requests for THIS node (local-destination answer)"
|
||||
)
|
||||
|
||||
|
||||
def test_fork_pin_matches_audit_tool():
|
||||
# The gate lives in the pinned microReticulum fork; the pin in
|
||||
# platformio.ini must agree with tools/audit_release_build.py so a bump is
|
||||
# atomic (all 5 sites moved together).
|
||||
ini = (ROOT / "platformio.ini").read_text()
|
||||
audit = (ROOT / "tools/audit_release_build.py").read_text()
|
||||
ini_pins = {
|
||||
line.split("microReticulum.git#")[1].strip()
|
||||
for line in ini.splitlines()
|
||||
if "microReticulum.git#" in line
|
||||
}
|
||||
audit_pins = {
|
||||
line.split('"')[3]
|
||||
for line in audit.splitlines()
|
||||
if line.strip().startswith('"microReticulum"')
|
||||
}
|
||||
assert ini_pins and ini_pins == audit_pins, (
|
||||
f"platformio.ini microReticulum pin {ini_pins} != audit tool pin {audit_pins}"
|
||||
)
|
||||
@@ -3,7 +3,7 @@ from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
MICROSTORE_PIN = "https://github.com/torlando-tech/microStore.git#2762f7606800ffb23f4a593947d4f58e259cda7a"
|
||||
MICRORETICULUM_PIN = "https://github.com/torlando-tech/microReticulum.git#ef071871421b244e4209f1c2b5b2a838b22bd756"
|
||||
MICRORETICULUM_PIN = "https://github.com/torlando-tech/microReticulum.git#e2c9d4d17a510e40821a0b0651de07469f78d2cc"
|
||||
MAX_RNS_PSRAM_POOL_BYTES = 1024 * 1024
|
||||
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ set(PYXIS_NOMADNET_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../lib/tdeck_ui/UI/LXMF"
|
||||
set(PYXIS_ROOT "${CMAKE_CURRENT_LIST_DIR}/../../..")
|
||||
set(PYXIS_MANIFEST_BASE "51e4b586c4c3867ae399f573557edda6f3b48a44")
|
||||
set(PYXIS_MANIFEST_BRANCH "feat/nomadnet-partials-core")
|
||||
set(PYXIS_MANIFEST_MICRORETICULUM "ef071871421b244e4209f1c2b5b2a838b22bd756")
|
||||
set(PYXIS_MANIFEST_MICRORETICULUM "e2c9d4d17a510e40821a0b0651de07469f78d2cc")
|
||||
execute_process(COMMAND git rev-parse HEAD WORKING_DIRECTORY "${RNS_SOURCE}"
|
||||
OUTPUT_VARIABLE _rns_commit OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
RESULT_VARIABLE _rns_git_result)
|
||||
|
||||
@@ -45,7 +45,7 @@ if os.environ.get("PYXIS_FLOW_SCENARIOS"):
|
||||
|
||||
MANIFEST_BASE = "51e4b586c4c3867ae399f573557edda6f3b48a44"
|
||||
MANIFEST_BRANCH = "feat/nomadnet-partials-core"
|
||||
MANIFEST_MICRORETICULUM = "ef071871421b244e4209f1c2b5b2a838b22bd756"
|
||||
MANIFEST_MICRORETICULUM = "e2c9d4d17a510e40821a0b0651de07469f78d2cc"
|
||||
MANIFEST_FILES = (
|
||||
"tests/native/nomadnet_x86_flow/CMakeLists.txt",
|
||||
"tests/native/nomadnet_x86_flow/BuildManifest.h.in",
|
||||
|
||||
@@ -45,7 +45,7 @@ EXCLUDED_SYMBOLS = (
|
||||
"MapTileHttpArduino::",
|
||||
)
|
||||
PINNED_DEPENDENCIES = {
|
||||
"microReticulum": "ef071871421b244e4209f1c2b5b2a838b22bd756",
|
||||
"microReticulum": "e2c9d4d17a510e40821a0b0651de07469f78d2cc",
|
||||
"microLXMF": "82d2e54d721d9e4ce3ffc0049d9996aa5632390a",
|
||||
"microStore": "2762f7606800ffb23f4a593947d4f58e259cda7a",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user