From 9c0e18e02a5fff0e56dddb12f56624f48836f265 Mon Sep 17 00:00:00 2001 From: Ollie202 Date: Thu, 7 May 2026 08:29:40 +0100 Subject: [PATCH] feat(network-monitor): inline .masm scripts as Rust string constants Replace the counter_program.masm and increment_counter.masm asset files with inline Rust string constants. Introduce CounterComponent following the IncrNonceAuthComponent pattern from miden-standards, backed by a LazyLock-compiled Library with a From impl. The note script is similarly inlined as INCREMENT_NOTE_SCRIPT in counter.rs. Closes #1831 --- CHANGELOG.md | 1 + .../src/assets/counter_program.masm | 54 --------- .../src/assets/increment_counter.masm | 9 -- bin/network-monitor/src/counter.rs | 27 +++-- bin/network-monitor/src/deploy/counter.rs | 112 ++++++++++++++---- 5 files changed, 106 insertions(+), 97 deletions(-) delete mode 100644 bin/network-monitor/src/assets/counter_program.masm delete mode 100644 bin/network-monitor/src/assets/increment_counter.masm diff --git a/CHANGELOG.md b/CHANGELOG.md index 59a710e028..a0711770f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## v0.14.11 (TBD) - 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)). +- Replaced network monitor `.masm` asset files with inline Rust string constants, improving maintainability ([#2058](https://github.com/0xMiden/node/pull/2058)). - Implement persistent RocksDB backend for `AccountStateForest`, improving startup time ([#2020](https://github.com/0xMiden/node/pull/2020)). ## v0.14.10 (2026-05-29) diff --git a/bin/network-monitor/src/assets/counter_program.masm b/bin/network-monitor/src/assets/counter_program.masm deleted file mode 100644 index 9f8679da93..0000000000 --- a/bin/network-monitor/src/assets/counter_program.masm +++ /dev/null @@ -1,54 +0,0 @@ -# Counter program for network monitoring with note authentication -# Storage layout: -# - OWNER_SLOT: authorized wallet account id as [suffix, prefix, 0, 0] -# - COUNTER_SLOT: counter value (u64) - -use miden::core::sys -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") - -# Increment function with note authentication -# => [] -pub proc increment - # Ensure the note sender matches the authorized wallet. - push.OWNER_SLOT[0..2] exec.active_account::get_item - # => [owner_suffix, owner_prefix, 0, 0] - - exec.active_note::get_sender - # => [sender_suffix, sender_prefix, owner_suffix, owner_prefix, 0, 0] - - exec.account_id::is_equal - # => [are_equal, 0, 0] - - assert.err="Note sender not authorized" drop drop - # => [] - - push.COUNTER_SLOT[0..2] exec.active_account::get_item - # => [count, 0, 0, 0] - - push.1 add - # => [count+1] - - push.COUNTER_SLOT[0..2] exec.native_account::set_item - # => [count, 0, 0, 0] - - dropw - # => [] -end - -# Get the counter (no auth required) -# => [count] -pub proc get_count - push.COUNTER_SLOT[0..2] exec.active_account::get_item - # => [count, 0, 0, 0] - - exec.sys::truncate_stack - # => [count] -end diff --git a/bin/network-monitor/src/assets/increment_counter.masm b/bin/network-monitor/src/assets/increment_counter.masm deleted file mode 100644 index 4a835bdd8b..0000000000 --- a/bin/network-monitor/src/assets/increment_counter.masm +++ /dev/null @@ -1,9 +0,0 @@ -# Note script to increment the external counter contract. -# This script is executed as a note and calls the -# `counter_contract::increment` entrypoint. - -use external_contract::counter_contract - -begin - call.counter_contract::increment -end diff --git a/bin/network-monitor/src/counter.rs b/bin/network-monitor/src/counter.rs index c1bb55867b..0f753efbb8 100644 --- a/bin/network-monitor/src/counter.rs +++ b/bin/network-monitor/src/counter.rs @@ -877,21 +877,22 @@ async fn create_and_submit_network_note( Ok((tx_id, final_account, block_height)) } +const INCREMENT_NOTE_SCRIPT: &str = " + use external_contract::counter_contract + + begin + call.counter_contract::increment + end +"; + /// Create the increment procedure script. fn create_increment_script() -> Result { - 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")?; - - // Compile the script directly as a NoteScript - let note_script = script_builder - .compile_note_script(include_str!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/src/assets/increment_counter.masm" - ))) + use crate::deploy::counter::COUNTER_PROGRAM_CODE; + + let note_script = CodeBuilder::new() + .with_linked_module("external_contract::counter_contract", COUNTER_PROGRAM_CODE) + .context("Failed to create script builder with library")? + .compile_note_script(INCREMENT_NOTE_SCRIPT) .context("Failed to compile note script")?; Ok(note_script) diff --git a/bin/network-monitor/src/deploy/counter.rs b/bin/network-monitor/src/deploy/counter.rs index 5479f15898..990dc76e36 100644 --- a/bin/network-monitor/src/deploy/counter.rs +++ b/bin/network-monitor/src/deploy/counter.rs @@ -15,6 +15,7 @@ use miden_protocol::account::{ StorageSlot, StorageSlotName, }; +use miden_protocol::assembly::Library; use miden_protocol::utils::sync::LazyLock; use miden_protocol::{Felt, Word}; use miden_standards::code_builder::CodeBuilder; @@ -33,39 +34,108 @@ pub static COUNTER_SLOT_NAME: LazyLock = 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 { - // Load and customize the MASM script - let script = - include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/assets/counter_program.masm")); +/// MASM source for the counter account component. +/// +/// Exposes two procedures: +/// - `increment`: verifies the note sender matches the stored owner, then increments the counter. +/// - `get_count`: returns the current counter value (no auth required). +pub const COUNTER_PROGRAM_CODE: &str = " + use miden::core::sys + 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\") + + # Increment function with note authentication + # => [] + pub proc increment + # Ensure the note sender matches the authorized wallet. + push.OWNER_SLOT[0..2] exec.active_account::get_item + # => [owner_suffix, owner_prefix, 0, 0] + + exec.active_note::get_sender + # => [sender_suffix, sender_prefix, owner_suffix, owner_prefix, 0, 0] + + exec.account_id::is_equal + # => [are_equal, 0, 0] + + assert.err=\"Note sender not authorized\" drop drop + # => [] + + push.COUNTER_SLOT[0..2] exec.active_account::get_item + # => [count, 0, 0, 0] + + push.1 add + # => [count+1] + + push.COUNTER_SLOT[0..2] exec.native_account::set_item + # => [count, 0, 0, 0] - // Compile the account code - let owner_account_id_prefix = owner_account_id.prefix().as_felt(); - let owner_account_id_suffix = owner_account_id.suffix(); + dropw + # => [] + end - 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]), - ); + # Get the counter (no auth required) + # => [count] + pub proc get_count + push.COUNTER_SLOT[0..2] exec.active_account::get_item + # => [count, 0, 0, 0] - let counter_slot = StorageSlot::with_value(COUNTER_SLOT_NAME.clone(), Word::empty()); + exec.sys::truncate_stack + # => [count] + end +"; - let component_code = - CodeBuilder::default().compile_component_code("counter::program", script)?; +static COUNTER_PROGRAM_LIBRARY: LazyLock = LazyLock::new(|| { + CodeBuilder::default() + .compile_component_code("counter::program", COUNTER_PROGRAM_CODE) + .expect("counter program code should be valid") + .into() +}); + +/// An [`AccountComponent`] implementing the counter contract used by the network monitor. +pub struct CounterComponent { + pub owner_account_id: AccountId, +} + +impl From 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 metadata = AccountComponentMetadata::new("counter::program", AccountType::all()); - let account_code = - AccountComponent::new(component_code, vec![counter_slot, owner_id_slot], metadata)?; + let counter_slot = StorageSlot::with_value(COUNTER_SLOT_NAME.clone(), Word::empty()); + let metadata = AccountComponentMetadata::new("counter::program", AccountType::all()); + + 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 { + 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()?;