Skip to content

Commit 0cc726e

Browse files
Merge next
2 parents 1889a47 + 8cd5825 commit 0cc726e

20 files changed

Lines changed: 1189 additions & 397 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
- [BREAKING] Renamed `ExplorerStatusDetails` fields in the network monitor's `/status` payload from `number_of_*` to `total_*` (`total_transactions`, `total_nullifiers`, `total_notes`, `total_account_updates`). The values now represent network-wide cumulative totals from the explorer's `overviewStats` query instead of last-block counts.
5050
- [BREAKING] Removed `--wallet-filepath` / `--counter-filepath` flags and the `MIDEN_MONITOR_WALLET_FILEPATH` / `MIDEN_MONITOR_COUNTER_FILEPATH` env vars from the network monitor. The monitor now keeps wallet and counter accounts fully in memory and regenerates them on every startup; the dashboard's counter value resets to zero on restart.
5151
- Added `--counter-pending-unhealthy-threshold` (env `MIDEN_MONITOR_COUNTER_PENDING_UNHEALTHY_THRESHOLD`, default `5`) to the network monitor: the Network Transactions card now flips unhealthy when the gap between expected and observed counter values stays above the threshold for three consecutive polls.
52+
- Allowed network transaction submission conditionally via the gRPC `SubmitProvenTx` and `SubmitProvenTxBatch` endpoints: the NTX builder can now send a key in the `x-miden-network-tx-auth` header that enables submitting network transactions ([#2131](https://github.com/0xMiden/node/issues/2131)).
53+
5254
## v0.14.11 (TBD)
5355

5456
- Replaced blocking-in-async operations in the validator, remote prover, and ntx-builder with `spawn_blocking` to avoid starving the Tokio runtime ([#2041](https://github.com/0xMiden/node/pull/2041)).

bin/node/src/commands/rpc.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ pub struct RpcOptions {
1818
#[arg(long = "rpc.listen", env = "MIDEN_NODE_RPC_LISTEN", value_name = "LISTEN")]
1919
pub listen: SocketAddr,
2020

21+
/// Optional metadata header value for internal network-transaction RPC authentication.
22+
#[arg(
23+
long = "rpc.network-tx-auth-header-value",
24+
env = "MIDEN_NODE_RPC_NETWORK_TX_AUTH_HEADER_VALUE",
25+
value_name = "VALUE",
26+
help_heading = super::section::RPC_CONFIGURATION_HELP_HEADING
27+
)]
28+
pub network_tx_auth_header_value: Option<String>,
29+
2130
#[command(flatten)]
2231
pub grpc: RpcGrpcOptions,
2332

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

Lines changed: 14 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use std::time::Duration;
99
use allowlist::{NoteScriptNotAllowlisted, partition_by_allowlist};
1010
use anyhow::Context;
1111
use candidate::TransactionCandidate;
12-
use futures::FutureExt;
1312
use miden_node_utils::ErrorReport;
1413
use miden_node_utils::lru_cache::LruCache;
1514
use miden_protocol::Word;
@@ -231,93 +230,29 @@ impl AccountActor {
231230
///
232231
/// The return value signals the shutdown category to the coordinator:
233232
///
234-
/// - `Ok(())`: intentional shutdown (idle timeout or account removal).
235-
/// - `Err(_)`: crash (database error, semaphore failure, or any other bug).
236-
pub async fn run(self, semaphore: Arc<Semaphore>) -> anyhow::Result<()> {
233+
/// - `Ok(())`: intentional shutdown (idle timeout or account not committed in time).
234+
/// - `Err(_)`: crash (database error or any other bug).
235+
pub async fn run(self, _semaphore: Arc<Semaphore>) -> anyhow::Result<()> {
237236
let account_id = self.account_id;
238237

239238
// Wait for the account to be committed to the DB. For newly created accounts, the creation
240-
// transaction must be committed before we start processing notes.
239+
// transaction must be committed before the actor becomes active.
241240
if !self.wait_for_committed_account(account_id).await? {
242241
return Ok(());
243242
}
244243

245-
// Determine initial mode by checking DB for available notes.
246-
let block_num = self.state.chain.chain_tip_block_number();
247-
let has_notes = self
248-
.state
249-
.db
250-
.has_available_notes(account_id, block_num, self.config.max_note_attempts)
251-
.await
252-
.context("failed to check for available notes")?;
253-
254-
let mut mode = if has_notes {
255-
ActorMode::NotesAvailable
256-
} else {
257-
ActorMode::NoViableNotes
258-
};
259-
260244
loop {
261-
// Enable or disable transaction execution based on actor mode.
262-
let tx_permit_acquisition = match mode {
263-
// Disable transaction execution.
264-
ActorMode::NoViableNotes | ActorMode::TransactionInflight(_) => {
265-
std::future::pending().boxed()
266-
},
267-
// Enable transaction execution.
268-
ActorMode::NotesAvailable => semaphore.acquire().boxed(),
269-
};
270-
271-
// Idle timeout timer: only ticks when in NoViableNotes mode. Mode changes cause the
272-
// next loop iteration to create a fresh sleep or pending.
273-
let idle_timeout_sleep = match mode {
274-
ActorMode::NoViableNotes => tokio::time::sleep(self.config.idle_timeout).boxed(),
275-
_ => std::future::pending().boxed(),
276-
};
277-
278245
tokio::select! {
279-
// Handle coordinator notifications. On notification, re-evaluate state from DB.
246+
// A committed block touched this account (or the coordinator woke everyone). PR 3
247+
// reconnects transaction execution here.
280248
_ = self.notify.notified() => {
281-
match mode {
282-
ActorMode::TransactionInflight(awaited_id) => {
283-
// Check DB: is the inflight tx still pending?
284-
let exists = self
285-
.state
286-
.db
287-
.transaction_exists(awaited_id)
288-
.await
289-
.context("failed to check transaction status")?;
290-
if exists {
291-
mode = ActorMode::NotesAvailable;
292-
}
293-
},
294-
_ => {
295-
mode = ActorMode::NotesAvailable;
296-
}
297-
}
298-
},
299-
// Execute transactions.
300-
permit = tx_permit_acquisition => {
301-
let _permit = permit.context("semaphore closed")?;
302-
303-
// Read the chain state.
304-
let chain_state = self.state.chain.get_cloned();
305-
306-
// Query DB for latest account and available notes.
307-
let tx_candidate = self.select_candidate_from_db(
308-
account_id,
309-
chain_state,
310-
).await?;
311-
312-
if let Some(tx_candidate) = tx_candidate {
313-
mode = self.execute_transactions(account_id, tx_candidate).await;
314-
} else {
315-
// No transactions to execute, wait for notifications.
316-
mode = ActorMode::NoViableNotes;
317-
}
249+
tracing::debug!(
250+
%account_id,
251+
"actor notified; transaction execution reconnects in PR 3",
252+
);
318253
}
319-
// Idle timeout: actor has been idle too long, deactivate account.
320-
_ = idle_timeout_sleep => {
254+
// Idle timeout: actor has been idle too long, deactivate.
255+
() = tokio::time::sleep(self.config.idle_timeout) => {
321256
tracing::info!(%account_id, "Account actor deactivated due to idle timeout");
322257
return Ok(());
323258
}
@@ -385,8 +320,8 @@ impl AccountActor {
385320
/// For accounts that are being created by an inflight transaction, this will idle
386321
/// until the transaction is committed. Returns `true` when the account is ready, or
387322
/// `false` if no commit arrived within [`ActorConfig::idle_timeout`] — in which case
388-
/// the coordinator will respawn a new actor when committed-chain processing or the account
389-
/// loader observes the account again.
323+
/// the coordinator will respawn a new actor when a later committed block targets the
324+
/// account again.
390325
async fn wait_for_committed_account(&self, account_id: AccountId) -> anyhow::Result<bool> {
391326
// Check if the account is already committed.
392327
if self
@@ -573,47 +508,6 @@ fn log_failed_notes(failed: Vec<FailedNote>) -> Vec<(Nullifier, NoteError)> {
573508

574509
#[cfg(test)]
575510
mod tests {
576-
577-
use std::sync::Arc;
578-
579-
use tokio::sync::{Notify, mpsc};
580-
581-
use super::*;
582-
583-
const OTHER_NOTE_SCRIPT: &str = "\
584-
@note_script
585-
pub proc main
586-
push.1 drop
587-
end";
588-
589-
async fn ack_actor_requests(mut rx: mpsc::Receiver<ActorRequest>, db: Db) {
590-
while let Some(request) = rx.recv().await {
591-
match request {
592-
ActorRequest::NotesFailed { failed_notes, block_num, ack_tx } => {
593-
db.notes_failed(failed_notes, block_num)
594-
.await
595-
.expect("test DB write should succeed");
596-
let _ = ack_tx.send(());
597-
},
598-
ActorRequest::CacheNoteScript { .. } => {},
599-
}
600-
}
601-
}
602-
603-
fn actor_with_request_handler(
604-
db: &Db,
605-
account_id: AccountId,
606-
) -> (AccountActor, AccountActorContext) {
607-
let (request_tx, request_rx) = mpsc::channel(8);
608-
let mut context = AccountActorContext::test(db);
609-
context.request_tx = request_tx;
610-
tokio::spawn(ack_actor_requests(request_rx, db.clone()));
611-
612-
let actor = AccountActor::new(account_id, &context, Arc::new(Notify::new()));
613-
614-
(actor, context)
615-
}
616-
617511
#[tokio::test]
618512
#[ignore = "wip refactor"]
619513
async fn select_candidate_keeps_allowlisted_notes() {

0 commit comments

Comments
 (0)