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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
- Replaced the network monitor's JavaScript dashboard with a server-rendered Maud + HTMX frontend ([#2024](https://github.com/0xMiden/node/pull/2024)).
- [BREAKING] Removed `CheckNullifiers` endpoint ([#2049](https://github.com/0xMiden/node/pull/2049)).
- [BREAKING] `BlockRange.block_to` is now required for all RPC endpoints ([#2056](https://github.com/0xMiden/node/pull/2056)).
- Compile network monitor counter program and note script at build time using `build.rs`, catching protocol breakage at compile time rather than at startup ([#1831](https://github.com/0xMiden/node/issues/1831)).
- 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)).
- Implement persistent RocksDB backend for `AccountStateForest`, improving startup time ([#2020](https://github.com/0xMiden/node/pull/2020)).

## v0.14.10 (2026-05-29)

Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

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

4 changes: 4 additions & 0 deletions bin/network-monitor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,7 @@ tonic = { features = ["codegen", "tls-native-roots", "transport"], wo
tonic-health = { workspace = true }
tracing = { workspace = true }
url = { features = ["serde"], workspace = true }

[build-dependencies]
fs-err = { workspace = true }
miden-protocol = { features = ["std"], workspace = true }
49 changes: 49 additions & 0 deletions bin/network-monitor/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use std::env;
use std::path::Path;
use std::sync::Arc;

use miden_protocol::assembly::diagnostics::NamedSource;
use miden_protocol::note::NoteScript;
use miden_protocol::transaction::TransactionKernel;
use miden_protocol::utils::serde::Serializable;

const COUNTER_MODULE_PATH: &str = "miden::monitor::counter_contract";

fn main() {
println!("cargo::rerun-if-changed=src/assets/counter_program.masm");
println!("cargo::rerun-if-changed=src/assets/increment_counter.masm");

let out_dir_str = env::var("OUT_DIR").expect("OUT_DIR must be set");
let out_dir = Path::new(&out_dir_str);

let counter_masm = fs_err::read_to_string("src/assets/counter_program.masm")
.expect("src/assets/counter_program.masm must exist");
let note_masm = fs_err::read_to_string("src/assets/increment_counter.masm")
.expect("src/assets/increment_counter.masm must exist");

let assembler = TransactionKernel::assembler().with_warnings_as_errors(true);

// Compile counter program to a library (.masl).
let counter_lib = assembler
.clone()
.assemble_library([NamedSource::new(COUNTER_MODULE_PATH, counter_masm)])
.expect("counter_program.masm should compile without errors");

counter_lib
.write_to_file(out_dir.join("counter_program.masl"))
.expect("writing counter_program.masl should succeed");

// Compile note script statically linked against the counter library.
let mut note_assembler = assembler;
note_assembler
.link_static_library(Arc::as_ref(&counter_lib))
.expect("linking counter library into note assembler should succeed");

let note_program = note_assembler
.assemble_program(note_masm)
.expect("increment_counter.masm should compile without errors");

let note_script_bytes = NoteScript::new(note_program).to_bytes();
fs_err::write(out_dir.join("increment_note_script.bin"), note_script_bytes)
.expect("writing increment_note_script.bin should succeed");
}
2 changes: 0 additions & 2 deletions bin/network-monitor/src/assets/counter_program.masm
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ use miden::protocol::active_account
use miden::protocol::native_account
use miden::protocol::active_note
use miden::protocol::account_id
use miden::protocol::tx


const COUNTER_SLOT = word("miden::monitor::counter_contract::counter")
const OWNER_SLOT = word("miden::monitor::counter_contract::owner")
Expand Down
2 changes: 1 addition & 1 deletion bin/network-monitor/src/assets/increment_counter.masm
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# This script is executed as a note and calls the
# `counter_contract::increment` entrypoint.

use external_contract::counter_contract
use miden::monitor::counter_contract

begin
call.counter_contract::increment
Expand Down
29 changes: 7 additions & 22 deletions bin/network-monitor/src/counter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
//! of the network account deployed at startup by creating and submitting network notes.

use std::path::Path;
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, LazyLock};
use std::time::{Duration, Instant};

use anyhow::{Context, Result};
Expand All @@ -31,7 +31,6 @@ use miden_protocol::transaction::{InputNotes, PartialBlockchain, TransactionArgs
use miden_protocol::utils::serde::{Deserializable, Serializable};
use miden_protocol::{Felt, Word};
use miden_standards::account::interface::{AccountInterface, AccountInterfaceExt};
use miden_standards::code_builder::CodeBuilder;
use miden_standards::note::{NetworkAccountTarget, NoteExecutionHint};
use miden_tx::auth::BasicAuthenticator;
use miden_tx::{LocalTransactionProver, TransactionExecutor};
Expand Down Expand Up @@ -65,6 +64,11 @@ const REGENERATE_COOLDOWN: Duration = Duration::from_secs(3600);
// SHARED STATE
// ================================================================================================

static INCREMENT_NOTE_SCRIPT: LazyLock<NoteScript> = LazyLock::new(|| {
let bytes = include_bytes!(concat!(env!("OUT_DIR"), "/increment_note_script.bin"));
NoteScript::read_from_bytes(bytes).expect("increment note script should be valid")
});

#[derive(Debug, Default, Clone)]
pub struct LatencyState {
pending: Option<PendingLatencyDetails>,
Expand Down Expand Up @@ -567,7 +571,7 @@ async fn setup_increment_task(

let block_header = get_genesis_block_header(rpc_client).await?;

let increment_script = create_increment_script()?;
let increment_script = INCREMENT_NOTE_SCRIPT.clone();

let mut data_store = MonitorDataStore::new(block_header.clone(), PartialBlockchain::default());
data_store.add_account(wallet_account.clone());
Expand Down Expand Up @@ -917,25 +921,6 @@ fn load_counter_account(file_path: &Path) -> Result<Account> {
Ok(account_file.account.clone())
}

/// Create the increment procedure script.
fn create_increment_script() -> Result<NoteScript> {
let script =
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/assets/counter_program.masm"));

let script_builder = CodeBuilder::new()
.with_linked_module("external_contract::counter_contract", script)
.context("Failed to create script builder with library")?;

let note_script = script_builder
.compile_note_script(include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/src/assets/increment_counter.masm"
)))
.context("Failed to compile note script")?;

Ok(note_script)
}

/// Create a network note that targets the counter account.
fn create_network_note(
wallet_account: &Account,
Expand Down
56 changes: 34 additions & 22 deletions bin/network-monitor/src/deploy/counter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ use miden_protocol::account::{
StorageSlot,
StorageSlotName,
};
use miden_protocol::assembly::Library;
use miden_protocol::utils::serde::Deserializable;
use miden_protocol::utils::sync::LazyLock;
use miden_protocol::{Felt, Word};
use miden_standards::code_builder::CodeBuilder;
use miden_standards::testing::account_component::IncrNonceAuthComponent;
use tracing::instrument;

Expand All @@ -33,39 +34,50 @@ pub static COUNTER_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
.expect("storage slot name should be valid")
});

/// Create a counter program account with custom MASM script.
#[instrument(target = COMPONENT, name = "create-counter-account", skip_all, ret(level = "debug"))]
pub fn create_counter_account(owner_account_id: AccountId) -> Result<Account> {
// Load and customize the MASM script
let script =
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/assets/counter_program.masm"));
static COUNTER_PROGRAM_LIBRARY: LazyLock<Library> = LazyLock::new(|| {
let bytes = include_bytes!(concat!(env!("OUT_DIR"), "/counter_program.masl"));
Library::read_from_bytes(bytes).expect("counter program library should be valid")
});

/// An [`AccountComponent`] implementing the counter contract used by the network monitor.
pub struct CounterComponent {
pub owner_account_id: AccountId,
}

// Compile the account code
let owner_account_id_prefix = owner_account_id.prefix().as_felt();
let owner_account_id_suffix = owner_account_id.suffix();
impl From<CounterComponent> for AccountComponent {
fn from(component: CounterComponent) -> Self {
let owner_account_id_prefix = component.owner_account_id.prefix().as_felt();
let owner_account_id_suffix = component.owner_account_id.suffix();

let owner_id_slot = StorageSlot::with_value(
OWNER_SLOT_NAME.clone(),
Word::from([owner_account_id_suffix, owner_account_id_prefix, Felt::ZERO, Felt::ZERO]),
);
let owner_id_slot = StorageSlot::with_value(
OWNER_SLOT_NAME.clone(),
Word::from([owner_account_id_suffix, owner_account_id_prefix, Felt::ZERO, Felt::ZERO]),
);

let counter_slot = StorageSlot::with_value(COUNTER_SLOT_NAME.clone(), Word::empty());
let counter_slot = StorageSlot::with_value(COUNTER_SLOT_NAME.clone(), Word::empty());

let component_code =
CodeBuilder::default().compile_component_code("counter::program", script)?;
let metadata = AccountComponentMetadata::new("counter::program", AccountType::all());

let metadata = AccountComponentMetadata::new("counter::program", AccountType::all());
let account_code =
AccountComponent::new(component_code, vec![counter_slot, owner_id_slot], metadata)?;
AccountComponent::new(
COUNTER_PROGRAM_LIBRARY.clone(),
vec![counter_slot, owner_id_slot],
metadata,
)
.expect("counter component should be valid")
}
}

/// Create a counter program account.
#[instrument(target = COMPONENT, name = "create-counter-account", skip_all, ret(level = "debug"))]
pub fn create_counter_account(owner_account_id: AccountId) -> Result<Account> {
let counter_component: AccountComponent = CounterComponent { owner_account_id }.into();
let incr_nonce_auth: AccountComponent = IncrNonceAuthComponent.into();

// Create the counter program account
let init_seed: [u8; 32] = rand::random();
let counter_account = AccountBuilder::new(init_seed)
.account_type(AccountType::RegularAccountUpdatableCode)
.storage_mode(AccountStorageMode::Network)
.with_component(account_code)
.with_component(counter_component)
.with_auth_component(incr_nonce_auth)
.build()?;

Expand Down
Loading