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
2 changes: 0 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions bin/ntx-builder/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,8 @@ doctest = false
anyhow = { workspace = true }
backon = { workspace = true }
clap = { features = ["env", "string"], workspace = true }
diesel = { features = ["numeric", "sqlite"], workspace = true }
futures = { workspace = true }
humantime = { workspace = true }
libsqlite3-sys = { workspace = true }
miden-node-db = { workspace = true }
miden-node-proto = { workspace = true }
miden-node-proto-build = { features = ["internal"], workspace = true }
Expand Down
5 changes: 0 additions & 5 deletions bin/ntx-builder/diesel.toml

This file was deleted.

25 changes: 17 additions & 8 deletions bin/ntx-builder/src/actor/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::sync::{Arc, Mutex};
use std::time::Duration;

use backon::ExponentialBuilder;
use miden_node_db::sqlite::Database;
use miden_node_utils::ErrorReport;
use miden_node_utils::lru_cache::LruCache;
use miden_node_utils::retry::{self, Retryable};
Expand Down Expand Up @@ -56,7 +57,7 @@ use tracing::Instrument;

use crate::actor::candidate::TransactionCandidate;
use crate::clients::{RemoteTransactionProver, RpcClient, RpcError};
use crate::db::Db;
use crate::db::queries;
use crate::{COMPONENT, LOG_TARGET};

