WIP:nip-pip:packet protocol#1376
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds support for the perfect_ip / PIP packet framing protocol to the nostr crate, including Nostr event encoding/decoding, manifests/repair requests, and example usage.
Changes:
- Introduces
nips::pipmodule with packet tree framing, XOR parity repair, and persistence helpers. - Exposes the PIP module via
nips/mod.rs. - Adds a
nip-pipexample and updates dev-dependencies to support relay interactions in examples/tests.
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| crates/nostr/src/nips/pip.rs | Implements packet tree slicing/parity, Nostr event conversion, integrity tracking, and tests for PIP/perfect_ip. |
| crates/nostr/src/nips/mod.rs | Exposes the new pip module. |
| crates/nostr/examples/nip-pip.rs | Adds a CLI example for publishing/reconstructing payloads via PIP events. |
| crates/nostr/Cargo.toml | Adds nostr-sdk dev-dependency and registers the nip-pip example. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| for slice in batch.packets { | ||
| let event = packet_slice_to_event(&slice, &manifest.root)?.finalize(keys)?; | ||
| let event_id = event.id.to_string(); | ||
|
|
||
| let output = client.send_event(&event).to([relay]).await?; | ||
| packet_event_ids.push(event.id.clone()); |
| pub fn packet_slice_to_event( | ||
| slice: &ProtocolSlice, | ||
| ) -> Result<EventBuilder, serde_json::Error> { | ||
| let type_tag = if slice.is_parity { "parity" } else { "data" }; | ||
|
|
||
| Ok(EventBuilder::new(PIP_SLICE_KIND, serde_json::to_string(slice)?) | ||
| .tag(Tag::identifier(slice.id.clone())) | ||
| .tag(Tag::custom("seq", [slice.header.seq_num.to_string()])) | ||
| .tag(Tag::custom("path", [slice.id.clone()])) |
| /// Recursively split a payload into MTU-safe slices and parity frames. | ||
| /// | ||
| /// Leaf packets are emitted when the payload is at or below | ||
| /// [`MAX_LEAF_PAYLOAD`]. Internal nodes emit left, right, and parity frames in | ||
| /// a deterministic order so the manifest and sequence numbers line up exactly. | ||
| pub fn process_slice(id: String, data: Vec<u8>, seq: &mut u32) -> Vec<ProtocolSlice> { | ||
| if data.len() <= MAX_LEAF_PAYLOAD { | ||
| let slice = ProtocolSlice { | ||
| id, | ||
| header: Header { | ||
| seq_num: *seq, | ||
| total_packets: 0, | ||
| }, | ||
| data, | ||
| is_parity: false, | ||
| }; | ||
| *seq += 1; | ||
| return vec![slice]; | ||
| } | ||
|
|
||
| let half = data.len() / 2; | ||
| let left_data = data[..half].to_vec(); | ||
| let right_data = data[half..].to_vec(); | ||
|
|
||
| let parity = calculate_parity(&left_data, &right_data); |
| pub fn persist(&self, path: impl AsRef<Path>) -> io::Result<()> { | ||
| let encoded = serde_json::to_vec(&self.received_slices) | ||
| .map_err(|error| io::Error::new(io::ErrorKind::Other, error))?; | ||
| std::fs::write(path, encoded) | ||
| } |
| #[test] | ||
| fn dump_full_protocol_for_nocapture() { | ||
| use hashes::{Hash, sha1::Hash as Sha1Hash}; |
| use nostr::prelude::*; | ||
| use nostr_sdk::prelude::Client; |
There was a problem hiding this comment.
I am aware - I will move all this
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1376 +/- ##
==========================================
+ Coverage 72.43% 72.66% +0.23%
==========================================
Files 202 203 +1
Lines 33143 34213 +1070
==========================================
+ Hits 24007 24861 +854
- Misses 9136 9352 +216
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
packet round trip: push -> fetch -> reconstruct Untitled2.mov |
7d85d61 to
9e9e727
Compare
9e9e727 to
a32b27e
Compare
related nip proposal:
nostr-protocol/nips#2364