Sender-controlled block_hint with no upper bound can permanently hide a committed note from sync
- Langage dominant
- Rust
- Étoiles
- 78
- Forks
- 129
- Merge moyen
- 4 j 14 h
- PR mergées (30 j)
- 52
Description
A note-transport sender controls the `block_hint` (`after_block_num`) attached to a relayed note, and this value is trusted without any bound. If it's set to a block number past the block where the note is actually committed, the note becomes permanently unreachable by sync - not just delayed.
Trace:
- `NoteInfo::block_hint` (`crates/rust-client/src/note_transport/mod.rs`) is sender-supplied metadata: "Sender-provided block hint... `None` when the sender did not provide a hint." It travels from `grpc.rs`'s `after_block_num` straight from the wire, unvalidated.
- `fetch_transport_notes` uses it directly: `let after_block_num = block_hint.unwrap_or(fallback_after_block_num);` - no upper bound check against current sync height or anything else.
- This becomes `NoteSyncHint::after_block_num`, which flows into `import_note_records_by_details` (`crates/rust-client/src/note/import.rs`) as `lowest_request_block`, passed to `sync_expected_notes`.
- `sync_expected_notes` scans `[request_block_num, current_block_num]` via `sync_notes_with_content`. If `request_block_num` is *higher* than the block the note actually committed at, that block is outside the scanned window - permanently, since the scan never looks backward on a later sync, it just moves `request_block_num` = the hint further from the note's real location as the client keeps trusting the same (wrong) hint.
I confirmed this with a test (`expected_note_with_block_hint_past_its_commit_block_is_never_found` in `crates/testing/miden-client-tests/src/tests.rs`): a P2ID note commits at block 1, is imported with a block hint of 11 (past its real commit block), and the client syncs 15 more times, advancing well past block 11. The note stays stuck in `Expected` state the entire time - it's never found, even though it has been on-chain since block 1 and the client's sync height has long since passed the hint.
Impact: this isn't limited to a hostile sender - any bug, clock skew, or stale-metadata issue on the sending or relaying side that produces a block hint higher than the note's actual commit block silently and permanently hides that note from the recipient's client. Since notes carry funds/state that must be actively consumed to matter, this is effectively a fund-visibility / griefing vector that a compromised or buggy relay could trigger by tampering with unauthenticated metadata, even if note *details* are end-to-end encrypted between sender and recipient.
Not proposing a specific fix here since there's a real design tradeoff (the hint exists for efficiency, and validating it against ground truth defeats some of that purpose), but as things stand there's no fallback at all: once a hint overshoots, there's no retry with a wider/lookback-based window, no cap on how far ahead of already-synced state a hint is trusted, nothing. Worth discussing what the right recovery mechanism should be - happy to help with either the fix or the regression test once there's agreement on approach.
Guide de contribution
Ouvrir le guide de contribution
Piste de recherche
The issue is in crates/rust-client/src/note_transport/mod.rs and crates/rust-client/src/note/import.rs. Start by examining the NoteInfo::block_hint field and how fetch_transport_notes uses it. Look at the sync_expected_notes function to understand the scanning logic. The test case expected_note_with_block_hint_past_its_commit_block_is_never_found in crates/testing/miden-client-tests/src/tests.rs reproduces the bug. A fix would involve adding a bound or recovery mechanism for the block_hint value.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- rust
- Domaine
- backend, blockchain
- Type d'issue
- Bug
- Difficulté
- 3/5
- Temps estimé
- 1-2 jours
- Activité
- Calme
- Clarté
- Clairement spécifiée
- Accessibilité débutants
- 55/100