Skip to content

fix(store): stop registering note tags for output notes#2323

Merged
igamigo merged 11 commits into
mainfrom
remove-output-note-tags
Jul 16, 2026
Merged

fix(store): stop registering note tags for output notes#2323
igamigo merged 11 commits into
mainfrom
remove-output-note-tags

Conversation

@igamigo

@igamigo igamigo commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator
  • Output notes no longer register note tags, avoiding one leaked note_tags row per created note. A migration removes leaked tags, preserves those still needed by pending input notes, deduplicates rows, and adds a unique (tag, source) index.
  • Added is_inclusion_pending getters to InputNoteRecord and OutputNoteRecord.
  • Backported fix(sync): bind public account proofs to sync target #2255 from next, pinning public-account get_account requests to the sync target block. This fixes the race that caused newly committed transactions to be marked Superseded, wedging sole-writer accounts and triggering the July 13 testnet faucet outage.
  • Extended MockRpcApi with per-block account states and added an end-to-end regression test for the outage.

igamigo and others added 5 commits July 14, 2026 23:48
…ked ones

Output notes are committed (with inclusion proofs) through account-matched
transaction sync, so their tags were dead weight, and with tag cleanup only
covering input notes they leaked one row per created note. A store migration
prunes the leaked tags (keeping those still needed by pre-inclusion input
notes), collapses duplicate rows, and adds a unique (tag, source) index.
Sync-time cleanup now also removes tags of input notes consumed within the
same sync, and migration schema versions and integrity hashes are committed
atomically via migration hooks.
Expresses the "note sync can still advance this record" predicate used by
tag cleanup and erased-note handling, mirroring the prune migration's guard.
Also drops the custom-sync warning from sync_state: accounts cannot be
removed from a client, so the StateSyncInput doc contract is sufficient.
…sync delta

Extends MockRpcApi with per-block account states (set_account_state_at) so
get_account can answer block-pinned queries with a state other than the
chain's latest, and adds a regression test reproducing the 2026-07-13
testnet faucet outage scenario: the client's own transaction commits
between the sync delta snapshot and the account fetch. With the fetch
pinned to the sync target the update is correctly ignored; with an
unpinned (chain tip) fetch the equal-nonce branch would falsely discard
the committed transaction as Superseded (verified: the test fails if the
pin is reverted).
@igamigo
igamigo force-pushed the remove-output-note-tags branch from dd24a83 to e1d641d Compare July 16, 2026 14:56
@igamigo
igamigo marked this pull request as ready for review July 16, 2026 15:29
Comment on lines -493 to -497
new_tags.extend(note_updates.updated_output_notes().map(|note| {
let note = note.inner();
NoteTagRecord::with_note_source(note.metadata().tag(), note.details_commitment())
}));

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is the real fix for the original problem

Comment on lines 770 to 780
let (proof_block_num, proof) = self
.rpc_api
.get_account(
account_id,
GetAccountRequest::new()
.at(AccountStateAt::Block(target_block_num))
.with_storage(StorageMapFetch::All)
.with_vault(VaultFetch::Always),
)
.await
.map_err(ClientError::RpcError)?;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is the backported fix from next

Comment on lines +64 to +69
/// Historical account states served by `get_account`, keyed by (block number, account id).
/// The [`MockChain`] only keeps the latest committed account state, so tests that need
/// `get_account` to answer block-pinned queries (`AccountStateAt::Block`) with a state
/// other than the latest one register the per-block state here. Queries for a block with
/// no registered entry fall back to the chain's latest committed account.
account_states_at: Arc<RwLock<BTreeMap<(BlockNumber, AccountId), Account>>>,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Added this mechanism so that the mockchain-backed client can serve requests for older account states

Comment thread crates/rust-client/src/sync/state_sync.rs
Comment thread crates/sqlite-store/src/db_management/utils.rs
/// The prune migration deletes output-note tags (and duplicates) but keeps tags still
/// needed by pre-inclusion input notes, input-only tags, and non-`Note`-sourced tags.
#[test]
fn migration_prunes_output_note_tags() {

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.

nice 🥇

Comment thread crates/rust-client/src/sync/state_sync.rs Outdated
@igamigo

igamigo commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator Author

I ended up simplifying the test setup a bit more on 5252bca, removing the need for the MockRpcClient::set_account_state_at() helper which was a bit hacky

@igamigo
igamigo merged commit 0e50bff into main Jul 16, 2026
23 checks passed
@igamigo
igamigo deleted the remove-output-note-tags branch July 16, 2026 20:39
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.

4 participants