Skip to content

Commit 9198f08

Browse files
review: add CacheNoteScript actor notification
1 parent e0696fd commit 9198f08

3 files changed

Lines changed: 47 additions & 9 deletions

File tree

crates/ntx-builder/src/actor/execute.rs

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@ pub enum NtxError {
7878

7979
type NtxResult<T> = Result<T, NtxError>;
8080

81+
/// The result of a successful transaction execution.
82+
///
83+
/// Contains the transaction ID, any notes that failed during filtering, and note scripts fetched
84+
/// from the remote store that should be persisted to the local DB cache.
85+
pub type NtxExecutionResult = (TransactionId, Vec<FailedNote>, Vec<(Word, NoteScript)>);
86+
8187
// NETWORK TRANSACTION CONTEXT
8288
// ================================================================================================
8389

@@ -138,8 +144,9 @@ impl NtxContext {
138144
///
139145
/// # Returns
140146
///
141-
/// On success, returns the [`TransactionId`] of the executed transaction and a list of
142-
/// [`FailedNote`]s representing notes that were filtered out before execution.
147+
/// On success, returns an [`NtxExecutionResult`] containing the transaction ID, any notes
148+
/// that failed during filtering, and note scripts fetched from the remote store that should
149+
/// be persisted to the local DB cache.
143150
///
144151
/// # Errors
145152
///
@@ -152,7 +159,7 @@ impl NtxContext {
152159
pub fn execute_transaction(
153160
self,
154161
tx: TransactionCandidate,
155-
) -> impl FutureMaybeSend<NtxResult<(TransactionId, Vec<FailedNote>)>> {
162+
) -> impl FutureMaybeSend<NtxResult<NtxExecutionResult>> {
156163
let TransactionCandidate {
157164
account,
158165
notes,
@@ -185,6 +192,9 @@ impl NtxContext {
185192
// Execute transaction.
186193
let executed_tx = Box::pin(self.execute(&data_store, successful_notes)).await?;
187194

195+
// Collect scripts fetched from the remote store during execution.
196+
let scripts_to_cache = data_store.take_fetched_scripts().await;
197+
188198
// Prove transaction.
189199
let tx_inputs: TransactionInputs = executed_tx.into();
190200
let proven_tx = Box::pin(self.prove(&tx_inputs)).await?;
@@ -195,7 +205,7 @@ impl NtxContext {
195205
// Submit transaction to block producer.
196206
self.submit(&proven_tx).await?;
197207

198-
Ok((proven_tx.id(), failed_notes))
208+
Ok((proven_tx.id(), failed_notes, scripts_to_cache))
199209
})
200210
.in_current_span()
201211
.await
@@ -341,8 +351,11 @@ struct NtxDataStore {
341351
store: StoreClient,
342352
/// LRU cache for storing retrieved note scripts to avoid repeated store calls.
343353
script_cache: LruCache<Word, NoteScript>,
344-
/// Local database for persistent note script caching.
354+
/// Local database for persistent note script.
345355
db: Db,
356+
/// Scripts fetched from the remote store during execution, to be persisted by the
357+
/// coordinator.
358+
fetched_scripts: Arc<Mutex<Vec<(Word, NoteScript)>>>,
346359
/// Mapping of storage map roots to storage slot names observed during various calls.
347360
///
348361
/// The registered slot names are subsequently used to retrieve storage map witnesses from the
@@ -388,10 +401,16 @@ impl NtxDataStore {
388401
store,
389402
script_cache,
390403
db,
404+
fetched_scripts: Arc::new(Mutex::new(Vec::new())),
391405
storage_slots: Arc::new(Mutex::new(BTreeMap::default())),
392406
}
393407
}
394408

409+
/// Returns the list of note scripts fetched from the remote store during execution.
410+
async fn take_fetched_scripts(&self) -> Vec<(Word, NoteScript)> {
411+
self.fetched_scripts.lock().await.drain(..).collect()
412+
}
413+
395414
/// Registers storage map slot names for the given account ID and storage header.
396415
///
397416
/// These slot names are subsequently used to query for storage map witnesses against the store.
@@ -550,8 +569,8 @@ impl DataStore for NtxDataStore {
550569
})?;
551570

552571
if let Some(script) = maybe_script {
553-
// Best-effort persist — don't fail the transaction if DB write fails.
554-
let _ = self.db.insert_note_script(script_root, &script).await;
572+
// Collect for later persistence by the coordinator.
573+
self.fetched_scripts.lock().await.push((script_root, script.clone()));
555574
self.script_cache.put(script_root, script.clone()).await;
556575
Ok(Some(script))
557576
} else {

crates/ntx-builder/src/actor/mod.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ pub enum ActorNotification {
4040
nullifiers: Vec<Nullifier>,
4141
block_num: BlockNumber,
4242
},
43+
/// A note script was fetched from the remote store and should be persisted to the local DB.
44+
CacheNoteScript { script_root: Word, script: NoteScript },
4345
}
4446

4547
// ACTOR SHUTDOWN REASON
@@ -368,11 +370,13 @@ impl AccountActor {
368370
let execution_result = context.execute_transaction(tx_candidate).await;
369371
match execution_result {
370372
// Execution completed without failed notes.
371-
Ok((tx_id, failed)) if failed.is_empty() => {
373+
Ok((tx_id, failed, scripts_to_cache)) if failed.is_empty() => {
374+
self.cache_note_scripts(scripts_to_cache).await;
372375
self.mode = ActorMode::TransactionInflight(tx_id);
373376
},
374377
// Execution completed with some failed notes.
375-
Ok((tx_id, failed)) => {
378+
Ok((tx_id, failed, scripts_to_cache)) => {
379+
self.cache_note_scripts(scripts_to_cache).await;
376380
let nullifiers: Vec<_> =
377381
failed.into_iter().map(|note| note.note.nullifier()).collect();
378382
self.mark_notes_failed(&nullifiers, block_num).await;
@@ -391,6 +395,16 @@ impl AccountActor {
391395
}
392396
}
393397

398+
/// Sends notifications to the coordinator to cache note scripts fetched from the remote store.
399+
async fn cache_note_scripts(&self, scripts: Vec<(Word, NoteScript)>) {
400+
for (script_root, script) in scripts {
401+
let _ = self
402+
.notification_tx
403+
.send(ActorNotification::CacheNoteScript { script_root, script })
404+
.await;
405+
}
406+
}
407+
394408
/// Sends a notification to the coordinator to mark notes as failed.
395409
async fn mark_notes_failed(&self, nullifiers: &[Nullifier], block_num: BlockNumber) {
396410
let _ = self

crates/ntx-builder/src/builder.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,11 @@ impl NetworkTransactionBuilder {
302302
tracing::error!(err = %err, "failed to mark notes as failed");
303303
}
304304
},
305+
ActorNotification::CacheNoteScript { script_root, script } => {
306+
if let Err(err) = self.db.insert_note_script(script_root, &script).await {
307+
tracing::error!(err = %err, "failed to cache note script");
308+
}
309+
},
305310
}
306311
}
307312

0 commit comments

Comments
 (0)