#[derive(Debug, thiserror::Error)]
Expand Down Expand Up @@ -167,7 +168,7 @@ pub struct NtxContext {
script_cache: LruCache<Word, NoteScript>,

/// Local database for persistent note script caching.
db: Db,
db: Database,

/// Maximum number of VM execution cycles for network transactions.
max_cycles: u32,
Expand All @@ -190,7 +191,7 @@ impl NtxContext {
prover: RemoteTransactionProver,
rpc: RpcClient,
script_cache: LruCache<Word, NoteScript>,
db: Db,
db: Database,
max_cycles: u32,
tx_args: TransactionArgs,
request_backoff_initial: Duration,
Expand Down Expand Up @@ -575,7 +576,7 @@ struct NtxDataStore {
/// LRU cache for storing retrieved note scripts to avoid repeated RPC calls.
script_cache: LruCache<Word, NoteScript>,
/// Local database for persistent note script.
db: Db,
db: Database,
/// Scripts fetched from the remote RPC service during execution, to be persisted by the
/// coordinator.
fetched_scripts: Arc<Mutex<Vec<(Word, NoteScript)>>>,
Expand Down Expand Up @@ -613,7 +614,7 @@ impl NtxDataStore {
chain_mmr: Arc<PartialBlockchain>,
rpc: RpcClient,
script_cache: LruCache<Word, NoteScript>,
db: Db,
db: Database,
request_backoff: ExponentialBuilder,
) -> Self {
let mast_store = TransactionMastStore::new();
Expand Down Expand Up @@ -818,9 +819,17 @@ impl DataStore for NtxDataStore {
}

// 2. Local DB.
if let Some(script) = self.db.lookup_note_script(script_root).await.map_err(|err| {
DataStoreError::other_with_source("failed to look up note script in local DB", err)
})? {
if let Some(script) = self
.db
.read("lookup_note_script", move |tx| queries::lookup_note_script(tx, &script_root))
.await
.map_err(|err| {
DataStoreError::other_with_source(
"failed to look up note script in local DB",
err,
)
})?
{
self.script_cache.put(script_root, script.clone());
return Ok(Some(script));
}
Expand Down
46 changes: 29 additions & 17 deletions bin/ntx-builder/src/actor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use allowlist::{NoteScriptNotAllowlisted, partition_by_allowlist};
use anyhow::Context;
use candidate::TransactionCandidate;
use futures::FutureExt;
use miden_node_db::sqlite::Database;
use miden_node_utils::ErrorReport;
use miden_node_utils::lru_cache::LruCache;
use miden_node_utils::shutdown::CancellationToken;
Expand All @@ -25,7 +26,7 @@ use tokio::sync::{Notify, Semaphore, mpsc};

use crate::chain_state::{ChainState, SharedChainState};
use crate::clients::{RemoteTransactionProver, RpcClient};
use crate::db::Db;
use crate::db::queries;
use crate::{LOG_TARGET, NoteError};

/// Builds the [`TransactionArgs`] shared by every network transaction.
Expand Down Expand Up @@ -86,7 +87,7 @@ pub struct GrpcClients {
#[derive(Clone)]
pub struct State {
/// Local database for account state, notes, and transaction tracking.
pub db: Db,
pub db: Database,
/// The latest chain state. A single chain state is shared among all actors.
pub chain: Arc<SharedChainState>,
/// Shared LRU cache for storing retrieved note scripts to avoid repeated RPC calls.
Expand Down Expand Up @@ -140,7 +141,7 @@ impl AccountActorContext {
///
/// The URLs are fake and actors spawned with this context will fail on their first gRPC call,
/// but this is sufficient for testing coordinator logic (registry, deactivation, etc.).
pub fn test(db: &crate::db::Db) -> Self {
pub fn test(db: &Database) -> Self {
use miden_protocol::crypto::merkle::mmr::{Forest, MmrPeaks, PartialMmr};
use url::Url;

Expand Down Expand Up @@ -305,17 +306,20 @@ impl AccountActor {
let mut account = self
.state
.db
.get_account(account_id)
.read("get_account", move |tx| queries::get_account(tx, account_id))
.await
.context("failed to load committed account")?
.context("no committed state for the account; the coordinator must only spawn actors for committed accounts")?;

// Determine initial mode by checking the DB for available notes.
let block_num = self.state.chain.chain_tip_block_number();
let max_note_attempts = self.config.max_note_attempts;
let has_notes = self
.state
.db
.has_available_notes(account_id, block_num, self.config.max_note_attempts)
.read("has_available_notes", move |tx| {
queries::has_available_notes(tx, account_id, block_num, max_note_attempts)
})
.await
.context("failed to check for available notes")?;
let mut mode = if has_notes {
Expand Down Expand Up @@ -408,10 +412,11 @@ impl AccountActor {
return Ok(ActorMode::NotesAvailable);
};

let account_id = self.account_id;
let landed = self
.state
.db
.account_last_tx(self.account_id)
.read("account_last_tx", move |tx| queries::account_last_tx(tx, account_id))
.await
.context("failed to check submitted tx landing")?
== Some(submitted_tx_id);
Expand Down Expand Up @@ -446,7 +451,7 @@ impl AccountActor {
if let Some(latest) = self
.state
.db
.get_account(self.account_id)
.read("get_account", move |tx| queries::get_account(tx, account_id))
.await
.context("failed to reload account after submission expiry")?
{
Expand All @@ -472,10 +477,13 @@ impl AccountActor {
let block_num = chain_state.chain_tip_header.block_num();
let max_notes = self.config.max_notes_per_tx.get();

let max_note_attempts = self.config.max_note_attempts;
let notes = self
.state
.db
.available_notes(account_id, block_num, self.config.max_note_attempts)
.read("available_notes", move |tx| {
queries::available_notes(tx, account_id, block_num, max_note_attempts)
})
.await
.context("failed to query DB for available notes")?;

Expand Down Expand Up @@ -783,7 +791,6 @@ mod tests {
use tokio::sync::Notify;

use super::*;
use crate::db::Db;
use crate::test_utils::{mock_account, mock_network_account_id, mock_transaction_id};

/// Builds a valid nonce-only [`AccountPatch`] that advances `account` by a single nonce.
Expand All @@ -799,7 +806,7 @@ mod tests {
}

/// Builds an actor wired to `db` for the given account, plus the in-memory account to drive.
fn test_actor(db: &Db, account: &Account) -> AccountActor {
fn test_actor(db: &Database, account: &Account) -> AccountActor {
let ctx = AccountActorContext::test(db);
AccountActor::new(account.id(), &ctx, Arc::new(Notify::new()))
}
Expand All @@ -808,13 +815,13 @@ mod tests {
/// actor advances its in-memory account by exactly the patch the transaction produced.
#[tokio::test]
async fn landing_advances_in_memory_account_by_its_patch() {
let (db, _dir) = Db::test_setup().await;
let (db, _dir) = crate::db::test_setup().await;
let account = mock_account(mock_network_account_id());
let account_id = account.id();
let submitted = mock_transaction_id(7);

// Seed the committed row so the landing check sees our submission as the latest tx.
db.upsert_account_for_test(account_id, account.clone(), submitted)
crate::db::upsert_account_for_test(&db, account_id, account.clone(), submitted)
.await
.unwrap();

Expand Down Expand Up @@ -848,7 +855,7 @@ mod tests {
/// in-memory account untouched.
#[tokio::test]
async fn pending_submission_keeps_waiting_without_touching_account() {
let (db, _dir) = Db::test_setup().await;
let (db, _dir) = crate::db::test_setup().await;
let account = mock_account(mock_network_account_id());

// Nothing seeded: `account_last_tx` returns `None`, so the submission has not landed. The
Expand Down Expand Up @@ -888,17 +895,22 @@ mod tests {
/// resident indefinitely.
#[tokio::test]
async fn idle_timeout_fires_despite_repeated_notifications() {
let (db, _dir) = Db::test_setup().await;
let (db, _dir) = crate::db::test_setup().await;
// A real network account with a populated allowlist, so re-evaluation on each wake reaches
// a clean "no viable notes" outcome instead of erroring on a missing allowlist slot.
let (account, _) = crate::test_utils::mock_network_account_update();
let account_id = account.id();

// Seed the committed account but no notes, so the actor starts and stays in NoViableNotes:
// every wake re-checks the DB, finds nothing, and returns to the idle state.
db.upsert_account_for_test(account_id, account.clone(), mock_transaction_id(1))
.await
.unwrap();
crate::db::upsert_account_for_test(
&db,
account_id,
account.clone(),
mock_transaction_id(1),
)
.await
.unwrap();

let mut ctx = AccountActorContext::test(&db);
// Short idle timeout keeps the test fast.
Expand Down
Loading
Loading