0xMiden / 0xMiden/web-sdk

sendPrivateNote relays sync height as the block hint, overshooting the commitment block (silent non-delivery on fast chains)

Open
#262 2 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.