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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
- Added `create_fungible_key` for construction of fungible asset keys ([#2575](https://github.com/0xMiden/protocol/pull/2575)).
- Added `InputNoteCommitment::from_parts()` for construction of input note commitments from a nullifier and optional note header ([#2588](https://github.com/0xMiden/protocol/pull/2588)).
- Added `bool` schema type to the type registry and updated ACL auth component to use it for boolean config fields ([#2591](https://github.com/0xMiden/protocol/pull/2591)).
- Added `Package` support in `MockChainBuilder` & `NoteScript` ([#2502](https://github.com/0xMiden/protocol/pull/2502)).

### Changes

Expand Down
20 changes: 10 additions & 10 deletions Cargo.lock

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

25 changes: 25 additions & 0 deletions crates/miden-protocol/src/note/script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use core::fmt::Display;
use core::num::TryFromIntError;

use miden_core::mast::MastNodeExt;
use miden_mast_package::{MastArtifact, Package};

use super::Felt;
use crate::assembly::mast::{ExternalNodeBuilder, MastForest, MastForestContributor, MastNodeId};
Expand Down Expand Up @@ -139,6 +140,30 @@ impl NoteScript {
Ok(Self { mast: Arc::new(mast), entrypoint })
}

/// Creates an [`NoteScript`] from a [`Package`].
///
/// # Arguments
///
/// * `package` - The package containing the
/// [`Library`](miden_mast_package::MastArtifact::Executable).
///
/// # Errors
///
/// Returns an error if:
/// - The package does not contain an executable artifact
pub fn from_package(package: &Package) -> Result<Self, NoteError> {
let program = match &package.mast {
MastArtifact::Executable(executable) => executable.as_ref().clone(),
MastArtifact::Library(_) => {
return Err(NoteError::other(
"expected Package to contain an executable, but got a library",
));
},
};

Ok(NoteScript::from_parts(program.mast_forest().clone(), program.entrypoint()))
}

// PUBLIC ACCESSORS
// --------------------------------------------------------------------------------------------

Expand Down
100 changes: 69 additions & 31 deletions crates/miden-standards/src/testing/note.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ use miden_protocol::note::{
NoteAttachment,
NoteMetadata,
NoteRecipient,
NoteScript,
NoteStorage,
NoteTag,
NoteType,
};
use miden_protocol::testing::note::DEFAULT_NOTE_CODE;
use miden_protocol::vm::Package;
use miden_protocol::{Felt, Word};
use rand::Rng;

Expand All @@ -26,6 +28,15 @@ use crate::code_builder::CodeBuilder;
// NOTE BUILDER
// ================================================================================================

#[derive(Debug, Clone)]
enum SourceCodeOrigin {
Masm {
dyn_libraries: Vec<Library>,
source_manager: Arc<dyn SourceManagerSync>,
},
Package(Arc<Package>),
}

#[derive(Debug, Clone)]
pub struct NoteBuilder {
sender: AccountId,
Expand All @@ -36,8 +47,7 @@ pub struct NoteBuilder {
tag: NoteTag,
code: String,
attachment: NoteAttachment,
dyn_libraries: Vec<Library>,
source_manager: Arc<dyn SourceManagerSync>,
source_code: SourceCodeOrigin,
}

impl NoteBuilder {
Expand All @@ -59,8 +69,10 @@ impl NoteBuilder {
tag: NoteTag::with_account_target(sender),
code: DEFAULT_NOTE_CODE.to_string(),
attachment: NoteAttachment::default(),
dyn_libraries: Vec::new(),
source_manager: Arc::new(DefaultSourceManager::default()),
source_code: SourceCodeOrigin::Masm {
dyn_libraries: Vec::new(),
source_manager: Arc::new(DefaultSourceManager::default()),
},
}
}

Expand Down Expand Up @@ -112,42 +124,68 @@ impl NoteBuilder {
/// build-time.
pub fn dynamically_linked_libraries(
mut self,
dyn_libraries: impl IntoIterator<Item = Library>,
dyn_libs: impl IntoIterator<Item = Library>,
) -> Self {
self.dyn_libraries.extend(dyn_libraries);
match &mut self.source_code {
SourceCodeOrigin::Masm { dyn_libraries, .. } => {
dyn_libraries.extend(dyn_libs);
},
SourceCodeOrigin::Package(_) => {
panic!("dynamic libraries cannot be set on a package")
},
}
self
}

pub fn source_manager(mut self, source_manager: Arc<dyn SourceManagerSync>) -> Self {
self.source_manager = source_manager;
pub fn source_manager(mut self, sm: Arc<dyn SourceManagerSync>) -> Self {
match &mut self.source_code {
SourceCodeOrigin::Masm { source_manager, .. } => {
*source_manager = sm;
},
SourceCodeOrigin::Package(_) => {
panic!("source manager cannot be set on a package")
},
}
self
}

/// Sets the source code origin to a package.
pub fn package(mut self, package: Package) -> Self {
self.source_code = SourceCodeOrigin::Package(Arc::new(package));
self
}

pub fn build(self) -> Result<Note, NoteError> {
// Generate a unique file name from the note's serial number, which should be unique per
// note. Only includes two elements in the file name which should be enough for the
// uniqueness in the testing context and does not result in overly long file names which do
// not render well in all situations.
let virtual_source_file = self.source_manager.load(
SourceLanguage::Masm,
Uri::new(format!(
"note_{:x}{:x}",
self.serial_num[0].as_canonical_u64(),
self.serial_num[1].as_canonical_u64()
)),
self.code,
);

let mut builder = CodeBuilder::with_source_manager(self.source_manager.clone());
for dyn_library in self.dyn_libraries {
builder
.link_dynamic_library(&dyn_library)
.expect("library should link successfully");
}
let note_script = match self.source_code {
SourceCodeOrigin::Masm { dyn_libraries, source_manager } => {
// Generate a unique file name from the note's serial number, which should be
// unique per note. Only includes two elements in the file name which should be
// enough for the uniqueness in the testing context and does not result in overly
// long file names which do not render well in all situations.
let virtual_source_file = source_manager.load(
SourceLanguage::Masm,
Uri::new(format!(
"note_{:x}{:x}",
self.serial_num[0].as_canonical_u64(),
self.serial_num[1].as_canonical_u64()
)),
self.code,
);

let mut builder = CodeBuilder::with_source_manager(source_manager.clone());
for dyn_library in dyn_libraries {
builder
.link_dynamic_library(&dyn_library)
.expect("library should link successfully");
}

builder
.compile_note_script(virtual_source_file)
.expect("note script should compile")
},
SourceCodeOrigin::Package(package) => NoteScript::from_package(&package)?,
};

let note_script = builder
.compile_note_script(virtual_source_file)
.expect("note script should compile");
let vault = NoteAssets::new(self.assets)?;
let metadata = NoteMetadata::new(self.sender, self.note_type)
.with_tag(self.tag)
Expand Down
15 changes: 15 additions & 0 deletions crates/miden-testing/src/mock_chain/chain_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use miden_protocol::account::delta::AccountUpdateDetails;
use miden_protocol::account::{
Account,
AccountBuilder,
AccountComponent,
AccountDelta,
AccountId,
AccountStorageMode,
Expand Down Expand Up @@ -484,6 +485,20 @@ impl MockChainBuilder {

Ok(account)
}
pub fn add_existing_account_from_components(
&mut self,
auth: Auth,
components: impl IntoIterator<Item = AccountComponent>,
) -> anyhow::Result<Account> {
let mut account_builder =
Account::builder(rand::rng().random()).storage_mode(AccountStorageMode::Public);

for component in components {
account_builder = account_builder.with_component(component);
}

self.add_account_from_builder(auth, account_builder, AccountState::Exists)
}

/// Adds the provided account to the list of genesis accounts.
///
Expand Down
Loading