Skip to content

Conversation

v0-e
Copy link
Collaborator

@v0-e v0-e commented Aug 1, 2025

Repo scaffolding with initial logic, mainly to provide a base for higher level discussions.

Client <-> Server/Node communications are gRPC.
Server is labelled as Node -- thinking of future decentralization.
Encryption is currently AES. Asymmetric encryption to be implemented.

Crates

  • proto: Protobuf definitions used and generated Rust code;
  • transport: Client and Node implementations. To be split into two further crates.

Current flow

Send note

  1. Client sends a Note. A Note is split into two parts, the NoteHeader and NoteDetails. The NoteDetails are sent encrypted;
  2. Server receives split Note, uniquely identified by its NoteId. Stores Note (split into several columns, including its NoteTag) in local database (currently SQLite).

Fetch notes

  1. Client requests notes by a NoteTag;
  2. Server responds with all notes with that tag;
  3. Client tries to decrypt notes (i.e., the NoteDetails of each response entry), ignoring those which decryption fails.

Mark received

  1. Client requests a note to be marked "received" through its NoteId (single request, can be a batch). A UserId is also sent (no current authentication);
  2. Server marks note as received by that UserId. A marked note should not be sent again when fetching notes, though this is to be implemented.

Discussions

  • Stored data: The NoteHeader is stored in plain-text, containing the sender ID. Does this raise any privacy concerns?
  • Encryption: Any preferred asymmetric encryption algorithm?
  • Authentication: Useful for marking notes / avoiding users with bad intent to mark notes of other users as received. Potentially ZK can be useful here, e.g.: I've received this note, it was for me. (the receiver ID is in the encrypted part of the note)

@v0-e v0-e requested review from mmagician and maksimryndin August 1, 2025 14:59
@v0-e
Copy link
Collaborator Author

v0-e commented Aug 4, 2025

Update: added filtering of fetched notes based on the "mark-received" interface.

As of now, the marking of notes is an optional and QoL feature.
The client may mark notes as received by using a user ID. This will make the server not reply with notes that are marked on new fetch requests.

Since there is no authentication currently, the user ID is by default a 16-byte random identifier (UUID), optionally also defined by the client. The randomness here helps a bit on privacy and unlinkability, and also avoids users with Mal-intent to mark other users' notes as marked.

In a decentralized setting, we may not to broadcast marking updates, leaving this feature state specific and isolated to each server as it is likely that users will maintain connections to a (single) preferred node of their choice.

Copy link
Collaborator

@maksimryndin maksimryndin left a comment

Choose a reason for hiding this comment

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

LGTM!

after this initial pr let's move with smaller ones :)

@mmagician
Copy link
Collaborator

Client requests a note to be marked "received" through its NoteId

Isn't this implied by the payload sent in response to fetch_notes? The node knows exactly which NoteIds it sent to the user, so I don't think there's a need for the user to communicate that again via a separate request. Lmk if I'm misunderstanding its purpose.

What we do want is preventing the repeat download of the same notes by the user. I think the first option from this comment should achieve this via a combination of passing the NoteTag and the block_num.

@v0-e
Copy link
Collaborator Author

v0-e commented Aug 5, 2025

Thank you @mmagician for taking a look!

Isn't this implied by the payload sent in response to fetch_notes? The node knows exactly which NoteIds it sent to the user, so I don't think there's a need for the user to communicate that again via a separate request. Lmk if I'm misunderstanding its purpose.

Indeed that would be a more natural flow.
The separation here was basic effort for privacy, where the user may not want to use identifiers (thinking a bit of the proposal future feature: "Privacy: Only Alice and Bob know that a transfer happened.").
The user may not send its UserId on a fetch_notes request. This way the server will not keep track of who read notes of specific tags.

What we do want is preventing the repeat download of the same notes by the user. I think the first option from 0xMiden/miden-proposals#4 (comment) should achieve this via a combination of passing the NoteTag and the block_num.

We can replace the marking of notes feature for this offset approach. It was actually implemented before this 👍
The marking of notes seemed slightly more user-friendly as the user wouldn't have to "guess" block numbers.

Just confirm if you prefer the block_num approach, and I'll switch to that mechanism 👍

@mmagician
Copy link
Collaborator

The separation here was basic effort for privacy, where the user may not want to use identifiers (thinking a bit of the 0xMiden/miden-proposals#4 future feature: "Privacy: Only Alice and Bob know that a transfer happened.").

Let me provide some context: NoteTags are a way for users to determine the privacy level they're willing to expose. Currently, for notes that are meant for local execution (think: client-side, as opposed to consumable by what we call "network accounts") we put 14 bits of the AccountId in the tag. But, once we introduce the Address struct, we will add a little more flexibility on how much information to reveal (with tag_len or similar). Here's a concrete example of A sending tokens -> B:

  1. Alice wants to send some tokens to Bob. She scans Bob's Address QR code (or otherwise looks it up in a public registry).
  2. She sees that Bob's tag_len = 2. She sets the NoteTag to the top 2 bits of Bob's AccountId (the rest are 0 or random). This provides a lot of privacy for Bob.
  3. Alice then encrypts the Note to Bob's encryption_key, and uses the private note transport layer to share it with him.
  4. Bob periodically calls fetch_notes(NoteTag::from(bobs_account_id, 2)). This results in a lot of downloads on Bob's end (roughly 25% of all notes will match the first 2 bits), but again - the purpose is that it provides a lot of privacy. Neither the operators of private note transport layer, nor other users (who can also query for notes with the same tags, incl. maliciously) can really figure out which ones are meant for Bob.
  5. Bob tries to decrypt all the notes he downloads, in the hope that some of the notes from Alice to him.

The user may not send its UserId on a fetch_notes request. This way the server will not keep track of who read notes of specific tags.

But if they call mark_received, this exposes the same level of information, I believe?


The marking of notes seemed slightly more user-friendly as the user wouldn't have to "guess" block numbers.

This is a valid point. The client would need to internally keep track of the block numbers. I think this is reasonable though, since the Miden wallet (for private accounts) by design already stores the account data locally. I've created an issue for this.

@v0-e
Copy link
Collaborator Author

v0-e commented Aug 5, 2025

Thank you for describing the flow. As it stands the implementation follows that logic, with the exception of the tag-length mechanism.

But if they call mark_received, this exposes the same level of information, I believe?

Indeed, it would be somewhat equivalent. That is why it would be an optional feature. I see the UserId as a random session identifier, just for the marked notes. It wouldn't be related with any other user IDs (account ID, etc.).

This is a valid point. The client would need to internally keep track of the block numbers. I think this is reasonable though, since the Miden wallet (for private accounts) by design already stores the account data locally. I've created an #6.

I like the design, however if the sender sends notes out-of-order, an older note will be ignored by the receiver.

Nevertheless, I'll work on a version of the implementation focused on the block-number approach/marking feature removed 👍

@v0-e v0-e merged commit c3193ff into main Aug 18, 2025
12 checks passed
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.

3 participants