Skip to content

refactor: convert note scripts from program to library format#2340

Merged
bobbinth merged 4 commits into0xMiden:nextfrom
Farukest:feat/issue-2339-clean
Jan 31, 2026
Merged

refactor: convert note scripts from program to library format#2340
bobbinth merged 4 commits into0xMiden:nextfrom
Farukest:feat/issue-2339-clean

Conversation

@Farukest
Copy link
Copy Markdown
Contributor

Summary

Converts note scripts from program format (begin...end) to library format with @note_script annotation support.

Changes

  • Add @note_script annotation to all standard note scripts (p2id, p2ide, swap, mint, burn)
  • Add NoteScript::from_library() constructor that finds the procedure with @note_script attribute
  • Update build.rs to compile note scripts as libraries (.masl) instead of programs (.masb)
  • Update well_known_note.rs to load NoteScript from Library
  • Remove wrapper script files (asm/note_scripts/*.masm)
  • Add NoteError variants for missing/multiple @note_script procedures

Note

The @note_script attribute propagation from MASM to Library is not yet fully supported in _miden-assembly 0.20_.
The implementation falls back to finding a procedure named main when the attribute is not found.

Closes #2339

@Farukest
Copy link
Copy Markdown
Contributor Author

Hey @bobbinth,
I'd like to highlight the Note section - the @note_script attribute is correctly parsed during compilation, but it's not preserved after Library serialization/deserialization in miden-assembly 0.20.
So the implementation falls back to finding a main procedure. Let me know if you'd prefer a different approach :)

@mmagician
Copy link
Copy Markdown
Collaborator

I'd like to highlight the Note section - the @note_script attribute is correctly parsed during compilation, but it's not preserved after Library serialization/deserialization in miden-assembly 0.20.

@Farukest could you prepare a short reproducible example (ideally incl. a branch) and file a bug accordingly please?

@Farukest
Copy link
Copy Markdown
Contributor Author

I'd like to highlight the Note section - the @note_script attribute is correctly parsed during compilation, but it's not preserved after Library serialization/deserialization in miden-assembly 0.20.

@Farukest could you prepare a short reproducible example (ideally incl. a branch) and file a bug accordingly please?

Hey @mmagician opened : bug report: 0xMiden/miden-vm#2608

This change implements issue 0xMiden#2339 - converting note scripts from program
format to library format with @note_script annotation support.

Changes:
- Add @note_script annotation to all standard note scripts (p2id, p2ide,
  swap, mint, burn) in asm/standards/notes/
- Add NoteScript::from_library constructor that finds the procedure with
  @note_script attribute (or falls back to 'main' procedure) and uses it
  as the entrypoint
- Update build.rs to compile note scripts as libraries (.masl) instead of
  programs (.masb) using assemble_library_from_dir with unique namespace
- Update well_known_note.rs to load NoteScript from Library
- Remove wrapper script files (asm/note_scripts/*.masm) that are no longer
  needed
- Add NoteError variants for missing/multiple @note_script procedures

Note: The @note_script attribute propagation from MASM to Library is not
yet fully supported in miden-assembly 0.20. The implementation falls back
to finding a procedure named 'main' when the attribute is not found.
@Farukest Farukest force-pushed the feat/issue-2339-clean branch from ba05848 to 05bd528 Compare January 26, 2026 12:00
@PhilippGackstatter
Copy link
Copy Markdown
Contributor

PhilippGackstatter commented Jan 29, 2026

Hi @Farukest, the bug in the VM was closed and we should be able to cargo update to get the fix. It looks like tests are already passing - does the PR need the VM patch anyway? If so, could you update this PR?

Whenever the PR is ready for a review, please request one (or ping if that doesn't work). Thanks!

Farukest and others added 2 commits January 30, 2026 15:10
Address review feedback: instead of the temp directory workaround in
build.rs, use simple wrapper files in note_scripts/ that re-export
the main procedure from miden::standards::notes with pub use.

Changes:
- Add wrapper files (BURN.masm, P2ID.masm, P2IDE.masm, MINT.masm,
  SWAP.masm) with pub use re-exports
- Simplify compile_note_scripts in build.rs to use assemble_library
  directly without temp directory hack
- Remove unused ASM_STANDARDS_NOTES_DIR constant and AsmPath import
Copy link
Copy Markdown
Contributor

@PhilippGackstatter PhilippGackstatter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thank you! It's great that we can now use procref for getting the root of note scripts 🎉.

const LOCAL_EXIT_TREE_SLOT=word("miden::agglayer::let")

const BURN_NOTE_ROOT = [15615638671708113717, 1774623749760042586, 2028263167268363492, 12931944505143778072]
const BURN_NOTE_ROOT = [2756591441383390862, 11367570355116383975, 7462969093049029253, 13293708936511347663]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like we can now remove this constant and instead do:

procref.::miden::standards::notes::burn::main
swapw
# => [SERIAL_NUM, SCRIPT_ROOT]

The same should work for the P2ID root in crates/miden-agglayer/asm/bridge/agglayer_faucet.masm.

Comment on lines +80 to +103
let mut entrypoint_by_attr = None;
let mut entrypoint_by_name = None;

for export in library.exports() {
if let Some(proc_export) = export.as_procedure() {
// Check for @note_script attribute
if proc_export.attributes.has(NOTE_SCRIPT_ATTRIBUTE) {
if entrypoint_by_attr.is_some() {
return Err(NoteError::NoteScriptMultipleProceduresWithAttribute);
}
entrypoint_by_attr = Some(proc_export.node);
}

// Check for procedure named "main" as fallback
if proc_export.path.last() == Some(NOTE_SCRIPT_MAIN_PROC) {
entrypoint_by_name = Some(proc_export.node);
}
}
}

// Prefer @note_script attribute, fallback to "main" procedure
let entrypoint = entrypoint_by_attr
.or(entrypoint_by_name)
.ok_or(NoteError::NoteScriptNoProcedureWithAttribute)?;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can search only for the @note_script attribute and if not found, error out. In other words, I wouldn't implement fall-back behavior to main.

I also think we don't really need to enforce that the procedure is called main although it is nice for uniformity and is also fine to keep as it's easy to remove later.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed

Comment on lines +123 to +124
let source_code = fs::read_to_string(&masm_file_path)
.expect("reading the note script's MASM source code should succeed");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we turn this into an error instead of panicking?

.to_str()
.ok_or_else(|| Report::msg("failed to convert file name to &str"))?;
let mut masb_file_path = target_dir.join(masm_file_name);
let named_source = NamedSource::new(script_name.clone(), source_code);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, for the BURN note, we'd get BURN as the library path. Can we lowercase this and prefix it with miden::standards::note_scripts, so we'd get miden::standards::note_scripts::burn as the library path?

Since the note scripts are part of miden::standards::notes already, I don't think these individual note libraries would really be used, but it doesn't hurt to make it consistent, since it is a part of our public API.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can actually get rid of these individual note libraries entirely - but I'd leave this for a follow-up PR.

Copy link
Copy Markdown
Collaborator

@mmagician mmagician left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM bar the comments that @PhilippGackstatter left

Comment on lines +80 to +103
let mut entrypoint_by_attr = None;
let mut entrypoint_by_name = None;

for export in library.exports() {
if let Some(proc_export) = export.as_procedure() {
// Check for @note_script attribute
if proc_export.attributes.has(NOTE_SCRIPT_ATTRIBUTE) {
if entrypoint_by_attr.is_some() {
return Err(NoteError::NoteScriptMultipleProceduresWithAttribute);
}
entrypoint_by_attr = Some(proc_export.node);
}

// Check for procedure named "main" as fallback
if proc_export.path.last() == Some(NOTE_SCRIPT_MAIN_PROC) {
entrypoint_by_name = Some(proc_export.node);
}
}
}

// Prefer @note_script attribute, fallback to "main" procedure
let entrypoint = entrypoint_by_attr
.or(entrypoint_by_name)
.ok_or(NoteError::NoteScriptNoProcedureWithAttribute)?;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed

Copy link
Copy Markdown
Contributor

@bobbinth bobbinth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Thank you! Once comments from @PhilippGackstatter are addressed, we should be good to merge.

.to_str()
.ok_or_else(|| Report::msg("failed to convert file name to &str"))?;
let mut masb_file_path = target_dir.join(masm_file_name);
let named_source = NamedSource::new(script_name.clone(), source_code);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can actually get rid of these individual note libraries entirely - but I'd leave this for a follow-up PR.

@Farukest Farukest force-pushed the feat/issue-2339-clean branch from f82adca to 7f43858 Compare January 30, 2026 20:22
Changes based on PR review:
- Replace hardcoded note script roots with procref in bridge_out.masm
  and agglayer_faucet.masm (BURN_NOTE_ROOT and P2ID_SCRIPT_ROOT)
- Remove main procedure fallback in NoteScript::from_library, require
  @note_script attribute
- Use pub use re-exports in note script wrappers to preserve @note_script
  attribute from the original procedures
- Improve error handling in build.rs (replace expect with proper errors)
- Use lowercase library paths (miden::standards::note_scripts::burn)
- Update miden-* dependencies to v0.20.3 for attribute serialization fix
@Farukest Farukest force-pushed the feat/issue-2339-clean branch from 7f43858 to f6d0881 Compare January 30, 2026 20:38
Farukest added a commit to Farukest/miden-base that referenced this pull request Jan 30, 2026
This removes the individual note script wrapper files (BURN, P2ID, P2IDE,
MINT, SWAP) and their separate compilation, as suggested in PR 0xMiden#2340.

Instead of compiling and loading each note script as a separate .masl file,
the note scripts are now loaded directly from the main standards library
using the new NoteScript::from_library_with_path method.

Changes:
- Add NoteScript::from_library_with_path method to extract a specific
  note script procedure from a library containing multiple note scripts
- Remove asm/note_scripts/ wrapper files
- Remove compile_note_scripts function from build.rs
- Update note Rust files (burn.rs, p2id.rs, p2ide.rs, mint.rs, swap.rs)
  to use StandardsLib with procedure path lookup
- Add NoteScriptProcedureNotFound and NoteScriptProcedureMissingAttribute
  error variants
Copy link
Copy Markdown
Contributor

@bobbinth bobbinth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Thank you! I left one comment inline, but I don't think we need to address it, and so, I'll merge the PR as is.

Comment on lines +133 to +145
fs::create_dir_all(&temp_dir).into_diagnostic()?;

// Copy the file to the temp directory with lowercase name
let temp_file = temp_dir.join(format!("{}.masm", script_name_lower));
fs::copy(&masm_file_path, &temp_file).into_diagnostic()?;

// write the binary MASB to the output dir
masb_file_path.set_extension("masb");
fs::write(masb_file_path, bytes).unwrap();
// Compile using assemble_library_from_dir with miden::standards::note_scripts namespace
let namespace = AsmPath::validate("miden::standards::note_scripts")
.expect("namespace path should be valid");
let script_library = assembler
.clone()
.assemble_library_from_dir(&temp_dir, namespace)
.wrap_err("failed to assemble note script library")?;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thinks we could have probably done this w/o creating a temporary directory, but given that this code will (hopefully) go away soon, it is fine to leave it as is.

@bobbinth bobbinth merged commit 8b4293b into 0xMiden:next Jan 31, 2026
15 checks passed
Farukest added a commit to Farukest/miden-base that referenced this pull request Jan 31, 2026
This removes the individual note script wrapper files (BURN, P2ID, P2IDE,
MINT, SWAP) and their separate compilation, as suggested in PR 0xMiden#2340.

Instead of compiling and loading each note script as a separate .masl file,
the note scripts are now loaded directly from the main standards library
using the new NoteScript::from_library_with_path method.

Changes:
- Add NoteScript::from_library_with_path method to extract a specific
  note script procedure from a library containing multiple note scripts
- Remove asm/note_scripts/ wrapper files
- Remove compile_note_scripts function from build.rs
- Update note Rust files (burn.rs, p2id.rs, p2ide.rs, mint.rs, swap.rs)
  to use StandardsLib with procedure path lookup
- Add NoteScriptProcedureNotFound and NoteScriptProcedureMissingAttribute
  error variants
PhilippGackstatter added a commit that referenced this pull request Feb 2, 2026
* refactor: convert note scripts from program to library format

This change implements issue #2339 - converting note scripts from program
format to library format with @note_script annotation support.

Changes:
- Add @note_script annotation to all standard note scripts (p2id, p2ide,
  swap, mint, burn) in asm/standards/notes/
- Add NoteScript::from_library constructor that finds the procedure with
  @note_script attribute (or falls back to 'main' procedure) and uses it
  as the entrypoint
- Update build.rs to compile note scripts as libraries (.masl) instead of
  programs (.masb) using assemble_library_from_dir with unique namespace
- Update well_known_note.rs to load NoteScript from Library
- Remove wrapper script files (asm/note_scripts/*.masm) that are no longer
  needed
- Add NoteError variants for missing/multiple @note_script procedures

Note: The @note_script attribute propagation from MASM to Library is not
yet fully supported in miden-assembly 0.20. The implementation falls back
to finding a procedure named 'main' when the attribute is not found.

* refactor: use pub use re-exports in note script wrappers

Address review feedback: instead of the temp directory workaround in
build.rs, use simple wrapper files in note_scripts/ that re-export
the main procedure from miden::standards::notes with pub use.

Changes:
- Add wrapper files (BURN.masm, P2ID.masm, P2IDE.masm, MINT.masm,
  SWAP.masm) with pub use re-exports
- Simplify compile_note_scripts in build.rs to use assemble_library
  directly without temp directory hack
- Remove unused ASM_STANDARDS_NOTES_DIR constant and AsmPath import

* refactor: address review feedback for note script library conversion

Changes based on PR review:
- Replace hardcoded note script roots with procref in bridge_out.masm
  and agglayer_faucet.masm (BURN_NOTE_ROOT and P2ID_SCRIPT_ROOT)
- Remove main procedure fallback in NoteScript::from_library, require
  @note_script attribute
- Update note script wrappers to use @note_script attribute with exec
  instead of pub use re-exports (MASM doesn't support attributes on
  pub use)
- Improve error handling in build.rs (replace expect with proper errors)
- Use lowercase library paths (miden::standards::note_scripts::burn)
- Update miden-* dependencies to v0.20.3 for attribute serialization fix

* refactor: remove individual note script libraries

This removes the individual note script wrapper files (BURN, P2ID, P2IDE,
MINT, SWAP) and their separate compilation, as suggested in PR #2340.

Instead of compiling and loading each note script as a separate .masl file,
the note scripts are now loaded directly from the main standards library
using the new NoteScript::from_library_with_path method.

Changes:
- Add NoteScript::from_library_with_path method to extract a specific
  note script procedure from a library containing multiple note scripts
- Remove asm/note_scripts/ wrapper files
- Remove compile_note_scripts function from build.rs
- Update note Rust files (burn.rs, p2id.rs, p2ide.rs, mint.rs, swap.rs)
  to use StandardsLib with procedure path lookup
- Add NoteScriptProcedureNotFound and NoteScriptProcedureMissingAttribute
  error variants

* refactor: use minimal MastForest with external node in from_library_with_path

Instead of copying the entire library's MastForest, create a minimal
forest containing only a single ExternalNode referencing the procedure's
digest. This significantly reduces memory usage as the actual procedure
code will be resolved at runtime via MastForestStore.

* refactor: rename from_library_with_path to from_library_reference

- Renamed NoteScript::from_library_with_path to from_library_reference
  to better indicate that the resulting note script contains only a
  reference to the procedure
- Updated docstring for create_external_node_forest helper function
- Updated all usages in miden-standards note modules

* fix: remove duplicate changelog entry

---------

Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com>
afa7789 pushed a commit to afa7789/miden-base that referenced this pull request Mar 9, 2026
afa7789 pushed a commit to afa7789/miden-base that referenced this pull request Mar 9, 2026
* refactor: convert note scripts from program to library format

This change implements issue 0xMiden#2339 - converting note scripts from program
format to library format with @note_script annotation support.

Changes:
- Add @note_script annotation to all standard note scripts (p2id, p2ide,
  swap, mint, burn) in asm/standards/notes/
- Add NoteScript::from_library constructor that finds the procedure with
  @note_script attribute (or falls back to 'main' procedure) and uses it
  as the entrypoint
- Update build.rs to compile note scripts as libraries (.masl) instead of
  programs (.masb) using assemble_library_from_dir with unique namespace
- Update well_known_note.rs to load NoteScript from Library
- Remove wrapper script files (asm/note_scripts/*.masm) that are no longer
  needed
- Add NoteError variants for missing/multiple @note_script procedures

Note: The @note_script attribute propagation from MASM to Library is not
yet fully supported in miden-assembly 0.20. The implementation falls back
to finding a procedure named 'main' when the attribute is not found.

* refactor: use pub use re-exports in note script wrappers

Address review feedback: instead of the temp directory workaround in
build.rs, use simple wrapper files in note_scripts/ that re-export
the main procedure from miden::standards::notes with pub use.

Changes:
- Add wrapper files (BURN.masm, P2ID.masm, P2IDE.masm, MINT.masm,
  SWAP.masm) with pub use re-exports
- Simplify compile_note_scripts in build.rs to use assemble_library
  directly without temp directory hack
- Remove unused ASM_STANDARDS_NOTES_DIR constant and AsmPath import

* refactor: address review feedback for note script library conversion

Changes based on PR review:
- Replace hardcoded note script roots with procref in bridge_out.masm
  and agglayer_faucet.masm (BURN_NOTE_ROOT and P2ID_SCRIPT_ROOT)
- Remove main procedure fallback in NoteScript::from_library, require
  @note_script attribute
- Update note script wrappers to use @note_script attribute with exec
  instead of pub use re-exports (MASM doesn't support attributes on
  pub use)
- Improve error handling in build.rs (replace expect with proper errors)
- Use lowercase library paths (miden::standards::note_scripts::burn)
- Update miden-* dependencies to v0.20.3 for attribute serialization fix

* refactor: remove individual note script libraries

This removes the individual note script wrapper files (BURN, P2ID, P2IDE,
MINT, SWAP) and their separate compilation, as suggested in PR 0xMiden#2340.

Instead of compiling and loading each note script as a separate .masl file,
the note scripts are now loaded directly from the main standards library
using the new NoteScript::from_library_with_path method.

Changes:
- Add NoteScript::from_library_with_path method to extract a specific
  note script procedure from a library containing multiple note scripts
- Remove asm/note_scripts/ wrapper files
- Remove compile_note_scripts function from build.rs
- Update note Rust files (burn.rs, p2id.rs, p2ide.rs, mint.rs, swap.rs)
  to use StandardsLib with procedure path lookup
- Add NoteScriptProcedureNotFound and NoteScriptProcedureMissingAttribute
  error variants

* refactor: use minimal MastForest with external node in from_library_with_path

Instead of copying the entire library's MastForest, create a minimal
forest containing only a single ExternalNode referencing the procedure's
digest. This significantly reduces memory usage as the actual procedure
code will be resolved at runtime via MastForestStore.

* refactor: rename from_library_with_path to from_library_reference

- Renamed NoteScript::from_library_with_path to from_library_reference
  to better indicate that the resulting note script contains only a
  reference to the procedure
- Updated docstring for create_external_node_forest helper function
- Updated all usages in miden-standards note modules

* fix: remove duplicate changelog entry

---------

Co-authored-by: Philipp Gackstatter <PhilippGackstatter@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Convert note scripts to libraries

4 participants