Skip to content
Closed
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
54 changes: 0 additions & 54 deletions bin/network-monitor/src/assets/counter_program.masm

This file was deleted.

9 changes: 0 additions & 9 deletions bin/network-monitor/src/assets/increment_counter.masm

This file was deleted.

27 changes: 14 additions & 13 deletions bin/network-monitor/src/counter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<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")?;

// 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)
Expand Down
112 changes: 91 additions & 21 deletions bin/network-monitor/src/deploy/counter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -33,39 +34,108 @@ 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"));
/// 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<Library> = 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<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 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<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