Skip to content

Include sender information on missing messages#120

Merged
kaichaosun merged 9 commits into
mainfrom
sender-wrap
Jun 2, 2026
Merged

Include sender information on missing messages#120
kaichaosun merged 9 commits into
mainfrom
sender-wrap

Conversation

@kaichaosun
Copy link
Copy Markdown
Contributor

In previous PR #105, we implemented causal history for detecting missing message.
This PR introduce extra sender_id to detect who the missing messages belongs to.

Fix #98


ReliablePayload {
message_id,
message_id: encoded_info,
Copy link
Copy Markdown
Contributor Author

@kaichaosun kaichaosun May 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In previous discussion, we decided to reuse the message_id in ReliablePayload protobuf for sender info.
Future improvements includes wire format naming and frontiers calculations is planned later.

@kaichaosun kaichaosun requested review from jazzz and osmaczko May 26, 2026 08:47
Copy link
Copy Markdown
Collaborator

@jazzz jazzz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change looks good.

My comments are all future considerations.

use crate::utils::{blake2b_hex, hash_size};

/// `hash_size::MessageId` is U32 (32 bytes) → 64 hex chars.
const MESSAGE_ID_HEX_LEN: usize = 64;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Sand] As hash_size are defined at compile time, you can derive constants from them.

use blake2::digest::typenum::Unsigned;
const MESSAGE_ID_HEX_LEN: usize = 2 * <hash_size::MessageId as hash_size::HashLen>::Size::USIZE;

Deriving it would allow it to be automatically updated should the value change in the future. Thought its not the prettyiest syntax currently. Your call.


/// Render to the wire-form string used by the `message_id` proto field.
pub fn encode(&self) -> String {
self.to_string()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Dust] Encoding as a String seem unnecessarily wasteful. Bytes would be more compact on the wire.

Copy link
Copy Markdown
Contributor Author

@kaichaosun kaichaosun May 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I try to make it consistent with nim-sds. Worth to make a change later with more communication happened.

I do notice that nim-sds add sender id recently, this is protobuf updated: logos-messaging/chat_proto#9, also updates changes here to use new wire format.

Comment on lines +61 to +70
pub fn decode(s: &str) -> Result<Self, ChatError> {
if s.len() < MESSAGE_ID_HEX_LEN {
return Err(ChatError::BadParsing("MessageId"));
}
let split = s.len() - MESSAGE_ID_HEX_LEN;
Ok(Self {
sender_id: s[..split].to_owned(),
message_id: s[split..].to_owned(),
})
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Sand] Prefer self describing memory layouts, when possible.

This is more of a design principal - but helpful to keep in mind.

This code as written is tightly coupled to the code that produced it - changing MESSAGE_ID_HEX_LEN would be break interop between clients.

Three improved design choices in order of robustness:

  • Deterministic parsing via TLV, such that any recipient can parse the values regardless of how they were generated.
  • Use a defined delimiter such as '|' or ':'. Decouples from payload from size changes but requires consistent order.
  • Use a single byte Sigil to define which parsing format should be used.

Delimiter seems like the right trade off here, given the string implementation and the narrowed scope.

Copy link
Copy Markdown
Contributor Author

@kaichaosun kaichaosun May 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed in new wire format.

Comment on lines 166 to 175
let frontier = Frontier::new(sender.to_string(), message_id);

let causal_history = state
.frontier
.frontiers
.iter()
.cloned()
.map(|message_id| HistoryEntry {
message_id,
.map(|f| HistoryEntry {
message_id: f.encode(),
retrieval_hint: Bytes::new(),
})
.collect();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Dust] consider flattening Frontier to a String with dedicated encode and decode functions.

I love adding types especially when:

  • Types represent a state transition through a pipeline. TypeState pattern.
  • Theres complicated functionality being provided
  • A Type is expected to grow to take on new behavior.

In this case it seems like we may just want to define causual_history::message_id as a string, which is a combination of the sender_id and derived_message_id.

Creating a type that is only used to concat two strings together, distracts from what is really happening.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specifically 'fn derived_message_id` already has access to SenderId and could handle the concat in one step.

Copy link
Copy Markdown
Contributor Author

@kaichaosun kaichaosun May 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the Frontier struct has more meaning after the new wire format.
derived_message_id is isolated as we may want to introduce a consistent reusable function to calculate the identifier going through libchat.

Comment on lines -53 to +119
reported_missing: HashSet<String>,
reported_missing: HashSet<Frontier>,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Dust] I like the removal of naked String types from the code base.

If you decided to flatten Frontier, consider using a type def or NewType wrapper (depending in your vision) to provide the same Type safety/Readability.

@kaichaosun kaichaosun merged commit 4df23aa into main Jun 2, 2026
7 checks passed
@kaichaosun kaichaosun deleted the sender-wrap branch June 2, 2026 13:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Group participants in a conversation can tell if a message is missing, and who sent it.

3 participants