Skip to content

Commit bf5b9e4

Browse files
moconn68matheus23
andauthored
docs: Update chat example to add a nonce (#324) (#325)
* docs: Update chat example to add a nonce (#324) * Update src/app/docs/examples/gossip-chat/page.mdx Co-authored-by: Philipp Krüger <[email protected]> --------- Co-authored-by: Philipp Krüger <[email protected]>
1 parent 8de1ce8 commit bf5b9e4

File tree

1 file changed

+47
-18
lines changed
  • src/app/docs/examples/gossip-chat

1 file changed

+47
-18
lines changed

src/app/docs/examples/gossip-chat/page.mdx

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,9 @@ And let's write a `Message::Message` that has a `String` with the actual chat me
184184

185185
Also, we want each of those messages to include the `NodeId` of the sender. In an actual application, we would encode and decode the messages with keypairs to ensure that everyone who sends a message is actually who they say they are. For more on that, check out our more robust chat example that exists in the [`iroh-gossip`](https://github.com/n0-computer/iroh-gossip/blob/main/examples/chat.rs) repo.
186186

187+
In addition, the nature of the gossip protocol could potentially cause messages to be sent multiple times. This is done intentionally, to ensure at-least-once delivery of each message to all nodes. This behavior is unexpected in most app contexts, so iroh will internally deduplicate messages based on the hash of their contents.
188+
In this case, if someone sends re-sends a message they already sent, it will be ignored by the other peeres. To circumvent this, each message should include a piece of unique data to prevent this deduplication. This can be done in a number of ways - we will use a [cryptographic nonce](https://en.wikipedia.org/wiki/Cryptographic_nonce).
189+
187190
We need to add crates that will allow us to serialize our new message types as bytes and deserialize bytes as our message type.
188191

189192
`serde` stands for `Serialize/Deserialize`. `serde-json` lets us easily encode and decode to the json format, but we can choose other formats. E.g., in the `iroh-gossip` example, we use `postcard`.
@@ -205,7 +208,13 @@ use serde::{Deserialize, Serialize};
205208

206209
// add the message code to the bottom
207210
#[derive(Debug, Serialize, Deserialize)]
208-
enum Message {
211+
struct Message {
212+
body: MessageBody,
213+
nonce: [u8; 16],
214+
}
215+
216+
#[derive(Debug, Serialize, Deserialize)]
217+
enum MessageBody {
209218
AboutMe { from: NodeId, name: String },
210219
Message { from: NodeId, text: String },
211220
}
@@ -215,6 +224,13 @@ impl Message {
215224
serde_json::from_slice(bytes).map_err(Into::into)
216225
}
217226

227+
pub fn new(body: MessageBody) -> Self {
228+
Self {
229+
body,
230+
nonce: rand::random(),
231+
}
232+
}
233+
218234
pub fn to_vec(&self) -> Vec<u8> {
219235
serde_json::to_vec(self).expect("serde_json::to_vec is infallible")
220236
}
@@ -229,10 +245,10 @@ sender.broadcast("sup".into()).await?;
229245

230246
// with:
231247
// Create an "about me" message
232-
let message = Message::AboutMe {
248+
let message = Message::new(MessageBody::AboutMe {
233249
from: endpoint.node_id(),
234250
name: String::from("alice"),
235-
};
251+
});
236252
// Turn the message into a `Vec`, and then use
237253
// `into` to coerse the `Vec` into `Bytes`
238254
sender.broadcast(message.to_vec().into()).await?;
@@ -273,15 +289,15 @@ async fn subscribe_loop(mut receiver: GossipReceiver) -> Result<()> {
273289
if let Event::Gossip(GossipEvent::Received(msg)) = event {
274290
// deserialize the message and match on the
275291
// message type:
276-
match Message::from_bytes(&msg.content)? {
277-
Message::AboutMe { from, name } => {
292+
match Message::from_bytes(&msg.content)?.body {
293+
MessageBody::AboutMe { from, name } => {
278294
// if it's an `AboutMe` message
279295
// add and entry into the map
280296
// and print the name
281297
names.insert(from, name.clone());
282298
println!("> {} is now known as {}", from.fmt_short(), name);
283299
}
284-
Message::Message { from, text } => {
300+
MessageBody::Message { from, text } => {
285301
// if it's a `Message` message,
286302
// get the name from the map
287303
// and print the message
@@ -334,10 +350,10 @@ async fn main() -> Result<()> {
334350

335351
let (sender, receiver) = gossip.subscribe(id, node_ids)?.split();
336352

337-
let message = Message::AboutMe {
353+
let message = Message::new(MessageBody::AboutMe {
338354
from: endpoint.node_id(),
339355
name: String::from("alice"),
340-
};
356+
});
341357
sender.broadcast(message.to_vec().into()).await?;
342358

343359
// subscribe and print loop
@@ -395,10 +411,10 @@ println!("> type a message and hit enter to broadcast...");
395411
// listen for lines that we have typed to be sent from `stdin`
396412
while let Some(text) = line_rx.recv().await {
397413
// create a message from the text
398-
let message = Message::Message {
414+
let message = Message::new(MessageBody::Message {
399415
from: endpoint.node_id(),
400416
text: text.clone(),
401-
};
417+
});
402418
// broadcast the encoded message
403419
sender.broadcast(message.to_vec().into()).await?;
404420
// print to ourselves the text that we sent
@@ -614,10 +630,10 @@ async fn main() -> Result<()> {
614630

615631
// broadcast our name, if set
616632
if let Some(name) = args.name {
617-
let message = Message::AboutMe {
633+
let message = Message::new(MessageBody::AboutMe {
618634
from: endpoint.node_id(),
619635
name,
620-
};
636+
});
621637
sender.broadcast(message.to_vec().into()).await?;
622638
}
623639

@@ -635,10 +651,10 @@ async fn main() -> Result<()> {
635651
// listen for lines that we have typed to be sent from `stdin`
636652
while let Some(text) = line_rx.recv().await {
637653
// create a message from the text
638-
let message = Message::Message {
654+
let message = Message::new(MessageBody::Message {
639655
from: endpoint.node_id(),
640656
text: text.clone(),
641-
};
657+
});
642658
// broadcast the encoded message
643659
sender.broadcast(message.to_vec().into()).await?;
644660
// print to ourselves the text that we sent
@@ -650,7 +666,13 @@ async fn main() -> Result<()> {
650666
}
651667

652668
#[derive(Debug, Serialize, Deserialize)]
653-
enum Message {
669+
struct Message {
670+
body: MessageBody,
671+
nonce: [u8; 16],
672+
}
673+
674+
#[derive(Debug, Serialize, Deserialize)]
675+
enum MessageBody {
654676
AboutMe { from: NodeId, name: String },
655677
Message { from: NodeId, text: String },
656678
}
@@ -660,6 +682,13 @@ impl Message {
660682
serde_json::from_slice(bytes).map_err(Into::into)
661683
}
662684

685+
pub fn new(body: MessageBody) -> Self {
686+
Self {
687+
body,
688+
nonce: rand::random(),
689+
}
690+
}
691+
663692
pub fn to_vec(&self) -> Vec<u8> {
664693
serde_json::to_vec(self).expect("serde_json::to_vec is infallible")
665694
}
@@ -675,15 +704,15 @@ async fn subscribe_loop(mut receiver: GossipReceiver) -> Result<()> {
675704
if let Event::Gossip(GossipEvent::Received(msg)) = event {
676705
// deserialize the message and match on the
677706
// message type:
678-
match Message::from_bytes(&msg.content)? {
679-
Message::AboutMe { from, name } => {
707+
match Message::from_bytes(&msg.content)?.body {
708+
MessageBody::AboutMe { from, name } => {
680709
// if it's an `AboutMe` message
681710
// add and entry into the map
682711
// and print the name
683712
names.insert(from, name.clone());
684713
println!("> {} is now known as {}", from.fmt_short(), name);
685714
}
686-
Message::Message { from, text } => {
715+
MessageBody::Message { from, text } => {
687716
// if it's a `Message` message,
688717
// get the name from the map
689718
// and print the message

0 commit comments

Comments
 (0)