sendPrivateNote relays sync height as the block hint, overshooting the commitment block (silent non-delivery on fast chains)
- Dominant language
- TypeScript
- Stars
- 1
- Forks
- 21
- Avg merge
- 12h 14m
- Merged PRs (30d)
- 41
Description
## Problem
`WebClient::send_private_note` (`crates/web-client/src/note_transport.rs`) relays the client's **current sync height** as the note's `after_block_num` hint:
```rust
let block_hint = client.get_sync_height().await?;
client.send_private_note_with_block_hint(note.into(), &address.into(), block_hint).await?;
```
The recipient imports the note as an `ExpectedNote` and scans **forward** from `after_block_num` for the note's on-chain commitment. Sync height is only a correct hint while it's **below** the commitment block `C` (i.e. the note isn't committed yet). If `sendPrivateNote` is called once the note is already committed and the client has synced to/past `C` — e.g. the caller waits for commit before relaying, or a background sync advanced the tip — then `hint ≥ C`, the recipient scans past the commitment, and **never finds the note**. Delivery silently fails.
This is latent on slow chains (sync height stays ≈ `C` at relay) and deterministic on fast devnets.
## Evidence
Reproduced in the wallet e2e against a fast custom devnet: private send→receive timed out every run (recipient balance stayed 0; the note was on the transport layer but the recipient never bound the commitment). The wallet worked around it by relaying **before** commit (0xMiden/wallet#502), but the SDK should give a correct hint regardless of *when* the caller relays.
## Proposed fix
Derive the hint from the note's **actual commitment block** when the note is committed, falling back to sync height when it isn't:
```rust
// prefer the note's on-chain commitment block (exact hint); fall back to sync
// height for the prompt-relay-before-commit path (still below the commitment).
let block_hint = note_commitment_block(client, note.id()).await? // output/input record inclusion proof
.unwrap_or(client.get_sync_height().await?);
```
`Client` already exposes `get_output_note(id)` / `get_input_note(id)`, whose records carry `inclusion_proof().location().block_num()`. This matches what the miden-client CLI's `notes --send` already does (`inclusion_proof().location().block_num()`), so the web SDK would reach parity.
## Alternative
Centralize the derivation in the rust-sdk note-transport relay so web / CLI / node clients stay consistent instead of each computing (or mis-computing) the hint.
Wallet-side workaround already merged/queued: 0xMiden/wallet#502 (relay before commit).
Contributor guide
Research direction
The issue is in `crates/web-client/src/note_transport.rs` in the `send_private_note` function. First, examine the `Client` methods `get_output_note(id)` and `get_input_note(id)` to access the note's inclusion proof and its block number. The fix involves using the commitment block from the proof as the hint, falling back to sync height. Run the wallet e2e tests against a fast devnet to verify the fix resolves the silent delivery failure.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust, typescript
- Domain
- backend, blockchain
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 65/100