Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* [BREAKING][store] The SQLite store now stores account IDs as serialized `BLOB` columns instead of hex `TEXT` ([#2309](https://github.com/0xMiden/rust-sdk/pull/2309)).
* [BREAKING][param][store] `Store::insert_block_header` now takes a `nodes` argument and persists the header with its MMR authentication nodes in a single transaction; the standalone `Store::insert_partial_blockchain_nodes` is removed. Header-only inserts (e.g. genesis) pass an empty slice ([#2294](https://github.com/0xMiden/rust-sdk/pull/2294)).
* [BREAKING][behavior][store] The `ConsumedExternal` note-metadata layout added in [#2308](https://github.com/0xMiden/rust-sdk/pull/2308) is now the only supported serialized format. The backward-compatible decoding of the older metadata-less layout is removed, so existing stores are not compatible and must be recreated ([#2313](https://github.com/0xMiden/rust-sdk/pull/2313)).
* [BREAKING][rust] The `NoteObserver` trait is removed and folded into `OnNoteReceived`: `on_note_received` now takes `committed_note` and `public_note` by reference plus a new `attachments: Option<&NoteAttachments>` argument, and the trait gains defaulted `name()` and post-sync `apply()` methods. `StateSync` now holds a list of `OnNoteReceived` observers (`StateSync::with_note_observer` now takes `Arc<dyn OnNoteReceived>`), and `NoteUpdateAction` gains an `Observe` variant that marks a block relevant without storing the note; when multiple observers vote on a note the verdicts fold by precedence (`Commit` > `Insert` > `Observe` > `Discard`) ([#2279](https://github.com/0xMiden/rust-sdk/issues/2279)).

### Fixes

Expand Down
2 changes: 1 addition & 1 deletion crates/rust-client/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ pub enum ClientError {

/// Logs a non-fatal observer failure without propagating it, so one observer
/// can't abort the others or the surrounding sync/transaction step. Shared by
/// the `NoteObserver` and `TransactionObserver` fan-out loops.
/// the `OnNoteReceived` and `TransactionObserver` fan-out loops.
pub(crate) fn log_observer_failure(
observer: &'static str,
op: &str,
Expand Down
13 changes: 7 additions & 6 deletions crates/rust-client/src/note/note_screener.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use alloc::vec::Vec;

use async_trait::async_trait;
use miden_protocol::account::{AccountCode, AccountId};
use miden_protocol::note::{Note, NoteId};
use miden_protocol::note::{Note, NoteAttachments, NoteId};
use miden_standards::note::NoteConsumptionStatus;
use miden_tx::{
NoteCheckerError,
Expand Down Expand Up @@ -195,8 +195,9 @@ impl OnNoteReceived for NoteScreener {
/// to check its relevance.
async fn on_note_received(
&self,
committed_note: CommittedNote,
public_note: Option<InputNoteRecord>,
committed_note: &CommittedNote,
public_note: Option<&InputNoteRecord>,
_attachments: Option<&NoteAttachments>,
) -> Result<NoteUpdateAction, ClientError> {
let note_id = *committed_note.note_id();

Expand Down Expand Up @@ -224,7 +225,7 @@ impl OnNoteReceived for NoteScreener {

if input_note_present || output_note_present {
// The note is being tracked by the client so it is relevant
return Ok(NoteUpdateAction::Commit(committed_note));
return Ok(NoteUpdateAction::Commit(committed_note.clone()));
}

match public_note {
Expand All @@ -233,7 +234,7 @@ impl OnNoteReceived for NoteScreener {
if let Some(metadata) = public_note.metadata()
&& self.store.get_unique_note_tags().await?.contains(&metadata.tag())
{
return Ok(NoteUpdateAction::Insert(public_note));
return Ok(NoteUpdateAction::Insert(public_note.clone()));
}

// The note is not being tracked by the client and is public so we can screen it
Expand All @@ -247,7 +248,7 @@ impl OnNoteReceived for NoteScreener {
.await?;
let is_relevant = !new_note_relevance.is_empty();
if is_relevant {
Ok(NoteUpdateAction::Insert(public_note))
Ok(NoteUpdateAction::Insert(public_note.clone()))
} else {
Ok(NoteUpdateAction::Discard)
}
Expand Down
31 changes: 16 additions & 15 deletions crates/rust-client/src/pswap/observer.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Per-note observer that collects every PSWAP-attachment note seen
//! during sync. Lineage-scope filtering happens later, in `discovery`.
//! Per-note [`OnNoteReceived`](crate::sync::OnNoteReceived) observer that collects every
//! PSWAP-attachment note seen during sync. Lineage-scope filtering happens later, in `discovery`.

use alloc::boxed::Box;
use alloc::sync::Arc;
Expand All @@ -15,24 +15,24 @@ use crate::ClientError;
use crate::pswap::discovery::discover_pswap_rounds;
use crate::pswap::lineage::ObservedPswapNote;
use crate::rpc::domain::note::CommittedNote;
use crate::store::Store;
use crate::sync::NoteObserver;
use crate::store::{InputNoteRecord, Store};
use crate::sync::{NoteUpdateAction, OnNoteReceived};
use crate::utils::RwLock;

// PSWAP CHAIN OBSERVER
// ================================================================================================

/// Per-sync collector of PSWAP-attachment notes seen this sync.
///
/// - `observe()` runs per-note during sync: reads the PSWAP attachment word straight off the note's
/// resolved attachments (carried inline on the sync window) and records a `ObservedPswapNote`. No
/// RPC round trip, no DB write.
/// - `on_note_received()` runs per-note during sync: reads the PSWAP attachment word straight off
/// the note's resolved attachments (carried inline on the sync window), records an
/// `ObservedPswapNote`, and votes [`NoteUpdateAction::Observe`] so the block is retained.
/// - `apply()` runs once post-sync: drains the collector, runs the correlator, applies round
/// updates.
pub struct PswapChainObserver {
store: Arc<dyn Store>,
/// `observe()` writes, `apply()` drains; never concurrent. The observer is
/// shared via the outer `Arc<dyn NoteObserver>` and only ever touched
/// `on_note_received()` writes, `apply()` drains; never concurrent. The observer is
/// shared via the outer `Arc<dyn OnNoteReceived>` and only ever touched
/// through `&self`, so the `RwLock` alone provides the needed interior
/// mutability — no inner `Arc`.
chain_note_updates: RwLock<Vec<ObservedPswapNote>>,
Expand All @@ -48,23 +48,24 @@ impl PswapChainObserver {
}

#[async_trait(?Send)]
impl NoteObserver for PswapChainObserver {
impl OnNoteReceived for PswapChainObserver {
fn name(&self) -> &'static str {
"PswapChainObserver"
}

async fn observe(
async fn on_note_received(
&self,
committed_note: &CommittedNote,
_public_note: Option<&InputNoteRecord>,
attachments: Option<&NoteAttachments>,
) -> Result<bool, ClientError> {
) -> Result<NoteUpdateAction, ClientError> {
// Notes without a PSWAP attachment are the common case; `extract_pswap_attachment`
// fast-rejects them. Foreign-order filtering happens later in `discovery`.
let Some(attachments) = attachments else {
return Ok(false);
return Ok(NoteUpdateAction::Discard);
};
let Some(attachment) = extract_pswap_attachment(attachments) else {
return Ok(false);
return Ok(NoteUpdateAction::Discard);
};

let inclusion_proof = committed_note.inclusion_proof().clone();
Expand All @@ -76,7 +77,7 @@ impl NoteObserver for PswapChainObserver {
block_num: inclusion_proof.location().block_num(),
inclusion_proof,
});
Ok(true)
Ok(NoteUpdateAction::Observe)
}

/// Drains the collector, runs the correlator, applies round updates.
Expand Down
5 changes: 1 addition & 4 deletions crates/rust-client/src/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,6 @@ mod block_header;
mod tag;
pub use tag::{NoteTagRecord, NoteTagSource};

mod note_observer;
pub use note_observer::NoteObserver;

mod state_sync;
pub use state_sync::{NoteUpdateAction, OnNoteReceived, StateSync, StateSyncInput};

Expand Down Expand Up @@ -120,7 +117,7 @@ where
self.ensure_genesis_in_place().await?;
self.ensure_rpc_limits_in_place().await?;

// Each `NoteObserver` owns its own per-sync state; `with_note_observer` just attaches.
// Each observer owns its own per-sync state; `with_note_observer` just attaches.
let note_screener = self.note_screener();
let state_sync =
StateSync::new(self.rpc_api.clone(), Arc::new(note_screener), self.tx_discard_delta)
Expand Down
36 changes: 0 additions & 36 deletions crates/rust-client/src/sync/note_observer.rs

This file was deleted.

Loading
Loading