reticiulum-specification/tools/regen_transport_link.py
John Poole 4a14dca3a4 Completed the transport-relayed Link three-tier unit.
Key findings:

Valid established-Link traffic uses HEADER_1 and link_table.
HEADER_2 Link traffic can cross the addressed relay, then is dropped by the next node.
Relay forwarding requires correct interface and hop count.
Relay forwarding does not require IDX_LT_VALIDATED or destination_type=LINK.
Endpoint Link delivery does require destination_type=LINK.
Link-addressed PROOF uses link_table; ordinary DATA proofs use reverse_table.
Added:

Tier 1 audit
Transport-Link flow
Verifier
Deterministic vectors
Updated SPEC.md, playbook.md, README files, and existing Link flow documentation.

Verification:

Deterministic vector regeneration: identical SHA-256
Full pinned suite: 21 passed, 0 failed
git diff --check: passed
No commit created.
2026-06-08 17:50:52 -07:00

96 lines
3.5 KiB
Python

"""
Regenerator for test-vectors/transport-link.json.
Derives deterministic relay-facing Link packets from the existing handshake
and DIRECT-LXMF vectors. Runtime forwarding behavior is verified separately by
tools/verify_transport_link.py against upstream RNS 1.2.4.
"""
from __future__ import annotations
import json
import os
import RNS
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
LINKS_PATH = os.path.join(REPO_ROOT, "test-vectors", "links.json")
LINK_LXMF_PATH = os.path.join(REPO_ROOT, "test-vectors", "link-lxmf.json")
IDS_PATH = os.path.join(REPO_ROOT, "test-vectors", "identities.json")
OUT_PATH = os.path.join(REPO_ROOT, "test-vectors", "transport-link.json")
NEXT_HOP_ID = bytes.fromhex("202122232425262728292a2b2c2d2e2f")
def load_json(path: str):
with open(path, "r", encoding="utf-8") as input_file:
return json.load(input_file)
def header_two(raw: bytes, transport_id: bytes) -> bytes:
flags = (
(RNS.Packet.HEADER_2 << 6)
| (RNS.Transport.TRANSPORT << 4)
| (raw[0] & 0x0F)
)
return bytes([flags, raw[1]]) + transport_id + raw[2:]
def bump_hops(raw: bytes) -> bytes:
return raw[:1] + bytes([raw[1] + 1]) + raw[2:]
def main() -> None:
links = load_json(LINKS_PATH)["vectors"][0]
link_lxmf = load_json(LINK_LXMF_PATH)["vectors"][0]
identities = load_json(IDS_PATH)["vectors"]
alice = next(item for item in identities if item["label"] == "alice")
relay_id = bytes.fromhex(alice["expected"]["identity_hash_hex"])
linkrequest = bytes.fromhex(links["expected"]["linkrequest_raw_hex"])
lrproof = bytes.fromhex(links["expected"]["lrproof_raw_hex"])
link_data = bytes.fromhex(link_lxmf["expected"]["link_packet_raw_hex"])
incoming_linkrequest = header_two(linkrequest, relay_id)
forwarded_linkrequest = bump_hops(linkrequest)
forwarded_lrproof = bump_hops(lrproof)
forwarded_link_data = bump_hops(link_data)
header_two_link_data = header_two(link_data, relay_id)
vector = {
"_about": (
"Transport-relayed Link fixtures derived from links.json and "
"link-lxmf.json. The runtime verifier exercises upstream "
"Transport.inbound with a one-relay path."
),
"inputs": {
"link_vector_label": links["label"],
"link_lxmf_vector_label": link_lxmf["label"],
"relay_identity_label": "alice",
"relay_identity_hash_hex": relay_id.hex(),
"next_hop_transport_id_hex": NEXT_HOP_ID.hex(),
},
"expected": {
"link_id_hex": links["expected"]["link_id_hex"],
"destination_hash_hex": linkrequest[2:18].hex(),
"incoming_header2_linkrequest_raw_hex": incoming_linkrequest.hex(),
"forwarded_header1_linkrequest_raw_hex": forwarded_linkrequest.hex(),
"incoming_lrproof_raw_hex": lrproof.hex(),
"forwarded_lrproof_raw_hex": forwarded_lrproof.hex(),
"incoming_link_data_raw_hex": link_data.hex(),
"forwarded_link_data_raw_hex": forwarded_link_data.hex(),
"invalid_header2_link_data_raw_hex": header_two_link_data.hex(),
},
"rns_version_at_generation": RNS.__version__,
"generator_script": "tools/regen_transport_link.py",
"verifies_spec_sections": ["6.3", "6.4.3", "12.2.4", "12.5"],
}
with open(OUT_PATH, "w", encoding="utf-8") as output_file:
json.dump(vector, output_file, indent=2)
output_file.write("\n")
print(f"Wrote {OUT_PATH}")
if __name__ == "__main__":
main()