reticiulum-specification/flows/receive-resource.md

97 lines
5 KiB
Markdown
Raw Normal View History

Add five companion flow docs - flows/receive-resource.md: inverse of send-resource. ADV ingestion, accept/reject decision, request_next loop, receive_part insertion, assemble + decrypt + hash-validate, RESOURCE_PRF emission, multi-segment continuation. - flows/receive-link-lxmf.md: responder side of the link handshake plus inbound LXMF DATA handling. validate_request -> handshake -> prove (LRPROOF emission) -> link_established callback wires delivery_packet. PACKET-form inbound runs delivery_packet directly; RESOURCE-form inbound runs through delivery_resource_advertised + delivery_resource_concluded pipeline. - flows/send-announce.md: random_hash construction (5B random + 5B BE-uint40 timestamp), optional ratchet rotation, signed_data assembly, sign + pack, the broadcast emission. Notes that ANNOUNCE packets are NOT encrypted (Packet.pack special-cases line 189-191) and the periodic re-announce loop drives 5-15min cadence. - flows/forward-announce.md: relay-side rebroadcast for transport-mode nodes. Eligibility checks (transport_enabled, not PATH_RESPONSE, not rate_blocked), announce_table queue, Transport.jobs drain with PATH_REQUEST_GRACE = 0.4s, per-interface announce_queue with ANNOUNCE_CAP = 2.0% airtime enforcement, lowest-hop-count-first emission order, hops byte increment, local-rebroadcast counter for loop break. - flows/send-propagated-lxmf.md: PROPAGATED method end to end. LXMessage.pack with body encrypted to recipient (propagation node never decrypts), Link establishment to the propagation node, optional propagation stamp (1000 PoW rounds vs 3000 for regular stamps), submission via Link DATA or Resource, state goes to SENT (not DELIVERED — recipient pulls via /get later per §5.8.3). flows/README.md status table updated; receive-propagated-lxmf.md added as the only remaining ⏳ flow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:21:05 -04:00
# Flow: receive a Resource (large body) over a Link
Re-anchor against RNS 1.2.4 / LXMF 0.9.7 + track upstream distribution shift Upstream RNS 1.2.4 (2026-05-07) announces it is "probably the last release that is also published to GitHub" — pip continues until rnpkg is complete and RNS is self-hosting. All 13 verifiers pass against 1.2.4 / 0.9.7; no wire-format, signing, or protocol behavior changed between 1.2.0 and 1.2.4, so the changes here are purely currency: - Pin tools/requirements.txt to rns==1.2.4 / lxmf==0.9.7 so the verifier stays reproducible if upstream stops mirroring to PyPI before the migration is ready. - Add an "Upstream distribution shift" watch-list to todo.md (local Reticulum node, repo destination hash, rnpkg install/upgrade commands, rsg signature verification, mirroring source citations). - Bump SPEC.md frontmatter and re-anchor ~50 line citations across Identity.py, Transport.py, Resource.py, Link.py, Reticulum.py, Packet.py, and LXMF/* (Identity.py drift was the heaviest at +13 to +31 lines; Transport.py was variable). Fix one numeric (MAX_RANDOM_BLOBS = 32 → 64) and one semantic (§6.6.3 LRPROOF MTU clamp citation pointed at the wrong location — corrected to point at the transit-relay clamp at Transport.py:1539-1556). - Update §10.4 decompression-bomb hazard to note upstream's 1.1.9 cap adoption, with citations to Resource.py:686-691 and Buffer.py:95-97 plus a "do not use one-shot bz2.decompress()" warning. - Re-anchor 11 flows/ files (version pins + ~30 line citations). - Bump version labels in tools/README.md, test-vectors/README.md, and 4 verifier docstrings + 2 hardcoded print strings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 07:42:25 -04:00
The inverse of [`send-resource.md`](send-resource.md). What happens chronologically on the receiver when an inbound Resource transfer arrives. Pinned against **RNS 1.2.4**; see [`../SPEC.md`](../SPEC.md) §10 for the wire bytes.
Add five companion flow docs - flows/receive-resource.md: inverse of send-resource. ADV ingestion, accept/reject decision, request_next loop, receive_part insertion, assemble + decrypt + hash-validate, RESOURCE_PRF emission, multi-segment continuation. - flows/receive-link-lxmf.md: responder side of the link handshake plus inbound LXMF DATA handling. validate_request -> handshake -> prove (LRPROOF emission) -> link_established callback wires delivery_packet. PACKET-form inbound runs delivery_packet directly; RESOURCE-form inbound runs through delivery_resource_advertised + delivery_resource_concluded pipeline. - flows/send-announce.md: random_hash construction (5B random + 5B BE-uint40 timestamp), optional ratchet rotation, signed_data assembly, sign + pack, the broadcast emission. Notes that ANNOUNCE packets are NOT encrypted (Packet.pack special-cases line 189-191) and the periodic re-announce loop drives 5-15min cadence. - flows/forward-announce.md: relay-side rebroadcast for transport-mode nodes. Eligibility checks (transport_enabled, not PATH_RESPONSE, not rate_blocked), announce_table queue, Transport.jobs drain with PATH_REQUEST_GRACE = 0.4s, per-interface announce_queue with ANNOUNCE_CAP = 2.0% airtime enforcement, lowest-hop-count-first emission order, hops byte increment, local-rebroadcast counter for loop break. - flows/send-propagated-lxmf.md: PROPAGATED method end to end. LXMessage.pack with body encrypted to recipient (propagation node never decrypts), Link establishment to the propagation node, optional propagation stamp (1000 PoW rounds vs 3000 for regular stamps), submission via Link DATA or Resource, state goes to SENT (not DELIVERED — recipient pulls via /get later per §5.8.3). flows/README.md status table updated; receive-propagated-lxmf.md added as the only remaining ⏳ flow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:21:05 -04:00
---
## Preconditions
- Link is `ACTIVE` (§6).
- Receiver registered a `resource_strategy` on the Link via `set_resource_strategy(...)``ACCEPT_NONE`, `ACCEPT_APP`, or `ACCEPT_ALL`. Default `ACCEPT_NONE` rejects every Resource on the link; LXMF and NomadNet flip this to `ACCEPT_APP` so an app callback can decide.
---
## Sequence
### 1. RESOURCE_ADV arrives
Inbound Link DATA packet with `context = RESOURCE_ADV (0x02)`. `Link.receive` at `RNS/Link.py:1065-1098` decrypts it and runs `RNS.ResourceAdvertisement.unpack` against the plaintext to extract the msgpack dict (§10.4).
### 2. Resource accept / reject decision
Branch by `resource_strategy`:
- **`ACCEPT_NONE`** → call `RNS.Resource.reject(adv_packet)` which sends `RESOURCE_RCL` back; resource is dropped.
- **`ACCEPT_APP`** → run the application callback `link.callbacks.resource(adv)`. If it returns truthy, `RNS.Resource.accept(...)`; otherwise reject.
- **`ACCEPT_ALL`** → unconditional `RNS.Resource.accept(...)`.
`Resource.accept` (`RNS/Resource.py:167-244`) constructs a receiver-side Resource object, copies fields from the advertisement, sets `status = TRANSFERRING`, and queues the first request.
### 3. Receiver issues the first RESOURCE_REQ
Re-anchor against RNS 1.2.4 / LXMF 0.9.7 + track upstream distribution shift Upstream RNS 1.2.4 (2026-05-07) announces it is "probably the last release that is also published to GitHub" — pip continues until rnpkg is complete and RNS is self-hosting. All 13 verifiers pass against 1.2.4 / 0.9.7; no wire-format, signing, or protocol behavior changed between 1.2.0 and 1.2.4, so the changes here are purely currency: - Pin tools/requirements.txt to rns==1.2.4 / lxmf==0.9.7 so the verifier stays reproducible if upstream stops mirroring to PyPI before the migration is ready. - Add an "Upstream distribution shift" watch-list to todo.md (local Reticulum node, repo destination hash, rnpkg install/upgrade commands, rsg signature verification, mirroring source citations). - Bump SPEC.md frontmatter and re-anchor ~50 line citations across Identity.py, Transport.py, Resource.py, Link.py, Reticulum.py, Packet.py, and LXMF/* (Identity.py drift was the heaviest at +13 to +31 lines; Transport.py was variable). Fix one numeric (MAX_RANDOM_BLOBS = 32 → 64) and one semantic (§6.6.3 LRPROOF MTU clamp citation pointed at the wrong location — corrected to point at the transit-relay clamp at Transport.py:1539-1556). - Update §10.4 decompression-bomb hazard to note upstream's 1.1.9 cap adoption, with citations to Resource.py:686-691 and Buffer.py:95-97 plus a "do not use one-shot bz2.decompress()" warning. - Re-anchor 11 flows/ files (version pins + ~30 line citations). - Bump version labels in tools/README.md, test-vectors/README.md, and 4 verifier docstrings + 2 hardcoded print strings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 07:42:25 -04:00
`Resource.request_next()` (`RNS/Resource.py:931-981`) builds the request body per §10.5:
Add five companion flow docs - flows/receive-resource.md: inverse of send-resource. ADV ingestion, accept/reject decision, request_next loop, receive_part insertion, assemble + decrypt + hash-validate, RESOURCE_PRF emission, multi-segment continuation. - flows/receive-link-lxmf.md: responder side of the link handshake plus inbound LXMF DATA handling. validate_request -> handshake -> prove (LRPROOF emission) -> link_established callback wires delivery_packet. PACKET-form inbound runs delivery_packet directly; RESOURCE-form inbound runs through delivery_resource_advertised + delivery_resource_concluded pipeline. - flows/send-announce.md: random_hash construction (5B random + 5B BE-uint40 timestamp), optional ratchet rotation, signed_data assembly, sign + pack, the broadcast emission. Notes that ANNOUNCE packets are NOT encrypted (Packet.pack special-cases line 189-191) and the periodic re-announce loop drives 5-15min cadence. - flows/forward-announce.md: relay-side rebroadcast for transport-mode nodes. Eligibility checks (transport_enabled, not PATH_RESPONSE, not rate_blocked), announce_table queue, Transport.jobs drain with PATH_REQUEST_GRACE = 0.4s, per-interface announce_queue with ANNOUNCE_CAP = 2.0% airtime enforcement, lowest-hop-count-first emission order, hops byte increment, local-rebroadcast counter for loop break. - flows/send-propagated-lxmf.md: PROPAGATED method end to end. LXMessage.pack with body encrypted to recipient (propagation node never decrypts), Link establishment to the propagation node, optional propagation stamp (1000 PoW rounds vs 3000 for regular stamps), submission via Link DATA or Resource, state goes to SENT (not DELIVERED — recipient pulls via /get later per §5.8.3). flows/README.md status table updated; receive-propagated-lxmf.md added as the only remaining ⏳ flow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 12:21:05 -04:00
```
exhausted_flag(1) [|| last_map_hash(4)] || resource_hash(32) || requested_map_hashes(N × 4)
```
`N = link.window` initially (default 4). Sent as Link DATA with `context = RESOURCE_REQ (0x03)`.
### 4. Sender fulfills with RESOURCE part packets
For each requested map_hash, the sender (per `flows/send-resource.md` step 5) emits one Link DATA packet with `context = RESOURCE (0x01)`, body = pre-encrypted part bytes. The receiver matches each arriving part to the hashmap by recomputing its 4-byte map_hash (`Resource.receive_part` line 831-932).
Successful match: `parts[i] = part_data`, `consecutive_completed_height` advances. The window grows by 1 each successful round (capped at `window_max`, with rate-detection upgrades to FAST or VERY_SLOW per §10.10).
### 5. Repeat steps 3-4 until `received_count == total_parts`
When the receiver has consumed every map_hash in the current segment, it issues another RESOURCE_REQ. If the hashmap is exhausted (`exhausted_flag = 0xFF`), the sender responds with a RESOURCE_HMU carrying the next hashmap segment (§10.7), and the loop continues.
### 6. `Resource.assemble()` reassembles, validates, decrypts
`Resource.py:672-726`:
1. `stream = b"".join(self.parts)` — concatenate every part.
2. `data = link.decrypt(stream)` — single Link Token decrypt of the whole blob (§10.12: encryption was applied to the whole concatenated body before splitting).
3. Strip the 4-byte `random_hash` prefix.
4. If `compressed`: bz2-decompress.
5. `calculated_hash = SHA256(data || random_hash)`. Compare to `self.hash` (= advertisement's `h` field). On match: `status = COMPLETE`. On mismatch: `status = CORRUPT`; cancel.
6. If `has_metadata`: peel off the 3-byte length-prefixed msgpack metadata blob, write to `meta_storagepath`.
7. Write the data to `storagepath` (file-backed) or hold in `self.data` (memory-backed).
8. Call the application callback (the one passed to `Resource.accept`).
### 7. RESOURCE_PRF emission
`Resource.prove()` (line 755-766) sends back:
```
proof_data = resource_hash(32) || full_proof(32)
where full_proof = SHA256(data_with_random || resource_hash)
```
as a PROOF-type packet with `context = RESOURCE_PRF (0x05)`. The sender's `validate_proof` matches `proof_data[32:]` against its precomputed `expected_proof` and transitions to `COMPLETE` (§10.8).
### 8. Multi-segment continuation
If `segment_index < total_segments`, the sender prepares and sends the next RESOURCE_ADV after receiving this segment's PRF. The receiver loops back to step 1 for the next segment. Each segment is a fully independent Resource transfer; the only thing that ties them together is the `original_hash` field in the advertisement.
### 9. Cancellation paths
`RESOURCE_ICL` (sender cancel) → receiver pops the matching incoming Resource and discards accumulated parts (`Link.py:1131-1138`).
`RESOURCE_RCL` (sender hears the receiver rejected) → already handled receiver-side at step 2.
---
## Source map
| Step | File | Function / line |
|---|---|---|
| 1 | `RNS/Link.py` | `Link.receive` RESOURCE_ADV branch, line 1065 |
| 2 | `RNS/Resource.py` | `Resource.accept`, `Resource.reject`, lines 155-244 |
| 3 | `RNS/Resource.py` | `request_next`, line 934 |
| 4 | `RNS/Resource.py` | `receive_part`, line 831 |
| 5 | `RNS/Link.py` | RESOURCE_HMU branch, line 1122 |
| 6 | `RNS/Resource.py` | `assemble`, line 672 |
| 7 | `RNS/Resource.py` | `prove`, line 755 |
| 8 | `RNS/Resource.py` | `__prepare_next_segment`, line 768 |
| 9 | `RNS/Link.py` | RESOURCE_ICL branch, line 1131 |